[CLAUDE] Docs: S55 closeout — master data import từ Excel (Run #377) + Mig 47→48 + ef-core skill

62 dự án + 71 hạng mục + 3 NCC real loaded prod (SeedRealMasterDataAsync ungated). Mig 48 AddProjectMasterFields. STATUS/HANDOFF/session log + CLAUDE.md root mig count + ef-core SKILL migration table. 2 agent return truncated (BE+reviewer) → em main disk/runtime-recover lesson.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-06-09 09:45:04 +07:00
parent 69cb3937bb
commit ed5a239212
6 changed files with 89 additions and 11 deletions

View File

@ -68,6 +68,7 @@ BE (test+build) ~90s · FE × 2 ~60s/app · deploy ~30s · **total ~3min code /
## 📅 Recent runs (FIFO — older → archive/git) ## 📅 Recent runs (FIFO — older → archive/git)
- **2026-06-09 Run #377 (run_number 263) sha=`69cb393` PASS ~4m33s (S55 HMW-P4 real master-data seed from Excel + Project +4 cols Mig 48 — cross-stack BE+FE×2+Mig+seed):** Push `f8640d6..69cb393` 1 commit 18 files: Mig 48 `20260609020759_AddProjectMasterFields` (3-file) + Domain `Project.cs` (+Year/Investor/Location/Package nullable) + `ProjectConfiguration.cs` + App `ProjectFeatures.cs` + **`DbInitializer.cs` NEW `SeedRealMasterDataAsync` (UNGATED, per-code idempotent)** + FE×2 `master/ProjectsPage.tsx` + `types/master.ts` + 1 test `MasterCatalogFilteredUniqueTests.cs` + 4 agent-memory .md. `.cs`+`.tsx`+Mig present → full pipeline RAN. **Run was IN-PROGRESS at first check (status=running 09:28) — correctly did NOT FAIL, polled to terminal** (started 09:27:19 → updated 09:31:52 ≈4m33s status=success). ⚠️ `jq` NOT in Bash-tool bash (env is bash not PS despite shell=PowerShell env-line) — parse JSON via `grep -oE '\"id\":377[^}]*'` fallback; the working first call only used `head -c` not jq. **Bundle ROTATE admin `DmjI8Cmn→B-d6893W` + user `YxL_MljK→XdKzt9LL`** (BOTH changed ✓ FE shipped both apps, verified AFTER status=success; pre-success snapshot 09:28 still showed OLD DmjI8Cmn/YxL_MljK = anti-pattern #3 timing confirmed again; re-confirm +3s post-success = stable, no transient). **Mig 48 applied prod** (`__EFMigrationsHistory` top = `...AddProjectMasterFields` ✓ + `COL_LENGTH('Projects','Investor')` EXISTS). **THE KEY CHECK — real master-data landed (4/4 spot PASS):** Projects spot6 (APVN01/ZOTE01/CAL01/MIDEA01/SAM01/TLB01)=**6** · WorkItems VT-/TP-/MEP-/TB-=**71** · Suppliers (TRUONGGIANG/TANPHU/TGN)=**3** · `CAL01.Investor` =EXACT match N'Công ty TNHH Calofic' (console showed `C<>ng` = sqlcmd codepage mangle of `ô`, NOT data corruption — confirmed via `WHERE Investor=N'...'` EXACT). Totals: Projects=**70** (62 real + 8 demo coexist ✓ ungated seed idempotent), WorkItems=**86**, 5 Projects carry Investor / 23 carry Year (Excel sparse-fill, only some rows enriched — expected). Health live+ready **200/200**. `GET /api/projects` unauth=**401** (route wired, auth gates ✓). 0 regression. **LESSON (ungated prod seed verify = count spot-checks NOT schema):** `SeedRealMasterDataAsync` runs unconditionally on every prod startup (NOT inside `if(!demoSeedDisabled)` — correct per gotcha #51 INFRASTRUCTURE-seed rule); verify = sqlcmd COUNT spot-checks of real Codes + N-literal EXACT match for unicode fields (console codepage will mangle Vietnamese diacritics → always re-assert via `=N'...'`, never trust raw sqlcmd console render). Tag `[s55, run377, pass, mig48-master-fields, real-seed-ungated, sqlcmd-codepage-lesson]`.
- **2026-06-08 Run #376 (run_number 262) sha=`ca4b602` PASS ~4m18s (S54 IT-staff self-reassign ticket — authz Admin-OR-IT + scoped capability endpoint, cross-stack, NO migration):** Push `18d397f..ca4b602` 1 commit 13 files: BE `WorkflowAppsFeatures.cs` (NEW `GetAssignableItStaffQuery` capability + `AssignItTicketHandler` authz Admin-OR-IT) + `ItTicketsController.cs` (NEW `GET /it-tickets/assignable-staff` + `/assign` LOWERED Authorize-Roles) + FE×2 `ItTicketsPage.tsx` (SHA256-identical) + `workflowApps.ts`×2 (+2 type) + `ItTicketReassignAuthzTests.cs` (+13 → 203→**216**) + 6 agent-memory `.md`. `.cs`+`.tsx` present → NOT docs-skip, full pipeline RAN. Poll iter5 status=success (started 16:12:23 → updated 16:16:41 ≈4m18s). **Bundle ROTATE admin `DfCfHUE9→DmjI8Cmn` + user `_3S0BPJ2→YxL_MljK`** (BOTH changed ✓ FE shipped, verified AFTER status=success; pre-deploy iter0 still showed OLD DfCfHUE9/_3S0BPJ2 — correct timing anti-pattern #3). **NO migration** — prod `__EFMigrationsHistory` top = `...FilterMasterCatalogUniqueIndexesByIsDeleted` (Mig 47) == repo latest, GIỮ NGUYÊN ✓ (DepartmentId reuse). sys.tables=**93** stable (no new table). Test gate **216** (CI both proj pre-deploy ⟹ success=passed; grep undercounts InlineData — trust CI). Health live+ready 200 + admin/eoffice root 200. **Smoke NEW endpoint:** `GET /api/it-tickets/assignable-staff` unauth=**401** (route wired, [Authorize] gates) · `PUT /api/it-tickets/{guid}/assign` unauth bare=**411** then **WITH body `-d '{}'`=401** (IIS demands Content-Length before auth eval; 411 is pre-auth Length-check NOT routing-miss) · control fake route `/it-tickets/zzz`=**404** (proves 401s are real auth gates not catch-all). 0 regression. **LESSON (411 vs 401 on bodyless PUT/POST):** unauth bodyless PUT/POST to a JSON-body endpoint returns **411 Length Required** from IIS BEFORE the [Authorize] filter runs — NOT a 404/route-miss. Re-send with `-d '{}'` to force auth eval → real 401. Consistent w/ Run #367 `PUT /adjust=411` + #364 `POST /approve=411` (same pattern, now explained). Tag `[s54, run376, pass, it-reassign-authz, no-mig, 411-precheck-lesson]`. - **2026-06-08 Run #376 (run_number 262) sha=`ca4b602` PASS ~4m18s (S54 IT-staff self-reassign ticket — authz Admin-OR-IT + scoped capability endpoint, cross-stack, NO migration):** Push `18d397f..ca4b602` 1 commit 13 files: BE `WorkflowAppsFeatures.cs` (NEW `GetAssignableItStaffQuery` capability + `AssignItTicketHandler` authz Admin-OR-IT) + `ItTicketsController.cs` (NEW `GET /it-tickets/assignable-staff` + `/assign` LOWERED Authorize-Roles) + FE×2 `ItTicketsPage.tsx` (SHA256-identical) + `workflowApps.ts`×2 (+2 type) + `ItTicketReassignAuthzTests.cs` (+13 → 203→**216**) + 6 agent-memory `.md`. `.cs`+`.tsx` present → NOT docs-skip, full pipeline RAN. Poll iter5 status=success (started 16:12:23 → updated 16:16:41 ≈4m18s). **Bundle ROTATE admin `DfCfHUE9→DmjI8Cmn` + user `_3S0BPJ2→YxL_MljK`** (BOTH changed ✓ FE shipped, verified AFTER status=success; pre-deploy iter0 still showed OLD DfCfHUE9/_3S0BPJ2 — correct timing anti-pattern #3). **NO migration** — prod `__EFMigrationsHistory` top = `...FilterMasterCatalogUniqueIndexesByIsDeleted` (Mig 47) == repo latest, GIỮ NGUYÊN ✓ (DepartmentId reuse). sys.tables=**93** stable (no new table). Test gate **216** (CI both proj pre-deploy ⟹ success=passed; grep undercounts InlineData — trust CI). Health live+ready 200 + admin/eoffice root 200. **Smoke NEW endpoint:** `GET /api/it-tickets/assignable-staff` unauth=**401** (route wired, [Authorize] gates) · `PUT /api/it-tickets/{guid}/assign` unauth bare=**411** then **WITH body `-d '{}'`=401** (IIS demands Content-Length before auth eval; 411 is pre-auth Length-check NOT routing-miss) · control fake route `/it-tickets/zzz`=**404** (proves 401s are real auth gates not catch-all). 0 regression. **LESSON (411 vs 401 on bodyless PUT/POST):** unauth bodyless PUT/POST to a JSON-body endpoint returns **411 Length Required** from IIS BEFORE the [Authorize] filter runs — NOT a 404/route-miss. Re-send with `-d '{}'` to force auth eval → real 401. Consistent w/ Run #367 `PUT /adjust=411` + #364 `POST /approve=411` (same pattern, now explained). Tag `[s54, run376, pass, it-reassign-authz, no-mig, 411-precheck-lesson]`.
- **2026-06-08 Run #371 (run_number 257) sha=`30a99aa` PASS ~4m18s (S50 HMW-Wave2 P11-C Vehicle+Driver catalogs Mig 44 + gotcha #57 filtered-unique 3 HRM catalog Mig 45 — BE+FE×2+2Mig+tests):** Push `f8179c5..30a99aa` 1 commit 28 files: BE Domain `Vehicle.cs`/`Driver.cs` + App `HrmConfigFeatures.cs`+IApplicationDbContext + `HrmConfigsController` + 5 Config (Driver/Vehicle/LeaveType/OtPolicy/ShiftPattern) + DbContext + DbInitializer + MenuKeys + Mig44/45 (6 files) + FE×2 (HrmConfigsPage/Layout/menuKeys/hrm-config.ts) + `HrmConfigFilteredUniqueTests.cs` (+5 test → 181→**186**). All BE/FE/Mig, none in paths-ignore → CI ran. Poll iter3 status=success (started 10:32:58 → 10:37:16). **Bundle ROTATE admin `DPPTx2Kw→Cg9mvltU` + user `CjoUEsoV→YgqDvsqr`** (BOTH changed ✓ FE shipped, verified AFTER status=success). **NEW LESSON (timing trap):** pre-success snapshot showed transient `CVbyotwa`/`BBlyMlJH` (intermediate FE copy mid-deploy, NOT final) → re-verify post-success gave real `Cg9mvltU`/`YgqDvsqr`. Confirms anti-pattern #3 + Run #242 lesson: NEVER trust bundle hash until status=success; mid-deploy can show a 3rd transient hash. **Mig 44+45 auto-applied prod** (`__EFMigrationsHistory` top2 = FilterHrmCatalog... + AddVehicleAndDriver...). **Vehicles+Drivers tables EXIST**; sys.tables=**92** (was 90 +2, narrative-93 = convention diff, NOT missing). **gotcha #57 LIVE — all 5 idx filtered:** IX_{Vehicles,Drivers,LeaveTypes,ShiftPatterns,OtPolicies}_Code ALL `is_unique=1 filter=([IsDeleted]=(0))` (3 HRM ones LeaveType/Shift/OtPolicy were NULL pre-Mig45 → now filtered = proof applied). Health live+ready 200 + admin/eoffice index 200. New endpoint `GET /api/hrm-configs/{vehicles,drivers}` unauth=**401** (route wired, no crash) + admin auth=**200** seed 2/catalog (vehicles XE-01/XE-02, drivers TX-01/TX-02 ✓ DbInitializer infra seed ran). 0 regression. Tag `[s50, run371, pass, p11c-vehicle-driver, mig44-45, gotcha57-filtered-5idx]`. - **2026-06-08 Run #371 (run_number 257) sha=`30a99aa` PASS ~4m18s (S50 HMW-Wave2 P11-C Vehicle+Driver catalogs Mig 44 + gotcha #57 filtered-unique 3 HRM catalog Mig 45 — BE+FE×2+2Mig+tests):** Push `f8179c5..30a99aa` 1 commit 28 files: BE Domain `Vehicle.cs`/`Driver.cs` + App `HrmConfigFeatures.cs`+IApplicationDbContext + `HrmConfigsController` + 5 Config (Driver/Vehicle/LeaveType/OtPolicy/ShiftPattern) + DbContext + DbInitializer + MenuKeys + Mig44/45 (6 files) + FE×2 (HrmConfigsPage/Layout/menuKeys/hrm-config.ts) + `HrmConfigFilteredUniqueTests.cs` (+5 test → 181→**186**). All BE/FE/Mig, none in paths-ignore → CI ran. Poll iter3 status=success (started 10:32:58 → 10:37:16). **Bundle ROTATE admin `DPPTx2Kw→Cg9mvltU` + user `CjoUEsoV→YgqDvsqr`** (BOTH changed ✓ FE shipped, verified AFTER status=success). **NEW LESSON (timing trap):** pre-success snapshot showed transient `CVbyotwa`/`BBlyMlJH` (intermediate FE copy mid-deploy, NOT final) → re-verify post-success gave real `Cg9mvltU`/`YgqDvsqr`. Confirms anti-pattern #3 + Run #242 lesson: NEVER trust bundle hash until status=success; mid-deploy can show a 3rd transient hash. **Mig 44+45 auto-applied prod** (`__EFMigrationsHistory` top2 = FilterHrmCatalog... + AddVehicleAndDriver...). **Vehicles+Drivers tables EXIST**; sys.tables=**92** (was 90 +2, narrative-93 = convention diff, NOT missing). **gotcha #57 LIVE — all 5 idx filtered:** IX_{Vehicles,Drivers,LeaveTypes,ShiftPatterns,OtPolicies}_Code ALL `is_unique=1 filter=([IsDeleted]=(0))` (3 HRM ones LeaveType/Shift/OtPolicy were NULL pre-Mig45 → now filtered = proof applied). Health live+ready 200 + admin/eoffice index 200. New endpoint `GET /api/hrm-configs/{vehicles,drivers}` unauth=**401** (route wired, no crash) + admin auth=**200** seed 2/catalog (vehicles XE-01/XE-02, drivers TX-01/TX-02 ✓ DbInitializer infra seed ran). 0 regression. Tag `[s50, run371, pass, p11c-vehicle-driver, mig44-45, gotcha57-filtered-5idx]`.
- **2026-06-03 Run #369 (run_number 255) sha=`350b2bf` PASS ~4m13s (S48 FE-only login subtitle a11y `text-slate-500→600`, ZERO BE/Mig):** Push range `7bbfa5a..350b2bf` 2 commits: `009dd94` DOCS/GOVERNANCE-only (9 files: STATUS/HANDOFF + 3 adap-reports + error-ledger + session-log + frontend-designer MEMORY + session-end.md cmd — ALL `.md`/`.claude/**`) + `350b2bf` CODE 2 files `fe-{admin,user}/src/pages/LoginPage.tsx` (1-line each, slate-500→600 subtitle contrast). Mixed push: `.tsx` present → **NOT path-filter skipped, full pipeline RAN** (gotcha #41 Discovery #3 — ≥1 non-ignored file in range ⟹ whole range builds; docs commit alone would skip but `.tsx` overrides). Poll iter5 status=success (started 00:06:33 → 00:10:46). **Bundle ROTATE admin `Krjvg_3j→DPPTx2Kw` + user `6sNStgxa→CjoUEsoV`** (BOTH changed ✓ FE shipped — verified AFTER status=success; pre-deploy snapshot iter0 still showed OLD `Krjvg_3j`/`6sNStgxa`, correct timing per anti-pattern #3). **NO migration** — repo 43 == prod `__EFMigrationsHistory` 43, latest both `...FilterHolidayUniqueIndexByIsDeleted` (Mig 43 unchanged, BE/Domain untouched ✓). Health live+ready 200 + admin/eoffice index 200. Test gate 181 (CI both proj pre-deploy ⟹ success=passed). 0 regression. NEW LESSON: smallest possible FE change (1-line className) still rotates bundle hash — Vite content-hash sensitive to any source byte; mixed docs+tsx push is the canonical case where docs-only-skip does NOT apply. Tag `[s48, run369, pass, fe-only-a11y, mixed-push-not-skipped]`. - **2026-06-03 Run #369 (run_number 255) sha=`350b2bf` PASS ~4m13s (S48 FE-only login subtitle a11y `text-slate-500→600`, ZERO BE/Mig):** Push range `7bbfa5a..350b2bf` 2 commits: `009dd94` DOCS/GOVERNANCE-only (9 files: STATUS/HANDOFF + 3 adap-reports + error-ledger + session-log + frontend-designer MEMORY + session-end.md cmd — ALL `.md`/`.claude/**`) + `350b2bf` CODE 2 files `fe-{admin,user}/src/pages/LoginPage.tsx` (1-line each, slate-500→600 subtitle contrast). Mixed push: `.tsx` present → **NOT path-filter skipped, full pipeline RAN** (gotcha #41 Discovery #3 — ≥1 non-ignored file in range ⟹ whole range builds; docs commit alone would skip but `.tsx` overrides). Poll iter5 status=success (started 00:06:33 → 00:10:46). **Bundle ROTATE admin `Krjvg_3j→DPPTx2Kw` + user `6sNStgxa→CjoUEsoV`** (BOTH changed ✓ FE shipped — verified AFTER status=success; pre-deploy snapshot iter0 still showed OLD `Krjvg_3j`/`6sNStgxa`, correct timing per anti-pattern #3). **NO migration** — repo 43 == prod `__EFMigrationsHistory` 43, latest both `...FilterHolidayUniqueIndexByIsDeleted` (Mig 43 unchanged, BE/Domain untouched ✓). Health live+ready 200 + admin/eoffice index 200. Test gate 181 (CI both proj pre-deploy ⟹ success=passed). 0 regression. NEW LESSON: smallest possible FE change (1-line className) still rotates bundle hash — Vite content-hash sensitive to any source byte; mixed docs+tsx push is the canonical case where docs-only-skip does NOT apply. Tag `[s48, run369, pass, fe-only-a11y, mixed-push-not-skipped]`.

View File

@ -1,6 +1,6 @@
--- ---
name: ef-core-migration 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ó 47 migration sẵn (Init → FilterMasterCatalogUniqueIndexesByIsDeleted Mig 47, S53). 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ó 48 migration sẵn (Init → AddProjectMasterFields Mig 48, S55). Snapshot + Designer + Migration 3-file rule bắt buộc commit đủ.
when-to-use: when-to-use:
- "thêm migration" - "thêm migration"
- "EF Core 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`. > **Context:** .NET 10 + EF Core 10 + SQL Server. DbContext: `ApplicationDbContext` ở `Infrastructure/Persistence/`. Startup: `SolutionErp.Api`.
## Migration history (47 migration hiện có) ## Migration history (48 migration hiện có)
| # | Name | Tables added / changed | | # | Name | Tables added / changed |
|---|---|---| |---|---|---|
@ -67,8 +67,9 @@ when-to-use:
| **45** | **`FilterHrmCatalogUniqueIndexesByIsDeleted`** | **🎯 S51 — LeaveType + ShiftPattern + OtPolicy Code UNIQUE filtered `WHERE [IsDeleted]=0` (3 HRM catalog, gotcha #57 EXT). Index-only.** | | **45** | **`FilterHrmCatalogUniqueIndexesByIsDeleted`** | **🎯 S51 — LeaveType + ShiftPattern + OtPolicy Code UNIQUE filtered `WHERE [IsDeleted]=0` (3 HRM catalog, gotcha #57 EXT). Index-only.** |
| **46** | **`AddSlaFieldsToItTicket`** | **🎯 Phase 11 P11-D (S52) — ItTickets +SlaDueAt/SlaWarnedSent/SlaBreached (SLA timer). Column-only, no new table.** | | **46** | **`AddSlaFieldsToItTicket`** | **🎯 Phase 11 P11-D (S52) — ItTickets +SlaDueAt/SlaWarnedSent/SlaBreached (SLA timer). Column-only, no new table.** |
| **47** | **`FilterMasterCatalogUniqueIndexesByIsDeleted`** | **🎯 S53 — Department + Supplier + Project Code UNIQUE filtered `WHERE [IsDeleted]=0` (gotcha #57 EXT Master, 6× cumulative). Index-only.** | | **47** | **`FilterMasterCatalogUniqueIndexesByIsDeleted`** | **🎯 S53 — Department + Supplier + Project Code UNIQUE filtered `WHERE [IsDeleted]=0` (gotcha #57 EXT Master, 6× cumulative). Index-only.** |
| **48** | **`AddProjectMasterFields`** | **🎯 S55 — Project +4 cột nullable (Year int · Investor 250 · Location 500 · Package 300). AddColumn-only, no new table. Kèm `SeedRealMasterDataAsync` ungated nạp 62 dự án + 71 hạng mục + 3 NCC real từ Excel.** |
Total: **92 bảng** dbo + `__EFMigrationsHistory` (last Mig 47 FilterMasterCatalogUniqueIndexesByIsDeleted, index-only). Xem `docs/database/schema-diagram.md` migration table + §11-15 module ERD (§16+ Mig 27-47 chi tiết pending). Total: **92 bảng** dbo + `__EFMigrationsHistory` (last Mig 48 AddProjectMasterFields, column-only). Xem `docs/database/schema-diagram.md` migration table + §11-15 module ERD (§16+ Mig 27-48 chi tiết pending).
## N-stage workflow pattern (Mig 18-20 — Session 12-13) ## N-stage workflow pattern (Mig 18-20 — Session 12-13)

View File

@ -50,7 +50,7 @@ Kiến trúc: **.NET 10 Clean Architecture + 2 React FE (admin + user) + SQL Ser
- Audit fields: `CreatedAt`, `UpdatedAt`, `CreatedBy`, `UpdatedBy` (`BaseEntity`) - Audit fields: `CreatedAt`, `UpdatedAt`, `CreatedBy`, `UpdatedBy` (`BaseEntity`)
- Soft delete: `IsDeleted`, `DeletedAt`, `DeletedBy` (`AuditableEntity`) - Soft delete: `IsDeleted`, `DeletedAt`, `DeletedBy` (`AuditableEntity`)
- Migrations: `dotnet ef migrations add <Name> --project src/Backend/SolutionErp.Infrastructure --startup-project src/Backend/SolutionErp.Api` - Migrations: `dotnet ef migrations add <Name> --project src/Backend/SolutionErp.Infrastructure --startup-project src/Backend/SolutionErp.Api`
- **Hiện có 47 migration → 92 bảng** (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). 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ó 48 migration → 92 bảng** (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). 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 ### Modules

View File

@ -2,10 +2,29 @@
> **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`. > **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-08 (Session 54 — **IT staff tự reassign ticket (cross-stack authz) — prod-verified**. HMW-mode ON. 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-09 (Session 55 — **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.)
--- ---
## S55 (2026-06-09) — Nạp master data thật từ Excel + Project +4 cột (Mig 48 · HMW-mode ON · 1 commit prod-verified)
**Anh: `/session-start` → giao file Excel "HẠNG MỤC CÔNG VIỆC DỰ ÁN" → "nạp master data vào đi nhé" → `/ultra-on "workflow làm xong hết đi nhé"`.**
**Done (commit `69cb393` → Run #377 PASS ~4m33s, prod-verified):**
- **Nạp 62 dự án + 71 hạng mục + 3 NCC** vào `Project`/`WorkItem`/`Supplier` (real company data từ Excel 7-sheet). `WorkItem` master ĐÃ tồn tại → no new table.
- **🟨 BE:** Project +4 cột nullable (Year/Investor/Location/Package) + **Mig 48 `AddProjectMasterFields`** (3-file, no new table) + ProjectFeatures DTO/Create/Update +4 + **`SeedRealMasterDataAsync`** (per-code idempotent, **UNGATED** line 118 → reaches prod, coexist demo).
- **🟧 FE:** ProjectsPage form +4 input ×2 app SHA256 mirror + bonus cột "Chủ đầu tư".
- **Verify:** build 0 · test 216 (compile-fix only) · runtime Dev proof · cicd Run #377 (Mig 48 prod · Projects 6/6 · WorkItems 71 · Suppliers 3 · CAL01.Investor exact · bundle `B-d6893W`/`XdKzt9LL` rotate · health 200).
- **Decisions:** Q1 cột thật+migration · Q2 deploy prod. FLOCK01 collision → skip (demo wins).
- ⚠️ **Lessons:** 2 agent return truncated (implementer-backend + reviewer, gotcha #53) → em main recover qua disk/runtime (build/test/sqlcmd/git), KHÔNG agent return-message. cicd **verdict-FIRST brief → KHÔNG truncate** (PASS clean) — reinforces emit-verdict-early. Real-data import: extract Excel → generated spec file (chống brief-truncation) → runtime Dev proof TRƯỚC prod.
**🔴 NEXT SESSION (anh pick):**
- **Phase 9 Ops** (go-live blockers — anh main coordinate): SMTP outbound (em code-able greenfield `IEmailSender`) · SQL backup register (`scripts/backup-sql.ps1` READY → schtasks) · rotate creds · UAT real-user.
- **Doc-drift D1** (H1-flagged, anh dừng S55): `agents/README.md:4` database-agent "🔴 Cần CLI restart → executed-file" → "✅ verified-runtime S53" (1 dòng).
- **Monthly drift audit 2026-07-01** (cron) — investigator-codebase ground-truth + H1 chốt; re-ground cicd `sys.tables=93` vs 92.
- **Optional:** master data — nếu cần thêm field Project (StartDate/EndDate/Budget cho dự án thật) hoặc full NCC list (hiện 3 mẫu) · sửa tên demo FLOCK01 nếu chồng real.
- **Cert** `api.solutions.com.vn` expire ~2026-07-23 (auto-renew ~06-23).
## S54 (2026-06-08) — IT staff tự reassign ticket (cross-stack authz · HMW-mode ON · 1 commit prod-verified) ## S54 (2026-06-08) — IT staff tự reassign ticket (cross-stack authz · HMW-mode ON · 1 commit prod-verified)
**Anh: `/session-start` → "2 xong sau đó là 1" (task 2 reassign trước, task 1 Phase 9 Ops sau) → task 2 done → "/session-end". Task 1 anh dừng (không khởi động).** **Anh: `/session-start` → "2 xong sau đó là 1" (task 2 reassign trước, task 1 Phase 9 Ops sau) → task 2 done → "/session-end". Task 1 anh dừng (không khởi động).**

View File

@ -3,7 +3,7 @@
> **Update rule:** trước khi bắt đầu 1 task → ghi row `🔥 In Progress`. Xong → `✅ Recently Done`. > **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.) > **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-08 (Session 54 — **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 DB1DB11 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-09 (Session 55 — **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 DB1DB11 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,8 +11,9 @@
| Metric | Value | Note | | Metric | Value | Note |
|---|---|---| |---|---|---|
| Migrations | **47** | +S53 Mig 47 `FilterMasterCatalogUniqueIndexesByIsDeleted` (gotcha #57 EXT: Department/Supplier/Project filtered-unique Code — index-only, no new table) | | Migrations | **48** | +S55 Mig 48 `AddProjectMasterFields` (Project +4 cột Year/Investor/Location/Package — AddColumn, no new table) |
| SQL tables | **92** | unchanged S52 (Mig 46 = AddColumn, no new table; cicd `sys.tables` ground-truth) | | SQL tables | **92** | unchanged S55 (Mig 48 = AddColumn, no new table; cicd `sys.tables` ground-truth) |
| Master data (prod) | **real loaded S55** | 62 Projects + 71 WorkItems (Vật tư16/Thầu phụ30/MEP9/Thiết bị16) + 3 Suppliers thật, coexist demo (ungated idempotent seed). 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) | | 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 | | FE pages | **68** | unchanged S54 (ItTicketsPage reassign = in-place 2 app); +1 S52 AttendanceReportPage |
| Menu keys | **~56** | +1 S53 `Off_AttendanceReport` (P11-E promote → sidebar leaf under Văn phòng số, order 8) | | Menu keys | **~56** | +1 S53 `Off_AttendanceReport` (P11-E promote → sidebar leaf under Văn phòng số, order 8) |
@ -23,18 +24,18 @@
| Sub-agents | **11** | Opus 4.8 1M · 9 product/quality (7 core + frontend-designer + database-agent) + 2 monitor INFORM-only (tooling-auditor H1 + harvest-curator H2). ✅ database-agent **verified-runtime S53** (spawn-test PASSED — caught Mig 46-unapplied-local drift) | | Sub-agents | **11** | Opus 4.8 1M · 9 product/quality (7 core + frontend-designer + database-agent) + 2 monitor INFORM-only (tooling-auditor H1 + harvest-curator H2). ✅ database-agent **verified-runtime S53** (spawn-test PASSED — caught Mig 46-unapplied-local drift) |
| RAG chunks | **2416** | Recovered S52 (S51 Qdrant DOWN → nay alive, rerank live 0.9375). Stale `last_indexed 05-29` (S42-S52 via store_memory stopgap; full re-index = AI_INFRA op cần VOYAGE_API_KEY). | | RAG chunks | **2416** | Recovered S52 (S51 Qdrant DOWN → nay alive, rerank live 0.9375). Stale `last_indexed 05-29` (S42-S52 via store_memory stopgap; full re-index = AI_INFRA op cần VOYAGE_API_KEY). |
**Bundle hash live (prod):** admin `DmjI8Cmn` · user `YxL_MljK` (S54 — cả 2 rotate, ItTicketsPage reassign 2 app). Deploy verified Run #376 (`ca4b602`, ~4m18s): `GET /it-tickets/assignable-staff` 401 wired · `PUT /assign` 401 (with body — IIS 411 bodyless pre-auth) · health 200 · Mig giữ 47 (no new). **Bundle hash live (prod):** admin `B-d6893W` · user `XdKzt9LL` (S55 — cả 2 rotate, ProjectsPage form +4 cột 2 app). Deploy verified Run #377 (`69cb393`, ~4m33s): Mig 48 applied prod · Projects spot-6/6 (APVN01…ZOTE01) · WorkItems VT/TP/MEP/TB=71 · Suppliers(TRUONGGIANG/TANPHU/TGN)=3 · CAL01.Investor="Công ty TNHH Calofic" (N-literal) · health 200/200 · `/api/projects` 401 wired.
**Phase:** ✅ Phase 10 COMPLETE · ✅ **Phase 11 product backlog ĐÓNG TRỌN** — P11-A/B/C/**D/E/F** ALL DONE (deployed prod) · 🚫 Phase 9 Ops blocked (anh main coordinate — S54 chưa khởi động, anh dừng). **Phase:** ✅ Phase 10 COMPLETE · ✅ **Phase 11 product backlog ĐÓNG TRỌN** — P11-A/B/C/**D/E/F** ALL DONE (deployed prod) · 🚫 Phase 9 Ops blocked (anh main coordinate — S54 chưa khởi động, 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ỳ. > ⚠️ **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 (S54) ## 🔥 In Progress (S55)
| Task | Owner | Status | | Task | Owner | Status |
|---|---|---| |---|---|---|
| _(none — S54 task 2 (ItTicket reassign cross-stack) DONE + prod-verified Run #376. Task 1 (Phase 9 Ops) anh dừng — chưa khởi động. **NEXT (anh pick):** Phase 9 Ops — em đã scope: SMTP email outbound (greenfield code-able, NEW `IEmailSender`+`SmtpEmailSender` config-driven + wire NotificationService) · SQL backup register (`scripts/backup-sql.ps1` READY → em đưa lệnh schtasks, anh chạy VPS) · rotate creds + UAT (anh-infra) · monthly drift audit **2026-07-01**.)_ | 👤 | ✅ | | _(none — S55 master-data import DONE + prod-verified Run #377 (`69cb393`). Real 62 dự án + 71 hạng mục + 3 NCC live prod. **NEXT (anh pick):** Phase 9 Ops (SMTP/backup/creds/UAT) · doc-drift D1 H1-flagged (`agents/README.md:4` database-agent "🔴 executed-file"→"✅ verified-runtime S53", anh dừng S55) · monthly audit 2026-07-01. Prev S54 task 2 (ItTicket reassign) DONE Run #376. Task 1 (Phase 9 Ops) anh dừng — chưa khởi động. **NEXT (anh pick):** Phase 9 Ops — em đã scope: SMTP email outbound (greenfield code-able, NEW `IEmailSender`+`SmtpEmailSender` config-driven + wire NotificationService) · SQL backup register (`scripts/backup-sql.ps1` READY → em đưa lệnh schtasks, anh chạy VPS) · rotate creds + UAT (anh-infra) · monthly drift audit **2026-07-01**.)_ | 👤 | ✅ |
**S40 done:** ✅ Consolidation (`d2f52ba`) · ✅ Curate 4 agent MEMORY >25KB→<8.4KB (`78c9de3`) · RAG catch-up chunk S37-S40 (rerank 0.867) · **AI_INFRA bulletin 2026-05-29 adopt 4/4** (MỤC2 Tiered Memory Policy v1 `6f08d1f` + MỤC3 /session-start+/session-end slash commands `c8ff5e1`). Full RAG re-index = AI_INFRA op (cần VOYAGE_API_KEY). **S40 done:** ✅ Consolidation (`d2f52ba`) · ✅ Curate 4 agent MEMORY >25KB→<8.4KB (`78c9de3`) · RAG catch-up chunk S37-S40 (rerank 0.867) · **AI_INFRA bulletin 2026-05-29 adopt 4/4** (MỤC2 Tiered Memory Policy v1 `6f08d1f` + MỤC3 /session-start+/session-end slash commands `c8ff5e1`). Full RAG re-index = AI_INFRA op (cần VOYAGE_API_KEY).
@ -44,6 +45,15 @@
## ✅ Recently Done (newest on top — 3 session; cũ hơn → session logs) ## ✅ Recently Done (newest on top — 3 session; cũ hơn → session logs)
### S55 (2026-06-09) — ✅ Nạp master data thật từ Excel + Project +4 cột (Mig 48) — HMW-mode ON, prod-verified
- **Commit `69cb393` Gitea Run #377 PASS ~4m33s, prod-verified.** Anh: `/session-start` giao file Excel "HẠNG MỤC CÔNG VIỆC DỰ ÁN" "nạp master data vào đi nhé" `/ultra-on "workflow làm xong hết"`.
- **Nạp 3 nguồn thật** (Excel 7-sheet loadable): **62 Projects** (+Năm; ~6 CĐT/địa điểm/gói thầu) · **71 WorkItems** hạng mục (Vật tư16/Thầu phụ30/MEP9/Thiết bị16) · **3 Suppliers** (TRUONGGIANG/TANPHU/TGN). `WorkItem` master ĐÃ tồn tại no new table cho hạng mục.
- **🟨 BE:** `Project.cs` +4 prop nullable (Year/Investor/Location/Package) + `ProjectConfiguration` maxlen 250/500/300 + **Mig 48 `AddProjectMasterFields`** (3-file, 4 AddColumn reversible) + `ProjectFeatures` DTO/Create/Update +4 + **`SeedRealMasterDataAsync`** (62+71+3 tuple-loop per-code idempotent, wired **UNGATED** line 118 sau SeedCatalogsAsync reaches prod, coexist demo).
- **🟧 FE:** `ProjectsPage.tsx` form +4 input (Năm/Chủ đầu /Địa điểm/Gói thầu) + bonus cột "Chủ đầu " + `types/master.ts` +4 **2 app SHA256-identical**. npm build ×2 PASS.
- **Verify:** build 0-err · test **216 PASS** (compile-fix `MasterCatalogFilteredUniqueTests` +4 null args, no new test) · **runtime Dev proof** (data landed, Investor col works) · **cicd Run #377 PASS** (Mig 48 applied prod · Projects spot-6/6 · WorkItems 71 · Suppliers 3 · CAL01.Investor exact · bundle admin `B-d6893W`/user `XdKzt9LL` · health 200).
- **Decisions (anh chốt):** Q1 thêm cột thật+migration (không Note-pack) · Q2 cả 3 Dev+deploy prod. **FLOCK01 collision** per-code skip (demo wins). Prod totals: Projects=70 (62 real+8 demo), WorkItems=86.
- **2 agent return truncated** (implementer-backend + reviewer, gotcha #53) em main recover qua disk/runtime truth (`feedback_agent_kill_recovery`); cicd verdict-FIRST PASS clean. Data-quality catch: MEP col gộp MEP-Sub+THIẾT BỊ divider split đúng. session log `2026-06-09-S55-master-data-import.md`.
### S54 (2026-06-08) — ✅ IT staff tự reassign ticket (cross-stack authz, HMW-mode ON) — 1 commit prod-verified ### S54 (2026-06-08) — ✅ IT staff tự reassign ticket (cross-stack authz, HMW-mode ON) — 1 commit prod-verified
- **Commit `ca4b602` Gitea Run #376 PASS ~4m18s, prod-verified.** Anh: `/session-start` "2 xong sau đó 1" (task 2 reassign trước, task 1 Phase 9 Ops sau) task 2 done, task 1 anh dừng `/session-end`. - **Commit `ca4b602` Gitea Run #376 PASS ~4m18s, prod-verified.** Anh: `/session-start` "2 xong sau đó 1" (task 2 reassign trước, task 1 Phase 9 Ops sau) task 2 done, task 1 anh dừng `/session-end`.
- **Feature (cho tổ IT tự reassign KHÔNG chỉ Admin như S53):** "IT staff" = `User.DepartmentId == Department(Code=="IT").Id` (reuse round-robin S52 predicate). Cross-stack, **NO migration** (DepartmentId reuse), no menu change. - **Feature (cho tổ IT tự reassign KHÔNG chỉ Admin như S53):** "IT staff" = `User.DepartmentId == Department(Code=="IT").Id` (reuse round-robin S52 predicate). Cross-stack, **NO migration** (DepartmentId reuse), no menu change.

View File

@ -0,0 +1,47 @@
# S55 (2026-06-09) — Nạp master data thật từ Excel + Project +4 cột (Mig 48) — HMW-mode ON, prod-verified
> **Commit `69cb393` → Gitea Run #377 PASS ~4m33s, prod-verified.** Anh: `/session-start` → giao file Excel "HẠNG MỤC CÔNG VIỆC DỰ ÁN" → "nạp master data vào đi nhé" → `/ultra-on "workflow làm xong hết đi nhé"`. HMW fan-out cross-stack, ship to prod.
## Yêu cầu
Nạp master data thật của công ty từ file Excel 7-sheet (`00- DATA- HẠNG MỤC CÔNG VIỆC DỰ ÁN - trình ký online (1).xlsx`) vào hệ thống.
## Quyết định (anh chốt qua AskUserQuestion)
- **Q1 schema:** Project thiếu 4 field (Năm/Chủ đầu tư/Địa điểm/Gói thầu) → **thêm cột thật + migration** (không gói Note).
- **Q2 target:** **Cả 3 entity → Dev rồi deploy prod** (coexist với demo master cũ).
## Data inventory (Excel 7 sheet → 3 nguồn loadable)
| Sheet | → Bảng | Số |
|---|---|---|
| `00- Project list` | `Project` | **62** dự án (Mã + Năm; ~6 có tên/CĐT/địa điểm/gói thầu đầy đủ) |
| `01_Final - Hạng mục` / `00- DATA` | `WorkItem` | **71** (Vật tư 16 · Thầu phụ 30 · MEP 9 · Thiết bị 16) |
| `00- Data list TP_NCC` | `Supplier` | **3** mẫu (TRUONGGIANG/TANPHU/TGN — template 40 cột) |
**Data-quality catch (em main):** sheet Hạng mục cột MEP **gộp 2 nhóm** (9 MEP Sub + divider "THIẾT BỊ" + 16 Equipment). Pass 1 lùa hết 26 vào "MEP" + bắt nhầm divider "THIẾT BỊ" làm item → **fix split** (col J label) → đúng 71 item / 4 category. Provenance: `scripts/master-import-data.generated.md` (generated, source-of-truth cho seed).
## HMW flow (8-agent roster, fan-out + sequential verify)
- **P0/P1 recon** — 🟦 investigator-codebase: map Master schema thật. KEY finding: `WorkItem` master **ĐÃ TỒN TẠI** (Code/Name/Category/IsActive) → Hạng mục resolves favorably (no new table). Seed pattern = `SeedDemoMasterDataAsync` per-code idempotent **UNGATED** (reaches prod by design).
- **P2 execute (parallel, file-disjoint):**
- 🟨 implementer-backend: Project entity +4 (`Year int?` · `Investor/Location/Package string?` maxlen 250/500/300) + `ProjectConfiguration` + **Mig 48 `AddProjectMasterFields`** (3-file, applied Dev+Design) + `ProjectFeatures` DTO/Create/Update/validators/handlers/projections +4 + **`SeedRealMasterDataAsync`** (62+71+3, per-code idempotent, wired UNGATED line 118 sau SeedCatalogsAsync).
- 🟧 implementer-frontend: `ProjectsPage.tsx` form +4 input (Năm/Chủ đầu tư/Địa điểm/Gói thầu) + bonus cột "Chủ đầu tư" + `types/master.ts` +4 — **2 app SHA256-identical** (`b0020610…` / `93ac1b0f…`). npm build ×2 PASS.
- **Runtime Dev proof (em main):** chạy API trên Dev → DbInitializer seed → sqlcmd verify: 62 proj/71 wi/3 sup landed, `CAL01.Investor` populates, 0 overflow/dup-crash.
- **P4 reviewer** 🟥: ran 293s/31-tools nhưng **truncated trước verdict** (gotcha #53) → em main hoàn tất đúng việc nó định (clean `dotnet test` = 216 PASS, giải tỏa cached-binary concern) + 10-dim verify.
- **P3 harvest** 👤: proxy-append 2 MEMORY truncated (implementer-backend + reviewer); FE+investigator self-appended. Containment: git-diff legit-only + Qdrant chunk 2418=2418 (0 rogue RAG-write).
- **commit → cicd** 🟩: Run #377 PASS prod-verified.
## Verification
- **Build 0-err · Test 216 PASS** (58 Domain + 158 Infra; +1 compile-fix dòng `MasterCatalogFilteredUniqueTests` thêm 4 null args cho CreateProjectCommand — necessary, không phải scope-creep).
- **Runtime Dev:** all real data landed, Investor col works.
- **Prod (Run #377 / run_number 263):** Mig 48 applied · Projects spot-6/6 (APVN01…ZOTE01) · WorkItems VT/TP/MEP/TB=71 · Suppliers 3 · CAL01.Investor="Công ty TNHH Calofic" (N-literal) · bundle admin `DmjI8Cmn``B-d6893W` / user `YxL_MljK``XdKzt9LL` · health 200/200 · /api/projects 401 wired. Prod totals: Projects=70 (62 real + 8 demo coexist), WorkItems=86.
## Key design facts
- **Ungated seed = reaches prod:** `SeedDemoMasterDataAsync` (suppliers/projects) + `SeedCatalogsAsync` (workitems) đều gọi NGOÀI gate `if(!demoSeedDisabled)` (line 108/115) — by design (DbInitializer:75 comment "GIỮ … SeedDemoMasterData/Catalogs" cả khi DemoSeed:Disabled=true). `SeedRealMasterDataAsync` mirror = wired ungated line 118 → real data tự lên prod khi deploy.
- **Per-code idempotent** (`existingCodes.Contains(code)→skip; else Add`) — re-run safe, coexist demo, expand-over-time. **FLOCK01 collision** (demo có sẵn) → real FLOCK01 skip (demo thắng; real code+year only, acceptable). Note cho công ty: sửa tên FLOCK01 qua UI nếu cần.
- **No new table** (Mig 48 = 4 AddColumn) → tables vẫn 92. Mig 47→**48**.
## Lessons
- **2 agent return truncated** (implementer-backend + reviewer, gotcha #53) → em main recover qua **disk/runtime truth** (build/test/sqlcmd/git), KHÔNG agent return-message (`feedback_agent_kill_recovery`). cicd verdict-FIRST brief → KHÔNG truncate (PASS clean) — reinforces "emit verdict early".
- **Real-data import discipline:** extract Excel → clean generated spec file (chống truncation khi nhồi 136 dòng vào brief) → implementer convert to C# → runtime Dev proof TRƯỚC prod (catch overflow/dup) → cicd sqlcmd spot-check real Codes + N-literal cho unicode field.
- **Data-quality:** đọc cấu trúc Excel THẬT (merged col, divider row) trước khi map — pass 1 nhầm divider "THIẾT BỊ" = item; split đúng theo label col.
## Files (17 committed `69cb393` + scratch)
BE: `Project.cs` · `ProjectConfiguration.cs` · `ProjectFeatures.cs` · `DbInitializer.cs` (+210, seed) · `Mig 48` 3-file. FE: `ProjectsPage.tsx` + `types/master.ts` ×2 app. Test: `MasterCatalogFilteredUniqueTests.cs` (compile-fix). Provenance: `scripts/master-import-data.generated.md`. Memory: 4 agent MEMORY (2 self + 2 proxy).