Files
solution-erp/docs/changelog/sessions/2026-06-25-S88-pe-stability-audit-d1-d2.md
pqhuy1987 d085436e49 [CLAUDE] Skill · Docs: fix iis-deploy-runbook stale paths + S88 web.config closeout
iis-deploy-runbook SKILL corrected (discovered during the index.html-hardening task):
- physical paths apps\SolutionErp\{Api,Admin,User} were STALE (empty dir) -> real
  solution-erp\{api,fe-admin,fe-user} (verified via appcmd list site).
- web.config rule list fixed to the ACTUAL live config (HTTP->HTTPS + SPA-Routes excl
  /api + .webmanifest + 3 security headers; NO /api|/hubs ARR proxy -- FE calls api via CORS)
  + documented the S88 cache hardening (index.html no-cache, assets immutable).
- log/appsettings/scp/backup paths swept to the real base.
Session log: index.html no-cache hardening DONE (ca136d8) + lesson reference.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 11:42:23 +07:00

64 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# S88 (2026-06-25) — PE stability audit → fix patchwork (D1) + complete multi-winner (D2)
**Anh:** `/session-start``/sleep-recovery-memory-l2 all` → "Plan review test, unittest... review invest lại 1 lần nữa xem có vấn đề gì đang làm chấp vá thì xử lý, tính năng đã ổn định rồi nhé. workflow chạy cho nhah ultra-on, mỗi bước đều phải có invest, review testing cẩn thận" → (sau audit + AskUserQuestion) "viết unittest... review/invest kỹ và workflow hoàn chỉnh các thứ luôn đi nhé, xong thì session-end".
HMW-mode ON. Frozen CEO-approved PE (Duyệt NCC) spec, testers signed off — hardening audit, NOT feature change (but anh chốt completing the multi-winner feature).
---
## Sleep-recovery (early) — 1 gist, NOT NO-OP
`/sleep-recovery-memory-l2 all`: 18/19 L2 verbatim SKIP (14× `2026-05-qN.md`/`runs.md` already in aggregate `2026-05.gist.md` gen:1 — verified by reading source-decl; 4× `2026-06.md` double-distill guard gen 1-2; 5 subs empty). **1 distilled:** `test-specialist/activity-s51-s52.md` (gen-0 orphan, no gist) → `activity-s51-s52.gist.md` (distill-gen:1), 4 records → 3 clusters (C-A merges 2 gotcha#57 episodes). harvest-curator Phase-1 propose → reviewer Fidelity-gate **FAIL v1** (merge mis-attributed Mig 45 to Master half; canonical = Mig 47) → re-distill split per-episode → PASS. commit `e939fab`. last_sleep_at → 2026-06-25.
## Audit — Workflow `wf_c15da996-be5` (6 area × invest→review pipeline + full-suite test, 13 agent, ~7min)
**Core verified SOLID** (both reviewers read real code): winnerQuoteTotal 3 canonical sites · SelectWinner multi-toggle/clear · urgent fail-closed asymmetric authz · drafter-exclusion notify · both finalize mechanisms · finalize tests. Test baseline GREEN 402/402.
**Findings split** safe-vs-behavior-affecting → AskUserQuestion ×4 → anh chốt **max scope** (fix-all-safe + complete-joint-winner + activate-CEO-notify + create-contract = **1 HĐ/winner**).
## D1 (`73cce1f` Run #347 PASS, test 402→413) — the fixes
| Fix | Detail |
|---|---|
| Block B financials | `currentProposalTotal` (:846) + `prevSelected` rollup (:832) re-key `SelectedSupplierId→IsWinner` — reported 0 / excluded peers for ≥2 joint winners; now match winnerQuoteTotal. **Behavior-affecting** (approved). |
| CEO-notify reactivated | `PurchaseEvaluationUrgentFeatures.cs` — notify block ran AFTER the only SaveChanges → Director notification never persisted. +`SaveChanges` after NotifyManyAsync (in try, best-effort). |
| HoSoLink preserve | `UpdatePeDraftHandler` HoSoLink absolute-set → null-safe + clear-via-empty (null=preserve, ""=clear). Header forms omit=preserve (auto-fix); HoSoLinkRow sends `hoSoLink.trim()` to clear. Bug-class S42/#73. |
| #70 guard | PRO budget row3/row8 `saving={adjustMut.isPending}``\|\| peFetching`. |
| Relocate | `SetPeCcmBudgetPeriodCommand` PeSuggestedPriceFeatures → PeWorkItemBudgetFeatures (byte-identical). |
| Comments | WinnerQuoteTotal DTO + AddItemDialog + type comments. |
Tests +11 (PeHoSoLinkTests spec-change update + PeWorkItemBudgetTests joint-winner + **PeUrgentNotifyDirectorTests** 6× real-notify-fixture persistence).
## D2 (`1aa3bdb` Run success, test 413→419) — complete multi-winner outward
- **create-contract 1→N**: `CreateContractFromEvaluationCommand``List<Guid>`, one draft HĐ per IsWinner supplier (was 409 dead-end for ≥2). **Key design:** `ContractCodeGenerator.GenerateAsync` runs its OWN Serializable transaction + SaveChanges + commit per call (self-commits the sequence) → so **gen ALL N codes first** (contracts built in a LOCAL list, NOT db.Contracts.Add) so the codegen's internal SaveChanges only flushes the sequence row (never a half-built contract), THEN loop-2 adds all + `pe.ContractId=first` + ONE final SaveChanges = atomic (no partial-create; failure wastes only seq numbers). GiaTri: single = details `ThanhTienNganSach` sum (legacy preserved); ≥2 = per-winner quote total (DangSoanThao draft, editable — anh Kiệt UAT). Controller → `{contractIds}`.
- **list-card winner names**: `PurchaseEvaluationListItemDto` +`WinnerSupplierNames List<string>` via EF collection-subquery over IsWinner suppliers (3 projection sites). **Verified: translates + runs on SQLite** (test-specialist translation-smoke — no 2-pass fallback needed). FE PeListPanel joins names for ≥2 (was blank).
- FE: dialog already showed winners + gated on isWinner; only response `{contractId}→{contractIds}` + toast "Đã tạo N HĐ" + navigate to first.
Tests +6 (CreateContractFromEvaluationMultiWinnerTests 5 [≥2-winner per-winner GiaTri 140/260 vs single legacy 800, idempotency, 0-winner, not-DaDuyet] + PeListWinnerNamesProjectionTests EF-translate smoke). reviewer **PASS** (codegen-sequencing + positional-record arg-order + no-partial-create all independently verified).
## State THẬT
Mig 59 · 88 bảng · **419 test (45D+374I)** · gotcha 73 · menu 54 · bundle admin **`BQq2eTP6`** / user **`dHa64I59`**.
## Cách chạy + #53
em-main-led BE core (financial/coupled/design-heavy = create-contract multi + Block B financials, em-main wrote directly per agent-boundary "schema/cross-stack design = em-main solo") + delegate impl-backend (relocate) ∥ impl-frontend (FE 2-app SHA-mirror) ∥ test-specialist (test-before bug + test-after feature) + reviewer adversarial + cicd-monitor. **#53 truncation ×3** (test-specialist 34.8KB / reviewer 27KB / cicd-monitor — returns rớt ở MEMORY-over-budget step) → em-main recover-disk (work landed) + self-gate (read the tests + own suite run) + **curl-self-verify deploy** (Gitea run status=success + bundle rotation, when cicd-monitor verdict truncated).
## Lessons
1. **Multi-entity create with a self-committing sequence-generator** = gen ALL codes first (entities local, un-tracked) → atomic final SaveChanges. → `feedback_multi_entity_create_self_committing_codegen`.
2. **Cardinality single→multi = grep MỌI consumer** (reinforced `feedback_cardinality_change_grep_consumers`): the S87 FE-only sweep (8 sites) missed 2 BE financial read-sites (Block B) — the audit caught them.
3. **Frozen-module workflow**: audit → AskUserQuestion to split safe-vs-behavior-affecting → user picks scope → fix with test+review per change. Behavior-affecting fixes need explicit user OK even when they're "more correct".
## §L closeout
- **§L.a:** 0 production-bug RCA. #53 ×3 = known curate-debt pattern (recover-disk held; no new code-fault).
- **§L.b(c):** test-specialist 34.8KB **OVER-SOFT** (root #53) + reviewer 27.1KB + FD 26.1KB over-autoinject — archive-gate strike-1 (DRY-RUN); **curate deferred to next session as #1 NEXT-em** (proper byte-exact L1→L2 + _INDEX, not rushed). sleep-check skip (ran today). Hot-feed %-print → REPORT.
- **§L.b(d)(f):** 6 sub spawned + flushed own MEMORY (committed). harvest landed (BE/FE/test/reviewer/cicd/inv deltas).
- **§L.b(g):** tooling — no new skill/plugin/roster change.
- **§L.b(h):** User-Mark — 0 new governance decision (product session) → display-only (4 Active-High).
- **infra-adoption:** N/A (no infra adoption — product work).
## 🔴 NEXT
- **Anh:** ⚠️ restart CLI (carry budget v3 + `/user-mark-*` + cadence). **UAT anh Kiệt Ctrl+F5:** ① ≥2-winner Block B số đúng · ② GẤP→Director chuông · ③ Link hồ sơ giữ khi sửa header · ④ ≥2-winner "Tạo HĐ" → N HĐ (xác nhận GiaTri per-winner = giá NCC chào).
- **Em:** curate test-specialist L1 (GẤP) · monthly 2026-07-01 (STATUS/HANDOFF re-tier mega-line bloat — line không Read được · detector test-count regex anchor · vocab-fork alias-map · ef-core Mig→59).
- **Pending product:** Section B cột Dự-án wire · Ngưỡng giá CEO Designer UAT · "C" chuyển phiếu→dự án.
## Post-closeout addenda (cùng phiên S88)
- **🔥 White-screen incident (eoffice/fe-user) — KHÔNG phải code bug:** anh báo màn-hình-trắng-tinh sau D2 deploy. Diagnose: deploy KHỎE — fresh load eoffice boot OK (login render, 0 console error qua Chrome MCP), BE all 200, MỌI asset 200 (old+new, KHÔNG 404), changes PE-only + landing `/dashboard` (em không đụng). ⟹ **client-side transient/stale-cache** (tab mở trong lúc IIS app-pool recycle khi deploy). Fix: anh hard-refresh `Ctrl+Shift+R` → khỏi. KHÔNG revert (code lành). **🔴 Robustness finding (NEXT/ops):** IIS serve `index.html` KHÔNG `Cache-Control: no-cache` (chỉ ETag/Last-Modified) → tab cũ có thể kẹt index stale qua deploy. Repo KHÔNG có `web.config` (SPA routing + headers = server-side VPS). Đề-xuất: add `web.config` vào `fe-user/public` + `fe-admin/public` (`index.html`→no-cache + `assets/*`→immutable 1y) SAU khi inspect server config (đừng vỡ SPA rewrite) — iis-deploy-runbook task, cẩn-thận-prod. (Lesson: build+deploy PASS ≠ runtime-render OK; nhưng white-screen ≠ luôn-là-code — verify deploy-health TRƯỚC khi revert.)
- **PE Block B table-align (`3f9dc32` Run PASS):** 2 bảng "TỔNG HỢP NGÂN SÁCH TRÌNH KÝ" (A NGÂN SÁCH / B THỰC HIỆN) lệch cột Dự án/PRO/CCM (A `w-40` vs B `w-44`, auto-layout). Fix: cả 2 `table-fixed` + đồng-nhất `w-20/w-44/w-44` → thẳng hàng 1 lưới. FE-only 2-app SHA `93a4e8a1`, build PASS. Bundle rotate admin `BQq2eTP6``DcAC7npx` / user `dHa64I59``PPg8JXNc`.
- **✅ index.html no-cache hardening DONE (`ca136d8`, task-chip — đóng white-screen-class):** Inspect live (runbook path **STALE** → THẬT `C:\inetpub\solution-erp\{api,fe-admin,fe-user}` qua `appcmd list site`) → author `web.config` replicate **EXACT** live rule (HTTP→HTTPS + SPA-Routes excl `^/api` + .webmanifest mime + 3 security header) + **root `clientCache=DisableCache`** (index.html + SPA-shell `/dashboard`… = `Cache-Control: no-cache` luôn-revalidate) + **`<location path="assets">` `immutable, max-age=1y`**. Version-control `fe-*/public/web.config` (Vite→dist→deploy `Copy-Item -Force`; `deploy.yml` `Remove-Item -Exclude web.config` = manage-tay trước S88). **Cẩn-thận-prod: backup→apply-live→verify-on-server TRƯỚC commit** — both site `index.html` no-cache + asset immutable + `/dashboard` 200 + http→https 301, **live-sha===committed-sha** (`1239B467`). `iis-deploy-runbook` SKILL FIXED (stale path table + web.config desc + log/scp/backup paths). `api`=.NET API → N/A. **Lesson `feedback_prod_config_inspect_before_change`.**