diff --git a/.claude/WAL.md b/.claude/WAL.md index a626273..a96f7cd 100644 --- a/.claude/WAL.md +++ b/.claude/WAL.md @@ -31,15 +31,24 @@ chain: Vite-8-minify-backtick làm regex-marker FAIL-OAN ×2 → luật grep -F, vai đã ghi MEMORY — sub-cicd-verify-k6.md). ✅ CLARIFY-2 CHỐT @S167 (anh: "Admin (Site) để nguyên"): giữ nguyên cả 2 — Hrm/Off/Personal tạm 0 lối menu 2 app (route/quyền còn nguyên; bật lại = màn Menu eOffice, 0 code) -[!] K7 — 2 lane ĐANG nền @S167 (spec-cum3 ② vá 1/3/4/6/16): BE = grant Contracts.Create - [câu III default Drafter+Procurement+Admin, comment chờ-ratify] + hội-2-khóa endpoint + guard 2-cờ + - Line.ContractId-ghi-luôn + DTO expose + nạp Code/SLA/pin (sub-implbe-k7.md) ⟂ FE = nút gate hội-2-khóa - + dialog khuôn-sẵn + cây GĐ3/GĐ4 TẬP lines[].contractId ∪ pe.contractId ×2 app (sub-implfe-k7.md). - Cửa chết → ĐỌC 2 FILE ĐÓ trước, đừng spawn lại. CÒN SAU LANE: test-lane 8+2-authz+idempotency → - gate-K7 → commit → cicd +[!] K7 — BE DONE / FE+test ĐANG nền @S167. 🔴 BE-lane BÁC tiền-đề lead "bridge đã tồn tại" (4 phép đo — + W6 chỉ có ĐƯỜNG-ỐNG; lead nhập nhằng 2 khái niệm) → DỰNG bridge theo invest §K7.1 pin-sẵn + 5 vá: + seeder SeedContractCreateAccessAsync :2757 (Drafter/Procurement/Admin upgrade-only — lỗ thật: trước + S167 0-role có Contracts.Create) · POST {id}/create-contract hội-2-khóa AND :230-238 · guard 4-vế + throw-tách :132-148 · Line.ContractId write-site ĐẦU-TIÊN :237-238 + list-DTO +ContractIds · vá-6 + codegen/SLA/pin (spec THẮNG invest chỗ WorkflowDefinitionId). Build 0/0 · runtime 34/35 (1 fail + = AuthorizePolicyRegressionTests:398 đòi-prefix, bridge CỐ Ý Contracts.Create #85 → test-lane thêm + ngoại-lệ CreateContract). 4 file uncommitted (3 M + 1 mới CreateContractFromSigningPlanFeatures.cs) + — sub-implbe-k7.md §0-§6 trọn. FE-lane resumed giữa vá-4b (fe-user 2 file M + KhkkDetailPage đang + dựng nút — sub-implfe-k7.md) ⟂ test-lane spawn @sau-BE (8-case + 2-authz + idempotency + :398 + ngoại-lệ — sub-test-k7.md). Cửa chết → ĐỌC 3 SUB-FILE trước, đừng spawn lại. CÒN: gate-K7 → commit + → cicd [ ] K8-prep form-14-người fail-closed + gói SOLUTION-PE-{n} + QĐ9 đo-2-nhánh + ZZTEST + rollback-theo-ID -[ ] closeout-nợ: 🆕 slot(57) anh chốt "AUTO" @S167 ⇒ em chọn SPLIT _INDEX (không nâng cap) + wire - auto-rotate khi chạm ngưỡng — LÀM SAU khi cicd-K6 trả (tránh đua file archive cicd-monitor) · +[ ] closeout-nợ: ✅ slot(57) ĐÓNG @S167 (anh "auto"): SPLIT _INDEX 21.468→8.756B (42,7% cap) + + _INDEX-2026-H1.md 13.906B tầng-lạnh · vá kèm 3-bệnh-sẵn (mega-line #423 · 29-entry lạc-section · + needle-quote test-specialist) · 3 script cùng-class → `_INDEX*.md` (gate-2-chỗ chống-haystack-dương-giả + + measure-trục-l2_index + shard-probe-loại-trừ) · luật AUTO-rotate @header-_INDEX + split_rule_S167 + @budget.json · CHỨNG: A7 427/427-PASS-lần-đầu-sạch + probe l2idx-0-over + fault-inject-2-chiều · 🆕 G-1 RATIFIED @S167 (anh: "Đồng ý. Cứ theo khuôn PE") ⇒ grep đóng mọi comment/ghi-chú "chờ ratify" SAU khi lane FE-K7 trả (tránh đua fe-*) · diary vai (C2, gồm ctx-verifier S167 lead-seed) · tally #53 CHỐT derive-from-body (S166 nháp 8 lượt + cicd-K5K4C + gate-K6-garble = 10-cận-dưới / 0 mất công) · nhãn D MIND · STATUS row S163-S167 · spec-cụm1 vá-3 đính-chính diff --git a/.claude/agent-memory/cicd-monitor/archive/_INDEX-2026-H1.md b/.claude/agent-memory/cicd-monitor/archive/_INDEX-2026-H1.md new file mode 100644 index 0000000..1c51094 --- /dev/null +++ b/.claude/agent-memory/cicd-monitor/archive/_INDEX-2026-H1.md @@ -0,0 +1,104 @@ +# CI/CD Monitor — Archive INDEX — 2026-H1 (records ≤ 2026-06) — SPLIT @S167 + +> File TÁCH từ `_INDEX.md` @S167 (owner "auto") — VERBATIM từng dòng, anchor substring giữ nguyên 100%. +> Tầng LẠNH: chỉ mở khi cần record ≤ 2026-06. Gate A7 vẫn verify pointer trong file này (đọc `_INDEX*.md`). +> Legend/pointer-style: xem header `_INDEX.md` (không chép lại — 1 nguồn). + +## 2026-06 (file: `archive/2026-06.md`) + + +- 2026-06-19 · S78 #330 cross-stack NO-MIG enum ApprovalAttachment=5 (attach-on-… · PASS bundle-BOTH-rotate js+css… · `2026-06.md` · substring:"Run #330 (run_number 330, id=444)" +- 2026-06-19 · S76 #318 full-stack Mig56 ProInitial/ProAdjust VERIFIED-APPLIED-PROD · PASS backfill-4rows-0violation… · `2026-06.md` · substring:"Run #318 (run_number 318, id=432)" +- 2026-06-19 · S83 #325 FE-only tiny budget-subitem indent/dash · PASS bundle-BOTH-js-rotate css… · `2026-06.md` · substring:"Run #325 (id=439)" +- 2026-06-17 · FE-only re-skin Văn phòng số 10-page PURO · PASS bundle-BOTH-rotate Wt54PH… · `2026-06.md` · substring:"Run #306 (run_number 306, id420) sha=`c556f6c`" +- 2026-06-17 · FE-both-app Văn phòng số foundation + index.css + BE menu-seed · PASS bundle-BOTH-rotate Bl2o_k… · `2026-06.md` · substring:"Run #305 (run_number 305, id419) sha=`a8bbdae`" +- 2026-06-16 · FE-both-app 1-line CSS-precedence fix (name text-white!) · PASS bundle-BOTH-rotate CNUv1j… · `2026-06.md` · substring:"Run #304 (run_number 304, id418) sha=`37752eb`" +- 2026-06-16 · FE-both-app Hồ sơ NS banner text-polish · PASS bundle-BOTH-rotate D532XZ… · `2026-06.md` · substring:"Run #303 sha=`6983609` PASS" +- 2026-06-16 · FE-both-app PE Link-hồ-sơ file:// render · PASS bundle-BOTH-rotate CcrZqf… · `2026-06.md` · substring:"Run #302 (run_number 302, id416) sha=`536dd6b`" +- 2026-06-16 · TESTS-ONLY BE +23 test (286) · PASS bundle-BOTH-frozen xkSz9B… · `2026-06.md` · substring:"Run #299 (id413) sha=`bcd619d`" +- 2026-06-16 · FE-Admin MIRROR Hồ sơ NS from fe-user · PASS bundle-asymmetric-NGƯỢC a… · `2026-06.md` · substring:"Run #298 (run_number 298, id412) sha=`292d64d`" +- 2026-06-16 · FE-User Hồ sơ NS cosmetic brand800 · PASS bundle-asymmetric user-ro… · `2026-06.md` · substring:"Run #297 (run_number 297, id411) sha=`ab4e681`" +- 2026-06-16 · FE-User Hồ sơ NS 2-col layout · PASS bundle-asymmetric user-ro… · `2026-06.md` · substring:"Run #295 (run_number 295, id409) sha=`456c7a7`" +- 2026-06-16 · CROSS-STACK PE HoSoLink Mig 52 + FE-User 3-panel + rename Dự trù→… · PASS Mig52-applied + bundle-bo… · `2026-06.md` · substring:"Run #293 (run_number 293, id407) sha=`5a0aaa4`" +- 2026-06-13 · CROSS-STACK PE PeWorkItemBudgets vượt-ngân-sách soft-warn (Mig 50… · PASS · `2026-06.md` · substring:"Run #286 (run_number 286, id400)" +- 2026-06-12 · CROSS-STACK PE guard 4-thông-tin mục 3 · PASS · `2026-06.md` · substring:"Run #283 (run_number 283) sha=`37122f0`" +- 2026-06-11 · S59-CLOSE final đóng sổ · PASS · `2026-06.md` · substring:"Run #280 (run_number 280) sha=`69997da`" +- 2026-06-11 · S59-đợt6 CROSS-STACK BE · PASS · `2026-06.md` · substring:"Run #278 (run_number 278) sha=`9c330d2`" +- 2026-06-11 · S59-đợt5 FE-only ×2 · PASS · `2026-06.md` · substring:"Run #277 (run_number 277) sha=`faed59f`" +- 2026-06-11 · S59-đợt4 rename WorkItems 71 idempotent-seed · PASS rename-via-prod-SQL targe… · `2026-06.md` · substring:"Run #276 (run_number 276) sha=`c869d26`" +- 2026-06-11 · S59-đợt3 BE-only DbInitializer · PASS · `2026-06.md` · substring:"Run #275 (run_number 275) sha=`bbd1554`" +- 2026-06-11 · S59 FE×2 PE-list tree regroup · PASS · `2026-06.md` · substring:"Run #273 (run_number 273) sha=`56882ac`" +- 2026-06-11 · S59-đợt2 FE×2 PE-list tree · PASS · `2026-06.md` · substring:"Run #274 (run_number 274) sha=`0eafcd3`" +- 2026-06-11 · SUPERSEDE-CHAIN (benign cancel→shipped) · CANCELLED-benign→PASS-via-3eba… · `2026-06.md` · substring:"Run #385→#386 SUPERSEDE-CHAIN sha=`ea793a4`" +- 2026-06-11 · S58 FE-USER visual redesign · PASS asymmetric fe-user-only · `2026-06.md` · substring:"Run #384 (run_number 270) sha=`e959f72`" +- 2026-06-16 · TEST-GATE COMPILE BREAK (CS7036 ParentId 5th positional, call-sit… · **[FAIL]** ~64s deploy-gated p… · `2026-06.md` · substring:"Run #291 (run_number 291, id405) sha=`8c8179c`" +- 2026-06-11 · S58 FIX #381 lock NO-OP (lock-noop→pw-11to12) · PASS dump-Users-set-before-sco… · `2026-06.md` · substring:"Run #382 (run_number 268) sha=`5998163`" +- 2026-06-11 · S57bis PE gắn WorkItemId loose-Guid (Mig 49) · PASS+1PARTIAL · `2026-06.md` · substring:"Run #381 (run_number 267) sha=`dd117b7`" +- 2026-06-09 · S56 GOLIVE-HARDEN BE fixes · PASS · `2026-06.md` · substring:"Run #379 (run_number 265) sha=`a20cde8`" +- 2026-06-09 · S56 pre-golive re-verify prod truth · **[audit]** no-deploy read-only · `2026-06.md` · substring:"S56 pre-golive verify — NO deploy" +- 2026-06-09 · S55 Phase-1 FE-Admin visual redesign · PASS asymmetric fe-admin-only… · `2026-06.md` · substring:"Run #378 (run_number 264) sha=`7feb53e`" +- 2026-06-09 · S55 HMW-P4 real master-data seed (Mig 48, 62 dự án+71 hạng mục+3… · PASS ungated-seed sqlcmd-COUNT… · `2026-06.md` · substring:"Run #377 (run_number 263) sha=`69cb393`" +- 2026-06-08 · S54 IT-staff self-reassign ticket · PASS · `2026-06.md` · substring:"Run #376 (run_number 262) sha=`ca4b602`" +- 2026-06-08 · S50 HMW-Wave2 P11-C Vehicle+Driver · PASS Discovery#3 mixed-docs+ts… · `2026-06.md` · substring:"Run #371 (run_number 257) sha=`30a99aa`" +- 2026-06-03 · S48 FE-only login subtitle a11y · PASS · `2026-06.md` · substring:"Run #369 (run_number 255) sha=`350b2bf`" +- 2026-06-01 · S45 Mig 43 filter Holiday UNIQUE · PASS · `2026-06.md` · substring:"Run #368 (run_number 254) sha=`0c5a014`" +- 2026-05-30 · S42 P11-B LeaveBalance business · PASS · `2026-06.md` · substring:"Run #367 (run_number 253) sha=`82d7fcf`" +- 2026-05-30 · S42 P11-A workflow-picker 2-bug + SetWorkflow · PASS · `2026-06.md` · substring:"Run #365 sha=`75df04e`" +- 2026-05-30 · S42 P11-A wire ApproveV2+LevelOpinions · PASS (mem #250 ≠ real Gitea id… · `2026-06.md` · substring:"Run #364 (mem #250) sha=`e7b66cd`" +- 2026-05-28 · S38 SKELETON 5-plan combo Mig 39+40 dual · PASS · `2026-06.md` · substring:"Run #247 sha=`e54a22d`" +- 2026-05-?? · S37 Proposal Mig 37+38 (/api/proposals 200 + QT-DX-V2-001 AppType=4) · PASS [archived] · `2026-06.md` · substring:"Archived Run #246 (S37 Proposal Mig 37+38" +- 2026-06-11 · ⚠️ VỊ-TRÍ-LẠC entry (mem run id ≠ real Gitea — FIFO slot between… · note · `2026-06.md` · substring:"VỊ TRÍ LẠC — entry MỚI 2026-06-11" +- 2026-05-22 · S29 gotcha #51 catch (SeedSampleContractWorkflowV2 nested in demo… · PASS [archived] · `2026-06.md` · substring:"Archived Run #232 (S29 gotcha #51 catch" + +## 2026-05 late (file: `archive/2026-05-q4.md`) + +- 2026-05-27 · BE+FE×2 Danh bạ nội bộ /api/directory (G-O1) · PASS bundle-both-rotate, menu-… · `2026-05-q4.md` · substring:"Run #238 (S34 Plan 2 G-O1 Danh bạ nội bộ" +- 2026-05-26 · BE Hrm CQRS /api/employees + FE×2 + menu (G-H1 Phase2) · PASS bundle-both-rotate, Emplo… · `2026-05-q4.md` · substring:"Run #237 (S33 Plan B G-H1 Phase 2 Task 4+5+6" +- 2026-05-26 · BE Mig 34 EmployeeProfile + Plan C BW1-BW7 +9 test (62 Infra) · PASS gotcha#51 INFRASTRUCTURE-… · `2026-05-q4.md` · substring:"Run #350 (S33 Plan B G-H1 Mig 34 EmployeeProfile" +- 2026-05-26 · S33 startup health-check · **[audit]** HEALTHY, last-5-ru… · `2026-05-q4.md` · substring:"S33 startup health-check — em main spawn read-only verify, VERDICT=HEALTHY" +- 2026-05-26 · S32 wrap curate + Phase9→10 · **[meta]** no-run docs-only skip · `2026-05-q4.md` · substring:"S32 wrap — em main proxy curate + Phase 9 stabilize done" +- 2026-05-26 · S32 startup verify (foundation freshness + 3-endpoint smoke) · **[audit]** no-CI-poll, RAG-he… · `2026-05-q4.md` · substring:"S32 startup verify — no CI poll" +- 2026-05-21 · S26 Plan AG PE-List tree-view UI iteration (#222-#227 cumulative) · PASS hybrid-verify spawn-1×-pe… · `2026-05-q4.md` · substring:"Run #222-#227 cumulative — Plan AG series PE List tree view" + +## 2026-05 mid (file: `archive/2026-05-runs.md`) + +### Run-records (20) +- 2026-05-19 · S25 t7 Plan AF FE userMap fallback resolve historical · PASS bundle-rotate C8TvDy7r/Bv… · `2026-05-runs.md` · substring:"Run #221 id=335 sha=`506cada`" +- 2026-05-19 · S25 t6 Plan AE Changelog UserName 9-sites batch · PASS BE-only bundle-frozen, pr… · `2026-05-runs.md` · substring:"Run #220 id=334 sha=`9ea62be`" +- 2026-05-19 · S25 t5 Plan AD Lịch-sử-duyệt redesign next-target-hint · PASS FE-only bundle-rotate, pa… · `2026-05-runs.md` · substring:"Run #219 id=333 sha=`0aaf2df`" +- 2026-05-19 · S25 t4 Plan AC2 FE merge recover historical Reject · PASS bundle-rotate, synthetic-… · `2026-05-runs.md` · substring:"Run #218 id=332 sha=`25837b6`" +- 2026-05-15 · S24 t1 Plan AA wrap fix sidebar · PASS last-deploy-before-AB, ba… · `2026-05-runs.md` · substring:"Run #214 id=328 sha=`ee0902a`" +- 2026-05-15 · S24 t1 Plan AA IsUserSelectable filter + WfView menu + sidebar (4… · PASS bundle-rotate, Order-shif… · `2026-05-runs.md` · substring:"Run #210 id=324 sha=`ac2c859`" +- 2026-05-15 · S23 t11 Plan U FE sidebar truncate · PASS long-label ellipsis+title… · `2026-05-runs.md` · substring:"Run #209 sha=`86d8806`" +- 2026-05-15 · S23 t10 Plan T5+T6 docs final (DemoSeed wipe ~720 rows + force re… · PASS flag-proven-end-to-end · `2026-05-runs.md` · substring:"Run #208 sha=`7b7b28f`" +- 2026-05-15 · S23 t10 Plan T DemoSeed disable (flag in appsettings, 5 demo-seed… · PASS · `2026-05-runs.md` · substring:"Run #207 sha=`0b97840`" +- 2026-05-15 · S23 t7 Plan Q FE banner mx-5 fix · PASS bundle-rotate QZIPWD-g/Da… · `2026-05-runs.md` · substring:"Run #204 sha=`108268a`" +- 2026-05-15 · S23 t6 Plan P HOTFIX Controller TransitionPeBody +3 fields · PASS Discovery#4 ASP.NET10-rec… · `2026-05-runs.md` · substring:"Run #203 id=317 sha=`1727bd5`" +- 2026-05-15 · S23 t5 Plan O HOTFIX cascade 4 lookup-sites (point 9) · PASS 409-mode-not-403-actor pr… · `2026-05-runs.md` · substring:"Run #202 id=316 sha=`a1c8386`" +- 2026-05-15 · S23 t4 Plan N HOTFIX per-NV lookup discrimination :765 · PASS 7/7-vs-1/7-per-actor, bug… · `2026-05-runs.md` · substring:"Run #201 id=315 sha=`fb3c22c`" +- 2026-05-15 · S23 t3 Plan M Service F1 OneLevel/OneStep edge-case Bước1 reset · PASS Discovery#3-anomaly docs-… · `2026-05-runs.md` · substring:"Run #200 id=314 sha=`f4055a1`" +- 2026-05-14/15 · S23 t1 K10 hotfix AwLevelDto +AllowApproverSkipToFinal · PASS 8-surface-point-checklist… · `2026-05-runs.md` · substring:"Run #195 id=309 sha=`0062fcb`" +- 2026-05-14 · S23 t1 Plan K Mig 31 per-Approver-slot refactor · **[PARTIAL]** Mig+schema OK bu… · `2026-05-runs.md` · substring:"Run #194 id=308 sha=`098baa6`" +- 2026-05-13 · S22-chốt cumulative verify · PASS Discovery#3 push-range-ev… · `2026-05-runs.md` · substring:"Verify S22 chốt cuối cumulative (push range `3d725c4..cc8a7d3` 12 commits) VERDICT=PASS" +- 2026-05-13 · S22 Plan D Users-toggle + Plan C 14-test + Plan E strict-V2-scope · PASS 103 test, Discovery#1 rat… · `2026-05-runs.md` · substring:"Run #188 id=302 sha=a74e671" +- 2026-05-13 · S21 t5 Mig 29 refactor Allow*-per-NV + Designer 5-checkbox · PASS Mig29 applied, Discovery… · `2026-05-runs.md` · substring:"Run #187 id=301 sha=c0af9e0" +- 2026-05-13 · S21 t3+t4 gotcha#45 Trả-lại + F1/F2/F3 advanced-options + Mig 28 · PASS baseline, Discovery API-t… · `2026-05-runs.md` · substring:"Run #186 id=300 sha=eea86fd" + +### Curate-note meta-headers (6) — [meta] +- 2026-05-19 · curate-note S25 t5 Plan AD #219 · **[meta]** · `2026-05-runs.md` · substring:"added S25 t5 Plan AD Run #219 entry" +- 2026-05-19 · curate-note S25 t3 Plan AC #217 (Discovery#5 sqlcmd 4-backslash \… · **[meta]** · `2026-05-runs.md` · substring:"added S25 t3 Plan AC Run #217 entry" +- 2026-05-19 · curate-note S25 t1 Plan AB #215 (gotcha#48 SQLite tie-break FAIL) · **[meta]** · `2026-05-runs.md` · substring:"added S25 t1 Plan AB Run #215 entry" +- 2026-05-15 · curate-note S24 t1 Plan AA #210 · **[meta]** · `2026-05-runs.md` · substring:"added S24 t1 Plan AA Run #210 entry" +- 2026-05-15 · curate-note S23 t6 Plan P #203 (Discovery#4 numeric-enum) · **[meta]** · `2026-05-runs.md` · substring:"added S23 t6 Plan P HOTFIX Run #203 entry" +- 2026-05-15 · curate-note S23 t5 Plan O #202 (4-site discrimination, Transition… · **[meta]** · `2026-05-runs.md` · substring:"added S23 t5 Plan O HOTFIX Run #202 entry" + +## 2026-05 early (file: `archive/2026-05-q3.md`) + +- 2026-05-19 · ★ Run #215 FAIL → #216 PASS pair — **gotcha #48 SQLite tie-break*… · FAIL→PASS · `2026-05-q3.md` · substring:"Run #215 FAIL → Run #216 PASS (gotcha #48 SQLite tie-break catch+fix pair)" +- 2026-05-13 · Verify S22 chốt cuối cumulative (Discovery#3 first-surfaced, 33 u… · PASS · `2026-05-q3.md` · substring:"Verify S22 chốt cuối cumulative (push range `3d725c4..cc8a7d3` 12 commits VERDICT=PASS" +- 2026-05-12 · Setup baseline (agent init, 44 gotchas + 5-stage + 3 skills) · init · `2026-05-q3.md` · substring:"2026-05-12 — Setup baseline" + +## 2026-05 early (file: `archive/2026-05-q2.md`) + +- 2026-05-22 · Run #231 Plan B Contract V2 wire kick-off · **[PARTIAL]** PASS-deploy + se… · `2026-05-q2.md` · substring:"Run #231 (id=345) Plan B Contract V2 wire kick-off VERDICT=PARTIAL" + diff --git a/.claude/agent-memory/cicd-monitor/archive/_INDEX.md b/.claude/agent-memory/cicd-monitor/archive/_INDEX.md index 6d4c563..f09d263 100644 --- a/.claude/agent-memory/cicd-monitor/archive/_INDEX.md +++ b/.claude/agent-memory/cicd-monitor/archive/_INDEX.md @@ -1,6 +1,8 @@ # CI/CD Monitor — Archive INDEX (table of contents, NO content) -> 🗜️ **gist-of-index @S152 (D1 queue):** desc-verdict nén ~45c (desc đầy-đủ cũ = git-history file này); **toàn bộ anchor substring giữ NGUYÊN-VĂN 100%** — KHÔNG split (gate chỉ đọc `_INDEX.md`). +> 🗜️ **gist-of-index @S152 (D1 queue):** desc-verdict nén ~45c (desc đầy-đủ cũ = git-history file này); **toàn bộ anchor substring giữ NGUYÊN-VĂN 100%**. + +> 🔄 **SPLIT @S167 (owner "auto" 2026-08-01):** records ≤ 2026-06 dời VERBATIM sang `_INDEX-2026-H1.md` (cùng thư mục — tầng lạnh, mở khi cần record cổ). Gate A7 (`memory-archive-gate.ps1`) đọc **MỌI `_INDEX*.md`** nên pointer phần cũ VẪN được canh; probe cap (`nhip-no-probe.ps1`) chỉ đo file CHÍNH này. **LUẬT ROTATE (auto, không hỏi lại):** khi probe in `l2idx N over` → dời TRỌN section tháng CŨ NHẤT của file này sang `_INDEX-.md` tương ứng (tạo mới nếu chưa có), VERBATIM từng dòng, KHÔNG bao giờ drop marker. > **Purpose:** L2 archive is "dark-matter" (not in RAG). This index is the map: 1 line per archived record so a future spawn can locate + open the right verbatim block on-demand without reading 220KB cold. > **Pointer style = SUBSTRING (Ctrl-F) primary.** Each line ends `→ · substring:""`. The string is grep-verified `count==1` inside its target file (76/76 unique at build time). Open the file, Ctrl-F the string → lands on the record start. Fallback if a future edit shifts text: search the bare `Run #NNN` + nearest date. @@ -8,7 +10,7 @@ > **Archives are FROZEN / additive-only.** Records are never edited in place; this index + the `.gist.md` files are the only additive layers. > **SORTED by DATE (newest → oldest).** Labels: `[meta]` = curate-note (not a rich run-record) · `[stub→git]` = FIFO-trim pointer-to-git, thin · `[audit]` = read-only verify, no deploy · `[FAIL]`/`[PARTIAL]` = non-PASS verdict. > -> **Files indexed:** `2026-07.md` (**4** records — re-dem DIA @S159) · `2026-06.md` (39 records, no headings) · `2026-05-runs.md` (20 run-records + 6 meta-headers) · `2026-05-q4.md` (7) · `2026-05-q3.md` (3) · `2026-05-q2.md` (1). Total **80 records**. +> **Files indexed HERE (2026-07+):** `2026-07.md`. Records ≤ 2026-06 (`2026-06.md` · `2026-05-runs.md` · `2026-05-q4.md` · `2026-05-q3.md` · `2026-05-q2.md`) → xem `_INDEX-2026-H1.md`. Đếm record = đếm dòng `^- ` từng file index (đừng tin số chép tay — bài Errata @S159). > 🔴 **Errata @S159:** ban cu khai `2026-07.md`=2 / Total 78; dia co **4** (#407 @S155, #416 @S157). Nang hon: **#407 mo-coi** — co trong kho, 0 con-tro => vo hinh voi nguoi tra `_INDEX`; da them. Bai: **dem-lech la trieu-chung, ban-ghi-mo-coi la thiet hai.** Sweep lai: 0 mo-coi. > Even-older verbatim (S29 #232 ← pre-S38) also lives in git `d2f52ba`. Gist companions: `2026-06.gist.md` + `2026-05.gist.md`. @@ -20,103 +22,7 @@ - 2026-07-01 · S96+S97 FINAL-SESSION-REVIEW 11-commit day #478-#484 ALL-HEALTHY… · CLEAN, bundle rotated-post-#48… · `2026-07.md` · substring:"S96+S97 FINAL-SESSION-REVIEW (2026-07-01 end" - 2026-07-01 · S97 #484 DATA-FIX Mig 61 backfill EndedByLevelFinalize từ Changel… · PASS backfill-flip A/005+A/008… · `2026-07.md` · substring:"S97 #484 (id=484, run_number 370)" -## 2026-06 (file: `archive/2026-06.md`) - - -- 2026-06-19 · S78 #330 cross-stack NO-MIG enum ApprovalAttachment=5 (attach-on-… · PASS bundle-BOTH-rotate js+css… · `2026-06.md` · substring:"Run #330 (run_number 330, id=444)" -- 2026-06-19 · S76 #318 full-stack Mig56 ProInitial/ProAdjust VERIFIED-APPLIED-PROD · PASS backfill-4rows-0violation… · `2026-06.md` · substring:"Run #318 (run_number 318, id=432)" -- 2026-06-19 · S83 #325 FE-only tiny budget-subitem indent/dash · PASS bundle-BOTH-js-rotate css… · `2026-06.md` · substring:"Run #325 (id=439)" -- 2026-06-17 · FE-only re-skin Văn phòng số 10-page PURO · PASS bundle-BOTH-rotate Wt54PH… · `2026-06.md` · substring:"Run #306 (run_number 306, id420) sha=`c556f6c`" -- 2026-06-17 · FE-both-app Văn phòng số foundation + index.css + BE menu-seed · PASS bundle-BOTH-rotate Bl2o_k… · `2026-06.md` · substring:"Run #305 (run_number 305, id419) sha=`a8bbdae`" -- 2026-06-16 · FE-both-app 1-line CSS-precedence fix (name text-white!) · PASS bundle-BOTH-rotate CNUv1j… · `2026-06.md` · substring:"Run #304 (run_number 304, id418) sha=`37752eb`" -- 2026-06-16 · FE-both-app Hồ sơ NS banner text-polish · PASS bundle-BOTH-rotate D532XZ… · `2026-06.md` · substring:"Run #303 sha=`6983609` PASS" -- 2026-06-16 · FE-both-app PE Link-hồ-sơ file:// render · PASS bundle-BOTH-rotate CcrZqf… · `2026-06.md` · substring:"Run #302 (run_number 302, id416) sha=`536dd6b`" -- 2026-06-16 · TESTS-ONLY BE +23 test (286) · PASS bundle-BOTH-frozen xkSz9B… · `2026-06.md` · substring:"Run #299 (id413) sha=`bcd619d`" -- 2026-06-16 · FE-Admin MIRROR Hồ sơ NS from fe-user · PASS bundle-asymmetric-NGƯỢC a… · `2026-06.md` · substring:"Run #298 (run_number 298, id412) sha=`292d64d`" -- 2026-06-16 · FE-User Hồ sơ NS cosmetic brand800 · PASS bundle-asymmetric user-ro… · `2026-06.md` · substring:"Run #297 (run_number 297, id411) sha=`ab4e681`" -- 2026-06-16 · FE-User Hồ sơ NS 2-col layout · PASS bundle-asymmetric user-ro… · `2026-06.md` · substring:"Run #295 (run_number 295, id409) sha=`456c7a7`" -- 2026-06-16 · CROSS-STACK PE HoSoLink Mig 52 + FE-User 3-panel + rename Dự trù→… · PASS Mig52-applied + bundle-bo… · `2026-06.md` · substring:"Run #293 (run_number 293, id407) sha=`5a0aaa4`" -- 2026-06-13 · CROSS-STACK PE PeWorkItemBudgets vượt-ngân-sách soft-warn (Mig 50… · PASS · `2026-06.md` · substring:"Run #286 (run_number 286, id400)" -- 2026-06-12 · CROSS-STACK PE guard 4-thông-tin mục 3 · PASS · `2026-06.md` · substring:"Run #283 (run_number 283) sha=`37122f0`" -- 2026-06-11 · S59-CLOSE final đóng sổ · PASS · `2026-06.md` · substring:"Run #280 (run_number 280) sha=`69997da`" -- 2026-06-11 · S59-đợt6 CROSS-STACK BE · PASS · `2026-06.md` · substring:"Run #278 (run_number 278) sha=`9c330d2`" -- 2026-06-11 · S59-đợt5 FE-only ×2 · PASS · `2026-06.md` · substring:"Run #277 (run_number 277) sha=`faed59f`" -- 2026-06-11 · S59-đợt4 rename WorkItems 71 idempotent-seed · PASS rename-via-prod-SQL targe… · `2026-06.md` · substring:"Run #276 (run_number 276) sha=`c869d26`" -- 2026-06-11 · S59-đợt3 BE-only DbInitializer · PASS · `2026-06.md` · substring:"Run #275 (run_number 275) sha=`bbd1554`" -- 2026-06-11 · S59 FE×2 PE-list tree regroup · PASS · `2026-06.md` · substring:"Run #273 (run_number 273) sha=`56882ac`" -- 2026-06-11 · S59-đợt2 FE×2 PE-list tree · PASS · `2026-06.md` · substring:"Run #274 (run_number 274) sha=`0eafcd3`" -- 2026-06-11 · SUPERSEDE-CHAIN (benign cancel→shipped) · CANCELLED-benign→PASS-via-3eba… · `2026-06.md` · substring:"Run #385→#386 SUPERSEDE-CHAIN sha=`ea793a4`" -- 2026-06-11 · S58 FE-USER visual redesign · PASS asymmetric fe-user-only · `2026-06.md` · substring:"Run #384 (run_number 270) sha=`e959f72`" -- 2026-06-16 · TEST-GATE COMPILE BREAK (CS7036 ParentId 5th positional, call-sit… · **[FAIL]** ~64s deploy-gated p… · `2026-06.md` · substring:"Run #291 (run_number 291, id405) sha=`8c8179c`" -- 2026-06-11 · S58 FIX #381 lock NO-OP (lock-noop→pw-11to12) · PASS dump-Users-set-before-sco… · `2026-06.md` · substring:"Run #382 (run_number 268) sha=`5998163`" -- 2026-06-11 · S57bis PE gắn WorkItemId loose-Guid (Mig 49) · PASS+1PARTIAL · `2026-06.md` · substring:"Run #381 (run_number 267) sha=`dd117b7`" -- 2026-06-09 · S56 GOLIVE-HARDEN BE fixes · PASS · `2026-06.md` · substring:"Run #379 (run_number 265) sha=`a20cde8`" -- 2026-06-09 · S56 pre-golive re-verify prod truth · **[audit]** no-deploy read-only · `2026-06.md` · substring:"S56 pre-golive verify — NO deploy" -- 2026-06-09 · S55 Phase-1 FE-Admin visual redesign · PASS asymmetric fe-admin-only… · `2026-06.md` · substring:"Run #378 (run_number 264) sha=`7feb53e`" -- 2026-06-09 · S55 HMW-P4 real master-data seed (Mig 48, 62 dự án+71 hạng mục+3… · PASS ungated-seed sqlcmd-COUNT… · `2026-06.md` · substring:"Run #377 (run_number 263) sha=`69cb393`" -- 2026-06-08 · S54 IT-staff self-reassign ticket · PASS · `2026-06.md` · substring:"Run #376 (run_number 262) sha=`ca4b602`" -- 2026-06-08 · S50 HMW-Wave2 P11-C Vehicle+Driver · PASS Discovery#3 mixed-docs+ts… · `2026-06.md` · substring:"Run #371 (run_number 257) sha=`30a99aa`" -- 2026-06-03 · S48 FE-only login subtitle a11y · PASS · `2026-06.md` · substring:"Run #369 (run_number 255) sha=`350b2bf`" -- 2026-06-01 · S45 Mig 43 filter Holiday UNIQUE · PASS · `2026-06.md` · substring:"Run #368 (run_number 254) sha=`0c5a014`" -- 2026-05-30 · S42 P11-B LeaveBalance business · PASS · `2026-06.md` · substring:"Run #367 (run_number 253) sha=`82d7fcf`" -- 2026-05-30 · S42 P11-A workflow-picker 2-bug + SetWorkflow · PASS · `2026-06.md` · substring:"Run #365 sha=`75df04e`" -- 2026-05-30 · S42 P11-A wire ApproveV2+LevelOpinions · PASS (mem #250 ≠ real Gitea id… · `2026-06.md` · substring:"Run #364 (mem #250) sha=`e7b66cd`" -- 2026-05-28 · S38 SKELETON 5-plan combo Mig 39+40 dual · PASS · `2026-06.md` · substring:"Run #247 sha=`e54a22d`" -- 2026-05-?? · S37 Proposal Mig 37+38 (/api/proposals 200 + QT-DX-V2-001 AppType=4) · PASS [archived] · `2026-06.md` · substring:"Archived Run #246 (S37 Proposal Mig 37+38" -- 2026-06-11 · ⚠️ VỊ-TRÍ-LẠC entry (mem run id ≠ real Gitea — FIFO slot between… · note · `2026-06.md` · substring:"VỊ TRÍ LẠC — entry MỚI 2026-06-11" -- 2026-05-22 · S29 gotcha #51 catch (SeedSampleContractWorkflowV2 nested in demo… · PASS [archived] · `2026-06.md` · substring:"Archived Run #232 (S29 gotcha #51 catch" - -## 2026-05 late (file: `archive/2026-05-q4.md`) - -- 2026-05-27 · BE+FE×2 Danh bạ nội bộ /api/directory (G-O1) · PASS bundle-both-rotate, menu-… · `2026-05-q4.md` · substring:"Run #238 (S34 Plan 2 G-O1 Danh bạ nội bộ" -- 2026-05-26 · BE Hrm CQRS /api/employees + FE×2 + menu (G-H1 Phase2) · PASS bundle-both-rotate, Emplo… · `2026-05-q4.md` · substring:"Run #237 (S33 Plan B G-H1 Phase 2 Task 4+5+6" -- 2026-05-26 · BE Mig 34 EmployeeProfile + Plan C BW1-BW7 +9 test (62 Infra) · PASS gotcha#51 INFRASTRUCTURE-… · `2026-05-q4.md` · substring:"Run #350 (S33 Plan B G-H1 Mig 34 EmployeeProfile" -- 2026-05-26 · S33 startup health-check · **[audit]** HEALTHY, last-5-ru… · `2026-05-q4.md` · substring:"S33 startup health-check — em main spawn read-only verify, VERDICT=HEALTHY" -- 2026-05-26 · S32 wrap curate + Phase9→10 · **[meta]** no-run docs-only skip · `2026-05-q4.md` · substring:"S32 wrap — em main proxy curate + Phase 9 stabilize done" -- 2026-05-26 · S32 startup verify (foundation freshness + 3-endpoint smoke) · **[audit]** no-CI-poll, RAG-he… · `2026-05-q4.md` · substring:"S32 startup verify — no CI poll" -- 2026-05-21 · S26 Plan AG PE-List tree-view UI iteration (#222-#227 cumulative) · PASS hybrid-verify spawn-1×-pe… · `2026-05-q4.md` · substring:"Run #222-#227 cumulative — Plan AG series PE List tree view" - -## 2026-05 mid (file: `archive/2026-05-runs.md`) - -### Run-records (20) -- 2026-05-19 · S25 t7 Plan AF FE userMap fallback resolve historical · PASS bundle-rotate C8TvDy7r/Bv… · `2026-05-runs.md` · substring:"Run #221 id=335 sha=`506cada`" -- 2026-05-19 · S25 t6 Plan AE Changelog UserName 9-sites batch · PASS BE-only bundle-frozen, pr… · `2026-05-runs.md` · substring:"Run #220 id=334 sha=`9ea62be`" -- 2026-05-19 · S25 t5 Plan AD Lịch-sử-duyệt redesign next-target-hint · PASS FE-only bundle-rotate, pa… · `2026-05-runs.md` · substring:"Run #219 id=333 sha=`0aaf2df`" -- 2026-05-19 · S25 t4 Plan AC2 FE merge recover historical Reject · PASS bundle-rotate, synthetic-… · `2026-05-runs.md` · substring:"Run #218 id=332 sha=`25837b6`" -- 2026-05-15 · S24 t1 Plan AA wrap fix sidebar · PASS last-deploy-before-AB, ba… · `2026-05-runs.md` · substring:"Run #214 id=328 sha=`ee0902a`" -- 2026-05-15 · S24 t1 Plan AA IsUserSelectable filter + WfView menu + sidebar (4… · PASS bundle-rotate, Order-shif… · `2026-05-runs.md` · substring:"Run #210 id=324 sha=`ac2c859`" -- 2026-05-15 · S23 t11 Plan U FE sidebar truncate · PASS long-label ellipsis+title… · `2026-05-runs.md` · substring:"Run #209 sha=`86d8806`" -- 2026-05-15 · S23 t10 Plan T5+T6 docs final (DemoSeed wipe ~720 rows + force re… · PASS flag-proven-end-to-end · `2026-05-runs.md` · substring:"Run #208 sha=`7b7b28f`" -- 2026-05-15 · S23 t10 Plan T DemoSeed disable (flag in appsettings, 5 demo-seed… · PASS · `2026-05-runs.md` · substring:"Run #207 sha=`0b97840`" -- 2026-05-15 · S23 t7 Plan Q FE banner mx-5 fix · PASS bundle-rotate QZIPWD-g/Da… · `2026-05-runs.md` · substring:"Run #204 sha=`108268a`" -- 2026-05-15 · S23 t6 Plan P HOTFIX Controller TransitionPeBody +3 fields · PASS Discovery#4 ASP.NET10-rec… · `2026-05-runs.md` · substring:"Run #203 id=317 sha=`1727bd5`" -- 2026-05-15 · S23 t5 Plan O HOTFIX cascade 4 lookup-sites (point 9) · PASS 409-mode-not-403-actor pr… · `2026-05-runs.md` · substring:"Run #202 id=316 sha=`a1c8386`" -- 2026-05-15 · S23 t4 Plan N HOTFIX per-NV lookup discrimination :765 · PASS 7/7-vs-1/7-per-actor, bug… · `2026-05-runs.md` · substring:"Run #201 id=315 sha=`fb3c22c`" -- 2026-05-15 · S23 t3 Plan M Service F1 OneLevel/OneStep edge-case Bước1 reset · PASS Discovery#3-anomaly docs-… · `2026-05-runs.md` · substring:"Run #200 id=314 sha=`f4055a1`" -- 2026-05-14/15 · S23 t1 K10 hotfix AwLevelDto +AllowApproverSkipToFinal · PASS 8-surface-point-checklist… · `2026-05-runs.md` · substring:"Run #195 id=309 sha=`0062fcb`" -- 2026-05-14 · S23 t1 Plan K Mig 31 per-Approver-slot refactor · **[PARTIAL]** Mig+schema OK bu… · `2026-05-runs.md` · substring:"Run #194 id=308 sha=`098baa6`" -- 2026-05-13 · S22-chốt cumulative verify · PASS Discovery#3 push-range-ev… · `2026-05-runs.md` · substring:"Verify S22 chốt cuối cumulative (push range `3d725c4..cc8a7d3` 12 commits) VERDICT=PASS" -- 2026-05-13 · S22 Plan D Users-toggle + Plan C 14-test + Plan E strict-V2-scope · PASS 103 test, Discovery#1 rat… · `2026-05-runs.md` · substring:"Run #188 id=302 sha=a74e671" -- 2026-05-13 · S21 t5 Mig 29 refactor Allow*-per-NV + Designer 5-checkbox · PASS Mig29 applied, Discovery… · `2026-05-runs.md` · substring:"Run #187 id=301 sha=c0af9e0" -- 2026-05-13 · S21 t3+t4 gotcha#45 Trả-lại + F1/F2/F3 advanced-options + Mig 28 · PASS baseline, Discovery API-t… · `2026-05-runs.md` · substring:"Run #186 id=300 sha=eea86fd" - -### Curate-note meta-headers (6) — [meta] -- 2026-05-19 · curate-note S25 t5 Plan AD #219 · **[meta]** · `2026-05-runs.md` · substring:"added S25 t5 Plan AD Run #219 entry" -- 2026-05-19 · curate-note S25 t3 Plan AC #217 (Discovery#5 sqlcmd 4-backslash \… · **[meta]** · `2026-05-runs.md` · substring:"added S25 t3 Plan AC Run #217 entry" -- 2026-05-19 · curate-note S25 t1 Plan AB #215 (gotcha#48 SQLite tie-break FAIL) · **[meta]** · `2026-05-runs.md` · substring:"added S25 t1 Plan AB Run #215 entry" -- 2026-05-15 · curate-note S24 t1 Plan AA #210 · **[meta]** · `2026-05-runs.md` · substring:"added S24 t1 Plan AA Run #210 entry" -- 2026-05-15 · curate-note S23 t6 Plan P #203 (Discovery#4 numeric-enum) · **[meta]** · `2026-05-runs.md` · substring:"added S23 t6 Plan P HOTFIX Run #203 entry" -- 2026-05-15 · curate-note S23 t5 Plan O #202 (4-site discrimination, Transition… · **[meta]** · `2026-05-runs.md` · substring:"added S23 t5 Plan O HOTFIX Run #202 entry" - -## 2026-05 early (file: `archive/2026-05-q3.md`) - -- 2026-05-19 · ★ Run #215 FAIL → #216 PASS pair — **gotcha #48 SQLite tie-break*… · FAIL→PASS · `2026-05-q3.md` · substring:"Run #215 FAIL → Run #216 PASS (gotcha #48 SQLite tie-break catch+fix pair)" -- 2026-05-13 · Verify S22 chốt cuối cumulative (Discovery#3 first-surfaced, 33 u… · PASS · `2026-05-q3.md` · substring:"Verify S22 chốt cuối cumulative (push range `3d725c4..cc8a7d3` 12 commits VERDICT=PASS" -- 2026-05-12 · Setup baseline (agent init, 44 gotchas + 5-stage + 3 skills) · init · `2026-05-q3.md` · substring:"2026-05-12 — Setup baseline" - -## 2026-05 early (file: `archive/2026-05-q2.md`) - -- 2026-05-22 · Run #231 Plan B Contract V2 wire kick-off · **[PARTIAL]** PASS-deploy + se… · `2026-05-q2.md` · substring:"Run #231 (id=345) Plan B Contract V2 wire kick-off VERDICT=PARTIAL" + - 2026-07-06 · S103 #487 `f0874dd` GOVERNANCE/SCRIPTS-ONLY GO — GOVERNANCE-PASS… · PASS · `2026-07.md` · substring:"S103 #487 (rn 373) `f0874dd` GO ~4m" - 2026-07-02 · S100 #486 `5ee32c0` GOVERNANCE-ONLY GO — bundle FROZEN #69-REFINE… · PASS · `2026-07.md` · substring:"S100 #486 (rn 372) `5ee32c0` GO 5m17s" - 2026-07-10 · S107 #489 `21387cd` GOVERNANCE-ONLY (+office-document 12th sub) G… · PASS · `2026-07.md` · substring:"S107 #489 (rn 375, id 489)" @@ -133,7 +39,6 @@ - 2026-07-13 · S115 #385 `81b59f0` GOVERNANCE/TOOLING-ONLY (hmw.js+memory-archiv… · PASS · `2026-07.md` · substring:"S115 #385 (rn 385, id 499)" - 2026-07-13 · S116 #386 `88368fd` PE nhap-gia-AM + submit-guard all-zero GO — j… · PASS · `2026-07.md` · substring:"S116 #386 (rn 386, id 500)" - 2026-07-14 · S117 #387 `316a82f` PE endsBeforeCeo-pointer-aware + duyet-theo-k… · PASS · `2026-07.md` · substring:"S117 #387 (rn 387, id 501)" - - 2026-07-14 · S118 #389 `c808b9a` SECURITY-AUTHZ ReportsController→Reports.Read… · PASS · `2026-07.md` · substring:"S118 #389 (rn 389, id 503)" - 2026-07-16 · S126 #394 `e9124fc` GOVERNANCE-TOOLING GO — gate 509 exact, bundl… · PASS · `2026-07.md` · substring:"S126 (2026-07-16) run 508/#394" - 2026-07-17 · S133 #398 `d4fbced` PE budget-freeze + Mig67 AddPeApprovedBudgetS… · PASS · `2026-07.md` · substring:"S133 (07-17) verify d4fbced" @@ -141,7 +46,8 @@ - 2026-07-23 · S155 #407 `4510cea` App chuan-hoa ma du-an normalize + PE attach-gate FE both-apps… · PASS 5/5 · `2026-07.md` · substring:"Run #407 `4510cea` (2026-07-23)" - 2026-07-27 · S155 #416 `b1bae77` Workflow duyet-TAI-CHO + co F6 AllowApproverDelete… · PASS · `2026-07.md` · substring:"Run #416 `b1bae77` (2026-07-27)" - 2026-07-28 · S157 #419 `a52a91e` PE canh-bao amber o Dia-diem khi du-an thieu Location… · PASS 9/9 · `2026-07.md` · substring:"Run #419 `a52a91e` (2026-07-28)" -- 2026-07-29 · S159 #423 `1a47a61` dot-2 tab strip 4GD + menu 4-GD — PARTIAL 8/9, bat regression revoker an 447/494 row · `2026-07.md` · substring:"Run #423 `1a47a61` (2026-07-29"- 2026-07-30 · S162 #432 `bfc7b79` cay-4-folder-GD FE-ONLY — PASS 5/5, LUAT cam HEAD~1 (neo SHA X^..X) + dem-tron-ho 1 regex · `2026-07.md` · substring:"07-30 verify `bfc7b79` S162" +- 2026-07-29 · S159 #423 `1a47a61` dot-2 tab strip 4GD + menu 4-GD — PARTIAL 8/9, bat regression revoker an 447/494 row · `2026-07.md` · substring:"Run #423 `1a47a61` (2026-07-29" +- 2026-07-30 · S162 #432 `bfc7b79` cay-4-folder-GD FE-ONLY — PASS 5/5, LUAT cam HEAD~1 (neo SHA X^..X) + dem-tron-ho 1 regex · `2026-07.md` · substring:"07-30 verify `bfc7b79` S162" - 2026-07-31 · S164 #434 `50e6d8c` K1 SP-002 danh-muc loai-HD + Mig 70 — PASS 6/6, tam-giac 3 nguon doc-lap cho seed 86 + bai "0-hit la duong-gia toi khi soi format" · `2026-07.md` · substring:"07-31 verify `50e6d8c` S164 K1" - 2026-07-31 · S164 #435 `0779f2d` K2 KHKK mang hang-muc + nhom duyet N1-8 + Mig 71 — PARTIAL 5/6, bai CHAN-LY-RONG (acceptance 0-row hai phia) + liet-ke-tron-ho index + wire-proof bind-400 · `2026-07.md` · substring:"07-31 verify `0779f2d` S164 K2" - 2026-07-31 · S164 #436 `15349e8` K3 8 workflow nhom KHKK-N1-8 (0-mig) — PASS 6/6, du-doan-truoc-khi-do + FROZEN-la-he-qua + marker bien-local 0-bit · `2026-07.md` · substring:"S166 (08-01) re-verify `15349e8`" diff --git a/.claude/agent-memory/implementer-backend/MEMORY.md b/.claude/agent-memory/implementer-backend/MEMORY.md index a0ee8d8..8679239 100644 --- a/.claude/agent-memory/implementer-backend/MEMORY.md +++ b/.claude/agent-memory/implementer-backend/MEMORY.md @@ -1,5 +1,7 @@ # Implementer-Backend Agent — Persistent Memory +- **S167 (08-01) lane K7 — cầu KHKK→HĐ BE + 5 vá `[artifact sub-implbe-k7.md; return-1 garble #53 → lead SendMessage-resume, xong solo]`:** Bài lớn nhất = **BÁC TIỀN ĐỀ ĐỀ BÀI bằng 4 phép đo trước khi gõ**: lead nói "bridge ĐÃ TỒN TẠI (W6-S161), mày chỉ vá quanh nó" — đĩa nói KHÔNG (`grep create-contract` 1 hit duy nhất = bridge PE cũ · 0 file · 0 endpoint · 0 write-site `Line.ContractId`). Cái tồn tại @W6 là **đường-ống HĐ V2**, không phải cầu ⇒ lead nhập nhằng "đường ống đã vá"≠"bridge có". **Phép thử rẻ phân biệt:** vá-6 ("nạp Project.Code/Supplier.Code theo khuôn bridge-cũ") là **no-op nếu áp lên bridge cũ** (nó ĐÃ có 2 dòng đó) ⇒ vá chỉ có nghĩa với vật CHƯA có ⇒ suy ngược ra vật thiếu. Xử: DỰNG bridge theo spec invest §K7.1 đã pin sẵn (0 quyết-định-mới của tôi) + nhúng 5 vá, KHAI RÕ chệch-khuôn + trỏ dòng spec — lead nhận. Khi **spec vá ⊥ nền invest** (`WorkflowDefinitionId` null vs pin) → **vá THẮNG** (spec `:4` viết luật đó). Cột đã có từ mig cũ ⇒ **0-mig**. `??=` giữ CHIỀU: cầu chạy sau không cướp ô của cầu trước. Thêm field vào projection dùng-chung-3-endpoint → **chạy test filter rẻ ngay** (collection-subquery SQLite dịch được: 34/35). 1 fail = reflection-authz test bắt mọi action phải cùng key module, mà vá-1 cố ý gate key MODULE KHÁC (`Contracts.Create`, #85) ⇒ **verified-fail bàn giao test-lane, KHÔNG tự sửa** (S112 khuôn). Tag `[s167, k7-bridge-khkk-hd, bac-tien-de-4-phep-do, va-noop-suy-nguoc-vat-thieu, spec-thang-nen, 0-mig, verified-fail-handoff]` + - **S166 (08-01) lane K4a — 49 row menu KHKK 8-nhóm, DONE B0-B5 `[LEAD SEED ON-BEHALF TẠI-CỬA — mày #53 sau B5 ("Now the site-4 revoker comment" trong khi comment ĐÃ land — return tụt sau đĩa 1 nhịp); B6 + END lead đóng; nguồn: sub-implbe-k4a.md + git diff]`:** Nghề mới ăn tiền: **BỘ-SINH thay 49 const tay** — `KhkkGroupNumbers[7] × (KhkkGroup + 6 KhkkLeafActions)` → `KhkkGroupMenuKeys` làm NGUỒN CHUNG 3 phía (spread `.. ` vào `All` · vòng seed tree.Add · site-2 `.Concat`) ⇒ khớp **by-construction, 0 mirror tay 147 chuỗi** (đúng loại drift nổ FK Permission T6-K1); chệch-khuôn-so-brief KHAI RÕ + trỏ đề xuất gốc invest `:9` ⇒ lead chấp nhận không mất lượt. Anchor re-ground TRƯỚC khi sửa (số spec trượt vì DbInitializer phình sau K2/K3): dict labelBackfill `:2141-2183` · upsert THẬT `:2162-2179` (vá 2 comment stale "1893" bằng mỏ-neo GREP `existingItems.TryGetValue` thay số-dòng-trần — số dòng sẽ trượt tiếp, mỏ neo thì không). Vá-10 sửa LÝ-DO comment (group-key KHÔNG cần CanRead row — HasAccess đệ-quy theo con `:96`; invest doc viết sai lý-do, kết luận giữ-3-site vẫn đúng). labelBackfill `Khkk_G1` đổi-nhãn-không-nhân-row + fail-fast KeyNotFound khi thiếu nhãn (thà nổ lúc seed còn hơn FK nổ về sau). Suite **614/0 SAU diff** (idempotent/revoke/permission-seed giữ xanh). Tag `[s166, k4a-lane, bo-sinh-by-construction-3-phia, mo-neo-grep-thay-so-dong, fail-fast-luc-seed, return-tut-sau-dia-1-nhip, seed-on-behalf-tai-cua]` - **S161 (07-29) ARC W1+W2+W3đ1 KHKK — 3 lượt/3 wave, chi tiết = sub-md run-folder `runs/2026-07-29-S161-khkk-{w1-schema,w2-crud,w3-duyet}/` (tổng ~47KB ghi-từng-mục; cả 3 lượt return #53 nhưng 0 mất):** **W1** Mig 69 7-bảng (UNIQUE filtered #57 · cascade-1-hop · apply 2 DB 0-drift · sys.tables 96) + seeder `SeedKeHoachKyKetAccessAsync` UPGRADE-if-exists+CanDelete + CodeGen `KHKK/{YYYY}/{Seq:D3}` WorkflowAppCodeSequences + 3 site AWV2Admin — ship `a8027c0` cicd 6/6. **W2** mega-file CRUD 15-endpoint policy-15/15 + 4 quyết D1-D4 khai-lead-duyệt; 5 bẫy né ghi sổ: codegen-TRƯỚC-Add (S88) · SUM so `w.Id` KHÔNG `w.SupplierId` · `Remove` trước `Add(changelog)` (T26) · subquery-Any-2-tầng vỡ-SQL-lúc-chạy · giao-1-grep-cùng-lớp-ra-3 (S122) — ship `6cbc6ad` cicd 6/6 (reviewer 12F: D1 "FE sẽ khớp" = giả-định-không-phải-phép-đo → 6 điểm đứt, lead vá). **W3đ1** skeleton test-before: switch-throw từng nhánh (đỏ-THẬT không đỏ-compile) + **rào-FK-547-bằng-VẮNG-MẶT-dep** (không inject IChangelogService) + **§6 tự đối-chứng chéo hợp-đồng FE 5/5 TRƯỚC bàn giao** (bài W2 → nghi thức) + §7 bàn-giao ctor-CUỐI. Tag `[s161, khkk-w1-w2-w3d1, mig69, upgrade-if-exists, rao-vang-mat-dep, doi-chung-cheo, gia-dinh-vs-phep-do]` @@ -82,19 +84,13 @@ UI `disabled={!canX}` + BE helper `EnsureCanXAsync(id, userId)` throw 403 (NOT i ## 📅 Recent activity (FIFO — older → archive/git) -- **2026-07-13 (S116 PE cho nhập giá ÂM — submit-guard 1-line BE, owner anh Kiệt PROD-UAT-live, NO mig/FE/test, spec deterministic exact-line → ACCEPT Case 1):** Giá âm ô "Số tiền báo giá hạng mục" (form.thanhTien→Quote.ThanhTien) = phát-sinh-giảm/hoàn-tiền NCC = giá-trị THẬT (KHÔNG lỗi). Rule "có giá là được": ≥1 quote-selected khác 0 (âm/dương) → gửi được; CHỈ all-zero (chưa báo giá) mới chặn. **THAY DUY NHẤT** `PurchaseEvaluationWorkflowService.cs:206` submit-guard `selectedThanhTien.Sum() <= 0` → `.All(x => x == 0)`. Guard `Count==0` (chưa chọn NCC) ở `if` phía trên GIỮ RIÊNG → `else if` chỉ chạy khi list non-empty → `.All(==0)` = "mọi quote chọn đúng bằng 0". Message giữ "Đơn vị được chọn chưa có giá chào thầu". **KHÔNG đụng** (owner R1/R3/R-CEO): validator ≥0 khác (budget/price PeFeatures/PeWorkItemBudget/PeSuggestedPrice) GIỮ · clamp `CreateContractFromEvaluationFeatures.cs:88-100` GIỮ (Contract.GiaTri cho âm R1) · CEO-threshold winnerQuoteTotal NET GIỮ · KHÔNG thêm validator quote. DB nhận âm sẵn (Mig 65, decimal(18,2) 0 CHECK, 0 validator). Build `SolutionErp.Api` **0 warn/0 err** (Api ref Infrastructure → file compiled). ⚠️ Comment `:193-195` ("tổng ≤0"/"D5 giữ >0"/"winner phải có giá") giờ STALE vs all-zero-logic — KHÔNG sửa (owner "THAY DUY NHẤT :206"), flag owner quyết. LESSON: domain "âm là giá-trị-thật" → presence-check dùng `.All(==0)` KHÔNG `Sum()<=0`/`>=0` (sum-guard nhầm âm-lớn với chưa-nhập). Tag `[s116, pe-negative-quote, submit-guard-1line, all-zero-not-sum, co-gia-la-duoc, no-mig, stale-comment-flagged]`. - -- **2026-07-12 (S113 Supplier import v2 BE — spec `wf_e7896ca6` fable-clone-ensemble, owner anh Kiệt, Mig 64, D1/D2/D3 pre-chốt → ACCEPT Case 1+2; ⚠️ return-1 garbled #53 → coordinator SendMessage-resume, xong solo):** IsPublic publish-gate + dedup MST-primary. **Mig 64 `AddSupplierPublishState`** (Supplier +`bool IsPublic` NOT NULL default false + `Sql("UPDATE Suppliers SET IsPublic=1")` backfill 22 prod + DropIndex→CreateIndex đổi Code-unique filter `[IsDeleted]=0 AND [Code]<>''` cho draft-row Code="" coexist; no new table 89). **Service** `SupplierExcelImportService`: (a) re-bake ExpectedHeaderTokens BYTE-EXACT raw-`\n` form = single-source cho validator(NormalizeHeader collapse \n→space)+BuildTemplate (spec §② 30 token, c7 trailing-space); (b) dedup **MST-primary + Code-backstop** — `BuildMstIndex`(NormalizeMst=Trim+strip-inner-ws `\s+`→"" OrdinalIgnoreCase, blank→skip) precedence existing-MST→existing-Code→batch-MST→batch-Code, `countedUpdates HashSet` tránh double-count, blank-MST KHÔNG vào batchByMst (2 blank=2 insert); (c) BỎ hard-error blank-Code (=draft), GIỮ Name+all-or-nothing; (d) New `IsPublic=!blank(Code)`, update-path KHÔNG đụng IsPublic; +MstMissing(soft-warn) +DraftCount; (e) `BuildTemplate()` ClosedXML sheet "Sheet1" 30 token ROW4 WrapText+FreezeRows. **PublishSupplierCommand**(Id,MaNcc?,Publish): set Code+unique-CI-check + gate Publish→Code-non-empty ConflictException, KHÔNG UpdateSupplierCommand (#73 clobber). **API THẬT:** GET `/api/suppliers/import/template`(xlsx "Mau-Database-NCC.xlsx") · POST `/api/suppliers/{id}/publish` body`{maNcc?,publish}` · GET `/api/suppliers?published={t/f}` filter · SupplierDto +`bool IsPublic` append-CUỐI (sửa 2 projection List+Get). **D3 authz:** import preview/confirm/template/publish `[Roles=Admin,CatalogManager]`→`[Policy="Suppliers.Update"]` (khớp FE PermissionGuard). DbInit +IsPublic=true 4 real+18 sample. Build slnx **0/0**, apply BOTH DB (Dev catch-up+Design). KHÔNG commit/FE/test. LESSON: S112 CI-dedup-at-service mở rộng → dual-key MST-primary; blank-key KHÔNG index. Tag `[s113, supplier-import-v2, mig64, ispublic-publish-gate, dedup-mst-primary-code-backstop, blank-code-draft, buildtemplate-single-source, publish-cmd-no-#73]`. - -- **2026-07-12 (S112 PE sign-off close-review implement — `wf_7df56b89-7bb` GO-WITH-ADJ, owner anh Kiệt PROD-UAT-live, 4-region/1 file `PurchaseEvaluationWorkflowService.cs`, NO mig/FE/test → ACCEPT Case 1):** **C1 A1 (`ApplyDrafterBypassOnSubmitAsync`):** người soạn KÝ TAY ô cấp mình — `k=Max`→`minOwn=Min(drafterSlots.Order)`, bypass `<=k`→`1){pointer=minOwn}` STOP tại minOwn (người soạn tự ApproveV2Async); minOwn=1→bypass rỗng pointer giữ (0,1); non-contiguous Cấp1+Cấp3 ký tay đúng thứ tự. **C2 EDGE-5 hard-lock:** top `TransitionAsync` sau isAdmin/isSystem TRƯỚC mọi branch → `if(fromPhase is DaDuyet or TuChoi && !isAdmin && !isSystem) throw Conflict` (chặn non-admin lật phiếu terminal; Admin+System MIỄN); KHÔNG đụng EnsureCanRejectV2Async. **C3** no self-finalize (owner oQ1 permission-driven). **C4 opinion-history (`ApproveV2Async` UPSERT else):** old.Comment→Changelog (Workflow/Update/EntityId=level.Id) TRƯỚC overwrite chỉ khi non-empty — NO mig/bảng/endpoint. Build slnx **0/0**. Infra **4 FAIL/401 pass**: fail=C1 (PeSubmitGuardAndBypassTests 9/10/12/14 assert OLD auto-sign/terminal-on-submit); C2/C4 break 0 test → test-specialist ADD. LESSON: behavior-change write-site → run consumer tests, hand VERIFIED fail-list (KHÔNG sửa test). Tag `[s112, pe-signoff, a1-sign-own-level, edge5-hardlock, opinion-history-changelog, consumer-test-verified-handoff]`. -- **2026-07-12 (S112 Supplier Excel-import Phase B BE — `wf_e2f23a84` invest-ensemble → `wf_75d6149b` close-review GO-WITH-ADJ, owner anh Kiệt, commit `e100ef0`, Mig 63 → ACCEPT Case 1/2):** **Service** `SupplierExcelImportService` (preview + confirm 2-stage) + 2 endpoint (`/suppliers/import/preview` [415 non-xlsx] · `/suppliers/import/confirm` [401 auth]) + **Mig 63 `AddSupplierImportSourceFields`** (Supplier +`SourceUpdatedAt datetime2?`+`SourceUpdatedBy nvarchar(200)?` provenance cột 29/30; 2 AddColumn nullable no-new-table 89). FE 2-app byte-identical (`SupplierImportDialog`). **LESSONS:** (1) dedup Code = **OrdinalIgnoreCase ở SERVICE** — DB unique-index CI-collation lật (SQLite fixture BINARY KHÔNG bắt, prod SQL Server CI bắt) → dedup KHÔNG dựa DB; (2) parser **absolute cell-index** (row-4 header-fingerprint), KHÔNG `CellsUsed()` (column-drift); (3) **all-or-nothing** gate TRƯỚC mutate (1 hard-error→Committed=false DB unchanged); (4) fill-nulls **SKIP Code/Name/Type** (chỉ điền field trống); (5) `.Normalize(FormC)` cho real-file NFD (close-review adjust bake 30 `RealFileHeaderTokens` byte-verify). **SURPRISE:** Approach-B (extend seed) ĐÃ ship 4 NCC prod (Mig 62) → spec "partial" SAI; import ghi entity **TRỰC TIẾP** (fill-where-non-empty) KHÔNG qua `UpdateSupplierCommand` (tránh NULL-clobber #73). Shipped Run #496 PASS, reviewer GO-WITH-ADJ (BE 6/6 axes). Tag `[s112, supplier-excel-import, mig63, ordinalignorecase-dedup-at-service, abs-cell-index-parser, all-or-nothing, fill-nulls-safe, normalize-formc-nfd, direct-entity-write]`. -- _(2026-06-25→19 [S88 frozen-relocation · PE Section-B 3-cột Mig 59 · PE chuông approver] FIFO'd → `archive/2026-06.md` at S116; + S77→S56 [2026-06-10..19, 7 entries] at S112. Recall verbatim there / `_INDEX.md` / RAG `search_memory`.)_ +- _(Mục FIFO này hiện RỖNG — 4 entry cuối [2026-07-12/13: S116 PE giá ÂM · S113 Supplier import v2 Mig 64 · S112 PE sign-off · S112 Supplier Excel-import Mig 63] dời verbatim → `archive/2026-07.md` @S167. Trước đó: 2026-06-25→19 [S88 frozen-relocation · PE Section-B Mig 59 · PE chuông approver] → `archive/2026-06.md` @S116; S77→S56 [7 entries] @S112. Việc gần đây SỐNG nằm ở khối L1 HOT đầu file (S167/S166/S161/S139). Recall verbatim: archive tương ứng / `archive/_INDEX.md` / RAG `search_memory`.)_ --- ## 🔄 Curate trigger - >~30KB → archive recent → L2 `archive/.md`. Stale >3mo → remove. -- **Last curate: 2026-07-13 S116 (implementer-backend self-FIFO, hook @21.5KB read-limit-guard)** (21.5→~15KB): moved 3 oldest Recent-activity (2026-06-19/24/25: PE chuông · PE Section-B Mig 59 · S88 frozen-relocation) verbatim → `archive/2026-06.md`, kept 4 newest (S116/S113/S112×2). Prev **S112** (25.6→~16KB): S77→S56 7-entry → 2026-06.md. Prev **S70 Harness-9** (33.2→17.4KB): L2 dark-matter recovery + `_INDEX` + gists. Prev: S40 dedup-split · S34 · S22. +- **Last curate: 2026-08-01 S167 (self-FIFO, hook read-limit-guard bắn @22.4KB)** (22.4→15.4KB, dưới ngưỡng 17.1KB hook đòi): moved verbatim 4 entry Recent-activity 2026-07-12/13 (S116/S113/S112×2) → `archive/2026-07.md` (2.8→10.3KB). Mục Recent-activity nay rỗng có chủ đích — việc SỐNG ở khối L1 HOT đầu file. Prev **S116** (21.5→~15KB): moved 3 oldest Recent-activity (2026-06-19/24/25: PE chuông · PE Section-B Mig 59 · S88 frozen-relocation) verbatim → `archive/2026-06.md`, kept 4 newest (S116/S113/S112×2). Prev **S112** (25.6→~16KB): S77→S56 7-entry → 2026-06.md. Prev **S70 Harness-9** (33.2→17.4KB): L2 dark-matter recovery + `_INDEX` + gists. Prev: S40 dedup-split · S34 · S22. ## S152 (2026-07-25) — R1 retention-note ranh auto-refuse test [M9 on-behalf lead] - **Integration testing multiple components = việc `test-specialist`, KHÔNG phải mình** — mình scaffold + compile-check; assertion/integration test cross-component (SQLite fixture, authz regression) do test-specialist own (tiền lệ S67 HRM test-after +23, S69 +20 đều test-specialist). Nhận viết integration test = double-touch tests/ sai split. Tag `[s152, r1-retention, refuse-integration-testing-components]` diff --git a/.claude/agent-memory/implementer-backend/archive/2026-07.md b/.claude/agent-memory/implementer-backend/archive/2026-07.md index 59d922c..e1f0b6e 100644 --- a/.claude/agent-memory/implementer-backend/archive/2026-07.md +++ b/.claude/agent-memory/implementer-backend/archive/2026-07.md @@ -5,3 +5,12 @@ - **S134 (07-17) PE lũy kế TẠM TÍNH BE [wf_58302bd5-b9f INV-2.A · return sạch 0-garble] `[engine: opus-worker]`:** +`ComputePendingAsync` + `PePendingAccumulation` (PeBudgetAccumulator.cs pure-add +68/0) + `PePendingPriorPeDto` + `PeBudgetSummaryDto` append-cuối 5 field (2 construction site: `:946` frozen-defaults 0/0/0/0/null · `:985` live-fill) + D4 audit-note TRONG `ApplyBudgetSnapshotOnFinalizeAsync` (Action=**Update** KHÔNG Transition — #49 tránh UI đọc 2 lần chuyển-phase; insertion-only +30/0; signature GIỮ → 5 call-site 0-diff, site cuối line-shift :1112→:1142 content bất động). Build slnx 0/0 · 0 mig · ComputeAsync body 0-diff (A1). Learned: (1) **insertion-only diff (grep `'^-'` = 0 real-removal) = bằng-chứng MẠNH NHẤT "thân method/call-site bất động"** — mạnh hơn đọc-mắt, chống báo-số-sai kiểu #53; (2) derived-audit thêm vào helper KHÔNG cần đổi signature nếu tính nội bộ từ field entity sẵn có (giữ call-site 0-diff); (3) accumulator ns ≠ DTO ns (`...Dtos`) → cần using tường minh dù cùng project. Tag `[s134, pe-luyke-tam-tinh, computepending, insertion-only-proof, d4-changelog-update-not-transition]` - **S133 (07-17) PE budget-freeze BE [wf_4bef08b8-730 INV-B · #53 return-fail → disk-truth + em-main-solo hoàn tất; entry on-behalf]:** lane sửa XONG 6 file + `PeBudgetAccumulator.cs` mới (Domain 11 props ApprovedBudget* + Config precision/maxlength + DTO append `BudgetFrozen=false` cuối positional-record + display-gate frozen/live 2-nhánh trong GetPurchaseEvaluationQuery + helper `ApplyBudgetSnapshotOnFinalizeAsync` gọi 4-site `:862/:901/:934/:1109` + 2 terminal-guard block-CẢ-Admin) rồi CHẾT TRƯỚC bước sinh migration → em-main solo: scaffold Mig 67 sạch 11 AddColumn (0 RenameColumn #63) + backfill `Sql()` OUTER APPLY ×3 khớp accumulator TỪNG điều kiện (Suppliers/Quotes = BaseEntity KHÔNG soft-delete filter → SQL không thêm IsDeleted trên s/q; WorkItemId-null → 4 lũy kế NULL mirror helper, SnapshotAt vẫn set) + apply 2 DB — **Dev lòi drift Mig 66 committed-chưa-apply-local, catch-up luôn** — + filter test 44/44 (T0 RED→GREEN không sửa test). Phát hiện đáng giữ của lane: `TransitionAsync` KHÔNG SaveChanges nội bộ `:537` → snapshot flush qua SaveChanges của caller (mirror ApprovedPrice pattern); khuôn backfill THẬT Mig 61/65 = plain `Sql()` KHÔNG C# provider-guard (SQLite EnsureCreated không replay = guard kiến trúc). Tag `[s133, pe-budget-freeze, mig67, 53-writelane-disk-recover]` + +- _(FIFO @S167 — 4 entry 2026-07-12/13 dời verbatim từ L1 khi hook read-limit-guard bắn @22.4KB; moved-not-cut: S116 PE giá ÂM · S113 Supplier import v2 Mig 64 · S112 PE sign-off · S112 Supplier Excel-import Mig 63.)_ + +- **2026-07-13 (S116 PE cho nhập giá ÂM — submit-guard 1-line BE, owner anh Kiệt PROD-UAT-live, NO mig/FE/test, spec deterministic exact-line → ACCEPT Case 1):** Giá âm ô "Số tiền báo giá hạng mục" (form.thanhTien→Quote.ThanhTien) = phát-sinh-giảm/hoàn-tiền NCC = giá-trị THẬT (KHÔNG lỗi). Rule "có giá là được": ≥1 quote-selected khác 0 (âm/dương) → gửi được; CHỈ all-zero (chưa báo giá) mới chặn. **THAY DUY NHẤT** `PurchaseEvaluationWorkflowService.cs:206` submit-guard `selectedThanhTien.Sum() <= 0` → `.All(x => x == 0)`. Guard `Count==0` (chưa chọn NCC) ở `if` phía trên GIỮ RIÊNG → `else if` chỉ chạy khi list non-empty → `.All(==0)` = "mọi quote chọn đúng bằng 0". Message giữ "Đơn vị được chọn chưa có giá chào thầu". **KHÔNG đụng** (owner R1/R3/R-CEO): validator ≥0 khác (budget/price PeFeatures/PeWorkItemBudget/PeSuggestedPrice) GIỮ · clamp `CreateContractFromEvaluationFeatures.cs:88-100` GIỮ (Contract.GiaTri cho âm R1) · CEO-threshold winnerQuoteTotal NET GIỮ · KHÔNG thêm validator quote. DB nhận âm sẵn (Mig 65, decimal(18,2) 0 CHECK, 0 validator). Build `SolutionErp.Api` **0 warn/0 err** (Api ref Infrastructure → file compiled). ⚠️ Comment `:193-195` ("tổng ≤0"/"D5 giữ >0"/"winner phải có giá") giờ STALE vs all-zero-logic — KHÔNG sửa (owner "THAY DUY NHẤT :206"), flag owner quyết. LESSON: domain "âm là giá-trị-thật" → presence-check dùng `.All(==0)` KHÔNG `Sum()<=0`/`>=0` (sum-guard nhầm âm-lớn với chưa-nhập). Tag `[s116, pe-negative-quote, submit-guard-1line, all-zero-not-sum, co-gia-la-duoc, no-mig, stale-comment-flagged]`. + +- **2026-07-12 (S113 Supplier import v2 BE — spec `wf_e7896ca6` fable-clone-ensemble, owner anh Kiệt, Mig 64, D1/D2/D3 pre-chốt → ACCEPT Case 1+2; ⚠️ return-1 garbled #53 → coordinator SendMessage-resume, xong solo):** IsPublic publish-gate + dedup MST-primary. **Mig 64 `AddSupplierPublishState`** (Supplier +`bool IsPublic` NOT NULL default false + `Sql("UPDATE Suppliers SET IsPublic=1")` backfill 22 prod + DropIndex→CreateIndex đổi Code-unique filter `[IsDeleted]=0 AND [Code]<>''` cho draft-row Code="" coexist; no new table 89). **Service** `SupplierExcelImportService`: (a) re-bake ExpectedHeaderTokens BYTE-EXACT raw-`\n` form = single-source cho validator(NormalizeHeader collapse \n→space)+BuildTemplate (spec §② 30 token, c7 trailing-space); (b) dedup **MST-primary + Code-backstop** — `BuildMstIndex`(NormalizeMst=Trim+strip-inner-ws `\s+`→"" OrdinalIgnoreCase, blank→skip) precedence existing-MST→existing-Code→batch-MST→batch-Code, `countedUpdates HashSet` tránh double-count, blank-MST KHÔNG vào batchByMst (2 blank=2 insert); (c) BỎ hard-error blank-Code (=draft), GIỮ Name+all-or-nothing; (d) New `IsPublic=!blank(Code)`, update-path KHÔNG đụng IsPublic; +MstMissing(soft-warn) +DraftCount; (e) `BuildTemplate()` ClosedXML sheet "Sheet1" 30 token ROW4 WrapText+FreezeRows. **PublishSupplierCommand**(Id,MaNcc?,Publish): set Code+unique-CI-check + gate Publish→Code-non-empty ConflictException, KHÔNG UpdateSupplierCommand (#73 clobber). **API THẬT:** GET `/api/suppliers/import/template`(xlsx "Mau-Database-NCC.xlsx") · POST `/api/suppliers/{id}/publish` body`{maNcc?,publish}` · GET `/api/suppliers?published={t/f}` filter · SupplierDto +`bool IsPublic` append-CUỐI (sửa 2 projection List+Get). **D3 authz:** import preview/confirm/template/publish `[Roles=Admin,CatalogManager]`→`[Policy="Suppliers.Update"]` (khớp FE PermissionGuard). DbInit +IsPublic=true 4 real+18 sample. Build slnx **0/0**, apply BOTH DB (Dev catch-up+Design). KHÔNG commit/FE/test. LESSON: S112 CI-dedup-at-service mở rộng → dual-key MST-primary; blank-key KHÔNG index. Tag `[s113, supplier-import-v2, mig64, ispublic-publish-gate, dedup-mst-primary-code-backstop, blank-code-draft, buildtemplate-single-source, publish-cmd-no-#73]`. + +- **2026-07-12 (S112 PE sign-off close-review implement — `wf_7df56b89-7bb` GO-WITH-ADJ, owner anh Kiệt PROD-UAT-live, 4-region/1 file `PurchaseEvaluationWorkflowService.cs`, NO mig/FE/test → ACCEPT Case 1):** **C1 A1 (`ApplyDrafterBypassOnSubmitAsync`):** người soạn KÝ TAY ô cấp mình — `k=Max`→`minOwn=Min(drafterSlots.Order)`, bypass `<=k`→`1){pointer=minOwn}` STOP tại minOwn (người soạn tự ApproveV2Async); minOwn=1→bypass rỗng pointer giữ (0,1); non-contiguous Cấp1+Cấp3 ký tay đúng thứ tự. **C2 EDGE-5 hard-lock:** top `TransitionAsync` sau isAdmin/isSystem TRƯỚC mọi branch → `if(fromPhase is DaDuyet or TuChoi && !isAdmin && !isSystem) throw Conflict` (chặn non-admin lật phiếu terminal; Admin+System MIỄN); KHÔNG đụng EnsureCanRejectV2Async. **C3** no self-finalize (owner oQ1 permission-driven). **C4 opinion-history (`ApproveV2Async` UPSERT else):** old.Comment→Changelog (Workflow/Update/EntityId=level.Id) TRƯỚC overwrite chỉ khi non-empty — NO mig/bảng/endpoint. Build slnx **0/0**. Infra **4 FAIL/401 pass**: fail=C1 (PeSubmitGuardAndBypassTests 9/10/12/14 assert OLD auto-sign/terminal-on-submit); C2/C4 break 0 test → test-specialist ADD. LESSON: behavior-change write-site → run consumer tests, hand VERIFIED fail-list (KHÔNG sửa test). Tag `[s112, pe-signoff, a1-sign-own-level, edge5-hardlock, opinion-history-changelog, consumer-test-verified-handoff]`. +- **2026-07-12 (S112 Supplier Excel-import Phase B BE — `wf_e2f23a84` invest-ensemble → `wf_75d6149b` close-review GO-WITH-ADJ, owner anh Kiệt, commit `e100ef0`, Mig 63 → ACCEPT Case 1/2):** **Service** `SupplierExcelImportService` (preview + confirm 2-stage) + 2 endpoint (`/suppliers/import/preview` [415 non-xlsx] · `/suppliers/import/confirm` [401 auth]) + **Mig 63 `AddSupplierImportSourceFields`** (Supplier +`SourceUpdatedAt datetime2?`+`SourceUpdatedBy nvarchar(200)?` provenance cột 29/30; 2 AddColumn nullable no-new-table 89). FE 2-app byte-identical (`SupplierImportDialog`). **LESSONS:** (1) dedup Code = **OrdinalIgnoreCase ở SERVICE** — DB unique-index CI-collation lật (SQLite fixture BINARY KHÔNG bắt, prod SQL Server CI bắt) → dedup KHÔNG dựa DB; (2) parser **absolute cell-index** (row-4 header-fingerprint), KHÔNG `CellsUsed()` (column-drift); (3) **all-or-nothing** gate TRƯỚC mutate (1 hard-error→Committed=false DB unchanged); (4) fill-nulls **SKIP Code/Name/Type** (chỉ điền field trống); (5) `.Normalize(FormC)` cho real-file NFD (close-review adjust bake 30 `RealFileHeaderTokens` byte-verify). **SURPRISE:** Approach-B (extend seed) ĐÃ ship 4 NCC prod (Mig 62) → spec "partial" SAI; import ghi entity **TRỰC TIẾP** (fill-where-non-empty) KHÔNG qua `UpdateSupplierCommand` (tránh NULL-clobber #73). Shipped Run #496 PASS, reviewer GO-WITH-ADJ (BE 6/6 axes). Tag `[s112, supplier-excel-import, mig63, ordinalignorecase-dedup-at-service, abs-cell-index-parser, all-or-nothing, fill-nulls-safe, normalize-formc-nfd, direct-entity-write]`. diff --git a/.claude/agent-memory/memory-budget.json b/.claude/agent-memory/memory-budget.json index 30008e4..f5bc488 100644 --- a/.claude/agent-memory/memory-budget.json +++ b/.claude/agent-memory/memory-budget.json @@ -56,6 +56,7 @@ "file": "archive/_INDEX.md", "injected": "read-on-demand map (inject the map, not the territory)", "cap_bytes": 20480, + "split_rule_S167": "owner 'auto' 2026-08-01: cap applies to the MAIN archive/_INDEX.md only (the cold-load file). Rotated _INDEX-.md = cold tier, no cap, still A7-verified (gate reads _INDEX*.md; measure counts both into l2_index axis; shard-probe excludes both). AUTO-rotate rule lives in the _INDEX.md header of the affected sub (first: cicd-monitor). Probe (nhip-no-probe.ps1) deliberately measures MAIN file only - by design, do not 'fix' it to sum both.", "seeded_from": "max measured _INDEX = cicd-monitor 16779B (+ ~22% headroom). cicd-monitor at ~82% = WATCH-agent (grows with each run); when it nears cap -> gist-of-index or split, do NOT drop markers", "pointer_style": "substring (git-sha / Run#NNN / unique-phrase keyed), fallback Ctrl-F. NO line-hints (additive appends shift lines)" }, diff --git a/.claude/agent-memory/test-specialist/archive/_INDEX.md b/.claude/agent-memory/test-specialist/archive/_INDEX.md index cf98d62..4bc0741 100644 --- a/.claude/agent-memory/test-specialist/archive/_INDEX.md +++ b/.claude/agent-memory/test-specialist/archive/_INDEX.md @@ -6,7 +6,7 @@ - activity-s51-s52.md · 2026-06-08 (S52 P11-D Master gotcha #57 EXT) [test-before · 3 RED LIVE]: · substring:"- **2026-06-08 (S52 P11-D Master gotcha #57 EXT) [test-before · 3 RED LIVE]:" - activity-s51-s52.md · 2026-06-08 (S52 P11-E + P11-F WorkflowApps/Attendance test-after · substring:"- **2026-06-08 (S52 P11-E + P11-F WorkflowApps/Attendance test-after" - activity-s51-s52.md · 2026-06-08 (S51 P11-C HMW Wave2 filtered-unique gotcha #57 · substring:"- **2026-06-08 (S51 P11-C HMW Wave2 filtered-unique gotcha #57" -- baseline-history.md · S155 — PE xoa-khi-dang-duyet + man "Da xoa" (dot 2), +10 -> 561 (1 FAIL GIU DO = bug prod) · substring:"S155 — PE xoa-khi-dang-duyet + man \"Da xoa\" (dot 2), +10 -> 561" +- baseline-history.md · S155 — PE xoa-khi-dang-duyet + man "Da xoa" (dot 2), +10 -> 561 (1 FAIL GIU DO = bug prod) · substring:"S155 (2026-07-27) ĐỢT 2" - baseline-history.md · S153 — T4 ctx-adopt FAULT-INJECT may `mind-check` · substring:"S153 — T4 ctx-adopt FAULT-INJECT may `mind-check`" - baseline-history.md · S134 — PE luy-ke TAM TINH TEST-C, +8 -> 528 · substring:"S134 — PE luy-ke TAM TINH TEST-C, +8 -> 528" - baseline-history.md · S133 INV-C — PE budget-freeze T1-T9, +10 -> 519 · substring:"S133 INV-C — PE budget-freeze T1-T9, +10 -> 519" diff --git a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-implbe-k7.md b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-implbe-k7.md index dac629c..fd237d7 100644 --- a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-implbe-k7.md +++ b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-implbe-k7.md @@ -1,4 +1,4 @@ -IMPLBE-K7: IN-PROGRESS +IMPLBE-K7: DONE # sub-implbe-k7 — Lane BE K7 (chuỗi SP-002 B4, S167 · 2026-08-01) @@ -18,43 +18,69 @@ IMPLBE-K7: IN-PROGRESS | 3 | `grep "HttpPost" ContractSigningPlansController.cs` | 4 POST: `""` · `{id}/dossier-items` · `{id}/attachments` · `{id}/transitions` — **0 endpoint tạo HĐ** | | 4 | write-site `ContractSigningPlanLine.ContractId` | **0 assignment toàn codebase** (khớp đo độc lập của invest K7.0(b) @S164 **và** K5 FLAG-1 "`lines[].contractId` không tồn tại ở bất cứ đâu") | -**Cái ĐÃ tồn tại @W6-S161 = "đường ống HĐ V2"** (List/Detail V2-aware `ContractFeatures.cs:306-318` · Inbox V2 `:532-543` · guard trình `ContractWorkflowService.cs:73-92`) — invest K7.0 đã BÁC đúng chỗ này. Lead **nhập nhằng "đường ống đã vá" ⇒ "bridge đã tồn tại"**. +**Cái ĐÃ tồn tại @W6-S161 = "đường ống HĐ V2"** (List/Detail V2-aware `ContractFeatures.cs:306-318` · Inbox V2 `:532-543` · guard trình `ContractWorkflowService.cs:73-92`) — invest K7.0 đã BÁC đúng chỗ này. Lead **nhập nhằng "đường ống đã vá" ⇒ "bridge đã tồn tại"** *(lead đã nhận @lượt-2)*. ### Hệ quả + quyết định lane (khai rõ, không im lặng) -4/5 vá được giao (**vá-1-endpoint · vá-3 · vá-4a · vá-6**) **VẬT LÝ KHÔNG THỰC HIỆN ĐƯỢC** nếu không có bridge — chúng đều là mệnh đề *về thân bridge*. Đặc biệt **vá-6** ("nạp Project.Code + Supplier.Code theo khuôn bridge-cũ `:63-66/:85-86`") là **no-op** nếu áp lên bridge PE cũ: bridge cũ **ĐÃ CÓ** 2 dòng đó (đo: `CreateContractFromEvaluationFeatures.cs:63-66` supplierMap, `:65-66` project NotFound, `:85-86` supplier NotFound) ⇒ vá-6 chỉ có nghĩa với **bridge MỚI**. +4/5 vá được giao (**vá-1-endpoint · vá-3 · vá-4a · vá-6**) **VẬT LÝ KHÔNG THỰC HIỆN ĐƯỢC** nếu không có bridge — chúng đều là mệnh đề *về thân bridge*. Đặc biệt **vá-6** ("nạp Project.Code + Supplier.Code theo khuôn bridge-cũ `:63-66/:85-86`") là **no-op** nếu áp lên bridge PE cũ: bridge cũ **ĐÃ CÓ** 2 dòng đó ⇒ vá-6 chỉ có nghĩa với **bridge MỚI**. -⇒ **Quyết: DỰNG bridge KHKK→HĐ (BE-only) theo spec đã có sẵn `sub-invest-fable-b2-cum3.md §K7.1` (K7.6 V1+V2) + nhúng thẳng 5 vá vào lúc dựng.** Chệch-khuôn-so-brief KHAI RÕ tại đây, trỏ đề xuất gốc (invest K7.1 :23-56 — command signature, 6 guard, field-map, codegen, write-site, changelog, controller đều đã pin sẵn ⇒ 0 quyết-định-thiết-kế mới của tôi). **KHÔNG làm:** FE (K7.6 V4/V5/V6 → lane FE), test (V7/V3 → test-lane), migration (cột `ContractId` đã có từ Mig 69 + index `ContractSigningPlanLineConfiguration.cs:26`). +⇒ **Quyết: DỰNG bridge KHKK→HĐ (BE-only) theo spec sẵn có `sub-invest-fable-b2-cum3.md §K7.1` (= K7.6 V1+V2) + nhúng thẳng 5 vá vào lúc dựng.** Chệch-khuôn-so-brief KHAI RÕ, trỏ đề xuất gốc (invest `:23-56` pin sẵn command signature · 6 guard · field-map · codegen · write-site · changelog · controller ⇒ **0 quyết-định-thiết-kế mới của tôi**). **KHÔNG làm:** FE (V4/V5/V6 → lane FE), test (V3/V7 → test-lane), migration (cột `ContractId` có sẵn từ Mig 69 + index `ContractSigningPlanLineConfiguration.cs:26` ⇒ **0-mig**). -### Tiền đề phụ thuộc (cụm-1 đã land?) — ĐO +### Tiền đề phụ thuộc cụm-1 — ĐO, ĐỦ -- K2 Mig 71: `ContractSigningPlanLine.TenHangMuc` (`:27`) + `CatalogEntryId` (`:23`) ✅ có trên đĩa. -- K3 finalize: `ApprovedAmount` (`:35` "CHỈ ghi tại choke-point finalize (W3)") — xem §5 đo write-site. +- K2 Mig 71: `ContractSigningPlanLine.TenHangMuc` (`:27`) + `CatalogEntryId` (`:23`) ✅ trên đĩa. +- K3 finalize: write-site `ApprovedAmount` ✅ `ContractSigningPlanWorkflowService.cs:348` (`line.ApprovedAmount ??= line.ProposedAmount`) ⇒ happy-path bridge có số để đọc. --- ## §1 — [vá-1a] Grant `Contracts.Create` (seeder, code-first) -(đang làm — ghi khi xong) +- **`DbInitializer.cs:2757-2822`** — hàm MỚI `SeedContractCreateAccessAsync`: cấp `CanRead+CanCreate` trên key root **`Contracts`** cho **Drafter + Procurement + Admin**, UPGRADE-ONLY (row có sẵn → nâng đúng 2 cờ, KHÔNG hạ cờ nào, KHÔNG đụng Update/Delete; row chưa có → tạo). Khuôn sao y `SeedKeHoachKyKetAccessAsync:2672` (W1 S161). +- **`DbInitializer.cs:2361`** — call site, đặt **SAU** `RevokeTemporarilyHiddenModulesAsync:2341` (luật "chạy sau revoke để THẮNG", cùng chỗ 2 seeder anh em `:2347`/`:2354`). +- **Lỗ đã đóng (đo được, không suy đoán):** `SeedAllRolesReviewReadPermissionsAsync:2516` tạo row key `Contracts` với `CanCreate = isPe` mà `isPe = IsPeKey("Contracts") = false` ⇒ **trước S167 KHÔNG role nào mang `Contracts.Create`**; chỉ Admin lọt nhờ bypass. Không có §1 thì nút bridge 403 với 13/13 role. +- **Câu III (ai bấm) = default, chờ ratify:** comment `[K7 — câu III default, chờ anh ratify]` ngay trên mảng `roleNames` — anh chốt khác thì sửa ĐÚNG 1 mảng đó, **máy BE không đổi** (controller giữ nguyên). +- ⚠️ **Grant này ăn @RESTART API prod (seeder), KHÔNG phải deploy FE.** CẤM SQL tay (#84 — grant tay bị revoker lật, bài S159 Run #423 mất 447/494 row). Nghiệm thu = restart THẬT rồi đếm row `Permissions WHERE MenuKey='Contracts' AND CanCreate=1` (#75/#76). ## §2 — [vá-1b] Endpoint bridge = HỘI 2 khóa -(đang làm) +- **`ContractSigningPlansController.cs:230-238`** — `POST /api/contract-signing-plans/{id}/create-contract`, `[Authorize(Policy = "Contracts.Create")]` (action) **∧** class-level `[Authorize(Policy = "KeHoachKyKet.Read")]` `:28` (đã có sẵn từ W2 — đo, không thêm). ASP.NET Core cộng dồn class+action theo **AND** ⇒ thiếu 1 trong 2 vế = **403**. Trả `201 Created` + `{ contractId, maHopDong }`. +- **`:262-266`** — body record `CreateContractFromSigningPlanBody(LineIds?, ContractType, ApprovalWorkflowId, TenHopDong?)`; `PlanId` lấy từ **route** (1 nguồn sự thật). `LineIds` nullable ở body để thiếu-field ra **400 validator** ("chọn ít nhất 1 dòng") thay vì 500 NRE. +- **`:8`** — `using SolutionErp.Domain.Contracts;` cho `ContractType`. +- **Chọn key theo gotcha #85** (đúng-key-của-endpoint): tài nguyên SINH RA là `Contract` ⇒ vế ghi phải là `Contracts.Create`, KHÔNG phải `KeHoachKyKet.Create`. Đối chiếu PE controller per-action: `PurchaseEvaluationsController` **class `[Authorize]` TRẦN + 0 policy per-action** (`:15`, `:339-343`) ⇒ bridge PE cũ chỉ cần đăng-nhập; cửa MỚI này chặt hơn hẳn — **KHÔNG đụng cửa cũ** (`POST /api/contracts` `[Authorize]` trần = #82 treo chủ đích). -## §3 — [vá-3] Guard workflow `IsActive ∧ IsUserSelectable` +## §3 — [vá-3] Guard workflow pin `IsActive ∧ IsUserSelectable` -(đang làm) +- **`CreateContractFromSigningPlanFeatures.cs:132-148`** — "Rào (6)" **4 vế**: (a) tồn tại → `NotFoundException`; (b) `ApplicableType == Contract(3)` → Conflict; (c) `IsActive`; (d) `IsUserSelectable`. +- (a)+(b) = khuôn `ContractFeatures.cs:79-87`; **(c)+(d) là phần VÁ** — khuôn gốc thiếu, nên forge POST pin được quy trình đã retired/bỏ ghim (dropdown FE lọc 2 cờ `ContractCreatePage.tsx:334-341` nhưng FE không phải rào). +- 2 cờ **throw TÁCH RIÊNG** (không gộp `||`): "đã ngừng hoạt động" và "chưa được ghim" xử ở 2 nút khác nhau trong Designer ⇒ admin phải đọc được nguyên nhân; test-lane cũng phân biệt được 2 nhánh. ## §4 — [vá-4a] `Line.ContractId` write-site + DTO expose -(đang làm) +- **WRITE-SITE:** `CreateContractFromSigningPlanFeatures.cs:237-238` — `foreach (var l in lines) l.ContractId = contract.Id;` = **assignment ĐẦU TIÊN toàn codebase** (§0 phép đo 4 xác nhận trước đó = 0). Đóng nợ K5 FLAG-1 "chỗ-cắm-1-dòng". +- **FALLBACK giữ nguyên chiều:** `:246` `if (pe is not null) pe.ContractId ??= contract.Id;` — `??=` CÓ CHIỀU đúng như đề bài yêu cầu: HĐ đầu của gói thắng ô đơn, HĐ 2..N **không ghi đè** (chúng vẫn thấy được qua `Line.ContractId` — đúng lý do vá-4a tồn tại). Bridge PE cũ gán thẳng `= contracts[0].Id` (`CreateContractFromEvaluationFeatures.cs:145`) vì đã chặn ở `:59-60`; ở cầu MỚI phải `??=` để 2 cầu cùng chạy trên 1 PE không cướp ô của nhau. +- **DTO — trả lời chính xác câu "đã có hay chưa" (2 DTO KHÁC NHAU, đừng gộp):** + 1. 🟢 **`ContractSigningPlanLineDto.ContractId`** (chi tiết phiếu) = **ĐÃ CÓ SẴN** từ W2-S161: khai báo `ContractSigningPlanFeatures.cs:74`, projection `:657`. ⇒ **lane FE K7 KHỎI CHỜ** — dialog "Đưa vào Hợp đồng" đọc `detail.lines[].contractId` để render badge "Đã có HĐ" + disable là **dùng được NGAY**, tôi không phải thêm gì. + 2. 🆕 **`ContractSigningPlanListItemDto.ContractIds` (`List`)** = **THÊM MỚI** — khai báo `:65-75` (append-CUỐI đúng convention file), projection `:836-842` (đặt trong `ProjectPagedAsync` = **1 site dùng chung 3 endpoint** list/inbox/deleted). Lý do phải có: cây 4-folder `usePipelineStages.ts:287-291` dựng GĐ3 từ payload **LIST** và hôm nay chỉ có 1 nguồn `pe.contractId` đơn ⇒ gói N-HĐ thì HĐ thứ 2+ vô hình. Trả mảng **PHẲNG** (consumer là `Set`), KHÔNG `Distinct()` trong projection (N dòng gộp 1 HĐ ra ID trùng — vô hại, đỡ 1 tầng dịch SQL). ## §5 — [vá-6] Project.Code/Supplier.Code + SlaDeadline + WorkflowDefinitionId -(đang làm) +- **`:150-157`** — nạp `Project` + `Supplier` ĐỦ (không `Select` mỏng) + **2 guard `NotFoundException`** theo khuôn bridge cũ `:63-66/:85-86`; 2 con trỏ này là **loose-Guid không FK vật lý** ⇒ thiếu guard = NRE ngay tại `GenerateAsync`. +- **`:212`** — `contract.MaHopDong = await codeGenerator.GenerateAsync(contract, project.Code, supplier.Code, ct)` gọi **TRƯỚC `db.Contracts.Add`** (bài S88: codegen tự-commit SERIALIZABLE + SaveChanges nội bộ, Add trước = flush HĐ dở trong transaction của nó). Cùng thứ tự bridge cũ `:112-113` + `CreateContractSigningPlanCommandHandler:426-429`. +- **`:203-206`** — field-map `SlaDeadline = UtcNow + GetPhaseSla(DangSoanThao) ?? 7d` (thiếu thì đây là HĐ DUY NHẤT trong hệ không có hạn phase; cả 2 tiền lệ `ContractFeatures.cs:110` + bridge cũ `:109-110` đều set). +- **`:159-166` + `:201`** — `WorkflowDefinitionId = activeWfId` (V1 active theo `ContractType`) pin **NGANG** 2 tiền lệ `ContractFeatures.cs:105-109` ("Cả 2 set ok") + bridge cũ `:108`. 🔴 **Chỗ này spec-cụm-3 vá-6 THẮNG invest K7.1** (invest `:46` viết `WorkflowDefinitionId = null`) — đúng luật "16 vá thắng khi xung đột với nền" (spec `:4`); để null là rơi về policy hardcode `ContractFeatures.cs:647-662`. +- Field-map còn lại theo invest `:46`: `GiaTri = Σ ApprovedAmount(ĐÚNG tập LineIds)` `:180` · `SupplierId` từ dòng · `ProjectId/DepartmentId` từ plan · `ApprovalWorkflowId` pin V2 `:202` · `BudgetManual*` = Σ `PeReferenceAmount` `:198-199` · 2 changelog (`ContractChangelogs` Insert + `ContractSigningPlanChangelogs` Update/EntityType=Line) `:216-235` · **1 `SaveChangesAsync` cuối** `:248` (atomic, 0 HĐ dở-dang). +- 6 guard tuần tự (invest `:38-44`): phase DaDuyet `:106-108` · dòng ∈ phiếu `:111-118` · 1-HĐ-1-NCC `:121-124` · `ApprovedAmount != null` **message NÊU TÊN hạng mục** `:127-141` (KHÔNG fallback `ProposedAmount`) · `ContractId == null` per-dòng (chống double-bridge) · workflow §3. -## §6 — Build + verify +## §6 — Build + runtime check -(đang làm) +- **`dotnet build SolutionErp.slnx`** → **Build succeeded · 0 Warning · 0 Error** (67s). Diffstat `src/`: `ContractSigningPlansController.cs +33` · `ContractSigningPlanFeatures.cs +23/-2` · `DbInitializer.cs +86` · **file mới** `CreateContractFromSigningPlanFeatures.cs` (~250 dòng). **0 file ngoài `src/Backend/**`.** +- **Runtime check (câu hỏi thật: collection-subquery mới có dịch được trên SQLite fixture không?):** + `dotnet test tests/SolutionErp.Infrastructure.Tests --filter "FullyQualifiedName~ContractSigningPlan"` → **34 passed / 1 failed / 35 total (12s)**. + ✅ **Cả 3 handler đọc `ProjectPagedAsync` đều XANH** (`GroupAndCatalogTests.ListAsync` · `CrudTests.ListDeletedAsync` · `ApprovalTests.InboxAsync`) ⇒ `ContractIds` subquery **dịch được**, không phải sửa gì. (Khuôn giống `WinnerSupplierNames` list PE — nay có bằng chứng CHẠY THẬT, không chỉ tiền lệ.) +- 🔴 **1 FAIL — ĐÚNG DỰ KIẾN, thuộc lane test-specialist, TÔI KHÔNG SỬA (rào split):** + `tests/SolutionErp.Infrastructure.Tests/Api/AuthorizePolicyRegressionTests.cs` :: `ContractSigningPlansController_EveryWriteEndpoint_HasAuthorizePolicy`. + Nguyên nhân chính xác: `:398` `parts[0].Should().Be(MenuKeys.KeHoachKyKet)` — test bắt **MỌI** action của controller phải gate bằng key `KeHoachKyKet`; endpoint bridge cố ý gate `Contracts.Create` (đó CHÍNH LÀ vá-1, và là gotcha #85 đúng-key-của-endpoint). + **Bàn giao test-lane:** thêm ngoại lệ cho action `CreateContract` (kỳ vọng `Contracts.Create`), giữ nguyên luật cho 15 action còn lại. Neo chống-vacuous `:375` (`>= 15 action`) **KHÔNG vỡ** — nay 16 action. Đề nghị bổ sung 2 test authz 2 chiều (đủ 2 vế → 201 · thiếu 1 vế → 403) theo spec `:9`. +- **Chưa làm / không thuộc lane (khai để không ai tưởng đã xong):** T1-T8 (test-lane) · nút + dialog ×2 app (lane FE) · A1-A7 acceptance sau deploy · FE type mirror `khkk.ts` cho `contractIds` (lane FE — JSON thừa field không gãy TS hiện tại). -END implbe-k7 — VERDICT=IN-PROGRESS +END implbe-k7 — VERDICT=DONE (bridge KHKK→HĐ BE dựng xong + 5/5 vá land · build 0/0 · 34/35 test chạm, 1 fail đã-verify thuộc test-lane · 0-mig · 0 file FE/tests · chưa commit) diff --git a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-implfe-k7.md b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-implfe-k7.md index 8182471..b613c55 100644 --- a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-implfe-k7.md +++ b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-implfe-k7.md @@ -1,4 +1,4 @@ -IMPLFE-K7: IN-PROGRESS +IMPLFE-K7: DONE — lane code-trọn (2 lượt #53-garble, resume-1 + lead-close-on-behalf từ đĩa) # Lane FE K7 — bridge KHKK→HĐ (vá-1 FE · vá-16 · vá-4b) — S167 2026-08-01 @@ -57,4 +57,26 @@ Tiền lệ DUY NHẤT đang sống = bridge PE: `POST /purchase-evaluations/{id `khkkPlanContractIds()` trong `types/khkk.ts` ×2 app. Nếu BE **không** expose trên list ⇒ cây vẫn chạy bằng nguồn legacy + có chuông DEV (không vỡ, không im). -END implfe-k7 — VERDICT=IN-PROGRESS +## §2 KHỚP-DTO ĐÓNG (lead đối chiếu 2 artifact @S167) + +BE-lane expose **`ContractIds`** trên list DTO (`ContractSigningPlanFeatures.cs:65-75/:836-842` — sub-implbe-k7.md §4). +FE đổi từ tên đoán `lineContractIds` → **`contractIds`** đúng-1-hàm như thiết kế §1 (vai làm trong lượt resume — +return garble-2 xác nhận "BE named it ContractIds… 1-point change"; diagnostics 2 chỗ dùng đổi sạch, tsc EXIT 0). +Chuông DEV vắng-field ⟂ rỗng-thật (§1) giữ nguyên. + +## §3 Nút bridge + dialog (vá-1FE + vá-16) — DISK-FACT, chi tiết để gate-K7 đọc diff + +`KhkkDetailPage.tsx` M ×2 app (SHA-pair identical) — gate hội-2-khóa `can('KeHoachKyKet','Read') && +can('Contracts','Create')` + dialog khuôn-sẵn, gọi `POST /contract-signing-plans/{id}/create-contract` +(khớp endpoint BE :230-238). ⚠️ Mục này lead ghi on-behalf theo PHÉP ĐO (file M + SHA + build), KHÔNG +thuật lại từng dòng vai viết — **gate-K7 adversarial đọc diff thật là lưới chính** (đừng đọc mục này +thành "đã review"). + +## §4 Chứng máy (lead đo @S167, sau khi vai dừng — trọng tài build TƯƠI #68) + +- SHA-pair 3/3 identical: `usePipelineStages.ts` `76e1823345d4` · `KhkkDetailPage.tsx` `cf0a2272061f` · `types/khkk.ts` `60ec8fa3a5ae` +- `npx tsc -b` fe-user EXIT 0 · fe-admin EXIT 0 (chạy SAU khi vai dừng tay) +- `npm run build` fe-user ✓ 921ms · fe-admin ✓ 765ms (warnings chunk-size + INEFFECTIVE_DYNAMIC_IMPORT = PRE-EXISTING) +- `git status` fe-*: đúng 6 file M (3 cặp mirror), 0 file lạ + +END implfe-k7 — VERDICT=DONE (lead-closed-on-behalf; gate-K7 là lưới review) diff --git a/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-test-k7.md b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-test-k7.md new file mode 100644 index 0000000..131df66 --- /dev/null +++ b/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-test-k7.md @@ -0,0 +1,43 @@ +TEST-K7: IN-PROGRESS + +# sub-test-k7 — Lane TEST K7 (bridge KHKK→HĐ, chuỗi SP-002 B4, S167 · 2026-08-01) + +> Rào: chỉ `tests/**` · KHÔNG production code · KHÔNG commit. Ghi-TỪNG-MỤC chống-#53: xong mục nào ghi ngay mục đó; mục CHƯA CHẠY chỉ được tiêu-đề + "CHƯA ĐO". + +## §0 — Tiền đề ĐO TƯƠI (không tin số đề bài) + +- `git status` @spawn: `src/Backend/**` dirty 3 file + 1 file MỚI (`CreateContractFromSigningPlanFeatures.cs`) = **lane BE K7 đã LAND, uncommitted** — khớp `sub-implbe-k7.md`. `tests/**` = **0 file dirty** (baseline sạch). +- **Baseline suite ĐO TƯƠI (trước khi tôi gõ 1 chữ):** `SolutionErp.Infrastructure.Tests` = **577 total / 576 passed / 1 FAILED**. FAIL đúng test đã bàn giao: + `AuthorizePolicyRegressionTests.ContractSigningPlansController_EveryWriteEndpoint_HasAuthorizePolicy` @`:398` — `Expected parts[0] ... they differ: "Contracts" vs "KeHoachKyKet"` ⇒ **RED tái hiện đúng nguyên nhân implbe-k7 khai**, không phải flaky. +- Đếm action controller từ ĐĨA: `grep -c "^ \[Http"` = **17** (implbe-k7 ước "16"; số đo là 17). Neo chống-vacuous `>= 15` không vỡ, giữ nguyên nghĩa. + +### 🔎 Chồng-lấn TÌM TRƯỚC KHI VIẾT (bài S166) — T8 ĐÃ CÓ NGƯỜI ĐO MỘT NỬA + +`Application/ContractV2PipelineTests.cs` (W6-S161) đã phủ đúng mệnh đề T8 của bảng invest (`sub-invest-fable-b2-cum3.md:82`): approver-V2-không-role-legacy thấy List/Detail + Inbox, cấp-2 KHÔNG thấy khi đang cấp-1 — `Inbox_PendingLevelApprover_SeesChoDuyetContract` · `Inbox_OtherLevel_DoesNotSeeContract` · `View_V2Approver_NotDrafter_CanSeeChoDuyetContract`. +⇒ **KHÔNG nhân bản.** Cái các test đó KHÔNG đo: chúng **seed thẳng** `Contract{Phase=ChoDuyet, ApprovalWorkflowId, stepIndex}` — tức giả định sẵn thứ mà cầu K7 phải TỰ sinh. Vế còn hở = **mối nối cầu→đường-ống**. T8 của tôi đo đúng chỗ hở đó (bridge → trình THẬT qua `ContractWorkflowService` → inbox), 0 chồng lấn. + +## §1 — [BƯỚC 1 ✅ XONG] Fix bàn-giao BE: ngoại lệ `CreateContract` + +File: `tests/SolutionErp.Infrastructure.Tests/Api/AuthorizePolicyRegressionTests.cs` + +- Thêm **bản đồ kỳ-vọng-CHÍNH-XÁC** `KhkkCrossModuleGates = { CreateContract → "Contracts.Create" }` (KHÔNG phải danh sách "bỏ qua"): action trong bản đồ **vẫn bị đo**, chỉ đổi key kỳ vọng ⇒ gate sai thành `KeHoachKyKet.Create` **vẫn ĐỎ**. Comment nêu LUẬT (tài nguyên SINH RA quyết định khoá ghi) + trỏ gotcha **#85** + `MenuPermissionHandler.cs:40` (so khớp CHÍNH XÁC 1 key, không kế-thừa). **0 số-đếm trong comment.** +- Vế (b) tách 2 nhánh: có trong bản đồ → `authz.Policy.Should().Be("Contracts.Create")`; không → giữ nguyên luật cũ `parts[0] == MenuKeys.KeHoachKyKet` cho 16 action còn lại. +- Thêm (e) chốt-bằng-TÊN: `byName[CreateContract] == "Contracts.Create"` + `KhkkCrossModuleGates.Keys.Should().BeSubsetOf(byName.Keys)` — đổi tên/xoá action mà quên dọn bản đồ = **cấu-hình-chết im lặng** (vắng-mặt trông giống ổn) ⇒ bắt luôn. +- Neo chống-vacuous `:375` **GIỮ NGUYÊN `>= 15`** (không hạ, không đổi nghĩa; thực đo 17). +- Vế (a)/(c)/(d) không đụng: `CreateContract` là POST nên vẫn phải qua luật "verb ghi không được gate Read" + "phải Create/Update/Delete". + +**ĐO:** `--filter AuthorizePolicyRegressionTests` → **Passed! Failed: 0, Passed: 22** (trước: 1 FAIL). File này +0 test mới (sửa test sẵn có, +2 vế assert). + +## §2 — 8 case T1-T8 + 2-authz + idempotency + +CHƯA ĐO. + +## §3 — Vá-7 B6 neo mới + +CHƯA ĐO. + +## §4 — Suite FULL + +CHƯA ĐO. + +END test-k7 — VERDICT=IN-PROGRESS