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>
10 KiB
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.GenerateAsyncruns 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 = detailsThanhTienNganSachsum (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
- 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. - 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. - 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-refreshCtrl+Shift+R→ khỏi. KHÔNG revert (code lành). 🔴 Robustness finding (NEXT/ops): IIS serveindex.htmlKHÔNGCache-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: addweb.configvàofe-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 (
3f9dc32Run 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 (Aw-40vs Bw-44, auto-layout). Fix: cả 2table-fixed+ đồng-nhấtw-20/w-44/w-44→ thẳng hàng 1 lưới. FE-only 2-app SHA93a4e8a1, build PASS. Bundle rotate adminBQq2eTP6→DcAC7npx/ userdHa64I59→PPg8JXNc. - ✅ index.html no-cache hardening DONE (
ca136d8, task-chip — đóng white-screen-class): Inspect live (runbook path STALE → THẬTC:\inetpub\solution-erp\{api,fe-admin,fe-user}quaappcmd list site) → authorweb.configreplicate EXACT live rule (HTTP→HTTPS + SPA-Routes excl^/api+ .webmanifest mime + 3 security header) + rootclientCache=DisableCache(index.html + SPA-shell/dashboard… =Cache-Control: no-cacheluôn-revalidate) +<location path="assets">immutable, max-age=1y. Version-controlfe-*/public/web.config(Vite→dist→deployCopy-Item -Force;deploy.ymlRemove-Item -Exclude web.config= manage-tay trước S88). Cẩn-thận-prod: backup→apply-live→verify-on-server TRƯỚC commit — both siteindex.htmlno-cache + asset immutable +/dashboard200 + http→https 301, live-sha===committed-sha (1239B467).iis-deploy-runbookSKILL FIXED (stale path table + web.config desc + log/scp/backup paths).api=.NET API → N/A. Lessonfeedback_prod_config_inspect_before_change.