From 9941e352bcbe370fedbfdd5949ecd6e7cc804df7 Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Wed, 17 Jun 2026 14:00:45 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20Docs:=20S69=20session-end=20closeout?= =?UTF-8?q?=20=E2=80=94=20V=C4=83n=20ph=C3=B2ng=20s=E1=BB=91=20golive=20+?= =?UTF-8?q?=20PE=20c=E1=BB=9D=20g=E1=BA=A5p/ng=C6=B0=E1=BB=A1ng=20(Run=20#?= =?UTF-8?q?305-308)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flush docs sau buổi sản phẩm cực lớn (4 deploy prod-verified, 2 workflow fan-out). - STATUS.md + HANDOFF.md: S69 header + Recently Done + In-Progress + count (Mig 52→53 · test 286→306 · menu 53→54 · gotcha 66→68 · bundle BgNCjwsG/CBvh0vtf Run #308) + Phase (Văn phòng số golive + PE cờ gấp/ngưỡng). - gotchas.md: +2 (#67 Tailwind accent palette thiếu-stop vỡ-màu-im-lặng · #68 stale-diagnostic-background-agent → chỉ tin build sau-cùng). - session log 2026-06-17-S69-vanphong-golive-pe-urgent-threshold.md. - CLAUDE.md (root): Mig 53 · PE module +Mig 53 · test 306 · gotcha 68. - ef-core-migration SKILL: +Mig 53 row AddPeUrgentAndCeoApprovalThreshold + count 52→53 (H1 flush). - harvest: cicd-monitor MEMORY #308 (H2 orphan commit). H2 GATE PASS · H1 0 new-alloc. curate-debt P1 next session: cicd-monitor 65.2KB worst. C (chuyển phiếu→dự án) chờ anh Kiệt spec. Co-Authored-By: Claude Opus 4.8 --- .claude/agent-memory/cicd-monitor/MEMORY.md | 6 ++- .claude/skills/ef-core-migration/SKILL.md | 7 +-- CLAUDE.md | 8 +-- docs/HANDOFF.md | 2 +- docs/STATUS.md | 24 ++++++--- ...S69-vanphong-golive-pe-urgent-threshold.md | 52 +++++++++++++++++++ docs/gotchas.md | 28 ++++++++++ 7 files changed, 109 insertions(+), 18 deletions(-) create mode 100644 docs/changelog/sessions/2026-06-17-S69-vanphong-golive-pe-urgent-threshold.md diff --git a/.claude/agent-memory/cicd-monitor/MEMORY.md b/.claude/agent-memory/cicd-monitor/MEMORY.md index afea072..8d6036b 100644 --- a/.claude/agent-memory/cicd-monitor/MEMORY.md +++ b/.claude/agent-memory/cicd-monitor/MEMORY.md @@ -48,9 +48,9 @@ Read-only CI/CD + post-deploy verifier SOLUTION_ERP. Polls Gitea Actions API, ve - **Prod:** api/admin/eoffice `.solutions.com.vn` · SSH `ssh vietreport-vps` (Administrator, id_ed25519) · IIS site phys paths (S42 verified): API `C:\inetpub\solution-erp\api` · admin `\fe-admin` · user `\fe-user` (3 sites Started). DB `.\SQLEXPRESS`/`SolutionErp`/`vrapp` SQL-auth. **Conn string key = `ConnectionStrings.Default` (NOT `DefaultConnection`!)** — read pw from prod appsettings.Production.json when `$env:PROD_DB_PASSWORD` empty. - **SSH→PS quoting (S42 lesson):** nested bash→ssh→powershell mangles `$var`/`\"`. Use `iconv UTF-16LE | base64` → `powershell -EncodedCommand $B64`. Single-quote literal paths. - **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 52 `20260616035929_AddHoSoLinkToPurchaseEvaluation`** (S65; PE +HoSoLink hyperlink-NAS, AddColumn-only no new table). Prev Mig 51 `AddDepartmentParentId` (S65 Department.ParentId loose-Guid org-tree, AddColumn-only) + Mig 50 `ReplaceBudgetModuleWithPeWorkItemBudgets` (S61 Budget→PeWorkItemBudgets net-reduce). Path `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/` (52 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. +- **Mig latest repo:** **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 S77:** admin `D532XZKG` (Run #303 sha 6983609 — ROTATED from `CcrZqfht`; EmployeesListPage employee-detail banner text-polish) · user `CuFaBoWt` (Run #303 sha 6983609 — ROTATED from `DniDFUB_`; same page both apps SHA256-identical `F013B748`). Prev live admin `CcrZqfht`/user `DniDFUB_` (Run #302 S76). ⚠️ ASYMMETRIC-deploy lesson (S66): FE-one-app commit → that app's bundle MUST rotate + OTHER app MUST stay frozen; admin-rotate-when-only-fe-user-changed = anomaly → flag. S50 mid-deploy transient lesson: pre-success snapshot can show intermediate FE copy in-flight — re-confirm hash AFTER status=success ALWAYS (anti-pattern #3). FROZEN-expectation runs (BE-only or other-app): hash MUST stay = live pre-deploy value; rotate w/o relevant FE change = anomaly. +- **Bundle hash live S71:** admin `BgNCjwsG` (Run #308 sha ebd7e1c — ROTATED from `Wt54PHYl`; PE cờ-gấp UI list/detail + designer ngưỡng) · user `CBvh0vtf` (Run #308 sha ebd7e1c — ROTATED from `B99fMU6X`; PE list/detail). BOTH rotate (FE-both-app). Prev live admin `Wt54PHYl`/user `B99fMU6X` (Run #306/#307 S70). [Older S77 #303: admin `D532XZKG`/user `CuFaBoWt`]. ⚠️ ASYMMETRIC-deploy lesson (S66): FE-one-app commit → that app's bundle MUST rotate + OTHER app MUST stay frozen; admin-rotate-when-only-fe-user-changed = anomaly → flag. S50 mid-deploy transient lesson: pre-success snapshot can show intermediate FE copy in-flight — re-confirm hash AFTER status=success ALWAYS (anti-pattern #3). FROZEN-expectation runs (BE-only or other-app): hash MUST stay = live pre-deploy value; rotate w/o relevant FE change = anomaly. - **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) @@ -68,6 +68,8 @@ BE (test+build) ~90s · FE × 2 ~60s/app · deploy ~30s · **total ~3min code / ## 📅 Recent runs (FIFO — older → archive/git) +- **2026-06-17 S71 Run #308 (id422) sha=`ebd7e1c` PASS ~4m41s (FULL-STACK: BE Mig 53 + ApproveV2 CCM-finalize-by-threshold + endpoint PUT /urgent + FE 2-app PE cờ-gấp UI + designer ngưỡng. PE "cờ gấp PRO/CCM + CCM duyệt-final theo ngưỡng giá trị" anh Kiệt FDC. 27 files: BE 9 {Controller +SSetUrgent, App PurchaseEvaluationUrgentFeatures.cs NEW, PurchaseEvaluationFeatures, CreateContractFromEvaluation, AwV2AdminFeatures, Dtos, Domain PE+AW, Config} + 3 Mig-file + Service PurchaseEvaluationWorkflowService + FE 6 {PeDetailTabs+ListPage+types ×2-app + ApprovalWorkflowsV2Page admin-only} + 2 test {PeUrgentToggleAuthzTests, PeCcmThresholdFinalizeTests} + 3 agent-memory .md. deploy 12/12 session after #297–#307 all PASS):** Push parent `1f8947e..ebd7e1c` (1 commit). `git diff --name-only 1f8947e ebd7e1c -- '*Persistence/Migrations*'` = 3 files (Mig 53 .cs/.Designer.cs/ModelSnapshot.cs) — REAL EF migration this time (contrast S69b/S70 which had ZERO). `.cs/.tsx` non-ignored → full pipeline RAN. GITEA_TOKEN present (PS-scope) → authed Gitea API; PROD_DB_PW empty → DB pw từ prod `appsettings.Production.json`→`ConnectionStrings.Default` (`vrapp`/`buKL3TGBkD0wDDbYVw65QeX9`, path `C:\inetpub\solution-erp\api`). Run IN-PROGRESS at spawn (running 13:27:58, 1st-seen updated 13:28:54) — pre-deploy NOT snapshotted as baseline (anti#3: prev-live = #306/#307 spec admin `Wt54PHYl`/user `B99fMU6X`). Poll-loop iter6 status=success (created 13:27:58 → success-update 13:32:39 ≈4m41s). CI gate (both proj pre-deploy ⟹ status=success ⟹ test **306** baseline (45D+261I; +14 PeUrgentToggleAuthz+PeCcmThresholdFinalize) passed; `conclusion` empty — `tasks` terminal=`status:success` không populate conclusion, trust success; 306 INFERRED gate-passes-pre-build invariant NOT log-confirmed numeric). **★ BUNDLE BOTH ROTATE (FE-both-app PE-UI ⟹ both MUST rotate; verified AFTER status=success + re-confirm STABLE 2nd-fetch identical no-transient — anti#3): admin ROTATE `Wt54PHYl→BgNCjwsG`** ✓ **+ user ROTATE `B99fMU6X→CBvh0vtf`** ✓. Asset reachable 200: admin js 1,592,331b + user js 1,496,984b (not white). Smoke **4×200** health: api `/health/ready`+`/health/live` + admin root + eoffice root. **★ ENDPOINT /urgent PROBE: `PUT /api/purchase-evaluations/{guid}/urgent` unauth → 401 (NOT 404)** ✓ — route EXISTS + class-level `[Authorize]` any-auth enforced (controller `[HttpPut("{id:guid}/urgent")] SetUrgent → SetPurchaseEvaluationUrgentCommand`; behavioral PRO/CCM-only authz = unit-test PeUrgentToggleAuthzTests, not probed). PE list unauth → 401 ✓ (reachable, gated). **🔑★ MIG 53 VERIFIED APPLIED PROD (sqlcmd over SSH, ground-truth not inference): prod `__EFMigrationsHistory` top5 = `20260617060207_AddPeUrgentAndCeoApprovalThreshold`(53)→`AddHoSoLinkToPurchaseEvaluation`(52)→`AddDepartmentParentId`(51)→`ReplaceBudgetModuleWithPeWorkItemBudgets`(50)→`AddWorkItemToPurchaseEvaluation`(49) == repo HEAD ✓ (DbInitializer auto-migrate-on-boot ran; app pool recycled w/ new binary). 3 NEW COLUMNS confirmed via sys.columns: `PurchaseEvaluations.IsUrgentByCcm`=bit is_nullable=0 ✓ + `PurchaseEvaluations.IsUrgentByPro`=bit is_nullable=0 ✓ + `ApprovalWorkflows.CeoApprovalThreshold`=decimal is_nullable=1 precision=18 scale=2 ✓ — ALL match Mig 53 Up() spec exactly. sys.tables=88 (excl mighist) UNCHANGED — 3 AddColumn no new table ✓.** 0 regression. NO prod-data mutation (read-only curls + sqlcmd SELECT-only; migration-apply = expected boot-side-effect of deploy). Behavioral CCM-finalize-by-threshold + cờ-gấp UI render = anh Kiệt UAT (em confirm Mig applied + columns + endpoint route + bundles shipped per spec). **LESSON: full-stack PE commit w/ REAL Mig = verify ALL 4 axes: (1) BE+FE both-bundle ROTATE (FE 2-app), (2) Mig history-top advance + 3 columns sys.columns ground-truth (NOT just "schema looks right" — query each col type/nullable/precision vs Up() spec), (3) sys.tables stays 88 (AddColumn-only ⟹ no table delta; rotate-to-89 would = unexpected CREATE TABLE leak), (4) new endpoint 401-not-404 unauth probe (route-exists proof; authz-logic = unit-test domain not curl). Mig-applied-on-prod proof = `__EFMigrationsHistory` top == repo-HEAD (DbInitializer MigrateAsync boot-hook) — if top stuck at Mig 52 ⟹ app pool didn't recycle / migrate-on-boot failed ⟹ FAIL even if status=success+bundle-rotated (deploy shipped binary but DB-side incomplete). Distinguishes this run from S69b/S70 (zero-migration BE/FE — those had NO history-advance EXPECTED). TOOLING: Bash=POSIX → write PS to `$LOCALAPPDATA/Temp/x.ps1` + `powershell.exe -NoProfile -File "C:\Users\pqhuy\AppData\Local\Temp\x.ps1"` (⚠️ bash→Windows path auto-convert mangles `/tmp/` → use `$LOCALAPPDATA/Temp` explicit Windows path); Invoke-RestMethod for Gitea `tasks` (match `head_sha -eq sha`, poll-loop in PS not bash — bash jq absent/python3 broken); SSH→PS base64 `-EncodedCommand` (UTF-16LE Unicode) for BOTH appsettings-read AND sqlcmd; sqlcmd pw-literal via PS here-string `$P=` var inside encoded payload + query-string built w/ `[char]39` for embedded single-quotes (object-name literals); `-W -h -1` no-header + `-s '|'` pipe-delim; read DB pw from prod appsettings when `$PROD_DB_PASSWORD` empty. NEVER fixed code (READ-only).** Tag `[s71, run308, pass, full-stack-pe-urgent-ccm-threshold, mig53-AddPeUrgentAndCeoApprovalThreshold-VERIFIED-APPLIED-PROD, 3-cols-sys-columns-confirmed-IsUrgentByPro-IsUrgentByCcm-bit0-CeoApprovalThreshold-decimal18-2-null, history-top-advance-53, tables88-unchanged-addcolumn-only, bundle-BOTH-rotate-BgNCjwsG-CBvh0vtf, asset-200-reachable, endpoint-urgent-401-not-404-route-exists, pe-list-401-gated, health-4x200, test306-inferred, deploy12of12, no-regression, behavioral-ccm-finalize-anh-kiet-uat]`. + - **2026-06-17 S69b Run #307 (run_number 307, id421) sha=`1f8947e` PASS ~4m33s (BE-ONLY GOLIVE security hướng-ra-prod "Văn phòng số" public Read+Create mọi role — NEW `SeedAllRolesOfficeModulePermissionsAsync` DbInitializer.cs chạy lúc API boot SAU `RevokeTemporarilyHiddenModulesAsync` để THẮNG revoke (mirror S65 HRM/Hồ-sơ-NS pattern), allow-list 16 Office key grant CanRead+CanCreate=true upgrade-only (row tồn tại→nâng, chưa có→tạo R+C=true U/D=false, KHÔNG hạ KHÔNG đụng Update/Delete); +6 test `OfficeModulePermissionSeedTests.cs` (286→292). KHÔNG FE KHÔNG migration. deploy 11/11 session after #297–#306 all PASS):** Push `c556f6c..1f8947e` (1 commit, 5 files: DbInitializer.cs + 1 test + 3 agent-memory `.md` cicd/reviewer/test-spec). Diff `git diff --name-only c556f6c 1f8947e -- '*Migrations*' '*Persistence/Migrations*'` = EMPTY ✓ (DbInitializer.cs ở `Persistence/` NOT `Persistence/Migrations/` — seed runtime-idempotent NOT EF-migration). `.cs` non-ignored → full pipeline RAN. GITEA_TOKEN+PROD_DB_PW env empty trong bash-scope (env:GITEA_TOKEN có ở PS-scope; PROD_DB_PW thật rỗng) → anon Gitea API + DB pw từ prod `appsettings.Production.json`→`ConnectionStrings.Default` (path `C:\inetpub\solution-erp\api`, uid `vrapp` len24). Run IN-PROGRESS at spawn (running 10:33:40→10:38) — pre-deploy bundle baseline captured BEFORE poll-loop (anti#3): admin `Wt54PHYl` + user `B99fMU6X` — both == S70 #306 spec baseline (still live, deploy not yet shipped). Polled iter5 status=success (created 10:33:40 → success-update 10:38:13 ≈4m33s). CI gate (both proj pre-deploy ⟹ status=success ⟹ test **292** baseline (45D+247I; +6 OfficeModulePermissionSeedTests) passed; `conclusion` empty — `tasks` endpoint terminal=`status:success` không populate `conclusion`, trust success; 292 INFERRED gate-passes-pre-build invariant NOT log-confirmed numeric). **★ BUNDLE BOTH FROZEN (BE-only ⟹ both MUST stay = pre-deploy live; verified AFTER status=success): admin `Wt54PHYl` UNCHANGED** ✓ **+ user `B99fMU6X` UNCHANGED** ✓ (no FE touch → no content-hash rotation; rotate-w/o-FE-change = anomaly, did NOT happen). Smoke **4×200**: api `/health/ready`+`/health/live` + admin root + eoffice root. **NO migration** — prod `__EFMigrationsHistory` top5 = `AddHoSoLinkToPurchaseEvaluation`(Mig52)→`AddDepartmentParentId`(51)→`ReplaceBudgetModuleWithPeWorkItemBudgets`(50)→`AddWorkItemToPurchaseEvaluation`(49)→`AddProjectMasterFields`(48) == repo HEAD GIỮ NGUYÊN ✓. **sys.tables=88** (sqlcmd COUNT excl mighist — unchanged, seed inserts/updates ROWS not tables). **🔑★ GOLIVE DB VERIFIED THỰC SỰ ÁP PROD (seed chạy lúc boot ⟹ proof = prod Permissions, NOT chỉ binary-shipped): sqlcmd `Roles` (13 total: Accounting/Admin/AuthorizedSigner/CatalogManager/CostControl/DeptManager/Director/Drafter/Equipment/Finance/HrAdmin/Procurement/ProjectManager). (1) ALLOW-LIST 16/16 R=1∧C=1 cho Drafter (non-admin demo-role) — Off,Off_Dashboard,Off_DanhBa,Off_PhongHop(+View+Book),Off_DeXuat(+List+Create+Inbox),Off_DonTu(+Leave+Ot+Travel),Off_DatXe,Off_ItTicket ✓. (2) CROSS-ROLE: MỌI 13 role count=16 R∧C (golive ÁP TOÀN BỘ không Drafter-fluke) ✓. (3) EXCLUDED-3 (Off_PhongHop_Manage/Off_AttendanceReport/Off_ChamCong) Drafter R=0∧C=0 ✓ + LEAK-check: CHỈ Admin có R=1 trên 3 key này (0 non-admin leak) ✓. (4) HRM/Personal Drafter KHÔNG mở: Hrm_Dashboard=0, all 7 Hrm_Config*=0, Personal=0 ✓; Hrm=1 + Hrm_HoSo=1 (S65 public-read GIỮ NGUYÊN không đổi) ✓. (5) Admin KHÔNG hạ: full Office incl 3 excluded all R=1∧C=1 ✓.** ⟹ boot-time seed CHẠY THẬT trên prod (app pool recycled w/ new binary post-deploy; nếu seed CHƯA chạy thì non-admin sẽ vẫn 0 — nhưng 16/16 across 13 roles = đã chạy). Menu-tree non-admin via API NOT tested (DB-query mục 4 đủ proof per spec). 0 regression. NO prod-data mutation ngoài chính golive seed (read-only curls + sqlcmd SELECT-only; seed-write là chủ đích của deploy). Visual menu-render = anh UAT. **LESSON: BE-only GOLIVE security-seed verify = both bundles MUST stay FROZEN (no FE → no rotate; rotate=anomaly) + Mig-top + sys.tables MUST stay prev (DbInitializer seed = runtime row insert/update, NOT EF-migration → KHÔNG advance __EFMigrationsHistory NOR sys.tables) + **CORE proof = prod Permissions DB query**, NOT bundle-frozen alone (frozen chỉ chứng minh FE không đổi, KHÔNG chứng minh API binary mới deploy + seed chạy — phải query Permissions cho non-admin role thấy allow-list grant + cross-role để loại fluke + leak-check excluded chỉ-Admin). upgrade-only seed THẮNG revoke vì chạy SAU trong SeedAsync. golive-state AMBIENT-after-this-deploy (parent commit c556f6c chưa có method này → pre-1f8947e non-admin Office = 0; bằng chứng "đã chạy" = 16/16). TOOLING (re-confirmed S70): Bash=POSIX (`$var`/`$env:` mangle through bash→ssh→PS layers) → write PS to `/tmp/x.ps1` + `powershell.exe -NoProfile -File "C:/.../Temp/x.ps1"` (⚠️ heredoc em-dash/non-ASCII corrupts → ASCII-only in PS source); jq ABSENT + system python3 broken → Invoke-RestMethod cho Gitea `tasks` (match `head_sha -eq sha`, `?limit=N` URI-direct); SSH→PS base64 `-EncodedCommand` (UTF-16LE `[Text.Encoding]::Unicode`) cho BOTH appsettings-read AND sqlcmd; sqlcmd pw-literal qua single-quote in B64-payload (no `$` survives clean), `-U/-P` direct + `-W -s '|'` pipe-delim + `-h -1` no-header; read DB pw from prod appsettings when `$PROD_DB_PASSWORD` empty. NEVER fixed code (READ-only).** Tag `[s69b, run307, pass, be-only-golive-vanphongso-public-read-create-allowlist16, SeedAllRolesOfficeModulePermissionsAsync-boot-after-revoke, bundle-BOTH-frozen-Wt54PHYl-B99fMU6X, no-mig-top-stays-mig52, tables88, GOLIVE-DB-VERIFIED-16of16-across-13-roles, excluded3-canread0-only-admin-leak0, hrm-personal-still-0, hrm-hoso-public-unchanged, admin-not-downgraded, test292-inferred, deploy11of11, seed-not-migration-no-history-advance, no-regression]`. - **2026-06-17 S70 Run #306 (run_number 306, id420) sha=`c556f6c` PASS ~4m42s (FE-ONLY re-skin "Văn phòng số" TOÀN MODULE 10 page PURO layout — PageHeader/KpiCard/WidgetCard apply + CSS Hồ sơ NS; presentation-only "phẫu thuật giữ nguyên logic", byte-identical logic. 20 .tsx: fe-admin 11 {AttendanceReport,InternalDirectory,ItTickets,MeetingCalendar,MeetingRooms,ProposalCreate,ProposalDetail,ProposalsList,WorkflowAppDetail,WorkflowAppsList}Page + fe-user 9 mirror (no AttendanceReport — admin-only) + 3 agent-memory .md (cicd/fe-designer/reviewer). NO BE/NO migration/NO new menu key. deploy 10/10 session after #297–#305 all PASS):** Push `a8bbdae..c556f6c` (1 commit, 23 files). `git diff --name-only a8bbdae c556f6c -- '*Migrations*' '*Persistence*'` = EMPTY ✓. `.tsx` non-ignored → full pipeline RAN (3 `.md` agent-memory match paths-ignore but co-mixed w/ 20 tsx → Discovery#3 range any-non-ignored ⟹ whole build). GITEA_TOKEN+PROD_DB_PW empty → anon Gitea API + DB pw từ prod `appsettings.Production.json`→`ConnectionStrings.Default` (path `C:\inetpub\solution-erp\api`, uid `vrapp` len24). Run IN-PROGRESS at spawn (status=running 09:57:54→09:58:40) — pre-deploy baseline captured BEFORE poll-loop: admin `Bl2o_kUq` (S69 #305 live) + user `BImrKQNn` (S69 #305 live) — both == spec baseline (still live, deploy not yet shipped, anti#3 honored no mid-flight verify). Polled status=success (started 09:57:54 → upd 10:02:36 ≈4m42s). CI gate (both proj pre-deploy ⟹ status=success ⟹ test **286** baseline (45D+241I; FE-only ⟹ 0 BE call-site risk) passed; `conclusion` empty — `tasks` endpoint terminal=`status:success` không populate `conclusion`, trust success; 286 INFERRED gate-passes-pre-build invariant NOT log-confirmed). **★ BUNDLE BOTH ROTATE (FE-both-app 10-page re-skin ⟹ both MUST rotate; verified AFTER status=success + re-confirm STABLE 2nd-fetch identical no-transient — anti#3): admin ROTATE `Bl2o_kUq→Wt54PHYl` (css `CKzdDktL→BpHtX3vS`)** ✓ **+ user ROTATE `BImrKQNn→B99fMU6X` (css `eoxUcs8v→DXRSCQW7`)** ✓. BOTH required → BOTH did. Asset reachable 200: admin js 1,588,241b + user js 1,493,955b (not white). ⚠️ Prod CI hashes ≠ spec local-build (fe-user `C8-p69Kn`/`DezuRkK9`, fe-admin `yFhLO2Wp`/`Dd2WiO6n`) — EXPECTED CI-rebuild content-hash divergence (only matters NEW≠baseline, BOTH rotated confirmed). Smoke **4×200** health: api `/health/ready`+`/health/live` + admin root + eoffice root. **Office-backed API live (admin bearer): GET /api/proposals 200 · /api/it-tickets 200 · /api/meeting-rooms 200 · /api/employees 200** (data layer healthy, not 500). `/api/workflow-apps`→404 = WRONG-route-guess NOT regression (FE-only can't change BE routing; real route differs — leave-requests/don-tu; FE uses api-client wrapper, route grep didn't land). **SPA deep-link 6 Office routes admin all 200** (/proposals, /it-tickets, /workflow-apps/leave, /directory, /meeting-calendar, /office/dashboard serve index.html — not 404/white). **NO migration** — prod `__EFMigrationsHistory` top = `20260616035929_AddHoSoLinkToPurchaseEvaluation` (Mig 52) == repo HEAD GIỮ NGUYÊN ✓ (prev-2 = AddDepartmentParentId Mig51 + ReplaceBudgetModuleWithPeWorkItemBudgets Mig50 chain intact). **sys.tables=88 verified** (sqlcmd COUNT excl mighist — unchanged). **★ OFFICE-HIDDEN CONFIRMED (non-admin Drafter=nv.test): sqlcmd `Off_Dashboard` perm row ONLY for Admin (1/13 roles) ✓ + Drafter has ALL 17 Off_* keys present but EVERY one CanRead=0 ✓ → menu gates on CanRead=1 ⟹ non-admin sees NO Office menu.** ⚠️ **LESSON-CORRECTION vs S69: my coarse `GROUP BY COUNT(*) WHERE MenuKey LIKE 'Off%'` first showed "all 13 roles ~18 Off perms" — MISLEADING; those are perm-ROWS-with-CanRead=0 (existence ≠ access). Must filter `CanRead=1` OR inspect per-key. MenuItems.IsVisible=1 on all Off keys = admin-side global-visible flag; user-visibility = IsVisible AND role.CanRead. Office-hidden state is AMBIENT (FE-only commit cannot touch Permissions/MenuItems seed — git diff zero BE/DbInitializer) = identical pre-c556f6c.** 0 regression. NO prod-data mutation (read-only curls + sqlcmd SELECT-only). Visual per-page render = anh user UAT (em chỉ confirm bundle shipped + route không 500/trắng per spec). **TOOLING (re-confirmed + NEW): Bash tool = POSIX bash → ⚠️ system `python3` BROKEN (ZKBioTime Python311 on PATH → `SRE module mismatch` fatal) AND `jq` NOT installed → MUST use `powershell.exe -NoProfile -File "C:/.../tmp/x.ps1"` + `Invoke-RestMethod` for ALL JSON parse (Gitea tasks match `head_sha -eq sha`); poll-loop in pure-bash w/ jq SILENTLY returns empty (no error) — verify parser works before trusting loop. SSH→PS base64 `-EncodedCommand` (UTF-16LE) for appsettings-read AND sqlcmd; sqlcmd string-LITERAL via `[char]39` concat (NOT doubled-quote); `-U/-P` direct (no -ConnectionString flag); `Roles`/`Permissions(RoleId,MenuKey,CanRead)`/`MenuItems([Key],IsVisible)`. NEVER fixed code (READ-only).** Tag `[s70, run306, pass, fe-only-vanphongso-reskin-10page-PURO, fe-both-app-bundle-BOTH-rotate-Wt54PHYl-B99fMU6X, asset-200-reachable, office-api-200-proposals-ittickets-meetingrooms-employees, workflow-apps-404-wrong-route-not-regression, spa-deeplink-6route-200, no-mig-top-stays-mig52, tables88-verified, office-hidden-confirmed-Off_Dashboard-admin-only-drafter-canread0, lesson-canread0-rows-not-access, ambient-not-deploy-caused, jq-absent-python3-broken-use-powershell, deploy10of10, no-regression, test286-inferred]`. - **2026-06-17 S69 Run #305 (run_number 305, id419) sha=`a8bbdae` PASS ~4-5m (FE BOTH-APP foundation "Văn phòng số" + index.css sync + BE menu-seed NO-mig: 3 shared component mới PageHeader/KpiCard/WidgetCard + OfficeDashboardPage landing route `/office/dashboard` 4-place-wire BOTH apps + `fe-admin/src/index.css` SYNCED (Hồ sơ NS accent tokens, rotate admin mạnh) + BE menu key `Off_Dashboard` (MenuKeys.cs L100 + DbInitializer.cs L1825 seed parent=`Off` Order0 LayoutDashboard); deploy 9/9 session after #297–#304 all PASS):** Push `764fe70..a8bbdae` (1 commit, 20 files). Diff: FE 14 files (2× {PageHeader,KpiCard,WidgetCard,OfficeDashboardPage,App.tsx,Layout.tsx,menuKeys.ts} + fe-admin index.css) + BE 2 (MenuKeys.cs + DbInitializer.cs) + 4 agent-memory `.md`. `.tsx`/`.cs`/`.css` non-ignored → full pipeline RAN (the 4 `**/*.md` agent-memory match paths-ignore but co-mixed w/ code → Discovery#3 range any-non-ignored ⟹ whole build). GITEA_TOKEN+PROD_DB_PW empty → anon Gitea API + DB pw từ prod `appsettings.Production.json`→`ConnectionStrings.Default` (path `C:\inetpub\solution-erp\api`, uid `vrapp`). Run IN-PROGRESS first 4 polls (running 09:26→09:29) — correctly did NOT verify-bundle-mid-flight (anti#3); pre-deploy baseline captured BEFORE poll-loop: admin `CNUv1jxY` (S78 #304 live) + user `CpOskeS1` (S78 #304 live) — both == spec S68 baseline (still live, deploy not yet shipped). Polled iter5 status=success (started ~09:25 → success 09:29:50 ≈4-5m). CI gate (both proj pre-deploy ⟹ status=success ⟹ test gate **286** baseline (45D+241I) passed; `conclusion` empty — `tasks` endpoint terminal=`status:success` doesn't populate `conclusion`, trust success; 286 INFERRED from gate-passes-pre-build invariant NOT log-confirmed numerically). **★ BUNDLE BOTH ROTATE (FE-both-app + index.css sync ⟹ both MUST rotate; verified AFTER status=success +re-confirm STABLE 2nd-fetch identical no-transient — anti#3): admin ROTATE `CNUv1jxY→Bl2o_kUq`** ✓ (Văn phòng số + index.css shipped) **+ user ROTATE `CpOskeS1→BImrKQNn`** ✓ (foundation shipped). BOTH required → BOTH did. ⚠️ **Prod CI hashes (`Bl2o_kUq`/`BImrKQNn`) ≠ spec local-build (`TbkadgKd`/`DrxDysO7`) — EXPECTED divergence (CI rebuilds w/ different Node/npm/dep-resolution → different content-hash; only matters NEW≠baseline, BOTH rotated confirmed ship). Spec assumption "prod khớp local nếu source-identical" holds ONLY if byte-identical build-env — it is NOT (S77 same lesson).** Smoke **4×200**: api `/health/ready`+`/health/live` + admin root + eoffice root. **NO migration** — prod `__EFMigrationsHistory` top = `20260616035929_AddHoSoLinkToPurchaseEvaluation` (Mig 52) == repo HEAD GIỮ NGUYÊN ✓ (`git diff --name-only 764fe70 a8bbdae -- '*Migrations*'` = EMPTY; menu-seed is runtime-idempotent DbInitializer NOT EF-migration → top did NOT advance). **sys.tables=88 verified** (sqlcmd COUNT excl mighist — unchanged, menu-seed inserts rows not tables). **★ MENU-SEED VERIFIED (NEW check this run — DbInitializer seed ungated reaches prod by design): sqlcmd `SELECT [Key],ParentKey,Label,IsVisible FROM MenuItems WHERE [Key]='Off_Dashboard'` → 1 row Key=`Off_Dashboard` Parent=`Off` Label="Bảng điều khiển Văn phòng số" IsVisible=1 ✓.** **★ OFFICE-HIDDEN CONFIRMED (RevokeTemporarilyHiddenModulesAsync StartsWith("Off") scope — DbInitializer.cs L2172): sqlcmd `SELECT r.Name FROM Permissions p JOIN Roles r ON r.Id=p.RoleId WHERE p.MenuKey='Off_Dashboard'` → ONLY `Admin` (CanRead=1), 1 row / 13 roles total ✓ → non-admin NO Off_Dashboard perm → Office stays hidden (revoke executed, admin auto via All[]).** 0 regression. NO prod-data mutation (read-only curls + sqlcmd SELECT-only). Visual "Dashboard landing render / Hồ sơ NS CSS" NOT verified (anh xem mắt) — only ship+rotate+health+mig-unchanged+tables88+menu-seed+office-hidden. **LESSON: FE-both-app + index.css-sync + BE-menu-seed (NO-EF-mig) verify = both bundles MUST ROTATE + Mig-top + sys.tables MUST stay prev (menu-seed = DbInitializer runtime row-insert, NOT a migration → does NOT advance __EFMigrationsHistory NOR sys.tables; verify seed via direct MenuItems SELECT not via mig-count). Office-hidden = query Permissions-by-role: temporarily-hidden module has perm row ONLY for Admin (All[] auto-grant) after RevokeTemporarilyHidden runs. ⚠️ Prod CI bundle-hash ≠ local-build-hash is NORMAL — never FAIL on hash-mismatch-to-local, only FAIL if NOT-rotated-from-baseline. TOOLING (re-confirmed S78): Bash=POSIX (`$var`/`$env:`/`''` literal-quote mangle through bash→ssh→PS→sqlcmd layers) → write PS to `.ps1` + `powershell -File "ABS/FWD/SLASH.ps1"`; Gitea `tasks` via `Invoke-RestMethod` (match `head_sha -eq sha`, `?limit=N` honored URI-direct); SSH→PS base64 `-EncodedCommand` (UTF-16LE iconv) for BOTH appsettings-read AND sqlcmd; ⚠️ **sqlcmd string-LITERAL in query: doubled `''x''` BREAKS (PS single-quote-string closes early) → build literal via `[char]39 + "x" + [char]39` concat (NOT `''`)**; this sqlcmd build supports `-U/-P` direct (no -ConnectionString flag); table name `Roles` NOT `AspNetRoles`, `Permissions(RoleId,MenuKey,CanRead)`; CLIXML/Progress stdout-noise grep-filter out. NEVER fixed code (READ-only).** Tag `[s69, run305, pass, fe-both-app-vanphongso-foundation, shared-PageHeader-KpiCard-WidgetCard, index-css-sync-admin, bundle-BOTH-rotate-Bl2o_kUq-BImrKQNn, prod-ci-hash-NE-local-EXPECTED, be-menu-seed-Off_Dashboard-NO-mig, no-mig-top-stays-mig52, tables88-verified, menu-seed-verified-sqlcmd, office-hidden-confirmed-admin-only-1of13, sqlcmd-char39-literal-not-doubled-quote, deploy9of9, no-regression, test286-inferred]`. diff --git a/.claude/skills/ef-core-migration/SKILL.md b/.claude/skills/ef-core-migration/SKILL.md index 2c762ca..91c6112 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ó 52 migration sẵn (Init → AddHoSoLinkToPurchaseEvaluation Mig 52, S65). 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ó 53 migration sẵn (Init → AddPeUrgentAndCeoApprovalThreshold Mig 53, S69). 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 (52 migration hiện có) +## Migration history (53 migration hiện có) | # | Name | Tables added / changed | |---|---|---| @@ -72,6 +72,7 @@ when-to-use: | **50** | **`ReplaceBudgetModuleWithPeWorkItemBudgets`** | **🎯 S61 (2026-06-13) — bảng mới `PeWorkItemBudgets` (1 record/cặp Dự án × Hạng mục, UNIQUE filtered `[IsDeleted]=0`) + **DROP module Budget cũ** (Budgets/BudgetDetails/BudgetApprovals/BudgetChangelogs…) + PE/Contracts DROP `BudgetId` + **backfill `BudgetManualAmount→BudgetPeriodAmount` TRƯỚC DropColumn** (phiếu UAT giữ số) + DELETE menu/permission `Bg_*` IN-list children-first. ⚠️ database-agent advise: KHÔNG FK vật lý PE/Contracts→Budgets → no DropForeignKey · DropIndex TRƯỚC DropColumn (SQL 5074) · IN-list thay LIKE `Bg_%`. Ngân sách giờ per-gói-thầu nhập theo role PRO/CCM. **gotcha #63** (EF scaffold RenameColumn SAI-semantics → Add+UPDATE+Drop) + **#64** (`dotnet ef database update` áp Design-DB 0-rows ≠ Dev-DB → backfill chạy thật lần đầu trên prod).** | | **51** | **`AddDepartmentParentId`** | **🎯 S65 — Department.ParentId `Guid?` loose-Guid (KHÔNG FK vật lý — org-tree phân cấp; `GET /departments/tree` ráp cây in-memory + rollup count theo `User.DepartmentId` + cycle-guard HashSet chặn tự-cha + vòng A→B→A). AddColumn-only, no new table.** | | **52** | **`AddHoSoLinkToPurchaseEvaluation`** | **🎯 S65 — PE.HoSoLink `nvarchar(1000)?` hyperlink NAS (mục "e. Link hồ sơ", FE `` null-safe). AddColumn-only, no new table.** | +| **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.** | Total: **88 bảng** dbo + `__EFMigrationsHistory` (re-ground S62 cicd `sys.tables`; last Mig 50 net 93→88 — Mig 51+52 đều AddColumn-only, không đổi số bảng). Xem `docs/database/schema-diagram.md` migration table + §11-15 module ERD (§16+ Mig 27-52 chi tiết pending). @@ -281,7 +282,7 @@ sqlcmd -S .\SQLEXPRESS -d SolutionErp -U vrapp -P -i migrate.sql ## Code pointers -- `src/Backend/SolutionErp.Infrastructure/Persistence/ApplicationDbContext.cs` — DbSet cho 88 bảng (52 migration) +- `src/Backend/SolutionErp.Infrastructure/Persistence/ApplicationDbContext.cs` — DbSet cho 88 bảng (53 migration) - `src/Backend/SolutionErp.Infrastructure/Persistence/DesignTimeDbContextFactory.cs` — EF tooling factory - `src/Backend/SolutionErp.Infrastructure/Persistence/DbInitializer.cs` — seed + warn + migrate runtime + backfill (idempotent reconcile pattern) - `src/Backend/SolutionErp.Infrastructure/Persistence/Configurations/` — IEntityTypeConfiguration per entity diff --git a/CLAUDE.md b/CLAUDE.md index 25902fc..e6b4552 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -50,20 +50,20 @@ Kiến trúc: **.NET 10 Clean Architecture + 2 React FE (admin + user) + SQL Ser - Audit fields: `CreatedAt`, `UpdatedAt`, `CreatedBy`, `UpdatedBy` (`BaseEntity`) - Soft delete: `IsDeleted`, `DeletedAt`, `DeletedBy` (`AuditableEntity`) - Migrations: `dotnet ef migrations add --project src/Backend/SolutionErp.Infrastructure --startup-project src/Backend/SolutionErp.Api` -- **Hiện có 52 migration → 88 bảng** (+S65: Mig 51 `AddDepartmentParentId` Department.ParentId loose-Guid no-FK org-tree phân cấp + Mig 52 `AddHoSoLinkToPurchaseEvaluation` PE HoSoLink nvarchar(1000) hyperlink NAS — cả 2 AddColumn no new table, tables giữ 88. Phase 10 COMPLETE + Phase 11 P11-A→F done — Mig 34-42 HRM/Office/WorkflowApps/Attendance + Contract V2 (32-33) + WireWorkflowApps V2 (41) + LeaveBalance (42) + Holiday filtered-unique (43, S45) + Vehicle/Driver catalog (44, S51) + HRM-catalog filtered-unique 3× (45, S51) + ItTicket SLA (46, S52) + Master filtered-unique 3× (47, S53 gotcha #57 EXT) + Project master fields Year/Investor/Location/Package (48, S55 — AddColumn no new table, kèm nạp 62 dự án + 71 hạng mục + 3 NCC real data từ Excel qua `SeedRealMasterDataAsync` ungated idempotent) + PE gắn Hạng mục công việc WorkItemId loose-Guid KHÔNG FK vật lý (49, S57bis — AddColumn+CreateIndex, no new table) + **Mig 50 `ReplaceBudgetModuleWithPeWorkItemBudgets` (S61, 2026-06-13) — XÓA module Budget cũ, thay bằng `PeWorkItemBudgets` ngân sách per-gói-thầu (1 record/cặp Dự án × Hạng mục, nhập theo role PRO/CCM, vượt ngân sách = cảnh báo mềm cho lưu S62); backfill `BudgetManualAmount→BudgetPeriodAmount` TRƯỚC DropColumn (phiếu UAT giữ số); net bảng 93→88; gotcha #63/#64**. V2 schema history S29-era bên dưới giữ nguyên — Mig 32+33 Plan B Contract V2 cookie-cutter mirror PE Mig 22-26 (S29). Mig 26 `AddPeLevelOpinionsForV2`: bảng mới `PurchaseEvaluationLevelOpinions` UNIQUE composite (PEId, LevelId), FK Cascade Pe + Restrict Level. Section 5 "Ý kiến cấp duyệt" V2 dynamic theo workflow đã pin: forEach Step (Phòng) → forEach Level (Cấp) → forEach NV → 1 OpinionBox. Service `ApproveV2Async` UPSERT auto khi NV duyệt — Q1=1B (sync gắn với Duyệt, KHÔNG form input rời). SignedByUserId track signer thật, FE banner "Admin duyệt thay" khi !== ApproverUserId. Comment empty → "(duyệt — không ý kiến)" placeholder. Phiếu V1 legacy fallback Mig 15 4 box readOnly (data history). Mig 25 `AddIsUserSelectableToApprovalWorkflows`: ALTER `ApprovalWorkflows` +`IsUserSelectable bit` (admin pin/unpin workflow nào cho user pick lúc create phiếu, multi-select độc lập IsActive). Backfill `WHERE IsActive=1 SET 1` giữ behavior cũ. Designer +badge "Cho user chọn" + button Ghim/Bỏ ghim. Workspace filter dropdown chỉ workflows `IsUserSelectable=true`. Mig 22-24 V2 schema (Session 17): `ApprovalWorkflows`/Steps/Levels — Quy trình > Bước (Phòng) > Cấp (N NV cụ thể qua ApproverUserId, OR-of-N cùng cấp). PE.ApprovalWorkflowId pin V2. PE.CurrentApprovalLevelOrder track. State machine 5 trạng thái: Nháp / Đã gửi duyệt / Trả lại (Phase riêng TraLai=98) / Từ chối / Đã duyệt. PE Service V2 wire match `actor.Id == ApproverUserId`. Contract V2 ĐÃ WIRE (Mig 32+33 Plan B S29 — cookie-cutter mirror PE V2: `ApproveV2Async` + `ContractLevelOpinions` UPSERT + Workspace V2 Select dropdown). Mig 21 V1 flat workflow vẫn live cho phiếu cũ.) +- **Hiện có 53 migration → 88 bảng** (+S69: Mig 53 `AddPeUrgentAndCeoApprovalThreshold` — PE +IsUrgentByPro/Ccm cờ gấp PRO/CCM + ApprovalWorkflow +CeoApprovalThreshold ngưỡng CCM duyệt-final, 3 AddColumn no new table. +S65: Mig 51 `AddDepartmentParentId` Department.ParentId loose-Guid no-FK org-tree phân cấp + Mig 52 `AddHoSoLinkToPurchaseEvaluation` PE HoSoLink nvarchar(1000) hyperlink NAS — cả 2 AddColumn no new table, tables giữ 88. Phase 10 COMPLETE + Phase 11 P11-A→F done — Mig 34-42 HRM/Office/WorkflowApps/Attendance + Contract V2 (32-33) + WireWorkflowApps V2 (41) + LeaveBalance (42) + Holiday filtered-unique (43, S45) + Vehicle/Driver catalog (44, S51) + HRM-catalog filtered-unique 3× (45, S51) + ItTicket SLA (46, S52) + Master filtered-unique 3× (47, S53 gotcha #57 EXT) + Project master fields Year/Investor/Location/Package (48, S55 — AddColumn no new table, kèm nạp 62 dự án + 71 hạng mục + 3 NCC real data từ Excel qua `SeedRealMasterDataAsync` ungated idempotent) + PE gắn Hạng mục công việc WorkItemId loose-Guid KHÔNG FK vật lý (49, S57bis — AddColumn+CreateIndex, no new table) + **Mig 50 `ReplaceBudgetModuleWithPeWorkItemBudgets` (S61, 2026-06-13) — XÓA module Budget cũ, thay bằng `PeWorkItemBudgets` ngân sách per-gói-thầu (1 record/cặp Dự án × Hạng mục, nhập theo role PRO/CCM, vượt ngân sách = cảnh báo mềm cho lưu S62); backfill `BudgetManualAmount→BudgetPeriodAmount` TRƯỚC DropColumn (phiếu UAT giữ số); net bảng 93→88; gotcha #63/#64**. V2 schema history S29-era bên dưới giữ nguyên — Mig 32+33 Plan B Contract V2 cookie-cutter mirror PE Mig 22-26 (S29). Mig 26 `AddPeLevelOpinionsForV2`: bảng mới `PurchaseEvaluationLevelOpinions` UNIQUE composite (PEId, LevelId), FK Cascade Pe + Restrict Level. Section 5 "Ý kiến cấp duyệt" V2 dynamic theo workflow đã pin: forEach Step (Phòng) → forEach Level (Cấp) → forEach NV → 1 OpinionBox. Service `ApproveV2Async` UPSERT auto khi NV duyệt — Q1=1B (sync gắn với Duyệt, KHÔNG form input rời). SignedByUserId track signer thật, FE banner "Admin duyệt thay" khi !== ApproverUserId. Comment empty → "(duyệt — không ý kiến)" placeholder. Phiếu V1 legacy fallback Mig 15 4 box readOnly (data history). Mig 25 `AddIsUserSelectableToApprovalWorkflows`: ALTER `ApprovalWorkflows` +`IsUserSelectable bit` (admin pin/unpin workflow nào cho user pick lúc create phiếu, multi-select độc lập IsActive). Backfill `WHERE IsActive=1 SET 1` giữ behavior cũ. Designer +badge "Cho user chọn" + button Ghim/Bỏ ghim. Workspace filter dropdown chỉ workflows `IsUserSelectable=true`. Mig 22-24 V2 schema (Session 17): `ApprovalWorkflows`/Steps/Levels — Quy trình > Bước (Phòng) > Cấp (N NV cụ thể qua ApproverUserId, OR-of-N cùng cấp). PE.ApprovalWorkflowId pin V2. PE.CurrentApprovalLevelOrder track. State machine 5 trạng thái: Nháp / Đã gửi duyệt / Trả lại (Phase riêng TraLai=98) / Từ chối / Đã duyệt. PE Service V2 wire match `actor.Id == ApproverUserId`. Contract V2 ĐÃ WIRE (Mig 32+33 Plan B S29 — cookie-cutter mirror PE V2: `ApproveV2Async` + `ContractLevelOpinions` UPSERT + Workspace V2 Select dropdown). Mig 21 V1 flat workflow vẫn live cho phiếu cũ.) ### Modules | Module | Namespace | Migration | Trạng thái | |---|---|---|---| | Contract (HĐ) | `Domain/Contracts/` | 1-11 | Feature-complete (7 ContractType × 9 phase) | -| PurchaseEvaluation (Duyệt NCC tiền-HĐ) | `Domain/PurchaseEvaluations/` | 12,13,15,49,50,52 | Feature-complete — +Hạng mục (Mig 49) +ngân sách per-gói-thầu role PRO/CCM (Mig 50, vượt=cảnh báo mềm S62) +**Link hồ sơ NAS hyperlink (Mig 52, S65)**. Export PDF pending | +| PurchaseEvaluation (Duyệt NCC tiền-HĐ) | `Domain/PurchaseEvaluations/` | 12,13,15,49,50,52,53 | Feature-complete — +Hạng mục (Mig 49) +ngân sách per-gói-thầu role PRO/CCM (Mig 50) +Link hồ sơ NAS (Mig 52) +**cờ gấp PRO/CCM + CCM duyệt-final theo ngưỡng giá trị (Mig 53, S69)**. Export PDF pending | | ~~Budget (Ngân sách dự án)~~ | — | 14 → **Mig 50 DROP** | ⚠️ **REMOVED S61** — module Budget cũ XÓA, thay bằng PE-budget-per-gói-thầu (`PeWorkItemBudgets`). FE pages/types/menu `Bg_*` gỡ hết | | Master (Supplier/Project/Department) | `Domain/Master/` | 2, 10 | Feature-complete | | Identity (User/Role/Permission/MenuItem) | `Domain/Identity/` | 1, 3, 11 | Feature-complete (30 demo user — 16 sample + 14 Solutions thật) | | Forms (Template + Clause) | `Domain/Forms/` | 4 | Feature-complete | | Notifications | `Domain/Notifications/` | 6 | In-app + SignalR OK, email SMTP TODO | -| **Tests** | `tests/SolutionErp.{Domain,Infrastructure}.Tests/` | — | **286 test pass** (45 Domain + 241 Infra) — CI gate + path filter docs-only skip | +| **Tests** | `tests/SolutionErp.{Domain,Infrastructure}.Tests/` | — | **306 test pass** (45 Domain + 261 Infra) — CI gate + path filter docs-only skip | ### Commit convention @@ -130,7 +130,7 @@ Quy tắc: | [`docs/database/database-guide.md`](docs/database/database-guide.md) | DB conventions + migration workflow + cheatsheet | | [`docs/database/schema-diagram.md`](docs/database/schema-diagram.md) | ⭐ ERD + luồng DB + data flow 88 table (+ §11 PE + §12 ~~Budget~~ DROP + §13 PEDeptOpinions + §14 Contract V2 LevelOpinions; §16+ Mig 32-52 pending) | | [`docs/flows/README.md`](docs/flows/README.md) | Index 6 flow (auth, permission, contract, form, SLA) | -| [`docs/gotchas.md`](docs/gotchas.md) | ⭐ 65 bẫy đã gặp — đọc trước khi debug tương tự | +| [`docs/gotchas.md`](docs/gotchas.md) | ⭐ 68 bẫy đã gặp — đọc trước khi debug tương tự | | [`.claude/skills/`](.claude/skills/README.md) | 6 skill: contract-workflow, form-engine, permission-matrix, dependency-audit-erp, ef-core-migration, iis-deploy-runbook | | [`docs/guides/vps-setup.md`](docs/guides/vps-setup.md) | ⭐ Master runbook deploy VPS shared với VIETREPORT | diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 4d9a87f..6255d88 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -2,7 +2,7 @@ > **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-16 (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-17 (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.) --- diff --git a/docs/STATUS.md b/docs/STATUS.md index e83df22..e0fd56f 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-16 (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-17 (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 | **52** | +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 | **53** | +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 | **53** | re-ground S61 (`MenuKeys.cs` const — Mig 50 gỡ 4 `Bg_*` Budget menu cũ). Prev 57 (S58) | -| Tests | **286 PASS** | 45 Domain + 241 Infra · 0 fail / 0 skip · **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 | **66** | +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) | +| 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 | **306 PASS** | 45 Domain + 261 Infra · 0 fail / 0 skip · **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 | **68** | +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 | **21** | +1 S64 `feedback_harness7_outward_writing_quality` (outward full-grammar VN · internal compressed — asymmetric). Prev re-grounded S54 (H1 disk-count base 19); +1 S54 `feedback_agent_cwd_relative_memory_misland` | | 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 **`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** · ✅ **PE ngân sách per-gói-thầu** (Mig 50 S61 — XÓA module Budget cũ, nhập role PRO/CCM, vượt=cảnh-báo-mềm S62) prod-verified · 🚫 Phase 9 Ops blocked (anh main coordinate — anh dừng). +**Bundle hash live (prod):** admin **`BgNCjwsG`** · user **`CBvh0vtf`** (S69 — 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). > ⚠️ **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 (S68) +## 🔥 In Progress (S69) | Task | Owner | Status | |---|---|---| +| **S69 — Văn phòng số port + golive (#305→#307) + PE cờ gấp/ngưỡng CCM (#308 Mig 53)** — 4 deploy prod-verified, HMW-mode ON, 2 workflow fan-out + ~14 spawn. Foundation PURO + re-skin 10 page + Office public 16-key allow-list + PE cờ gấp/threshold. 2 gotcha NEW (#67/#68) · 2 truncation #53 recover-disk. **NEXT (anh/anh Kiệt UAT):** cấu hình "Ngưỡng giá trị gói CEO" trong Workflow Designer + test cờ gấp PRO/CCM; **xác nhận:** quy trình đặt CCM(CostControl)-trước-CEO + CEO = role Director (notify đích). **NEXT (em):** 🔴 curate cicd-monitor **65.2KB** (worst, trend tăng) + inv-codebase 47 + reviewer 43.5 + impl-be 33 over-cap · doc-flush docs/CLAUDE.md full + schema-diagram §16+. **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`. | 👤 + ~14 sub | ✅ | | **S68 — Hồ sơ NS header chi tiết NV: nổi bật (size/weight/badge màu) + fix tên đen→trắng (gotcha #66)** — em main solo, 3 commit (FE `6983609`+`37752eb` · docs `11bc96d`), 2 deploy prod-verified Run #303-304 (anh UAT realtime): tên `text-xl extrabold`+drop-shadow · meta `text-[13px] font-medium` trắng-đậm · badge pill màu emerald/amber/slate theo trạng thái (#303 `D532XZKG`/`CuFaBoWt`) → anh báo "tên đen nền xanh ko nổi bật" → **rule `h1-h4{color:#0b1220}` unlayered thắng `text-white`** (Tailwind v4) → ép `text-white!` + thu nhỏ `text-lg` (#304 `CNUv1jxY`/`CpOskeS1`). Bootstrap: 2 monitor RE-REPORT CLEAN + 2 doc-drift fix (dep-audit 64→65, root CLAUDE 263→286). **NEXT (anh):** xác nhận mắt tên trắng+gọn ưng chưa (muốn nhỏ hơn→`text-base`; màu nhấn nếu cần). **NEXT (em):** 🔴 curate cicd-monitor **44.1KB** + inv-codebase 38.5 + reviewer 35.4 + impl-backend 30.7 over-cap · doc-flush docs/CLAUDE.md count + schema §16+. → session log `2026-06-16-S68-hoso-header-name-color-fix.md`. | 👤 + 3 sub | ✅ | | **S67 — Hồ sơ NS visual polish (font/màu/list) + fe-admin mirror + PE Link hồ sơ auto-detect/file-link + research auto-open** — 6 deploy prod-verified #297→#302 (anh + anh Kiệt FDC UAT realtime): font/màu xanh đậm fe-user (#297) · **fe-admin mirror** master-detail + accent tokens index.css (#298) · **+23 test→286** (#299) · **list flex-row hết tràn ngang** + đồng nhất cỡ (#300) · PE Link auto-detect web/Copy (#301) → **link file:// bấm-thử** (#302). Research 3-agent: auto-mở `O:\` = chỉ Edge GPO `IntranetFileLinksEnabled`. test-specialist trunc #53→recover-disk. bundle `CcrZqfht`/`DniDFUB_`. **NEXT (anh):** ① bấm thử link `O:\` (mở/no-op) báo em môi trường · ② IT-guide Edge GPO nếu muốn 1-click `O:\` · ③ visual list+màu ưng chưa (đổi `brand-700` nếu muốn nhạt hơn). **NEXT (em):** curate cicd-monitor 39.8KB + inv-codebase 39.4KB over-cap · doc-flush docs/CLAUDE.md count + schema-diagram §16+. → session log `2026-06-16-S67-hoso-visual-pe-link-research.md`. | 👤 + 4 sub | ✅ | | **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): HRM public read 13 role (#289) · foundation màu fe-user (#290) · Mig 51 Department.ParentId + picker phòng cha fe-admin (#292) · Employee 3-panel→**2-cột** 5-tab + tô màu + gốc cây "SOLUTION COMPANY" (#293/#294/#295) · Mig 52 PE HoSoLink hyperlink NAS + rename "Dự trù PRO"→"Ngân sách PRO" (#293). **Workflow fan-out chạy THẬT lần đầu** (PE) — FE+reviewer empty-return → em main recover-disk + self-gate. gotcha #65. **NEXT:** mirror Employee page→fe-admin · test-after (HoSoLink/ParentId/HRM-perm) · **cicd-monitor 82KB curate P1**. → session log `2026-06-16-S65-hrm-golive-employee-masterdetail-pe-link.md`. | 👤 + 9 sub | ✅ | @@ -49,6 +50,13 @@ ## ✅ Recently Done (newest on top — 3 session; cũ hơn → session logs) +### S69 (2026-06-17) — ✅ 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 `a8bbdae` Văn phòng số foundation** (workflow `office-puro-foundation` 5-agent): sync fe-admin/index.css ← fe-user (đóng drift S66-S68 + gotcha #66 fix) + 3 shared component PageHeader/KpiCard/WidgetCard (×2 SHA256, tái dùng token Hồ sơ NS) + OfficeDashboardPage 2-cột widget kiểu PURO HomePage (reuse data hooks, no BE) + menu key Off_Dashboard (no-mig idempotent seed, giữ ẩn). em main fix KpiCard activeBorder -300→-500 (**gotcha #67** — Tailwind accent palette thiếu stop). +- **#306 `c556f6c` re-skin TRỌN 10 page** (workflow `office-puro-reskin-all` 7 designer song song write-only + reviewer): Đề xuất(List/Create/Detail)/Đơn từ/Ticket/Danh bạ/Phòng họp/Báo cáo CC → PURO layout + CSS Hồ sơ NS, **phẫu thuật giữ 100% logic** (reviewer verify mọi api.*/queryKey byte-identical HEAD vs working). em main build-tập-trung + mirror SHA256 ×2 + recover stale-diagnostic (**gotcha #68**). 9 page fe-user↔fe-admin SHA256-identical. +- **#307 `1f8947e` Office golive public** (+6 test): `SeedAllRolesOfficeModulePermissionsAsync` grant read+create **16-key allow-list mọi role** (mirror S65 HRM pattern, chạy SAU revoke để thắng). Excluded (giữ ẩn): Off_PhongHop_Manage (admin CRUD) + Off_AttendanceReport (riêng tư) + Off_ChamCong (Cá nhân). reviewer PASS security (cascade-safe: Off KHÔNG phải inherit-root → excluded-3 giữ false; no write-path: controller [Authorize(Roles=Admin)] độc lập menu-key). cicd DB-verify **16/16 × 13 role**, excluded-3 = 0, HRM/Personal vẫn ẩn, admin không hạ. +- **#308 `ebd7e1c` PE cờ gấp + CCM duyệt-final theo ngưỡng** (Mig 53, anh Kiệt FDC sau họp sếp, +14 test): ApprovalWorkflow +`CeoApprovalThreshold` (admin nhập Designer, null=luồng cũ rollout-an-toàn); `ApproveV2Async`: actor role CostControl + winnerQuoteTotal (tổng giá NCC chọn) < ngưỡng → DaDuyet bỏ CEO (Q4 theo role). PE +IsUrgentByPro(đỏ)/IsUrgentByCcm(xanh); endpoint PUT /urgent role-gated (PRO/CCM/Admin) + notify CEO (Director) khi bật, visibility-only (Q3 không đổi luồng). FE ×2: Designer ô ngưỡng + PE detail cờ gấp toggle/badge + hint giá-trị-vs-ngưỡng + list badge. Test PeCcmThresholdFinalize 5 (load-bearing: CCM dưới ngưỡng → DaDuyet skip CEO) + PeUrgentToggleAuthz 9. reviewer PASS security. **C (sau duyệt → chuyển phiếu đến dự án) chờ anh Kiệt spec form.** +- **§L:** 2 gotcha NEW (#67 Tailwind palette-thiếu-stop · #68 stale-diagnostic-background-agent). 2 truncation #53 (impl-backend positional-record DTO mid-fix + impl-frontend) → em main recover-disk + tự viết endpoint cờ gấp (agent chưa kịp) + tự fix 2 CS7036. H2 GATE PASS (1 non-block: PE-CCM-threshold test-covered not standalone-reviewed). **State: Mig 53 · 88 bảng · 306 test · 68 gotcha · menu 54 · bundle `BgNCjwsG`/`CBvh0vtf`.** ⚠️ curate-debt P1: cicd-monitor **65.2KB** (worst, trend 41→54→56→61→62→65) + inv-codebase 47 + reviewer 43.5 + impl-be 33. → session log `2026-06-17-S69-vanphong-golive-pe-urgent-threshold.md`. + ### S68 (2026-06-16) — ✅ Hồ sơ NS header chi tiết NV: làm nổi bật + fix tên render đen→trắng — em main solo, 2 deploy prod-verified Run #303-304 (anh UAT realtime, HMW-mode ON) - **Bootstrap (`/session-start`):** 2 monitor RE-REPORT CLEAN — 🟫 H1 roster 11/11 `model: inherit` confirmed trên disk (Harness-8 runtime-landed, hết "pending-restart"), ef-core skill FRESH (Mig 52), plugin 18/15/3; flag 1 stale `dep-audit SKILL:153` (64→65) + **drift-correction: curate-debt = 4 agent over-cap, không phải 2** (S67 sót reviewer 35.4 + impl-backend 30.7). ⬜ H2 0-orphan · 0-corruption · 5-trục PASS (S67 harvest landed trọn). → em main patch 2 doc-drift `11bc96d` (dep-audit 64→65 + root CLAUDE test 263→286). - **#303 `6983609` header polish ×2 app SHA256:** tên NV `text-lg→text-xl font-extrabold`+`drop-shadow-sm` · dòng meta `text-xs text-white/85 → text-[13px] font-medium text-white` (to+đục+dày nét, mã NV `font-semibold`) · badge trạng thái từ pill trắng-mờ chìm → **pill đặc màu theo status** (`EmployeeStatusColor` emerald/amber/slate + chấm `bg-current` + shadow). Build×2 PASS, bundle `D532XZKG`/`CuFaBoWt`, cicd Run #303 PASS. diff --git a/docs/changelog/sessions/2026-06-17-S69-vanphong-golive-pe-urgent-threshold.md b/docs/changelog/sessions/2026-06-17-S69-vanphong-golive-pe-urgent-threshold.md new file mode 100644 index 0000000..735eb9d --- /dev/null +++ b/docs/changelog/sessions/2026-06-17-S69-vanphong-golive-pe-urgent-threshold.md @@ -0,0 +1,52 @@ +# S69 (2026-06-17) — 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 (`office-puro-foundation` 5-agent + `office-puro-reskin-all` 8-agent) + nhiều spawn lẻ. Buổi sản phẩm CỰC LỚN. + +**Anh: `/session-start` → "port Văn phòng số giống PURO NamGroup, CSS giống Hồ sơ NS" `/ultra-on` → (foundation) → "làm hết đi, PE golive rồi giờ còn Văn phòng số" (re-skin all) → "public Văn phòng số cho all user" (golive) → [screenshot Zalo anh Kiệt FDC: cờ gấp + CCM duyệt-final theo ngưỡng + chuyển phiếu→dự án] → "publish + session-end".** + +--- + +## #305 `a8bbdae` — Văn phòng số foundation (workflow 5-agent) +- **Phát hiện then chốt:** SE ĐÃ CÓ module Office với feature map 1:1 PURO (Danh bạ/Phòng họp/Đề xuất/Đơn từ/Đặt xe/Ticket) → đây là **re-layout + re-skin**, không phải build mới. Anh chốt 2 nhánh: "Nền tảng trước" + "Giữ menu SE (không phẳng-hoá sidebar)". +- **Foundation:** sync `fe-admin/src/index.css` ← fe-user (đóng drift S66-S68: heading 600→700, ink #0f172a→#0b1220, label-eyebrow slate→brand-600 + gotcha #66 rule) → 2 app đồng bộ. + 3 shared component `PageHeader`/`KpiCard`/`WidgetCard` (×2 SHA256, tái dùng token Hồ sơ NS) + `OfficeDashboardPage` 2-cột widget kiểu PURO HomePage (reuse data hooks Đề xuất/Đơn từ/Ticket/Phòng họp, đếm client-side, no BE) + menu key `Off_Dashboard` (no-mig idempotent seed dưới Off, giữ ẩn). +- **gotcha #67 NEW:** em main fix `KpiCard` activeBorder `-300`→`-500` — accent palette (teal/violet/amberx/greenx) chỉ ship 50/100/500/600/700; `-300` trên teal/violet rơi DEFAULT Tailwind (sai-hệ), amberx/greenx drop hẳn. Build PASS cả 2 → reviewer soi dist CSS mới bắt. +- cicd Run #305: bundle admin `Bl2o_kUq`/user `BImrKQNn`, Off_Dashboard seeded, Office still hidden (admin-only). + +## #306 `c556f6c` — re-skin TRỌN 10 page (workflow 7 designer song song + reviewer) +- **Phẫu thuật trình bày, giữ 100% logic:** Đề xuất(List/Create/Detail) + Đơn từ/Đặt xe(List/Detail) + Ticket CNTT + Danh bạ + Phòng họp(Calendar/Rooms) + Báo cáo CC(fe-admin) → PURO layout (PageHeader + KpiCard-filter row + card-accent) + CSS Hồ sơ NS. reviewer verify **mọi `api.*`/`queryKey` byte-identical HEAD vs working tree** (logic preserved). +- **Cơ chế chống parallel-build-interference:** 7 designer ghi SONG SONG fe-user → **cấm chạy npm build** (project cấm worktree do Windows MAX_PATH) → em main **build-tập-trung 1 lần** + mirror SHA256 sang fe-admin (9 page identical@HEAD verify trước cp) + build fe-admin. +- **gotcha #68 NEW:** harness bắn loạt diagnostic `× unused/Element` sau workflow — đều STALE mid-edit snapshot; build sau-cùng exit 0 cả 2 app. Bài học: agent nền → chỉ tin build SẠCH sau-cùng, bỏ qua diagnostic giữa-chừng. +- cicd Run #306: bundle admin `Wt54PHYl`/user `B99fMU6X`, 6 Office SPA route 200, Office vẫn ẩn. + +## #307 `1f8947e` — Office golive public (+6 test) +- Anh: "public Văn phòng số cho all user eoffice". `SeedAllRolesOfficeModulePermissionsAsync` grant **read+create 16-key allow-list mọi role** (mirror đúng S65 HRM `SeedAllRolesHrmProfileReadPermissionsAsync` — chạy SAU `RevokeTemporarilyHiddenModulesAsync` để THẮNG; upgrade-only nâng false→true). +- **Allow-list 16:** Off + Dashboard + DanhBa + PhongHop(View/Book) + DeXuat(List/Create/Inbox) + DonTu(Leave/Ot/Travel) + DatXe + ItTicket. +- **Excluded (giữ ẩn):** Off_PhongHop_Manage (admin CRUD phòng) + Off_AttendanceReport (báo cáo riêng-tư) + Off_ChamCong (Cá nhân, golive riêng). HRM (trừ Hồ sơ NS) + Personal VẪN ẩn. +- **reviewer PASS security** (2 finding load-bearing): (1) cascade-safe — `Off` KHÔNG phải inherit-root trong GetMyMenuTree (chỉ Contracts/Workflows/PE/PeWf) → excluded-3 giữ false không lan; (2) no write-path opened — Office controller dùng class-`[Authorize]` self-service + per-action `[Authorize(Roles=Admin)]` cho admin-write → CanCreate chỉ mở menu+nút, API authz độc lập menu-key. +- **+6 test** `OfficeModulePermissionSeedTests`. cicd **DB-verify sqlcmd: 16/16 allow-list read+create=1 × 13 role**, excluded-3 = 0, HRM/Personal vẫn 0, admin không hạ. Bundle BE-only frozen. + +## #308 `ebd7e1c` — PE cờ gấp PRO/CCM + CCM duyệt-final theo ngưỡng (Mig 53, anh Kiệt FDC, +14 test) +- Yêu cầu anh Kiệt sau họp sếp (screenshot Zalo). Anh chốt 4 quyết định (AskUserQuestion): ngưỡng cấu-hình-được · giá-trị = winnerQuoteTotal · cờ gấp visibility-only · nhận diện CCM/CEO theo role. +- **B — CCM duyệt-final theo ngưỡng giá trị:** ApprovalWorkflow +`CeoApprovalThreshold` (decimal? admin nhập Designer, **null = luồng tuyến tính cũ → rollout an toàn** dark-launch). `ApproveV2Async`: actor role `CostControl` (CCM) + `winnerQuoteTotal` (tổng ThanhTien NCC được chọn) < ngưỡng → `DaDuyet` luôn, bỏ CEO (guard chưa-ở-slot-cuối). Vai PRO=Procurement, CCM=CostControl, CEO=Director (mapping qua AppRoles). +- **A — cờ gấp per-vai (visibility-only):** PE +`IsUrgentByPro`(đỏ)/`IsUrgentByCcm`(xanh). Endpoint `PUT /purchase-evaluations/{id}/urgent` role-gated (Procurement→ByPro, CostControl→ByCcm, Admin→cả 2, khác→Forbidden) + notify CEO (Director) qua `INotificationService.NotifyManyAsync` khi MỚI bật (best-effort try/catch). Q3: KHÔNG đổi luồng. +- **FE ×2:** Workflow Designer ô "Ngưỡng giá trị gói CEO" (fe-admin) + PE detail cờ gấp toggle/badge theo role + hint "giá trị gói vs ngưỡng → CCM duyệt-final/cần CEO" + PE list badge. +- **+14 test:** `PeCcmThresholdFinalizeTests` 5 (load-bearing: CCM dưới ngưỡng → DaDuyet skip CEO; ≥ ngưỡng → lên CEO; null → linear; non-CCM → không finalize) + `PeUrgentToggleAuthzTests` 9. reviewer PASS security (cascade-safe + no-write-path). cicd Run #308: Mig 53 applied (3 cột, sys.tables=88), endpoint /urgent 401-not-404, bundle admin `BgNCjwsG`/user `CBvh0vtf`. +- **C (sau duyệt → chuyển phiếu đến dự án):** anh Kiệt "sẽ làm chi tiết với em để lên form" → DEFERRED, chờ spec. + +--- + +## §L AUTO-MAINTAIN +- **AS scan / RCA:** 2 truncation #53 (impl-backend truncate giữa fix positional-record DTO → em main fix 2 CS7036 + tự viết endpoint cờ gấp agent chưa kịp; impl-frontend truncate → em main verify-complete-on-disk). 0 production-bug (cả 2 build PASS sau-cùng). Class quen #53 — recover-disk + self-gate, không cần RCA mới. +- **gotcha NEW ×2:** #67 (Tailwind accent palette thiếu-stop vỡ-màu-im-lặng) · #68 (stale-diagnostic-background-agent → chỉ tin build sau-cùng). → `feedback_stale_diagnostic_background_agent` (user memory). +- **H2 harvest GATE PASS** (1 non-block: PE-CCM-threshold logic test-covered [14 test] not standalone-reviewed — acceptable). 0 corruption/stray/wave. cicd #308 orphan → commit closeout. +- **H1 tooling CHỐT:** 0 new skill/plugin/roster (0 new-alloc). Count-flush must-fix: ef-core SKILL Mig 53 + skills/README + dep-audit + root CLAUDE + STATUS + HANDOFF (done closeout). docs/CLAUDE.md full + schema-diagram §16+ = monthly-defer. +- **curate-debt P1 (next session):** cicd-monitor **65.2KB** (worst, trend 41→54→56→61→62→65) + inv-codebase 47 + reviewer 43.5 + impl-be 33 over 30KB cap. + +## State THẬT cuối S69 +**Mig 53 · 88 bảng · 306 test (45D+261I) · 68 gotcha · menu 54 · bundle admin `BgNCjwsG`/user `CBvh0vtf` (Run #308).** + +## 🔴 NEXT +- **Anh/anh Kiệt UAT:** (1) cấu hình "Ngưỡng giá trị gói CEO" Workflow Designer + test phiếu < / ≥ ngưỡng; (2) test cờ gấp PRO/CCM → badge + notify CEO; (3) **xác nhận** quy trình đặt CCM(CostControl)-trước-CEO + CEO = role Director (đích notify). +- **C** (chuyển phiếu→dự án) chờ anh Kiệt spec form. +- **Em:** curate cicd-monitor 65.2KB P1 · doc-flush docs/CLAUDE.md full + schema-diagram §16+ Mig 32-53 (monthly 2026-07-01). +- **Ops của anh (giữ S58/S59):** tzutil VPS · anh Chương email typo · 5 real staff password · gán CNTT lock nv.cao/nv.truong. diff --git a/docs/gotchas.md b/docs/gotchas.md index 7402921..24aeef2 100644 --- a/docs/gotchas.md +++ b/docs/gotchas.md @@ -1176,6 +1176,34 @@ for h in resp.points: # ← .points không phải iterable trực tiếp --- +### 67. Tailwind v4 — accent palette tự-chế thiếu stop (chỉ 50/100/500/600/700) → dùng `-300` "vỡ màu im lặng": tên-trùng-built-in rơi DEFAULT, tên-tự-chế drop hẳn; build PASS (Session 69) + +**Triệu chứng:** Component `KpiCard` tái dùng có `activeBorder: 'border-{accent}-300'` cho 5 accent. Build PASS 0 error, nhưng active-border render SAI: teal/violet ra tone teal/violet MẶC ĐỊNH Tailwind (khác hệ custom #0ea5a4...), amberx/greenx KHÔNG có border (class drop). brand-300 OK (brand full 50-900). + +**Cơ chế:** `@theme` SE định nghĩa accent palette (teal/violet/amberx/greenx) CHỈ ship `50/100/500/600/700` (brand ngoại lệ full). Tailwind v4: `border-teal-300` — `teal` TRÙNG tên built-in → emit DEFAULT teal-300 (#5eead4, khác --color-teal-*); `border-amberx-300` — `amberx` TỰ CHẾ không có --color-amberx-300 → class drop, không emit. Cả 2 **build KHÔNG báo lỗi** (Tailwind không validate color tồn tại) → phải SOI dist CSS. + +**Guard:** (1) Component tái dùng accent-aware CHỈ dùng stop trong "hợp đồng chung" mọi accent (50/100/500/600/700) — đừng mượn stop chỉ brand có. (2) Nghi "vỡ màu im lặng" → grep dist CSS class tồn tại + đúng `var(--color-...)`. (3) Tên tự-chế (amberx/greenx) AN TOÀN hơn tên-trùng-built-in (teal/violet): miss = drop hẳn (dễ thấy) thay vì rơi-default-sai-tone (ẩn). (4) reviewer dimension "color-trap" = grep added-lines `(teal|violet|amberx|greenx)-(200|300|400|800|900)`. + +**Credit:** reviewer S69 (soi dist Office foundation) → em main fix `activeBorder -300 → -500` ×2 app SHA256. + +**References:** `fe-{user,admin}/src/components/ui/KpiCard.tsx` ACCENT map · `fe-*/src/index.css` @theme accent stops · gotcha #66. + +--- + +### 68. IDE TypeScript diagnostic giữa background-agent/workflow = snapshot DỞ-DANG — chỉ tin build SẠCH chạy SAU agent xong (Session 69) + +**Triệu chứng:** Sau workflow re-skin 7 designer song song (+ sau PE-FE agent), harness bắn loạt `× 'X' is declared but never read` / `× Type 'Element' not assignable`. Em suýt sửa theo. Build `npm run build` chạy SAU → **exit 0, 0 error** cả 2 app. Các × đều FALSE-ALARM. + +**Cơ chế:** IDE TS language-server bắn diagnostic theo TỪNG lần save dở-dang của agent (thêm import/state TRƯỚC khi render JSX dùng nó → "unused"; xóa JSX TRƯỚC khi gỡ import → "unused"). Nhiều agent ghi SONG SONG → snapshot càng nhiễu, KHÔNG phản ánh trạng-thái-cuối nhất-quán nào. + +**Guard:** (1) Agent/workflow NỀN: **bỏ qua diagnostic giữa-chừng**, tín hiệu thật = 1 lần build SẠCH chạy SAU agent hoàn-tất (lý do em main build-tập-trung). (2) Đừng vội sửa/cp-đè theo diagnostic — suýt cp đè file vốn đã hoàn chỉnh (2×). (3) Phân biệt diagnostic ai-edit: em main edit real-time → tin; agent nền edit → verify build trước. (4) Cùng họ gotcha #53 (return truncated): disk + build = source-of-truth, KHÔNG tin return/diagnostic suông. + +**Credit:** em main S69 — 2× suýt sửa theo stale-diagnostic (re-skin + PE-FE), build sau-cùng vạch false-alarm. + +**References:** workflow `office-puro-reskin-all` + PE-FE implementer-frontend · gotcha #53 · gotcha #3. + +--- + ## Checklist debug bug mới 1. Build pass không? → fail → check using + package version compat