[CLAUDE] Docs: S118 closeout - STATUS/HANDOFF/session-log + memory reconcile (509 test / gotcha 82)
Session S118: phan quyen PRO tra.bui (Procurement exception to S92 revoke) + va lo hong ReportsController authz. 2 prod deploy #388/#389 verified; 3 code/docs commit da push (e88e75ddocs-reconcile +c2135baPRO-perm-seeder +c808b9aReports-authz). Closeout (docs/memory-only -> CI-skip per paths-ignore): - STATUS: S118 mega-line blurb + canonical rows Tests 501->509 + Gotchas 81->82 (governance-detector [OK] canonical matches disk 509/82). - HANDOFF: S118 brief + NEXT anh (tra.bui re-login · binh.le decision · account-trung Le-Thanh-Binh) + lessons (wipe-durability / menu-flag!=API / verify-lane-garble-recover / Serilog-durability-proof). - Session log 2026-07-14-S118-perm-pro-tra-bui-reports-authz.md. - User memory: +feedback_permission_grant_two_layers (menu-flag grant != API-power, 2-layer verify); garble-recover x18->x20 (cicd VERIFY-lane x2 -> em-main-solo curl+sqlcmd). - Agent-memory: cicd fidelity-fix (harvest-curator F1: false "CLEAN-return" label -> garble+recover, substance disk-verified TRUE) + tooling-auditor/harvest-curator @end diaries (H1/H2 gate PASS 5-truc). - H22 §5.0: fold 2 trailing wal: flush commits (670c888 + e5f655f) into this closeout. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -14,7 +14,7 @@ Read-only CI/CD + post-deploy verifier SOLUTION_ERP. Polls Gitea Actions API, ve
|
||||
|
||||
## ⚠️ cicd-lane return-garble tracker (#53)
|
||||
|
||||
cicd-spawn đôi khi trả memory-curation slice ("Compressing S112/S111") thay verdict (×5/6 qua S114; cumulative ×18 all-agents → [[feedback_agent_return_garble_recover]], root-cause = curate-at-return). Recover: em-main-solo curl-verify. **S115 (#385)+S116 (#386) cicd-lane CLEAN-return no-garble ×2; S117 (#387) garble→SendMessage-resume (em-main curl cross-verify khớp, verdict disk-true) — return-mechanics garbled NHƯNG verdict-substance OK.**
|
||||
cicd-spawn đôi khi trả memory-curation slice ("Compressing S112/S111") thay verdict (×5/6 qua S114; cumulative ×18 all-agents → [[feedback_agent_return_garble_recover]], root-cause = curate-at-return). Recover: em-main-solo curl-verify. **S115 (#385)+S116 (#386) cicd-lane CLEAN-return no-garble ×2; S117 (#387) garble→SendMessage-resume (em-main curl cross-verify khớp, verdict disk-true) — return-mechanics garbled NHƯNG verdict-substance OK.** **S118 (#388+#389) cicd-lane garble ×2 → em-main-solo curl+sqlcmd recover (#388 durability tra.bui=20 seed-race "9 upgraded" · #389 Reports.Read→Admin-only+401), both verdict-substance disk-verified OK; curate-at-return root-cause persists (harvest-curator S118-end bắt nhãn "CLEAN-return" tự-dán sai → habitual pattern).**
|
||||
|
||||
---
|
||||
|
||||
@ -38,7 +38,7 @@ cicd-spawn đôi khi trả memory-curation slice ("Compressing S112/S111") thay
|
||||
- **Stage 0 RAG infra:** `Get-Service Qdrant` Running + `http://localhost:6333/healthz`. Collection `proj_solution_erp`.
|
||||
- **Stage 1 Push+filter:** `git log -1 --format='%H %s'` + `git log origin/main..HEAD` empty + diff vs paths-ignore (docs-only → SKIPPED-DOCS return).
|
||||
- **Stage 2 Gitea poll** (max 10 iter × 60s): API `.../actions/tasks?limit=5` (NOT `/runs` 404). Match `head_sha` (NOT run_number — cancelled runs shift numbering). ⚠️ `updated_at` stale ~2min (gotcha #46) → cross-check VPS mtime. Foreground-sleep BLOCKED Windows → Monitor-style busy-wait-curl-spin ~30s/iter.
|
||||
- **Stage 3 Test gate:** baseline **505 PASS** (45 Domain + 460 Infra; S118 #388 `c2135ba` — 501+4 `ProcurementMasterAccessSeedTests` scope-isolation; Infra 456→460, Domain frozen 45; prior 501 [S117 #387 `316a82f` PE approved-price-range], 495, 486, 477, 458, 440, 436, 434, 421). CI runs both proj BEFORE build/deploy → status=success ⟹ test gate passed (`tasks` terminal=`status:success`, `conclusion` NOT populated — trust success NOT log-numeric). Local grep undercounts (Theory/InlineData). Phase 9 UAT skip OK.
|
||||
- **Stage 3 Test gate:** baseline **509 PASS** (45 Domain + 464 Infra; S118 #389 `c808b9a` — 505+4 `AuthorizePolicyRegressionTests` ReportsController Reports.Read gate; Infra 460→464, Domain frozen 45; prior 505 [#388 `c2135ba` ProcurementMasterAccessSeed], 501, 495, 486, 458, 440, 421). CI runs both proj BEFORE build/deploy → status=success ⟹ test gate passed (`tasks` terminal=`status:success`, `conclusion` NOT populated — trust success NOT log-numeric). Local grep undercounts (Theory/InlineData). Phase 9 UAT skip OK.
|
||||
- **Stage 4 Post-deploy** (if SUCCESS): auth login bearer (admin + nv.test gotcha #44; token=`accessToken` route `/api/auth/login`) → 3-5 endpoint smoke 2XX (incl new — NEW-endpoint bodyless-POST returns **411 IIS Length-Required pre-auth NOT 404**; re-probe WITH body → 401 confirms wired) → FE bundle hash 2 app (per #69 = real-size-vs-fake not hash-delta) → SignalR negotiate (gotcha #25) → EF mig prod==repo.
|
||||
- **Stage 4.6 (S29 CRITICAL):** sqlcmd seed sample verify post-deploy (NOT chỉ schema). Seed=runtime-row-insert (no history/tables advance) → verify via DB-query (e.g. Permissions count), NOT bundle-frozen-alone.
|
||||
- Discovery: ASP.NET 10 record enum needs numeric input unless `JsonStringEnumConverter` (SOL has NO converter → FE sends numeric). sqlcmd ssh Windows-auth `\\\\SQLEXPRESS` 4-backslash. INFRASTRUCTURE seed MUST run (NOT inside `if(!demoSeedDisabled)`); DEMO seed gated → gotcha #51.
|
||||
@ -56,7 +56,7 @@ cicd-spawn đôi khi trả memory-curation slice ("Compressing S112/S111") thay
|
||||
- **Gitea:** `git.baocaogiaoduc.vn/vietreport-admin/solution-erp` · workflow `.gitea/workflows/deploy.yml` · paths-ignore 7-entry (D10 S111 #495): `['docs/**','**/*.md','.claude/skills/**','.claude/agent-memory/**','.claude/workflows/runs/**','.gitignore','scripts/**.md']`. Anon API works (public repo) when GITEA_TOKEN absent.
|
||||
- **Prod:** api/admin/eoffice `.solutions.com.vn` · SSH `ssh vietreport-vps` (Administrator, id_ed25519) · IIS phys paths: API `C:\inetpub\solution-erp\api` · admin `\fe-admin` · user `\fe-user`. DB `.\SQLEXPRESS`/`SolutionErp`/`vrapp` SQL-auth. **Conn key = `ConnectionStrings.Default` (NOT `DefaultConnection`)**. DB pw = `buKL3TGBkD0wDDbYVw65QeX9` (User Id=`vrapp`; ⚠️ Password field has NO `vrapp/` prefix — S89 login-failed when prefix included; read verbatim from prod `appsettings.Production.json` ConnectionStrings.Default when `$env:PROD_DB_PASSWORD` empty). ⚠️ skill-doc path `C:\inetpub\apps\SolutionErp\Api` STALE.
|
||||
- **SSH→PS quoting (S42):** nested bash→ssh→powershell mangles `$var`/`\"` → use `iconv UTF-16LE | base64` → `powershell -EncodedCommand $B64`; OR write ps1-file + `powershell -File $(cygpath -w)`. sqlcmd over SSH direct `-W -h -1` pw-inline. sys-catalog string-concat → `COLLATE DATABASE_DEFAULT` (collation conflict).
|
||||
- **Tests baseline:** **501 PASS** (45 Domain + 456 Infra · 0 fail/skip; S117 #387 `316a82f`; prior 495/486/477/458/440/436/434/421). Phase 9 UAT skip OK. 🔑 **Concrete count via web-UI log path `https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp/actions/runs/{run_number}/jobs/0/logs` (~18KB, NO `/api/v1/` prefix) → per-project `Passed! - Failed: 0, Passed: N, Skipped: 0`. ⚠️ S117 REFINE: `/api/v1/…/actions/runs/{n}/jobs/0/logs` AND `/api/v1/…/tasks/{id}/logs` BOTH now 404 (Gitea) — ONLY the bare web-UI runs path (no api/v1) returns log body; status=success alone still ⟹ gate-passed (tests run pre-deploy).**
|
||||
- **Tests baseline:** **509 PASS** (45 Domain + 464 Infra · 0 fail/skip; S118 #389 `c808b9a`; prior 505/501/495/486/458/440/421). Phase 9 UAT skip OK. 🔑 **Concrete count via web-UI log path `https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp/actions/runs/{run_number}/jobs/0/logs` (~18KB, NO `/api/v1/` prefix) → per-project `Passed! - Failed: 0, Passed: N, Skipped: 0`. ⚠️ S117 REFINE: `/api/v1/…/actions/runs/{n}/jobs/0/logs` AND `/api/v1/…/tasks/{id}/logs` BOTH now 404 (Gitea) — ONLY the bare web-UI runs path (no api/v1) returns log body; status=success alone still ⟹ gate-passed (tests run pre-deploy).**
|
||||
- **Mig latest repo: Mig 66 `AddPeApprovedPriceRange`** (`20260714035636`, S117 `316a82f`; single AddColumn `PurchaseEvaluations.ApprovedPriceMaxAmount decimal(18,2) nullable` — cột giá-chốt MAX của "duyệt theo khoảng Min–Max" src ProMinMax; VERIFIED-APPLIED-PROD `COL_LENGTH=9` + history-top `20260714035636_AddPeApprovedPriceRange` + sys.tables **89 giữ** [AddColumn no-new-table]). Prev Mig 65 `BackfillPeQuoteIsSelectedAndWinnerInvariant` / 64 `AddSupplierPublishState` / 62 `ExpandSupplierFields` (see docs/CLAUDE.md migration table). Path `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/`. Prod check `sqlcmd __EFMigrationsHistory ORDER BY MigrationId DESC`. ⚠️ **`sys.tables`(is_ms_shipped=0)=89** — AddColumn/data-only don't change count; commit touching no new table ⟹ 89 correct, don't FAIL. enum-additive = int NO-mig (history NOT-advance + empty `git diff -- '*Migrations*'`).
|
||||
- **Bundle hash live:** current ship-baseline = S117 #387 (admin js `cFI7ih4a`/css `CfunQHbd` · user js `MxgooVZw`/css `DGqqHQXS`; size 1,656,696/1,563,044b; LM 04:18:21/04:19:16 GMT = 11:18/11:19+07 07-14) — JS-rotate ×2/CSS-frozen from S116 (Dd55jBpj/DVDr1UQ4). See S117 recent-run entry for markers. ⚠️ id≠run_number (head_sha-keyed). PER #69 TWO-MODE hash is SNAPSHOT-per-run NOT stable-baseline (rotate on real-src OR benign-0-src; freeze on 0-src) — ALWAYS re-snapshot LIVE post-success; ship-proof = LM-in-window + real-size (vs 919b SPA-trap) + marker-grep, NOT hash-delta. js-rotate/css-frozen = FE-logic-no-style fingerprint.
|
||||
- **Bearer:** admin `admin@solutions.com.vn/Admin@123456` (full) · UAT `nv.test@solutions.com.vn/TestUser@123456` (Drafter CCM, gotcha #44 check).
|
||||
@ -77,8 +77,9 @@ BE (test+build) ~90s · FE × 2 ~60s/app · deploy ~30s · **total ~3min code /
|
||||
|
||||
## 📅 Recent runs (compressed — verbatim → archive via `_INDEX.md`)
|
||||
|
||||
- **🟢 S117 #387 (rn 387, id 501) `316a82f` PE endsBeforeCeo-pointer-aware + duyệt-theo-khoảng-Min–Max + Mig 66 `AddPeApprovedPriceRange` APPLIED-PROD GO 5m36s (11:14:09→11:19:45+07 2026-07-14; 19-file: BE 7 [Domain/Config/Features/Service/IService/Dtos/Controller] + FE 6 [PeDetailTabs+PeWorkflowPanel+types ×2-app] + Mig 3 + test 2 [NEW PeApprovedPriceFinalizeTests + PeFinalizeProjectionTests mod]):** status=success · **gate CONCRETE web-UI-path `runs/387/jobs/0/logs` (NO /api/v1/): Domain 45 + Infra 456 = 501** (+6 vs S116 495; Infra 450→456, Domain frozen 45; 0-fail/0-skip) · **Mig 66 APPLIED-PROD** sqlcmd `COL_LENGTH('PurchaseEvaluations','ApprovedPriceMaxAmount')=9` + history-top `20260714035636_AddPeApprovedPriceRange` + sys.tables **89 giữ** (AddColumn decimal(18,2) no-new-table) · **FE JS-ROTATE ×2/CSS-frozen** admin js `Dd55jBpj`→`cFI7ih4a` (css `CfunQHbd` frozen) · user js `DVDr1UQ4`→`MxgooVZw` (css `DGqqHQXS` frozen) = FE-logic-no-style fingerprint · byte-verify #77 BOTH-app NEW bundle: `cả Min và Max`=1 + `duyệt theo khoảng`=1 + `ProMinMax`=2 + `Min–Max`(en-dash)=1 PRESENT (all=0 in OLD DVDr1UQ4 captured pre-deploy — NEW-code ship proof; ⚠️ `PRO Min`=1 in OLD = pre-existing label `ProMin:'PRO Min'` = FALSE-discriminator) · size real 1,656,696/1,563,044b (vs 919b SPA-trap) · LM admin 04:18:21/user 04:19:16 GMT = 11:18/11:19+07 IN-window · smoke ALL 200 (api ready+live, admin, eoffice) · PE 401-secured: GET-list no-token+bogus→401, POST /transitions bogus+no-token→401, /select-winner bogus→401 (no 500) · 🔑 pre-deploy bundle-snapshot BEFORE poll-success (captured OLD-hash+OLD-feature=0) → post-success re-snapshot = clean rotation proof. cicd-lane CLEAN-return.
|
||||
- _(**S116 #386 `88368fd`** PE-nhập-giá-ÂM ô-báo-giá + submit-guard all-zero [gate 495=45+450, js-rotate×2 D51OYyGV→Dd55jBpj/BVdssm5S→DVDr1UQ4 css-frozen, byte-verify 4-str PRESENT, 🔑 poll-til-success TRƯỚC re-snapshot tránh frozen-trap] **+ S115 #385 `81b59f0`** GOVERNANCE hmw.js+.ps1 non-ignored [gate 486 frozen, bundle FROZEN×4 fresh-LM+identical-size = #69-REFINE 0-src proof, #41 non-.md-in-.claude STILL triggers] **+ S114 #384 `d436af2`** PE multi-NCC per-hạng-mục + Mig 65 backfill-invariant APPLIED-PROD [gate 486, js-rotate×2 acprMqfB→D51OYyGV/Ck3BTDdA→BVdssm5S, byte `trúng thầu theo từng hạng mục`, select-winner 401] — all GO ~5m, FE real-ship, smoke ALL 200 · full verbatim → `archive/2026-07.md` via `_INDEX`)_
|
||||
- **🟢 S118 #389 (rn 389, id 503) `c808b9a` SECURITY-AUTHZ gate ReportsController→Reports.Read GO 5m34s (15:55→16:00:57+07 2026-07-14; 3-file: ReportsController.cs [+2 `[Authorize(Policy="Reports.Read")]` on `dashboard`+`contracts/export`; `my-dashboard` kept-open scope-currentUser] + AuthorizePolicyRegressionTests.cs [+4 reflection] + gotchas.md):** status=success · **gate `runs/389/jobs/0/logs`: Domain 45 + Infra 464 = 509** (+4 vs #388 505; Infra 460→464 = +4 authz reflection; 0-fail/skip) · **dll NEW** 15:58:21+07 (was 15:33:21 = app restarted in-window) · **🎯 DB `Reports.Read`→Admin-ONLY** sqlcmd prod (scp .sql→`-i`, cleaned): 1 role CanRead=1 (`Admin`); `Procurement` row CanRead=0 → non-admin dashboard/export=**403 by-construction** (4-attr-test + policy-Admin-only-DB) · smoke: health/live 200, admin 200, eoffice 200, dashboard/export/my-dashboard no-token=**401** (class-level `[Authorize]` intact) · **NO-FE** (0 fe-src → bundle=S117 `cFI7ih4a`/`MxgooVZw` frozen) · **NO-Mig**. Password-free per task (403 by-construction not curl-proven). **cicd-lane return GARBLED #53** (memory-curation slice, NOT verdict) → **em-main-solo curl+sqlcmd recovered**; verdict-substance disk-verified TRUE (509-gate + `Reports.Read`→Admin-only + 401 no-token all git/prod-corroborated). [harvest-curator S118-end F1: nhãn "CLEAN-return" cũ SAI, đã sửa.]
|
||||
- _(**S117 #387 `316a82f`** PE duyệt-khoảng Min–Max + Mig 66 `AddPeApprovedPriceRange` APPLIED-PROD GO 5m36s [gate 501=45+456; js-rotate×2 `Dd55jBpj`→`cFI7ih4a`/`DVDr1UQ4`→`MxgooVZw` css-frozen `CfunQHbd`/`DGqqHQXS`; byte-verify #77 `cả Min và Max`/`duyệt theo khoảng`/`ProMinMax`/`Min–Max` PRESENT (=0 OLD); Mig66 `COL_LENGTH=9` + history-top + sys.tables 89; 🔑 pre-deploy snapshot BEFORE poll-success = clean-rotation-proof; PE 401-secured] · full verbatim → `archive/2026-07.md` via `_INDEX`)_
|
||||
- _(**S114-S116 #384-#386** PE multi-NCC per-hạng-mục (Mig 65 backfill-invariant APPLIED-PROD) + nhập-giá-ÂM + submit-guard + S115 GOVERNANCE-hmw GO ×3 [gate 486→495; js-rotate×2 `acprMqfB`→`D51OYyGV`→`Dd55jBpj`/css-frozen; S115 FROZEN×4 #69-REFINE 0-src proof + #41 non-.md-in-.claude STILL triggers; byte-verify str-PRESENT; 🔑 poll-til-success TRƯỚC re-snapshot tránh frozen-trap] — FE real-ship, smoke ALL 200 · verbatim → `archive/2026-07.md` via `_INDEX`)_
|
||||
- _(**S111 #495 `8aa3869` H22-WAL-adopt** [deploy.yml SELF-change +2 paths-ignore, run parsed+ran-full = workflow-self-change PASS; bundle FROZEN ×6-streak; 🔑 **AS-6 baseline-shift**: post-#495 memory-push (budget.json/agent-memory/run-trace) HẾT trigger CI, phân-loại giờ soi file ngoài 7-glob] **+ S112 #496 `e100ef0`** PE-workflow+Supplier-import-Phase-B+Mig63 `AddSupplierImportSourceFields` [gate 458, 🔑 411-refine: bodyless-POST→411 ANY-route → re-probe WITH-body → 401/415-matched vs 404-missing] **+ S113 #383 `5fa11b5`** Supplier-import-v2+Mig64 `AddSupplierPublishState`-APPLIED-PROD [IsPublic 37/37, gate 477] — all GO ~5m, FE real-ship, Mig-applied-prod, smoke ALL 200 · full verbatim → `archive/2026-07.md` via `_INDEX`)_
|
||||
- _(**S107-closeout→S110-spot #490-#494** GOVERNANCE/MEMORY-push GO ×5 [`d74f67f`·`69957fd`·`f5a5c3e`·`6ee60cd`·`ac3f122` — trigger #41 non-.md-in-push: budget.json ×2 + hmw.js ×3, pattern DEAD từ D10 #495] — bundle FROZEN-streak ×5 sau ROTATE #489 [#69 TWO-MODE; LM-in-window + real-size 1,644,931/1,551,280b = signal duy nhất] · Mig 62 + sys.tables 89 frozen · gate 440 · smoke 3×200 ALL · 🔑 #494 spot-detection: LM-prod-newer-than-last-verified-window = missed-run detector · full verbatim → `archive/2026-07.md` via `_INDEX`)_
|
||||
- _(**S107 #489 `21387cd` GOVERNANCE-ONLY (+office-document 12th sub) GO 5m33s** — JS ROTATED-benign `ChSNAJBK`→`BVyH-FY7` / `Bn2w1znH`→`EDsZebvn` CSS-frozen, 🔑 #69 TWO-MODE CONFIRMED (0-fe-src ROTATE #312/#489 hoặc FROZEN #333/#485-487 — ship-proof = LM-in-window + real-size, hash-delta phụ) · **S104 #488 `778fc98` FULL-STACK Supplier 9→27 + Mig 62 `ExpandSupplierFields` APPLIED-PROD** — AddColumn×18-exact + sys.tables-89-invariant + un-cram 4-NCC Note=NULL/Pkg/Bank/LegalRep-populated-EXACT + DTO-18-new-keys-live + JS-rotate-real-src + 🔑 FULL-STACK-SCHEMA-ADD+DATA-UN-CRAM PASS-pattern — full verbatim → `archive/2026-07.md` via `_INDEX`)_
|
||||
@ -93,5 +94,5 @@ BE (test+build) ~90s · FE × 2 ~60s/app · deploy ~30s · **total ~3min code /
|
||||
|
||||
## 🔄 Curate trigger
|
||||
- >~30KB → archive recent runs → L2 `archive/<period>.md` (byte-exact append) + `_INDEX.md` substring pointer. Dup failure patterns → merge. Stale >3mo → remove.
|
||||
- **Last curate: 2026-07-13 S115 (self, PostToolUse 21.6→<17KB)**: #384/#383/#496/#495 (S114/S113/S112/S111) verbatim → `archive/2026-07.md` + `_INDEX` +4 pointers → S114 compact-stub + S111-113 merged-stub; top S114 garble-block → 1-line tracker.
|
||||
- **Last curate: 2026-07-14 S118 (self #389-run: S117 #387 verbatim→`archive/2026-07.md` moved-not-cut byte-id, baselines 505→509; prev S115 self 21.6→<17KB)**: #384/#383/#496/#495 (S114/S113/S112/S111) verbatim → `archive/2026-07.md` + `_INDEX` +4 pointers → S114 compact-stub + S111-113 merged-stub; top S114 garble-block → 1-line tracker.
|
||||
- **Prev: S111 07-12 self 20.5→17 (#490-#494→archive) · S107 07-10 self 20.4→16.9 (#489/#488→archive) · S104 07-06 self 20.5→17.9 · S98 07-02 em-main 19.9→17.06 (`2026-07.md` NEW) · S80 06-20 em-main 38.8→16 · S70 06-17 Harness-9 65.2→23.2 · earlier S66 · S40.**
|
||||
|
||||
@ -28,3 +28,5 @@
|
||||
|
||||
- **🟢 S116 #386 (rn 386, id 500) `88368fd` PE-nhập-giá-ÂM ô báo giá + submit-guard all-zero GO 5m28s (17:08:42→17:14:10+07 2026-07-13; 7-file: FE PeDetailTabs.tsx ×2 mirror [112 dòng/app] + BE PurchaseEvaluationWorkflowService.cs [:206 `selectedThanhTien.Sum()<=0`→`.All(x=>x==0)` — cho giá ÂM=phát-sinh-giảm/hoàn-tiền NCC=giá THẬT] + 4 test-file; NO migration → tables 89 giữ):** status=success · **gate CONCRETE `runs/386/jobs/0/logs`: Domain 45 + Infra 450 = 495** (+9 vs S114/S115 486; Infra 441→450, Domain frozen 45) · **FE JS-ROTATE ×2 / CSS-frozen** admin js `D51OYyGV`→`Dd55jBpj` (css `CfunQHbd` frozen) · user js `BVdssm5S`→`DVDr1UQ4` (css `DGqqHQXS` frozen) = FE-logic-no-style fingerprint (khớp PeDetailTabs.tsx logic-only) · byte-verify #77 NEW bundle: `Đã nhập giá chào thầu`=1 + `hoàn tiền NCC`=1 + `Đơn vị được chọn chưa có giá chào thầu`=1 + `phát sinh giảm`(toggle)=1 PRESENT (đều =0 ở old-frozen D51OYyGV pre-deploy — chứng NEW-code ship; `Có giá là được`=0 minify-diff non-load-bearing) · size real 1,656,224/1,562,572b (vs 919b SPA-trap) · LM admin 10:12:47 / user 10:13:41 GMT = 17:12/17:13+07 IN-window · smoke ALL: api ready+live 200, admin+eoffice 200, PE 401 alive+secured · 🔑 **PRE-DEPLOY frozen-bundle-at-S115 TRAP-avoided**: poll til status=success TRƯỚC re-snapshot (grep-sớm khi status=running → false-FAIL frozen; xác nhận CI running 17:11 → success 17:14 → bundle rotate). cicd-lane CLEAN-return no-garble.
|
||||
- **🟢 S115 #385 (rn 385, id 499) `81b59f0` GOVERNANCE/TOOLING-ONLY GO 5m23s (12:25:31→12:30:54+07 2026-07-13, harness adopt presence-not-age + hmw.js STOP-HARD; 20-file: 0 src/fe/mig/test — non-ignored trigger = `.claude/workflows/hmw.js` + `scripts/memory-archive-gate.ps1` [`.ps1`∉`scripts/**.md` ⟹ CI-run ĐÚNG #41; rest .md/.json under docs/agent-memory/workflows-runs/broadcasts IGNORED]):** status=success · **gate CONCRETE `runs/385/jobs/0/logs`: Domain 45 + Infra 441 = 486/0-fail/0-skip = S114 baseline EXACT** (0 src/tests diff → count frozen ✓) · **FE bundle FROZEN ×4** admin js `D51OYyGV`/css `CfunQHbd` · user js `BVdssm5S`/css `DGqqHQXS` = S114 verbatim (NO rotate) — deploy physically re-copied (LM 05:29:32/05:30:26 GMT = 12:29:32/12:30:26+07 IN-window) BUT byte-identical (size 1,655,105/1,561,453b = S114 EXACT) ⟹ #69-REFINE 0-fe-src=identical-Vite=same-hash+size+fresh-LM · SPA-trap ctrl fake-hash 919b text/html · Mig FROZEN 65 (0 migration-file diff; API health/ready 200=startup OK) · smoke 4/4 200 (api ready+live, admin, eoffice). 🔑 GOVERNANCE-ONLY PASS: non-.md-in-.claude (hmw.js/.ps1) STILL trigger CI (#41 LIVE post-#495 D10); FROZEN-hash+fresh-LM+identical-size = no-accidental-rotation proof. cicd-lane clean-return no-garble.
|
||||
|
||||
- **🟢 S117 #387 (rn 387, id 501) `316a82f` PE endsBeforeCeo-pointer-aware + duyệt-theo-khoảng-Min–Max + Mig 66 `AddPeApprovedPriceRange` APPLIED-PROD GO 5m36s (11:14:09→11:19:45+07 2026-07-14; 19-file: BE 7 [Domain/Config/Features/Service/IService/Dtos/Controller] + FE 6 [PeDetailTabs+PeWorkflowPanel+types ×2-app] + Mig 3 + test 2 [NEW PeApprovedPriceFinalizeTests + PeFinalizeProjectionTests mod]):** status=success · **gate CONCRETE web-UI-path `runs/387/jobs/0/logs` (NO /api/v1/): Domain 45 + Infra 456 = 501** (+6 vs S116 495; Infra 450→456, Domain frozen 45; 0-fail/0-skip) · **Mig 66 APPLIED-PROD** sqlcmd `COL_LENGTH('PurchaseEvaluations','ApprovedPriceMaxAmount')=9` + history-top `20260714035636_AddPeApprovedPriceRange` + sys.tables **89 giữ** (AddColumn decimal(18,2) no-new-table) · **FE JS-ROTATE ×2/CSS-frozen** admin js `Dd55jBpj`→`cFI7ih4a` (css `CfunQHbd` frozen) · user js `DVDr1UQ4`→`MxgooVZw` (css `DGqqHQXS` frozen) = FE-logic-no-style fingerprint · byte-verify #77 BOTH-app NEW bundle: `cả Min và Max`=1 + `duyệt theo khoảng`=1 + `ProMinMax`=2 + `Min–Max`(en-dash)=1 PRESENT (all=0 in OLD DVDr1UQ4 captured pre-deploy — NEW-code ship proof; ⚠️ `PRO Min`=1 in OLD = pre-existing label `ProMin:'PRO Min'` = FALSE-discriminator) · size real 1,656,696/1,563,044b (vs 919b SPA-trap) · LM admin 04:18:21/user 04:19:16 GMT = 11:18/11:19+07 IN-window · smoke ALL 200 (api ready+live, admin, eoffice) · PE 401-secured: GET-list no-token+bogus→401, POST /transitions bogus+no-token→401, /select-winner bogus→401 (no 500) · 🔑 pre-deploy bundle-snapshot BEFORE poll-success (captured OLD-hash+OLD-feature=0) → post-success re-snapshot = clean rotation proof. cicd-lane CLEAN-return.
|
||||
|
||||
@ -129,3 +129,4 @@
|
||||
- 2026-07-13 · S114 #384 `d436af2` PE multi-NCC per hang muc + Mig65 backfill invariant APPLIED-PROD GO — js-rotate x2/css-frozen D51OYyGV/BVdssm5S, gate 486 · PASS · `2026-07.md` · substring:"S114 #384 (rn 384, id 498)"
|
||||
- 2026-07-13 · S115 #385 `81b59f0` GOVERNANCE/TOOLING-ONLY (hmw.js+memory-archive-gate.ps1) GO — FROZEN x4 D51OYyGV/BVdssm5S fresh-LM identical-size #69-REFINE, gate 486 · 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 — js-rotate x2/css-frozen Dd55jBpj/DVDr1UQ4, gate 495 (45+450), byte-verify 4-str PRESENT · PASS · `2026-07.md` · substring:"S116 #386 (rn 386, id 500)"
|
||||
- 2026-07-14 · S117 #387 `316a82f` PE endsBeforeCeo-pointer-aware + duyet-theo-khoang Min-Max + Mig66 AddPeApprovedPriceRange APPLIED-PROD GO — js-rotate x2/css-frozen cFI7ih4a/MxgooVZw, gate 501 (45+456), byte-verify 4-str PRESENT · PASS · `2026-07.md` · substring:"S117 #387 (rn 387, id 501)"
|
||||
|
||||
@ -45,3 +45,4 @@ H2 harvest-MD-integrity auditor **SOLUTION_ERP-self**. Read-only + **propose-onl
|
||||
- **2026-07-13 (S116 @end GATE — self-write diary, VERIFY-idempotent 0 re-APPEND):** Model `claude-opus-4-8`. GATE **PASS 5-trục + close-gate 0/24** (PE nhập giá ÂM ô báo giá, shipped `88368fd` + CI #386; HEAD `0edbc58` wal-flush-landing, tree clean). **Coverage 🟡 5/6 role-bucket đủ (1 sub-gap):** 3 impl self-write đủ 4-field (impl-fe :9-15 sign-toggle-immediate-commit `math.abs`-no-global-mod SHA `3e16dba5` ×2 + gotcha `details[].quotes`≠`suppliers[]` · impl-be :77 guard-1line ACCEPT + stale-comment-flag · test-spec :59 486→495 +9 T1-T4) + cicd :79 #386 GO 5m28s; invest-ensemble 4-lane RETURN-only em-main consolidated 4→2 (:78 Budget-consumers + :97 edge/precedent, FE+BE-surface embedded, knowledge-complete); review-ensemble 3-lane RETURN-only → Lane1 correctness-wire :21 **TIERED-pointer** (`project_pe_negative_quote_correctness_review.md` 2908B EXISTS not-dead) + Lane3 financial-invariant :77 — **NHƯNG Lane2 mirror-completeness KHÔNG có spawn-record riêng** (knowledge NOT-lost: run.md:11 'mirror clean' + correctness-pointer + invest:97 SHA-identical + impl-fe SHA-pair) → propose em-main APPEND minimal Lane2. fable-real deep-pass #53-GARBLE 0-verdict → em-main-solo gate (run.md:13), nothing-to-harvest. **Fidelity STRONG 0-fab → KHÔNG escalate:** `88368fd` = 7-file EXACT (2FE+1BE+4test no-mig) · BE `Sum()<=0`→`.All(x==0)` verbatim in-diff · 9 new `[Fact]/[Theory]` = +9 = gate 495. Corruption 0: 0-byte 0 · tail 0a 7/7 · FFFD 0 worker (self=1 baseline #9) · cicd:58 `$(cygpath)` = intentional-cmd-example không shell-baked · moved-not-cut ×2 VERIFIED (reviewer→2026-07 3-anchor · impl-be FIFO→2026-06 5-anchor). Close-gate: run.md embedded-synth `✅ PIPELINE COMPLETE`:19 + run.md/spec git-tracked HEAD; orphan **0/24** (14×06-18 sep · h21/h22/presence sep · 4×S112+S113emb2+S114+S116 embedded). **method-#10 DOUBLE session-drift-tag S116** (cả 2 = em-main ON-BEHALF harvest, self-write all-correct-S116): reviewer:77 label `S117`-forward + invest:78 tag `[s115?]`-backward → mislabel clusters tại on-behalf-harvest → propose normalize→S116. Nit: run.md:20-23 leftover template `[ ]` rows dup completed Stages4-6 (benign residue, propose trim). Chore: fe-designer 21.5K idle-07-01 · invest-cb 21.1 · impl-fe 21.1 · cicd 20.4 · reviewer 19.2 (post-curate + re-append) · SELF 19.1→~21K → **self-compact S110→S111 STILL-DUE** phiên tới; all < 25.6 cap. Tag `[s116-end-gate, pass-5truc, close-gate-0of24, review-lane2-mirror-gap, double-session-drift-tag-m10, fidelity-strong-7file-exact]`.
|
||||
- **2026-07-14 (S117 @end GATE — self-write diary, VERIFY-idempotent 0 re-APPEND):** Model `claude-opus-4-8`. GATE **PASS 5-trục + close-gate 0-orphan** (2 chỉnh PE UAT anh Kiệt: lũy-kế display-lie fix + duyệt-theo-khoảng Min–Max ProMinMax, shipped `316a82f` + CI #387 501-gate; HEAD `fc1b8d9` closeout tree-clean). **Coverage 5/5 spawn** (inv×2 share investigator-codebase/ + test-spec + impl-fe + cicd): inv-luyke :107 `[fable-real-single·Fable-S117]` wf ab2657ee (#53-garble→resume + em-main prod-DB verify) · inv-minmax :104 wf abbbb86d 0-garble Model-A · test-spec :59 +5 ProMinMax · impl-fe :9 `[s117]` SHA 011ee8cf/617b0aa0 · cicd :41/59/60/61 #387. **BE both-features = em-main-solo by-design** (financial signature-ripple) → 0 impl-backend record expected (KHÔNG gap). **Fidelity STRONG 0-fab → KHÔNG escalate:** commit 316a82f file-list EXACT match mọi claim (Mig 66 3-file `ApprovedPriceMaxAmount` + entity+9 + service+34 + 6 FE 3/app symmetric + 2 test-file); STATUS 501; Mig 66 COL_LENGTH=9 prod; 2 garble-lane (invest-1+cicd) 0 claim-bịa (em-main-curl cross-verify khớp). Corruption 0: tail 0a 6/6 · FFFD 0 · impl-fe compact 23.3→13.3KB **MOVED-NOT-CUT VERIFIED** (archive/2026-H1.md +5 entry verbatim, 6/6 SHA-anchor; HOT S117/S116/S113 giữ) · cicd:58 `$(cygpath)` intentional-cmd. Close-gate: 2 /fable-real folder spec-*.md present+tracked (luyke 5828B/minmax 4202B, dual-accept ≠ hmw-synthesis); 0 orphan (no run.md-sans-synth); delta mồ-côi 0 (4 memory WAL-committed 11:00–11:29, porcelain clean). **3 benign nit INFORM (0 data-loss, KHÔNG escalate):** (a) inv-minmax mis-tag **S118→S117** (method-#10 drift fwd-1; content+spec đều S117) · (b) test-spec baseline **500/455→501/456** (thiếu em-main-solo +1 luyke `PeFinalizeProjectionTests`; đếm own +5 only) · (c) cicd:17 self-claim "S117 CLEAN no-garble ×3" vs session-log+task = garble-then-resume (under-report own garble; verdict-substance disk-true). Chore: 0 memory >25.6 cap (cicd 19.8/inv-cb 19.6 max); impl-fe healthy 13.3 post-compact; SELF 15.6→~17.2K → self-compact S113 phiên tới soft-due. Tag `[s117-end-gate, pass-5truc, close-gate-0orphan-fable-real-spec, moved-not-cut-verified, 3nit-s118mistag-testcount500-cicdgarbleclaim, fidelity-strong-commit-exact]`.
|
||||
- **2026-07-14 (S118 @start RE-REPORT — self-write diary, VERIFY-idempotent 0 re-APPEND):** Model `claude-fable-5`. Verdict 🟢 **ALL-CLEAN — 0 harvest-mới · 0 orphan/26 · 0 delta mồ-côi · corruption 0 · 3-nit-S117 ALL-FIXED**. Post-S117-gate đúng 2 commit chạm memory, cả 2 accounted: `1b8b065` (em-main reconcile 3 diary = đóng trọn 3 benign-nit INFORM @S117-end, diff-verified: (a) inv-cb `## S118`→`## S117` · (b) test-spec 500/455→501/456 + nguồn +1 em-main-solo `PeFinalizeProjectionTests` · (c) cicd "no-garble ×3"→"×2; S117 garble→SendMessage-resume verdict-substance OK") + `5c7569e` final-flush (+1 dòng = chính S117@end-entry SELF). Porcelain 0 + 12/12 L1 mtime ≤ 11:56:15 ⟹ 0 mồ-côi by-construction (method-#3). Orphan **0/26** dual-accept: 14×06-18 (5 sub-synth maxdepth-2 + 9 flat) · 3 flat Jul (h21/h22/presence) · 6 embedded (4×S112+S113+S114) · **pe-negative-quote embedded NON-heading `✅ PIPELINE COMPLETE`:19** (regex `^## synthesis` MISS → verify-trực-tiếp trước flag, đừng tin heading-scan đơn thuần) · 2×S117 fable-real spec-only KHÔNG run.md (spec 5828B/4202B tracked) = by-design ≠ orphan. Corruption 0: 0-byte 0/12 · tail 0a 8/8 touched · FFFD 0/12 — SELF-baseline FFFD 1→0 (entry chứa self-quote đã rời L1 qua archive, sạch hơn không phải mất; cập nhật baseline method-#9). Chore: 0 L1 > 25.6 cap (fe-designer 21.5K idle-07-01 · cicd 19.96 · inv-cb 19.6); SELF ~19.3K > 17.1 target → **self-compact S113-block DUE phiên này** (plan đứng từ S117-end). Delta cần APPEND: 0. Tag `[s118-start, all-clean, 0-orphan-0of26, 3nit-all-fixed-1b8b065, embedded-nonheading-pipeline-complete, fffd-baseline-1to0]`.
|
||||
- **2026-07-14 (S118 @end GATE — em-main-APPENDED on-behalf từ propose-only):** Model lead=Opus. GATE **PASS 5-trục** — close OK 0-corruption/data-loss/orphan/fabrication. 7-spawn/5-bucket cov 6/7 (cicd#1 #388 durability PARTIAL: method`:63`+baseline`:41` captured, GO-run-verdict missing). close-gate **0-orphan** (no S118 run-trace by-construction — session = Agent-tool spawns only, no fan-out). corruption 0 (moved-not-cut cicd S117#387→archive byte-id `e5f655f`). **Fidelity 2 cicd-garble INFORM (NOT escalate — all substance git+em-main-verified TRUE):** F1 #389`:80` tự-dán "CLEAN-return" SAI (thực garble→em-main-solo-recover) → **em-main FIXED** · F2 #388 garble unrecorded (durable method survived) · F3 tracker`:17` stale-S117 → **em-main FIXED**. cicd = habitual mislabel-garble-as-clean pattern (archived S117#387 mang cùng nhãn sai). 5 propose-delta: cicd`:80`/`:17`/`:97` em-main-reconciled + this @end line. Tag `[s118-end-gate, pass-5truc, cicd-garble-x2-returnlabel-nit-FIXED, moved-not-cut-verified, 0-orphan-no-runtrace]`.
|
||||
|
||||
@ -15,7 +15,7 @@ WRITE specialist độc quyền `tests/**`. xUnit + FluentAssertions 7.2 + EF SQ
|
||||
- ❌ NOT: production code `src/Backend/**` + `fe-*/**` → test reveal bug → REPORT em main, KHÔNG fix
|
||||
- ❌ NOT: decide WHAT to test (test plan) → em main + reviewer chốt priority
|
||||
|
||||
## 📊 Baseline: 505 tests = 505 PASS (45 Domain + 460 Infra) as of S118 (was 501@S117; +4 Infra Procurement master-access seeder isolation regression, filtered-run 4/4)
|
||||
## 📊 Baseline: 509 tests = 509 PASS (45 Domain + 464 Infra) as of S118b (was 505@S118; +4 Infra ReportsController company-wide-report authz-regression, filtered-run 19/19, full 509/509)
|
||||
- Count history + verbose per-session test-design notes (S54→D2): [archive/baseline-history.md](archive/baseline-history.md) (L2, on-demand).
|
||||
- Latest (D2): +6 PE Mig 58 multi-winner — `CreateContractFromEvaluationMultiWinnerTests.cs` (5, 1→N HĐ per IsWinner, GiaTri per-winner=quote-total / single=legacy-budget-sum) + `PeListWinnerNamesProjectionTests.cs` (1, EF collection-projection translation-smoke). No prod bug.
|
||||
> Pattern S67: private-static seed/init → invoke qua REFLECTION (`GetMethod(name, NonPublic|Static)` + `Invoke(null, [db, roleManager, NullLogger.Instance])`); seed MenuItem rows TRƯỚC Permission (FK MenuKey→MenuItem.Key Cascade, SQLite Error 19 nếu thiếu). Cycle-guard test: SqliteDbFixture đủ (no User); rollup-count test cần IdentityFixture.
|
||||
@ -56,6 +56,7 @@ Test theo CODE (single source truth), document mismatch header comment + report.
|
||||
|
||||
## 📅 Recent activity (one-line FIFO — verbose in [archive/baseline-history.md](archive/baseline-history.md))
|
||||
|
||||
- **S118b (2026-07-14) ReportsController company-wide-report authz REGRESSION (test-after SECURITY — reviewer catch) — 505→509 PASS (+4 Infra), no-red, NO prod bug:** EXTEND `Api/AuthorizePolicyRegressionTests.cs` (Pattern 10 reflection, +4 [Fact] → file 15→19). Fix S118: 2 endpoint company-wide `Dashboard`+`ExportContracts` (lộ tài chính HĐ toàn cty: tổng giá trị, top NCC/dự án, xu-hướng-12-tháng, export-Excel) NAY gate `[Authorize(Policy="Reports.Read")]` (chỉ Admin, khớp S92); `MyDashboard` CỐ Ý giữ mở (chỉ class-level `[Authorize]` trần, scope currentUser). Trước: cả controller class-level `[Authorize]` trần → mọi user đăng nhập gọi được = lộ tài chính. **T1** class-level `[Authorize]` Policy=null+Roles=null (bare — nếu hardcode Policy sẽ khóa my-dashboard). **T2/T3** Dashboard+ExportContracts action-Policy==`Reports.Read` (gỡ→NotBeNull-fail = tái-lộ toàn cty). **T4** MyDashboard `(attr?.Policy).Should().BeNull()` — cho phép bare `[Authorize]` vô-hại NHƯNG chặn mọi Policy (thêm policy→khóa dashboard cá-nhân role không-Admin). csproj ĐÃ ref Api (line 25, từ S45) — KHÔNG vướng reference. KHÔNG touch prod.
|
||||
- **S118 (2026-07-14) Procurement master-access seeder SCOPE-ISOLATION regression (test-after SECURITY — anh Kiệt FDC) — 501→505 PASS (+4 Infra), no-red, NO prod bug:** new `ProcurementMasterAccessSeedTests.cs` (4 [Fact]) guard seeder MỚI `SeedProcurementMasterAccessAsync` (private static, DbInitializer) — NGOẠI-LỆ role Procurement mở lại Danh mục SAU S92 revoke ("chỉ Admin thấy"). Chain thật: Revoke→SeedProcurement. **T1 upgrade-path** Procurement sau revoke→seed đúng ma trận (Suppliers R+C+U D=false + 8 read-only chỉ R). **T2 ⭐SCOPE-ISOLATION (core)** DeptManager đứng RIÊNG (KHÔNG kèm Procurement): pre-grant→revoke→seed → 9 key VẪN all-false (row tồn tại giữ vết) = grant KHÔNG rò role khác; +CONTRAST assert Procurement THẬT được nâng (chống vacuous-pass). **T3 insert-path** DB mới no-revoke → INSERT 9 row, Delete=false + count==9. **T4 idempotent** seed ×2 → bất biến + count==9 no-dup + role khác vẫn ẩn. Reflection harness MIRROR `AdminOnlyModulesRevokeTests` (NonPublic|Static, sig (ApplicationDbContext,RoleManager<Role>,ILogger), FK seed MenuItem→Role→Permission). No user cần (perm role-level). **Reflection KHÔNG vướng — KHÔNG cần đổi visibility.** KHÔNG touch prod.
|
||||
- **S117 (2026-07-14) PE "duyệt theo KHOẢNG" ProMinMax range-price TEST-BEFORE-MERGE (financial-critical) — 495→500 PASS (+5 Infra, file 10→15), no-red, NO prod bug:** feature anh Kiệt: PRO có cả Min&Max → người duyệt cấp cuối chọn CẢ HAI (source="ProMinMax") → `ApprovedPriceAmount=Min`, `ApprovedPriceMaxAmount=Max`. Bất-biến `Max!=null ⟺ Source=="ProMinMax"`. Prod đã land: `ApplyApprovedPriceOnFinalize(...,decimal? maxAmount=null)` +`ValidApprovedPriceSources`+"ProMinMax"; `TransitionAsync`/`ApproveV2Async` +`approvedPriceMaxAmount` (SAU source). 5 new [Fact] qua reachable terminal path (mirror case-1 SeedSingleApproverWorkflow, KHÔNG gọi private): (a) happy Min100/Max130 set cả 2 · (a-boundary) **min==max OK** lock guard `>` vs `>=` (revert→RED) · (b) thiếu Max→Conflict "*phải có cả giá Min và giá Max*" not-finalized · (c) min>max→Conflict "*không được lớn hơn giá Max*" · (d) nguồn ĐƠN+Max→Conflict "*kèm giá Max*" wire-bug-guard. +regression assert `Max==null` vào Theory 3b (4 nguồn đơn). **⚠️ Gotcha (self-inflicted-avoided):** prod method 5-param nay → `InvokeApply` reflection helper phải pass 5 args (`{...,maxAmount}`); optional param KHÔNG auto-bind qua `MethodInfo.Invoke` → nếu giữ 4 args = `TargetParameterCountException` phá unit-test 4/4b/4c. Đã fix helper. KHÔNG touch prod.
|
||||
- **S116 (2026-07-13) PE báo giá SỐ ÂM regression-lock (test-before cho guard R2-revised — critical-algo/financial) — 486→495 PASS (+9 Infra), no-red, NO prod bug:** feature anh Kiệt cho ThanhTien ÂM (phát-sinh-giảm/hoàn tiền NCC = giá THẬT). BE ĐÃ đổi submit-guard `:202-208` `Sum()<=0`→`Count==0`(chưa chọn)/`else All(x==0)`(chưa có giá). 4 nhóm test khớp hành-vi MỚI: **T1** `PeWorkItemBudgetTests` (+1) quote ÂM −20tr KHÔNG chạm PeWorkItemBudget record (FullAmount ngân sách bất biến) NHƯNG CHẢY vào CurrentProposalTotal=−20tr (chứng tách-biệt cột đề-xuất vs ngân-sách, "0 code" kiến-trúc). **T2** `PeMultiWinnerTests` (+2) winnerQuoteTotal=SUM CÓ DẤU khác-winner(+50,−20=30) + cùng-winner-2-dòng(80,−30=50). **T3** `PeSubmitGuardAndBypassTests` (+4, +2 helper `SeedWinnerWithQuotesAsync` params + `BuildV2PeReadyToSubmitWithQuotesAsync`): all-zero[0,0]→BLOCKED "chưa có giá"; pure-neg[−500k]→ChoDuyet; mixed net>0→ChoDuyet; 🔒 **mixed net==0 từng-dòng≠0 [+500k,−500k]→ChoDuyet** = ranh giới phân-biệt All(==0) vs Sum()<=0 (revert→RED). **T4** `PeCcmThresholdFinalizeTests` (+2, +helper 2-signed-quote): NET threshold — [+1.2tỷ,−0.3tỷ]=0.9tỷ<ngưỡng→CCM finalize no-throw + CONTRAST [+1.2tỷ] alone=1.2tỷ≥ngưỡng→Conflict (dòng âm LOAD-BEARING, R-CEO giữ NET). KHÔNG touch prod.
|
||||
|
||||
@ -46,3 +46,4 @@ H1 tooling-freshness auditor **SOLUTION_ERP-self**. Read-only + **propose-only**
|
||||
- **2026-07-12 (S111 @end CHỐT — H22 WAL: ①CMD 16→18 · ②③ 0-đổi · ④ 1-FLAG LOW arch:377; new-alloc 0-external; FLAG-1 CLOSED):** +2 commit (`318a271` STATUS:39 flip LANDED · `8aa3869` H22 30-file) + tree 1-M `WAL.md` = hook-persist expected (squash §5.0 — ĐỪNG flag stray). ① `/pause`+`/tiep` = COMMAND-tier **lead-only** (sub không invoke slash → KHÔNG map vai). ③ **TOOLING-MỚI: project `.claude/settings.json` FIRST-EVER = hooks.Stop `wal-flush.ps1` ONLY** · WAL.md TRACKED 15/40-dòng · hook VERIFIED-RUNTIME ×2 `12071c5`/`67be443`. ④ anchors đủ (chi-tiết → session-log S111 + return-text): ss:47-54 BƯỚC-0.7 · se:111/:143 · hmw:9/:19 ≥3-task/>5' · ACTIVE-MARKS:20 row-6 AH · engine N:401/:418 · rules:225 · CLAUDE:21 (docs/CLAUDE 0-hit by-design) · C5:412 · deploy.yml D10 +2 path → **AS-6 class đổi — cicd-monitor lane**. 🟡 FLAG LOW: `docs/architecture.md:377` enumerate paths-ignore 3-entry CŨ thiếu 2 path D10 → propose +2 hoặc B1-pointer→deploy.yml (gotchas #41/#47 = narrative-history OK; migration-todos:154 generic OK). Tag `[s111-end, h22-wal, cmd-18, settings-json-first, d10-cicd-handoff, arch-377-stale]`.
|
||||
- **2026-07-13 (S116 @end CHỐT — PE nhập-giá-ÂM = product-only; ①②③ 0-đổi PASS · ④ 2 STALE derived-summary STATUS:20+:27; new-alloc 0; ⚠️ mem-gap S112-S115 re-grounded-disk):** HEAD `88368fd` product commit (FE PeDetailTabs.tsx ×2 + BE `:206` 1-cond `.All(x=>x==0)` cho giá-ÂM=hoàn-tiền-NCC + 4 test-file, **NO migration** → tables 89 giữ). ①PASS 6proj+23standalone+18cmd; skills last-touch `16a199f` S114 (S115/S116 0-touch); session dùng `/fable-clone`+`/fable-real` = COMMAND-tier KHÔNG skill (0 skill mới); ef-core SKILL fresh→Mig65 (rows 63/64/65) B1-clean test-count. ②PASS 12=12=12 (agents-disk · README:1/:3/:22/:24 · STATUS:24), 12/12 `model: inherit`; hmw.js MOVED→`.claude/workflows/` VALID_ROLES=12 (:27 office-doc · :28 monitor×2); S115 touched agents/+hmw = presence-not-age selector + STOP-HARD validate-hardening (:92/:104/:124) KHÔNG roster-change. ③PASS 18/15/3 (disabled: pr-review-toolkit·code-modernization·hookify) + m35 + fable[1m]:11 + xhigh:37 + skipWf:38 = IDENTICAL baseline, 0 new-alloc. ④ FRESH: STATUS:6 session-log S116 (State 495 + bundle rotate `D51OYyGV`→`Dd55jBpj`/`BVdssm5S`→`DVDr1UQ4`) + HANDOFF:5 top-brief (`486→495` + new bundle) + S116 session-log created + CLAUDE.md/skills-README B1-clean. 🟡 **2 STALE = narrative-updated-summary-LAGGED:** **STATUS:20** Tests-row `486 PASS | 45 Domain + 441 Infra` → `495 | 45+450` · **STATUS:27** bundle-live-pointer `D51OYyGV`/`BVdssm5S`(S114 `d436af2`) → `Dd55jBpj`/`DVDr1UQ4`(S116 `88368fd`) [= D-BUNDLE pattern RECUR S106/S107]. Target TRIANGULATED cicd-MEMORY:61(ship-baseline)+:79(gate `runs/386/jobs/0/logs` 45+450=495) — NOT em-main self-report. Method: STATUS mega-line → Grep `-o` context-window `.{0,45}(486|495).{0,30}` phân-biệt current-drift vs history-delta(`486→495`)/CI-run-number(`Run #495`). Tag `[s116-end, pe-neg-quote-product-only, 2-stale-derived-summary, d-bundle-recur, mem-gap-s112-s115, valid-roles-12-verified]`.
|
||||
- **2026-07-14 (S118 @start RE-REPORT — ALL-4 PASS + 2 FLAG LOW tồn-đọng [CLAUDE.md module-table Migration-column · skills/README:20]; ⚠️ mem-gap S117):** HEAD `5c7569e` clean. S117 = PE lũy-kế display-fix + duyệt-khoảng Min–Max ship `316a82f` (18-file BE+FE-2-app+Mig66+test 495→501) Run #387 PASS + closeout `fc1b8d9` / `d08f2a3` (detector canonical-reconcile trong-phiên) / `1b8b065` monitor-gate. **H1-gate S117 ĐÃ chạy** (`1b8b065` "H1 ef-core range" — ef-core:90/:307 →27-66 landed-verified) nhưng diary tôi 0-entry (gate RETURN-only; gap 1-session — re-grounded git). ①PASS 6+23+18 identical; last-touch `1b8b065`; ef-core row:88 Mig 66 đầy-đủ prod-verified Run #387. ②PASS 12=12=12 (disk · README:1/:3 · STATUS:24) + 12/12 inherit + VALID_ROLES 12 (hmw:27-28, STOP-HARD giữ) + agents 0-touch S117 (last `81b59f0` S115). ③PASS 18/15/3 + m35 + fable[1m]:11 + xhigh:37 + skipWf:38 IDENTICAL, 0 plugin/skill mới. ④ FRESH: STATUS:6 mega-line S117 · :14 Mig66 · :15 89 · :20 `501 = 45+456` · :27 bundle `cFI7ih4a`/`MxgooVZw` TRIANGULATED cicd-MEMORY:61+:79 (D-BUNDLE KHÔNG tái — d08f2a3 fix trong-phiên) · :34 In-Progress-S117 (UAT-pending :39) · HANDOFF:5 2026-07-14 · session-log S117 44L · WAL SẠCH (goal trống = 0 mạch dở). 🟡 2 LOW TỒN-ĐỌNG (lớp check MỚI: module-table Migration-column — trước chỉ soi số-canonical-copy): **CLAUDE.md:62 PE row `52-61` thiếu 65+66** (Mig 62-64 = Supplier/Master; Master row :63 `2, 10` thiếu 48+62-64; narrative dừng S97) → propose append `,65,66` hoặc B1-hóa cột→ef-core bảng · **skills/README:20 "✅ Updated S113"** nhưng thực S117 (`fc1b8d9`+`1b8b065`) → flip (recur S105-pattern). Model: self-report `claude-fable-5`. Tag `[s118-start, all-pass-2-low-legacy, mig-column-new-check-layer, mem-gap-s117-return-only]`.
|
||||
- **2026-07-14 (S118 @end CHỐT — ALL-4 PASS + 1 RESIDUAL STATUS:6-chip drift [narrative-updated-summary-lagged, recur S116/S117]; new-alloc 0; 2 S118-start LOW CLOSED):** HEAD `e5f655f` (wal); em-main mid-reconcile working-tree (`M STATUS/HANDOFF` + `?? session-log S118`). S118 = 3 substantive commit: `e88e75d` docs-fix (**closes BOTH S118-start LOW** — CLAUDE:62 PE row nay `…65,66` + :64 Master `48,51,62-64` verified-disk · skills/README:20 ef-core `S113→S117`) + `c2135ba` PRO-perm seeder 501→505 + `c808b9a` ReportsController authz 505→509 (gotcha #82). ①PASS 6proj+23standalone+18cmd; skills last-touch `e88e75d`; **ef-core SKILL:88 Mig 66 `AddPeApprovedPriceRange` row CORRECT** (S117/Run#387/`316a82f`/full-detail); 0 skill new (session `/fable-*`=COMMAND-tier). ②PASS 12=12=12 (disk 12-persona+README · VALID_ROLES hmw.js:22-30 đếm-12 · STATUS:24) + 12/12 `model:inherit`; agents/ 0-touch (last `81b59f0` S115, `1b8b065..HEAD -- .claude/agents/` empty). ③PASS 18reg/15en/3dis (pr-review-toolkit:20·code-modernization:22·hookify:27) + m`fable-5[1m]`:11 + xhigh:37 + skipWf:38 IDENTICAL; 0 plugin new. ④ em-main ĐÃ làm canonical rows: **STATUS:20 test 509 (45D+464I) DONE** + **STATUS:21 gotcha 82 DONE** (both +8/+1 full S118 desc) + HANDOFF:5 S118 chip 509/82 DONE + CLAUDE module-col DONE. 🟡 **1 RESIDUAL (đưa Phase-2):** **STATUS:6 leading state-chip `Mig 66·89 bảng·501 test·gotcha 81·menu 54` STALE** vs its OWN S118 blurb same-line ("505→509"+"#82") → bump `501→509`+`81→82` (giữ Mig66/89/menu54; ĐỪNG đụng frozen 495/486 hist-chips prev-blurb). = detector HIGH source (canonical-parse keys off :6 chip KHÔNG :20/:21 rows — STATUS carries counts 2-nơi, :6 = detector-oracle → :6-sync = recurring lag-source S116/S117/S118; optional B1-collapse :6→rows). C1-MED "#82 broken-ref" auto-clears on bump (#82 VALID gotchas.md:1351 `### 82.` well-formed, 82-entries). **Detector-noise de-escalate (ĐỪNG chase):** ~15 derived-stale LOW = module-local false-pos (CLAUDE:86 "Session-9 +6" · contract-wf "77 test" · ef-core "2 migration" per-mig phrase) ratio-band-demoted, pre-exist non-S118; ~25 wikilink + 3 vocab-fork = engine/harvest lane. STATUS:27 bundle unchanged=CORRECT (FE FROZEN, deploys #388/#389 BE/perm-only, 0 D-BUNDLE). **Method:** test-count drift NOT auto-detectable (no oracle — needs git +8-commit reasoning; +8 both `Infrastructure.Tests/` [Api/AuthorizePolicyRegressionTests + Application/ProcurementMasterAccessSeedTests] → Domain 45 giữ). ⚠️ mem-gap S117 carried. Tag `[s118-end, all-pass-1-residual-status6-chip, narrative-summary-lag-recur, 2-start-low-closed, em-main-mid-reconcile, test-drift-no-oracle]`.
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,65 @@
|
||||
# S118 — 2026-07-14 — Phân quyền PRO tra.bui + vá lỗ hổng ReportsController
|
||||
|
||||
> **Nguồn:** UAT anh Kiệt FDC (chat Zalo) — "phân quyền tra.bui đi nhé" (ảnh chat: bạn PRO login không thấy menu).
|
||||
> **Mode:** HMW-ON · lead Opus 4.8 (owner-choice per-session) · em-main-led + 7 spawn + 3 script.
|
||||
> **Commit:** `e88e75d` (docs-reconcile bootstrap) + `c2135ba` (PRO-perm seeder + test) + `c808b9a` (Reports-authz + test + gotcha) — 3 pushed, 2 deploy #388/#389 verified.
|
||||
|
||||
## Bối cảnh
|
||||
Anh Kiệt báo `tra.bui` (Bùi Lê Thủy Trà, TP Cung ứng) đăng nhập không thấy menu quản lý NCC/Suppliers, dù "em phân quyền hình như đủ". Chat xoay quanh công bố NCC (S113: phải có Mã NCC mới cho công bố).
|
||||
|
||||
## Task 1 — Phân quyền tra.bui (Procurement)
|
||||
|
||||
### Điều tra (read-only, không đoán)
|
||||
- Skill `permission-matrix` → `GetMyMenuTreeQuery` gate menu thuần theo Permission `CanRead` (KHÔNG Admin-bypass ở menu-tree).
|
||||
- `DbInitializer.cs`: `tra.bui` seed sẵn roles `[Procurement, DeptManager]`.
|
||||
- **Prod read-only** (SSH `vietreport-vps` + sqlcmd đọc password từ `appsettings.Production.json`, cơ chế plan-ca): tra.bui **active OK, roles đúng**, nhưng chỉ đọc **11 menu — toàn `Pe_*`** (Duyệt NCC), KHÔNG Master/Suppliers.
|
||||
- **Root-cause:** seeder `RevokeTemporarilyHiddenModulesAsync` (S92 owner-decision, dòng ~2140-2181) chạy **mỗi startup** set CanRead=0 cho `Master`/`Suppliers`/`Projects`/`Departments`/`Catalog*`/`Contracts`/`Hrm*`/`Off*`/`Personal` với MỌI role non-admin ("chỉ Admin thấy"). `Pe_*` được chừa → PE giữ cho user.
|
||||
|
||||
### Owner 3-decision (AskUser)
|
||||
1. **Scope:** Master đầy đủ (Suppliers + Projects + Departments + Catalogs + 4 leaf) + Dashboard + Reports.
|
||||
2. **Durability:** làm bền qua code (không để grant tay bị revoke reset next-deploy).
|
||||
3. **Edit-NCC:** chỉ công-bố/import/thêm-mới (KHÔNG sửa/xóa).
|
||||
|
||||
### Fix 2 tầng
|
||||
1. **Grant tạm prod** — MERGE idempotent vào `Permissions` cho RoleId Procurement (`5B21CDC1-...`): tra.bui 11→**22** (verify LIVE). Row Master/Suppliers đã tồn tại all-0 (placeholder) → UPDATE; Dashboard/Reports chưa có → INSERT.
|
||||
2. **Code-hóa bền** — method mới `SeedProcurementMasterAccessAsync` chạy **NGAY SAU** `RevokeTemporarilyHiddenModulesAsync` (mirror pattern re-grant-thắng-revoke S65 Hrm / S69 Office). **CHỈ role Procurement**, upgrade-only (nâng cờ desired lên true, không hạ, không đụng role khác).
|
||||
|
||||
### Reviewer catch (PASS-có-điều-kiện) → owner refine
|
||||
- **M1:** menu `Reports` gọi `GET /reports/dashboard` lộ `TotalValueActive` + top NCC/dự án theo giá trị + xu hướng 12 tháng → **ngược S92** ("HĐ chỉ Admin thấy"). Owner chốt **bỏ cả Reports + Dashboard** → seeder còn **9 key**, tra.bui còn **20 menu**. Revert Dashboard/Reports trên prod DB.
|
||||
- **M2:** `Suppliers.Update` chỉ mở Publish/Import; `PUT/DELETE /suppliers` gate `[Authorize(Roles="Admin,CatalogManager")]` → PRO vẫn 403 khi Sửa/Xóa NCC. Owner chốt chỉ cần công-bố/import/thêm → grant hiện đủ.
|
||||
|
||||
### Verify durability (bằng chứng vàng)
|
||||
Serilog prod sau deploy restart:
|
||||
```
|
||||
15:35:47.115 [INF] Revoked 9 permission rows ... (chỉ Admin thấy - S58 + S92)
|
||||
15:35:47.159 [INF] Seeded Procurement master access: 0 added + 9 upgraded ... (PRO exception to S92, S118)
|
||||
```
|
||||
→ revoke set 9 row false, seeder lật lại true 44ms sau ⇒ **seeder THẮNG revoke trên startup thật**. Verify ×2 deploy (tra.bui giữ 20). Scope-isolation prod: `binh.le` (CostControl, no-Procurement)=11 · DeptManager-standalone Master/Suppliers=0 (S92 nguyên vẹn role khác).
|
||||
|
||||
### Test
|
||||
`+4 ProcurementMasterAccessSeedTests` (reflection gọi private seeder, không đụng prod code): upgrade-path + **scope-isolation** (DeptManager riêng vẫn revoke) + insert-path + idempotent. 501→505.
|
||||
|
||||
## Task 2 — Vá lỗ hổng ReportsController (task anh mở từ chip reviewer flag)
|
||||
|
||||
- **Lỗ hổng:** `ReportsController` chỉ `[Authorize]` trần (không policy) → MỌI user đăng nhập gọi được `GET /reports/dashboard` (tài chính HĐ toàn công ty) + `GET /reports/contracts/export` (dump toàn bộ HĐ). Menu bị S92 ẩn nhưng API mở.
|
||||
- **Phân loại DTO:** `dashboard` + `contracts/export` = company-wide nhạy cảm; `my-dashboard` = per-user (scope `currentUser`, an toàn).
|
||||
- **Owner chốt:** chỉ Admin (gate `Reports.Read` policy — linh hoạt mở role khác qua ma trận).
|
||||
- **Fix:** `[Authorize(Policy="Reports.Read")]` per-method cho `Dashboard` + `ExportContracts`; `MyDashboard` giữ `[Authorize]` trần.
|
||||
- **Test:** +4 reflection authz (`AuthorizePolicyRegressionTests`, mirror gotcha #44 S45). 505→509.
|
||||
- **Prod verify:** Reports.Read=chỉ Admin (DB), dashboard no-token=401, health 200×3. Non-admin→403 by-construction.
|
||||
- **gotcha #82** ghi: menu-flag grant ≠ quyền API thật (display-layer ⟂ API-authz-layer).
|
||||
|
||||
## Lessons
|
||||
- **wipe-durability-check-reseed (#75/#76):** grant tay prod sẽ bị seeder revoke reset next-deploy → phải code-hóa; bắt kịp TRƯỚC khi báo "done".
|
||||
- **menu-flag ≠ API-power (#82):** grep authz-attribute controller đích trước khi tin hiệu-lực grant.
|
||||
- **#53 cicd-garble ×2 (verify-lane):** cả 2 cicd-monitor trả memory-curation thay verdict → em-main-solo recover (curl 401/health + sqlcmd DB, 2 nguồn khớp). verify-lane garble ≠ mất-việc.
|
||||
- **prod-config-inspect-before-change:** điều tra prod-DB THẬT (SSH sqlcmd) trước khi kết luận — lật ngược giả thuyết ban đầu ("thiếu quyền" → thực ra "scope gap + cache").
|
||||
- **Serilog-as-durability-proof:** log-line startup = bằng chứng seeder thắng revoke (mạnh hơn end-state DB đơn thuần).
|
||||
|
||||
## Carry (chờ anh)
|
||||
- `binh.le` (CostControl+Drafter, 11 menu, khác phòng CCM) — có cần thấy NCC?
|
||||
- 2 account trùng "Lê Thanh Bình": `binh.lethanh` vs `thanh.lethanh` (cùng roles, cùng 20 menu) — dùng cái nào, dọn không?
|
||||
- Audit authz-vs-menu toàn bộ (nhiều controller master `[Authorize]` trần — gotcha #82) — non-blocking, có thể task riêng.
|
||||
|
||||
## State
|
||||
**Mig 66 · 89 bảng · 509 test (45 Domain + 464 Infra) · gotcha 82 · menu 54 · bundle admin `cFI7ih4a`/user `MxgooVZw` (FE FROZEN — session BE/permission-only) · prod 37 NCC/17 user.**
|
||||
Reference in New Issue
Block a user