From ed5a239212503377c06d5ca89d8176ea6d1c3e5a Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Tue, 9 Jun 2026 09:45:04 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20Docs:=20S55=20closeout=20=E2=80=94?= =?UTF-8?q?=20master=20data=20import=20t=E1=BB=AB=20Excel=20(Run=20#377)?= =?UTF-8?q?=20+=20Mig=2047=E2=86=9248=20+=20ef-core=20skill?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .claude/agent-memory/cicd-monitor/MEMORY.md | 1 + .claude/skills/ef-core-migration/SKILL.md | 7 +-- CLAUDE.md | 2 +- docs/HANDOFF.md | 21 ++++++++- docs/STATUS.md | 22 ++++++--- .../2026-06-09-S55-master-data-import.md | 47 +++++++++++++++++++ 6 files changed, 89 insertions(+), 11 deletions(-) create mode 100644 docs/changelog/sessions/2026-06-09-S55-master-data-import.md diff --git a/.claude/agent-memory/cicd-monitor/MEMORY.md b/.claude/agent-memory/cicd-monitor/MEMORY.md index a12a26b..c9dd8ab 100644 --- a/.claude/agent-memory/cicd-monitor/MEMORY.md +++ b/.claude/agent-memory/cicd-monitor/MEMORY.md @@ -68,6 +68,7 @@ BE (test+build) ~90s · FE × 2 ~60s/app · deploy ~30s · **total ~3min code / ## 📅 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 #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]`. diff --git a/.claude/skills/ef-core-migration/SKILL.md b/.claude/skills/ef-core-migration/SKILL.md index 373dbec..284ae2d 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ó 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: - "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 (47 migration hiện có) +## Migration history (48 migration hiện có) | # | 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.** | | **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.** | +| **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) diff --git a/CLAUDE.md b/CLAUDE.md index eb0514d..8e81d93 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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`) - 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ó 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 diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 66ea427..2fe2ca6 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -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`. -**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) **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).** diff --git a/docs/STATUS.md b/docs/STATUS.md index 063a0c2..81f0237 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-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 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-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 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,8 +11,9 @@ | Metric | Value | Note | |---|---|---| -| Migrations | **47** | +S53 Mig 47 `FilterMasterCatalogUniqueIndexesByIsDeleted` (gotcha #57 EXT: Department/Supplier/Project filtered-unique Code — index-only, no new table) | -| SQL tables | **92** | unchanged S52 (Mig 46 = AddColumn, no new table; cicd `sys.tables` ground-truth) | +| Migrations | **48** | +S55 Mig 48 `AddProjectMasterFields` (Project +4 cột Year/Investor/Location/Package — AddColumn, no new table) | +| 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) | | 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) | @@ -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) | | 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). > ⚠️ **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 | |---|---|---| -| _(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). @@ -44,6 +45,15 @@ ## ✅ 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** (Mã+Năm; ~6 có 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 tư/Địa điểm/Gói thầu) + bonus cột "Chủ đầu tư" + `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 - **Commit `ca4b602` → Gitea Run #376 PASS ~4m18s, prod-verified.** Anh: `/session-start` → "2 xong sau đó là 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. diff --git a/docs/changelog/sessions/2026-06-09-S55-master-data-import.md b/docs/changelog/sessions/2026-06-09-S55-master-data-import.md new file mode 100644 index 0000000..cd41298 --- /dev/null +++ b/docs/changelog/sessions/2026-06-09-S55-master-data-import.md @@ -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).