3-stage Workflow run-id evidence: investigate wf_be952f3c-97f / implement wf_a58e0d15-beb / audit wf_9520d8cd-4fe. PART 1 (L2 recovery): 4 over-cap sub (cicd-monitor/investigator-codebase/reviewer/implementer-backend) curated L1->L2 byte-exact + archive/_INDEX.md (substring sha-keyed pointers, no line-hints) + <period>.gist.md (4-field distill, distill-gen:1, verbatim frozen). All 4 MEMORY.md now < 25KB auto-inject cap (closes P1 curate-debt). ~240KB archive no longer RAG-dark. 0-byte-loss git+sha verified (Stage C audit + em-main self-gate on 2 reviewer StructuredOutput no-returns). Read-side gap fixed (MEMORY.md L5 header -> _INDEX). + memory-budget.json (seed-by-measure) + scripts/measure-agent-memory.ps1 + .ragignore guard. PART 2/3 (process mandate): every adap = 2 separate workflows (implement + review) + report with run-id; short-but-needs-confirm still requires review. Codified in .claude/commands/adap-apply.md + agents/README.md (Upgrade S70) + session-start.md (§2.1.2 budget-audit, pending-restart). adap-report + email-back to AI_INFRA (body-hash 7c07b716e775). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
98 KiB
CI/CD Monitor — Archive 2026-06 (L2 COLD, read-on-demand)
Curated S66 (2026-06-16) from L1 MEMORY.md to keep L1 < ~35KB. Older run-records (S62 #286 06-13 -> S29 #232) moved here VERBATIM. L1 retains the 6 current-session 06-16 deploys (#289-#295). Note: #291 (06-16 FAIL, CS7036) full forensic detail lives here; its lesson = canonical gotcha #65 + summarized inline in the L1 #292 entry, so not orphaned. #383 entry (was tagged VI-TRI-LAC in L1 FIFO) now correctly archived.
-
2026-06-13 S62 Run #286 (run_number 286, id400) sha=
7926c21PASS ~4m41s (PE "vượt ngân sách" → SOFT-WARNING: gỡ chặn số âm — CROSS-STACK 1 BE validator-rule-removal + 2 FE PeDetailTabs ×2 + 1 test flip, NO migration): Push79ef8da..7926c214 files: BEPurchaseEvaluationFeatures.cs(gỡ 1 FluentValidation ruleExpectedRemainingAmount >= 0trongAdjustPurchaseEvaluationBudgetCommandValidator) +PeDetailTabs.tsx×2 app (allowNegative row8 + banner "Vượt ngân sách") +PeWorkItemBudgetTests.cs(flip 1 test)..cs+.tsx→ full pipeline RAN. GITEA_TOKEN+PROD_DB_PW empty → anon Gitea API + DB pw từ prodappsettings.Production.json→ConnectionStrings.Default. Run IN-PROGRESS first poll (running 11:15) — correctly did NOT FAIL/verify-bundle-mid-flight (anti-pattern #3), polled iter5 status=success (started 11:14:00 → success 11:18:41 ≈4m41s). CI gate (both test proj pre-deploy ⟹ status=success ⟹ test gate 263 baseline (45 Domain + 218 Infra) passed;tasksendpoint reports terminal asstatus:success,conclusionempty — trust success). Bundle ROTATE BOTH (load-bearing FE×2, verified AFTER status=success +re-confirm STABLE no transient — anti-pattern#3): adminDsGZlNzT→0xKYGhhf+ userDTL_bjzQ→C81ZdG9G✓ both touched (FE changed both apps). Title "Solutions ERP · Admin" preserved. Health live+ready 200/200 + admin/eoffice root 200. Smoke: PE unauth 401 (auth gate real) + control/api/zzz-not-a-route404 (routing live, 401 not catch-all). NO migration — prod__EFMigrationsHistorytop =20260612173224_ReplaceBudgetModuleWithPeWorkItemBudgets(= S61 "Mig 50", Budget→PeWorkItemBudgets replace) == repo HEAD, GIỮ NGUYÊN ✓ (commit 0 migration files; repo 50 mig .cs total). sys.tables(excl mighist)=88 (S61 replace-mig DROPPED Budget tables 93→88; convention/count shift from S61 NOT this commit — FE+validator-only cannot alter schema). 0 regression. LESSON: validator-rule-removal (negative-allow soft-warning) = internal handler-pipeline behavior — cannot curl-assert "now accepts negative ExpectedRemainingAmount" without authed multi-step adjust flow → rely on +flip PeWorkItemBudgetTests in CI gate 263 passing + bundle-rotate-both (FE banner shipped). Table count 88 (not 93) is S61 Budget-replace aftermath, not regression — always cross-ref what the COMMIT touched vs ambient schema state. Tag[s62, run286, pass, pe-budget-soft-warning, allow-negative, cross-stack, bundle-rotate-both, no-mig, test263, tables88-s61-aftermath]. -
2026-06-12 S60 Run #283 (run_number 283) sha=
37122f0PASS ~5m (PE guard 4-thông-tin mục 3 khi gửi duyệt + bypass người-soạn-trong-chuỗi-duyệt + rename heading "Đơn vị NCC/TP được chọn" — CROSS-STACK 1 BE service + 2 FE PeDetailTabs ×2 + 1 NEW test file): Push792c030..37122f07 files:PurchaseEvaluationWorkflowService.cs(BE submit-guard + drafter-bypass) +PeDetailTabs.tsx×2 app +PeSubmitGuardAndBypassTests.cs(NEW, +14 → 240→254 expected) + 3 agent-memory.md(harvest-curator/investigator-codebase/test-specialist —.claude/agent-memory/**matches**/*.mdglob → ignored, but.cs+.tsxpresent ⟹ whole-range builds, Discovery #3). GITEA_TOKEN+PROD_DB_PW empty → anon Gitea API + DB pw từ prodappsettings.Production.json→ConnectionStrings.Default. Run IN-PROGRESS first poll (running 11:55) — correctly did NOT FAIL, polled iter5 status=success (started ~11:54 → success 11:59:26 ≈5m). CI gate (both test proj pre-deploy ⟹ status=success ⟹ test gate 254 passed;tasksendpoint reports terminal asstatus:success,conclusionempty — trust success). Bundle ROTATE BOTH (load-bearing, verified AFTER status=success — anti-pattern#3): adminB1DtNT9C→akytoBnc+ userD6uF3Mln→BzSdQmN0✓ both touched (FE changed both apps). Brand1F7DC1preserved both HTML. Health live+ready 200/200 + admin/eoffice root 200. Smoke: PE unauth 401 + contracts unauth 401 + control/api/zzz-not-a-route404 (auth gates real, routing live). NO migration — prod__EFMigrationsHistorytop = Mig 49AddWorkItemToPurchaseEvaluation== repo, GIỮ NGUYÊN ✓ (PE.MaPhieu col, not Code). sys.tables(excl mighist)=92 (convention diff vs narrative-93, no new table — col/logic-only). DATA INTACT (no-touch verify, sqlcmd): PE_count=3 · PE/2026/A/001 EXISTS (=1, phiếu UAT thật giữ nguyên ✓) · Suppliers=23 · WorkItems=71 — counts vs S59 (PE was 1 #275, Suppliers 22 #278) GREW from legit ongoing-UAT (this commit = FE+BE-service, NO DbInitializer/seed change → cannot resurrect/wipe; growth is user activity not deploy-induced). 0 regression. LESSON (cross-stack submit-guard + drafter-bypass = ship-proof via run-success + test 254 + bundle-rotate-both + PE-data-preserved; the guard/bypass logic is internal handler behavior — cannot curl-assert "block submit when mục-3 incomplete" or "skip drafter in chain" without authed multi-step flow → rely on +14 PeSubmitGuardAndBypassTests in CI gate passing). SSH→sqlcmd viaiconv UTF-16LE|base64→powershell -EncodedCommand(nested bash→ssh→PS strips$vars/mangles quotes); PE code column =MaPhieuNOTCode. Tag[s60, run283, pass, pe-submit-guard, drafter-bypass-in-chain, cross-stack, bundle-rotate-both, no-mig, test254, pe-a001-preserved]. ↳đợt2 (14:14): Run #284 (run_number 284, id398) sha=6db195dPASS ~4m31s — GỠ hành động "Từ chối" khỏi quy trình PE (chỉ còn Duyệt/Trả lại; CROSS-STACK DomainPurchaseEvaluationPolicy.cs+ InfraPurchaseEvaluationWorkflowService.csguard + FEPeWorkflowPanel.tsx×2 app + 2 NEW testPurchaseEvaluationPolicyTests/PurchaseEvaluationWorkflowServiceGuardTests, +2 → 254→256 expected: 59 Domain + 197 Infra). Push37122f0..6db195d6 files (.cs+.tsx → full pipeline). Tokens empty → anon Gitea API + prod appsettings DB pwConnectionStrings.Default. Run IN-PROGRESS first poll (running 14:31) — correctly did NOT FAIL, polled iter5 status=success (14:30:51→14:35:22 ≈4m31s; CI both-proj-pre-deploy ⟹ success ⟹ 256-gate passed,conclusionempty trust success). Bundle ROTATE BOTH (verified AFTER status=success — anti#3): adminakytoBnc→DSvM8h3A+ userBzSdQmN0→Cs2Tt5n6✓ both touched. Health live+ready 200/200 + admin/eoffice root 200 + PE unauth 401 + control /api/zzz-not-a-route 404. NO migration — prod top=Mig 49AddWorkItemToPurchaseEvaluation==repo GIỮ ✓. sys.tables(excl mighist)=92. DATA INTACT: PE_count=4 (grew from 3 @#283 — legit ongoing-UAT; BE-policy+FE-only NO seed change → cannot resurrect/wipe) · PE/2026/A/001 EXISTS (=1 phiếu UAT thật giữ ✓). 0 regression. LESSON: "Từ chối"-removal = internal policy/handler behavior, cannot curl-assert "reject action gone" without authed multi-step flow → rely on +2 PolicyTests/GuardTests in CI gate passing. Tag[s60-dot2, run284, pass, pe-remove-reject-action, cross-stack, bundle-rotate-both, no-mig, test256, pe-a001-preserved]. -
2026-06-11 S59-CLOSE Run #280 (run_number 280) sha=
69997daPASS ~4m24s (FINAL đóng sổ session — FE-only ×2 PeDetailTabs+PeHeaderForm bỏ ô "Tên ngân sách" manual budget UAT vòng4): Pushf21c55d..69997da4.tsx(PeDetailTabs+PeHeaderForm ×2 app). Run #279 (id393) sha=f21c55d(NCC table-fixed UAT vòng3) =cancelled@18:22:33 — supersede-BENIGN: #280 push @18:22:34 (1s gap → Gitea concurrency-cancel in-flight) +git merge-base --is-ancestor f21c55d 69997da=TRUE ✓ (f21c55dpreserved trong HEAD, ships via #280 — verified diff f21c55d→69997da chỉ +4 PeDetail/Header file, không re-touch 12 file vòng3). Tokens empty → anon Gitea API + prod appsettings DB. Poll iter4 status=success (18:22:34→18:26:58). Bundle ROTATE BOTH FINAL (verified AFTER success +re-confirm STABLE no transient — anti#3): adminBSh2fG2X→BKy_8OO9+ userD22KfpPc→XcZ6PRyA✓ session-close hash, brand1F7DC1+"Solutions ERP" preserved ×2. Health live+ready 200/200 + admin/eoffice root 200 + PE unauth 401 + control 404. NO migration (FE-only, Mig 49 held). LESSON (mirror Run #385 supersede-chain): same-SHAcancelledmid-flight = concurrency-supersede bởi newer push (1s HEAD-move), KHÔNG build/deploy-fault → ancestor-check TRUE = benign, verify prod qua SUCCESSFUL run #280 (NOT cancelled #279), KHÔNG escalate. Tag[s59-close, run280-pass, run279-cancelled-benign, supersede-chain, fe-budget-name-remove-x2, bundle-rotate-both-FINAL, no-mig]. ↳FINAL-v2 (tối):80b64dd(Run #281 cancelled-BENIGN) gỡ "Điều khoản thanh toán" 3-form ×2 → superseded bởi792c030Run #282 PASS ~4m (UAT vòng6 bỏ nút "+Thêm hạng mục" PeDetailTabs ×2). Ancestor 80b64dd⊂792c030=TRUE ✓ (792c030chỉ re-touch PeDetailTabs, KHÔNG đụng PeHeaderForm/PeWorkspaceCreateView → paymentTerms-removal survives). Verify qua #282-success. Bundle ROTATE BOTH ĐÓNG-SỔ-THẬT (AFTER success +re-confirm STABLE no transient): adminBKy_8OO9→B1DtNT9C+ userXcZ6PRyA→D6uF3Mln✓, brand1F7DC1ok. Health live+ready 200/200 + 2 FE root 200. NO mig (FE-only). Lần thứ 3 liên tiếp supersede-chain (#279/#281 cancelled-benign) — pattern stable. -
2026-06-11 Run #278 (run_number 278) sha=
9c330d2PASS ~3m45s (S59-đợt6 CROSS-STACK — BE SuppliersController POST hạ[Authorize(Roles="Admin,CatalogManager")]→ class-level[Authorize]any-auth (anh chốt quick-add NCC đi-thầu phát sinh liên tục), PUT/DELETE GIỮ khóa Admin+CatalogManager; FE×2 PeDetailTabs AddSupplierDialog SearchableSelect+quick-create+upload-multi + PeWorkflowPanel ẩn Trả-lại/Từ-chối khi drafterUserId==currentUser): Pushfaed59f..9c330d25 files: 4 FE.tsx(PeDetailTabs+PeWorkflowPanel ×2 app) +SuppliersController.cs..cs+.tsx→ full pipeline RAN. GITEA_TOKEN+PROD_DB_PW empty → anon Gitea API + DB pw từ prod appsettings.Production.json→ConnectionStrings.Default(vrapp/buKL3...). Run IN-PROGRESS first poll (running) — polled iter6 status=success. Bundle ROTATE BOTH (load-bearing FE×2, verified AFTER status=success +re-confirm stable ×2 NO transient — anti-pattern#3): adminex7Tc92G→BSh2fG2X+ userDzUeSk96→D22KfpPc✓ both touched. Brand1F7DC1+"Solutions ERP" preserved. Health live+ready 200/200 + admin/eoffice root 200. NO migration — prod__EFMigrationsHistorytop = Mig 49AddWorkItemToPurchaseEvaluation== repo ✓. ★ AUTHZ PROBE 4-điểm (the change-point — asymmetric POST-open/DELETE-locked verify) ALL PASS: (a) unauth POST /api/suppliers (no token) = 401 ✓ (vẫn phải login, KHÔNG anonymous — class[Authorize]giữ); (b1) nv.test (Drafter non-admin) POST{code:ZZCICD-TEST,type:1}= 201 ✓ idbc64c0c0-...(quick-add mở OK); (b2) nv.test DELETE same-id CÙNG token = 403 ✓ (Sửa/Xóa vẫn khóa Admin+CatalogManager — method-level attr giữ); cleanup admin DELETE = 204 + GET = 404 ✓ (probe gỡ sạch). Spot sqlcmd ground-truth: WorkItems active=71 HELD ✓ (no resurrect) + Suppliers active=22 ✓ (==pre-probe; DELETE là SOFTIsDeleted=1→ active count về 22, total=23 với 1 tombstone ZZCICDIsDeleted=1— by-design audit, KHÔNG leak: list/GET ẩn nó). ⚠️ API/api/supplierslist trả 20 (paginated/filtered default page — KHÔNG authoritative, dùng sqlcmd cho count thật). Test gate (CI both proj pre-deploy ⟹ success=passed). 0 regression. LESSON: cross-stack authz-relax verify = probe CẢ asymmetry — (i) unauth vẫn 401 (relax ≠ anonymous), (ii) target-role action mở (201), (iii) SIBLING action vẫn locked (403 same token), (iv) cleanup soft-delete → active-count về baseline + tombstone total+1 (soft-delete ≠ rác nếu list/GET ẩn). API list-count KHÔNG tin (pagination) → sqlcmdWHERE IsDeleted=0cho count thật. Tag[s59-dot6, run278, pass, supplier-post-authz-relax, asymmetric-probe-401-201-403, soft-delete-cleanup, bundle-rotate-both, no-mig, wi71-held]. -
2026-06-11 Run #277 (run_number 277) sha=
faed59fPASS ~4m09s (S59-đợt5 FE-only ×2 — NEW ui/SearchableSelect combobox gõ-lọc-bỏ-dấu + PeWorkspaceCreateView/PeHeaderForm Hạng mục+Dự án combobox + auto-fill Địa điểm từ Project.Location + PeDetailTabs paymentTerms Textarea; UAT 4-điểm screenshot 16:40): Pushc869d26..faed59f8 files all.tsx(4 fe-admin + 4 fe-user, NEW SearchableSelect mirror ×2) → pipeline RAN. Tokens empty → anon API + prod appsettings pw. Run IN-PROGRESS first poll (running 17:40) — polled iter5 status=success (17:40:18→17:44:27). Bundle ROTATE BOTH (verified AFTER success +re-confirm stable NO transient — anti-pattern#3): adminBBA0KSWu→ex7Tc92G+ userDzdTI18G→DzUeSk96✓ both apps touched. Brand1F7DC1preserved both HTML. Health live+ready 200/200 + admin/eoffice root 200. NO migration (FE-only). Spot DB: WorkItems active=71 HELD (==#276 ✓ no resurrect — FE-only no restart risk) + PE=1 (info-only UAT leftover unchanged, NOT regression). Test gate (CI both proj pre-deploy ⟹ success=passed). 0 regression. LESSON: FE-only follow-up of a data-session needs only light WorkItems=71+PE re-confirm (no infra re-audit). SSH→sqlcmd pw-read: nested bash→ssh→PS strips$vars→ useiconv UTF-16LE|base64→powershell -EncodedCommand. Tag[s59-dot5, run277, pass, fe-searchableselect-x2, bundle-rotate-both, no-mig, wi71-held]. -
2026-06-11 Run #276 (run_number 276) sha=
c869d26PASS ~4m33s (S59-đợt4 FINAL — rename 71 WorkItems theo format PMH anh Kiệt FDC: BE DbInitializer seed-tuple mới MAT-n/SUB-n/MEP-SUB-n/MEP-EQU-n + FE×2 sort numeric, SQL rename ran TAY prod TRƯỚC push giữ Id): Pushbbd1554..c869d269 files: 6 FE (PeHeaderForm+PeWorkspaceCreateView+PurchaseEvaluationsListPage×2 app) +DbInitializer.cs+s59-rename-workitems-pmh.sql+ generated.md..cs+.tsx→ full pipeline RAN. GITEA_TOKEN+PROD_DB_PW empty → anon API + prod appsettings pwConnectionStrings.Default. Run IN-PROGRESS first poll (running 17:14) — polled iter5 status=success (17:14:14→17:18:47). Bundle ROTATE BOTH (3 FE file both apps, verified AFTER success +re-confirm stable NO transient — anti-pattern#3): adminDuU7OTym→BBA0KSWu+ userDWyeTzf3→DzdTI18G✓ both reachable 200, brand1F7DC1+title "Solutions ERP" preserved. Health live+ready 200/200 + admin/eoffice root 200 + PE unauth 401 + control 404. NO migration — prod top=Mig 49AddWorkItemToPurchaseEvaluation==repo ✓. ★ RISK-POINT CLEARED (app restart ran SeedRealMasterDataAsync w/ NEW tuples — rename SQL ALREADY took → seed found all 71 new codes exist → added 0, NOT 142): WorkItems=ĐÚNG 71 (142=FAIL averted) + old VT-/TP-/TB-=0 ✓ + new MAT-/SUB-/MEP-=71 ✓ + spotMEP-SUB-1="1 MEP Sub MEP (Full)" EXACT ✓ + sample MAT-1/SUB-1/MEP-SUB-9/MEP-EQU-16 all present. PE=1 (info-only, UAT leftover A/001 — not resurrect). Test gate 240 (CI both proj pre-deploy ⟹ success=passed). 0 regression. LESSON: rename-via-prod-SQL + seed-tuple-change verify = (a) target count EXACT (double = idempotent-seed re-added because SQL rename didn't take), (b) old-prefix=0 + new-prefix=full-count, (c) spot Code→Name exact match. Idempotent UNGATED seed re-runs on every restart → if hand-SQL rename succeeded BEFORE deploy, seed finds new codes present → adds 0 (safe); if rename failed → seed ADDS duplicates = 2× count = critical FAIL. Tag[s59-dot4, run276, pass, workitems-rename-pmh, bundle-rotate-both, no-mig, count-71-no-dup]. -
2026-06-11 Run #275 (run_number 275) sha=
bbd1554PASS ~3m44s (S59-đợt3 BE-only DbInitializer GỠ seed 15 WorkItems demo — chị Trà Sol chốt giữ đúng 71 mã PMH +scripts/s59-wipe-demo-workitems.sqlran TAY prod TRƯỚC push 86→71): Push0eafcd3..bbd15542 filesDbInitializer.cs+ wipe-sql ONLY, NO FE/Mig..cs→ full pipeline RAN. GITEA_TOKEN+PROD_DB_PW empty → anon API + prod appsettings pwConnectionStrings.Default. Run IN-PROGRESS first poll (running 16:57) — polled iter6 status=success (16:57:41→17:01:25). Bundle FROZEN adminDuU7OTym+ userDWyeTzf3(== #274 UNCHANGED ✓ CORRECT BE-only — verified AFTER status=success, no FE leak). NO migration — prod__EFMigrationsHistorytop = Mig 49AddWorkItemToPurchaseEvaluation== repo ✓. Health live+ready 200/200 + admin/eoffice root 200. ★ RESURRECT-VERIFY (risk point — app recycle post-deploy did NOT re-seed demo WorkItems, seed-block removal HELD): WorkItems=71 (ACTIVE=71 INACTIVE=0 DELETED=0 — hard-wipe not soft) + 4 categories EXACT: MEP 9 / Thầu phụ-Xây dựng 30 / Thiết bị 16 / Vật tư-Xây dựng 16 ✓ + demo-codes DAO-MONG/SON-NUOC/VC-PHE-THAI = 0 ✓ (SeedRealMasterDataAsync idempotent re-ran, added 0 — 71 codes all exist). ⚠️ PARTIAL item 5 — PE=1 không phải 0: 1 rowPE/2026/A/001Phase=10 created 09:46:42 UTC = 16:46 chiều local (CreatedAt lưu UTC — em main annotate S59-end kẻo đọc nhầm timeline; sau wipe Run #273 ~15:36 local, là phiếu UAT thật user tạo buổi test chiều, KHÔNG do deploy resurrect — BE-only bundle-frozen, DbInitializer KHÔNG seed transactional PE). INFO-flag, NOT deploy-fail. Test gate 240 (CI both proj pre-deploy ⟹ success=passed). 0 regression. LESSON: seed-removal verify (mirror wipe-verify #273) = (a) target table count exact + category breakdown + demo-code spot-check 0 AFTER recycle (idempotent real-seed re-runs but adds 0), (b) ACTIVE/DELETED split proves hard-wipe vs soft-delete. PE=1 leftover-UAT ≠ regression: check CreatedAt timestamp vs wipe-time — pre-wipe-then-new-create is legit UAT activity, only assert PE=0 if no UAT happened between wipe and deploy. Tag[s59-dot3, run275, pass-partial, be-seed-removal, workitems-71-no-resurrect, bundle-frozen, no-mig, pe1-uat-leftover]. -
2026-06-11 Run #273 (run_number 273) sha=
56882acPASS ~3m34s (S59 FE×2 PE-list tree regroup "Dự án(Năm)→Hạng mục→Phiếu" bỏ tầng NCC + prod data WIPE): Push1577927..56882ac3 files:PurchaseEvaluationsListPage.tsx×2 app (SHA256 mirror identical, Panel-1 tree drop NCC tier) +scripts/s59-wipe-testing-data.sql(ran TAY prod TRƯỚC push, NOT in pipeline)..tsxpresent → pipeline RAN. ⚠️ GITEA_TOKEN+PROD_DB_PASSWORD empty → anon Gitea API (works, public) + read DB pw from prodappsettings.Production.json→ConnectionStrings.Default. Run IN-PROGRESS first poll (running 15:36) — polled iter5 status=success (15:36:26→15:40:00). Bundle ROTATE BOTH (load-bearing, verified AFTER success +6s stable NO transient — anti-pattern#3): adminDMm9rtNA→R9uGRxvw+ userBUkOMn_Y→DikfX1RD✓ both apps touched. Brand1F7DC1preserved both HTML. Health live+ready 200/200 + admin/eoffice root 200. NO migration — prod top=Mig 49AddWorkItemToPurchaseEvaluation==repo ✓. sys.tables(excl mighist)=92. ★ WIPE-VERIFY (the risk point — app restart post-deploy did NOT resurrect demo data, DemoSeed:Disabled gate HELD): PE=0 · Contracts=0 · Notifications=0 · PESeq=0 (all wiped clean, stayed clean post-recycle) + infra preserved: ApprovalWorkflows=7 all IsActive=1 · Projects=70 · WorkItems=86 · Users=55 — ALL counts EXACT match expected. Test gate 240 (CI both proj pre-deploy ⟹ success=passed). 0 regression. LESSON: post-wipe deploy verify = confirm both halves — (a) demo/transactional tables stay 0 after app-pool recycle (gate held, no re-seed) AND (b) infra/master tables (AW/Projects/WorkItems/Users) untouched (wipe was surgical, not nuke). Count-exact on both sides = PASS. Tag[s59, run273, pass, fe-list-regroup-x2, bundle-rotate-both, prod-wipe-verify, no-mig]. -
2026-06-11 Run #274 (run_number 274) sha=
0eafcd3PASS ~4m51s (S59-đợt2 FE×2 PE-list tree 4-tầng "Năm>Dự án>Hạng mục>Phiếu" — follow-up Run #273 đổi từ 2-tầng-gộp-label sang 4-tầng explicit): Push56882ac..0eafcd32 filesPurchaseEvaluationsListPage.tsx×2 app ONLY (SHA256 mirror identical95d524ee)..tsx→ pipeline RAN. GITEA_TOKEN+PROD_DB_PW empty → anon API + prod appsettings pw. Run IN-PROGRESS first poll (running 16:37) — polled iter6 status=success (16:37:06→16:41:57). Bundle ROTATE BOTH (verified AFTER success +re-confirm stable NO transient — anti-pattern#3): adminR9uGRxvw→DuU7OTym+ userDikfX1RD→DWyeTzf3✓ both touched. Health live+ready 200/200 + admin/eoffice root 200. NO migration (FE-only). Spot-check PE=0 held post-deploy (FE-only no restart-resurrection risk; wipe gate still HELD from #273). Test gate 240 (CI both proj pre-deploy ⟹ success=passed). 0 regression. LESSON: FE-only follow-up of a wipe-session needs only light PE=0 re-confirm (no full infra re-audit) — restart risk already cleared #273. Tag[s59-dot2, run274, pass, fe-list-4tier-x2, bundle-rotate-both, no-mig, pe-zero-held]. -
2026-06-11 Run #385→#386 SUPERSEDE-CHAIN sha=
ea793a4CANCELLED(benign)→shipped-via3ebaf84#386 PASS ~4m25s (S58 brand-accent polish x2 app then PE-workitem-merge): Target push6e53e33..ea793a48 files FE polish CẢ 2 app (Layout/TopBar/PageHeader/DataTable each — stripe đỉnh + logo-zone tint + PageHeader accent bar + thead brand-50/60), NO BE/Mig. Run #385 (run_number 271) status=cancelled@14:14:22 — NOT a fail: superseded by newer push3ebaf84(#386 run_number 272) landed @14:14:31 (Gitea concurrency-guard cancels in-flight same-branch run). HEAD moved ea793a4→3ebaf84. Verifiedea793a4IS ancestor of3ebaf84+ the 8 polish files NOT re-touched by3ebaf84→ polish PRESERVED in tree, ships via #386. #386 adds 4 PE files (PeHeaderForm/PeWorkspaceCreateView ×2 app, anh Kiệt FDC 14:06 — gộp Tên gói thầu=chọn Hạng mục) → both apps rebuilt anyway. Polled #386 to status=success(started 14:14:31→14:18:56). Bundle ROTATE BOTH (load-bearing, verified AFTER #386 success — anti-pattern #3): adminCP4CB1ym→DMm9rtNA(cssvMtY6u47→DDlKud5i) + userCKjwqnGL→BUkOMn_Y(cssCV0H5hnq→BgAUPcnL) ✓ both touched → both rotate. Brand preserved both apps:1F7DC1in HTML +Be Vietnam Pro+1f7dc1in CSS bundle; BONUS polish landed:brand-50/brand-60Tailwind classes present in BOTH CSS bundles (thead/tint/accent shipped). Health live+ready 200/200 + admin/eoffice root 200. NO migration — prod__EFMigrationsHistorytop = Mig 49AddWorkItemToPurchaseEvaluation== repo, GIỮ NGUYÊN ✓ (neitherea793a4nor3ebaf84has Mig). Smoke PE+contracts unauth=401 + control/api/zzz-not-a-route=404 (auth gates real). Test gate (CI both proj pre-deploy ⟹ #386 success=passed). Prior today #382/#383/#384 all PASS. LESSON (cancelled ≠ fail — supersede-chain verify): a same-SHA run flipping tocancelledmid-flight is almost always Gitea concurrency-supersede by a newer push, NOT a build/deploy fault → MUST (1) check tasks list for newer run + HEAD movement, (2)git merge-base --is-ancestorconfirm target commit preserved in new HEAD, (3)git diff target..newHEAD -- <target-files>empty ⟹ target changes survive, (4) verify prod via the SUCCESSFUL superseding run not the cancelled one. Do NOT report FAIL/escalate on a benign supersede-cancel. Tag[s58, run385-cancelled-benign, run386-pass, supersede-chain, brand-polish-x2, bundle-rotate-both, no-mig]. -
2026-06-11 Run #384 (run_number 270) sha=
e959f72PASS ~4m30s (S58 FE-USER visual redesign density-first per AI_INFRA UI/UX guide — keep brand #1F7DC1/Be Vietnam Pro/slate; FE-USER-ONLY, ZERO BE/Mig/fe-admin): Push6c5fd26..e959f721 commit 16 files: 14 fe-user (index.csstokens + 6 ui primitives Button/Dialog/Input/Label/Select/Textarea + 6 shell DataTable/EmptyState/Layout/PageHeader/PhaseBadge/TopBar + LoginPage) + 2 broadcasts.md. NO fe-admin, NO.cs, NO Mig..tsx/.csspresent → NOT docs-skip, pipeline RAN. ⚠️ GITEA_TOKEN empty both shells → unauth public API (200, no token needed). Run IN-PROGRESS at first poll (status=running 13:51) — correctly did NOT FAIL, polled iter6 status=success (started 13:51:18 → 13:55:48). ASYMMETRIC bundle (load-bearing) PASS: user ROTATEBmZ3VHnm→CKjwqnGL(redesign shipped, verified AFTER status=success, stable on +recheck no transient) + admin FROZENCP4CB1ym(=#382 UNCHANGED ✓ scope-correct, NO fe-admin leak — mirror Run #378 asymmetric fe-admin-only logic, inverted). user.jsHEAD 200 app/js 1.47MB + CSS rotateindex-CV0H5hnq.css200 63KB. Brand preserved:1F7DC1in HTML +Be Vietnam Pro+1f7dc1in CSS bundle ✓; title "Solutions ERP". Health live+ready 200/200 + admin/eoffice root 200. NO migration — prod__EFMigrationsHistorytop = Mig 49AddWorkItemToPurchaseEvaluation== repo, GIỮ NGUYÊN ✓. sys.tables(excl mighist)=92 (FE-only no new table). Smoke PE unauth=401 + control/api/zzz=404 (auth gates real). Test gate 240 (CI both proj pre-deploy ⟹ success=passed). 0 regression. Prior today #382(5998163lock-fix)+#383(6c5fd26hide-modules) both PASS as noted. LESSON (single-app FE-USER redesign — asymmetric verify, inverse of #378): PASS criteria asymmetric — user hash MUST rotate (ship-proof) AND admin hash MUST stay frozen (scope-proof, no accidental fe-admin redeploy). admin-unchanged is POSITIVE here. Visual-only CSS-token+className redesign rotates bundle exactly like logic change (Vite content-hash byte-sensitive). SSH→sqlcmd<>/NOT LIKE '__%'quoting traps:<mangled by PS redirect (use!=/CONCAT-CHAR),_is LIKE-wildcard (escape'[_][_]%'). Tag[s58, run384, pass, fe-user-only-redesign, asymmetric-bundle-verify, no-mig, brand-preserved]. -
2026-06-16 S? Run #291 (run_number 291, id405) sha=
8c8179c❌ FAIL ~64s (TEST-GATE COMPILE BREAK — Department.ParentId phân cấp cây tổ chức, BE Mig + FE-admin picker; DEPLOY DID NOT RUN, prod UNCHANGED @baseline): Pushc98030f..8c8179c2 commits:0f44d97BE (Mig20260616032402_AddDepartmentParentId= AddColumnParentId Guid? nullable+ CreateIndexIX_Departments_ParentId, NO new table;GET /api/departments/tree; Create/Update nhận parentId —CreateDepartmentCommandrecord signature CHANGED to(string,string,Guid?,string?,Guid?)+5th positional ParentId,UpdateDepartmentCommand+6th) +8c8179cFE-admin DepartmentsPage picker "Phòng cha". Tokens empty → anon Gitea + prod-appsettings DB pw. Task #291 status=failure created 10:41:14 → updated 10:42:18 = ~64s (far < ~3min normal → early-stage fail, NOT deploy stage). Anon API can't reach/tasks/{id}+/logs(404 — need auth) + WebFetch Actions UI JS-rendered (empty) → reproduced FAIL locally (anti-pattern #2 no-speculate):dotnet build SolutionErp.slnx -c Release→ error CS7036tests/SolutionErp.Infrastructure.Tests/Application/MasterCatalogFilteredUniqueTests.cs(63,25): "no argument given that corresponds to the required parameter 'ParentId' of CreateDepartmentCommand". Root cause: spec-change miss — commit addedParentIdas REQUIRED positional param but the gotcha#57 filtered-unique test line 63 still calls old 4-argnew CreateDepartmentCommand("DUP1","Phòng ban mới", null, null)(needs 5). grepnew CreateDepartmentCommand\(repo-wide = ONLY this 1 broken call site; UpdateDepartmentCommand has 0 test call site (compiles). Test gate runs BEFORE build/deploy → compile-fail in test proj ⟹ wholedotnet build slnx(incl tests) fails ⟹ deploy never ran. Prod CONFIRMED untouched @ baselinec98030f(Run #290): admin bundleDRob3iVl(FROZEN=baseline ✓ NOT rotated — picker NOT shipped) · userDxK3fCfh(FROZEN=baseline ✓ correct, fe-user untouched) · health api live+ready 200/200 + admin/eoffice root 200 · prod__EFMigrationsHistorytop =20260612173224_ReplaceBudgetModuleWithPeWorkItemBudgets(Mig 50) —AddDepartmentParentIdNOT applied ✓ (deploy didn't run, DbInitializer never fired). All 4 post-deploy KEY checks correctly N/A (no ship). ESCALATE em-main: fix = updateMasterCatalogFilteredUniqueTests.cs:63to pass 5th arg (e.g...., null, null, null)) — CLAUDE.md §7 "spec change = update test cũ + code chung commit" violated; re-push → re-verify deploy. READ-only, did NOT fix. LESSON: short-duration (~60s)failureon a BE/FE commit = test-gate or BE-build compile break, NOT deploy/transient — when anon API blocks logs,dotnet build SolutionErp.slnxlocally reproduces exact CS error + line; a record-constructor positional-param ADD silently breaks every un-updated call site (grepnew <Command>\(to enumerate). FAIL ⟹ deploy gated ⟹ verify prod = STILL baseline (bundles frozen + mig NOT applied) rather than skipping post-deploy entirely. Tag[s?, run291, FAIL, test-gate-compile-break, CS7036-CreateDepartmentCommand-5th-param, dept-parentid-tree, deploy-did-not-run, prod-unchanged-baseline, bundle-frozen-both, mig-not-applied, escalate-fix-test-line63]. -
2026-06-11 Run #382 (run_number 268) sha=
5998163PASS ~3m31s (S58 FIX the Run #381 lock NO-OP — DbInitializer.cs ONLY, BE-only, NO Mig/FE): Pushdd117b7..59981631 commit 1 fileDbInitializer.cs(+28/-5). Fix: (1)LockDemoSampleUsersAsyncunion +20 UAT-matrix prod email ({act,equ,fin,hra,pm,qs}.{nv,pp,tp}@+bod.{1,2}@) into prior 14 named-person = 34-email list; (2)DemoUserPassword11→12 chars (User@123456→User@1234567) fixing silent CreateAsync-fail vs prodRequiredLength=12(S56 helpdesk-inert root cause)..cspresent → full pipeline RAN. Poll iter5 status=success (started 12:58:06 → 13:01:37). Bundle FROZEN adminCP4CB1ym+ userBmZ3VHnm(= #381 UNCHANGED ✓ CORRECT for BE-only, verified AFTER status=success — NOT ship-fail). NO migration — prod__EFMigrationsHistorytop = Mig 49AddWorkItemToPurchaseEvaluation== repo, GIỮ NGUYÊN ✓. sys.tables=93 unchanged. Health live/ready 200 + admin/eoffice root 200. THE FIX VERIFIED prod (Users table — note: custom Identity table nameUsersNOTAspNetUsers): total 55 users · 21 active · 34 inactive==34 locked-future (== lock-list size exactly). 12-sample UAT-matrix allactive=0 locked=1(#381 NO-OP now RESOLVED — these exist in prod + got locked ✓). Named-person 14/14 found+locked (CREATED this startup via 12-char pw fix + locked same run). Must-stay-active 6/6 admin·catalog.manager·nv.test·chuong.phan@solution.com.vn(typo-domain)·nv.cao+nv.truong ALLactive=1(IT helpdesk pool ALIVE — S56 ops-pending RESOLVED by pw fix, created this startup not in lock-list). 5 new real staff (thanh.lethanh/anh.nguyen/tring.le/truong.le/long.nguyen) all CREATED+active=1✓ (12-char pw passes RequiredLength=12). Smoke nv.test login OK (token 477) + GET /api/menus 200 + /purchase-evaluations 200. 0 regression. LESSON: lock-by-email NO-OP (#381) was a DATA-mismatch not code-bug → S58 reconciled email-list to actual prod population (UAT-matrix created via admin UI, never in seed) + the 11-vs-12 pw bug was a SECOND latent cause silently blocking ALL non-existing-user CREATE on prod (RequiredLength=12) — same fix resurrected 16 named + 5 staff + helpdesk pool. Verify lock-fix = dump Users cohorts (active/inactive split + named exact-IN), NOT just total count. Tag[s58, run382, pass, fix-lock-noop, pw-11to12, be-only-bundle-frozen]. -
2026-06-11 Run #381 (run_number 267) sha=
dd117b7PASS+1PARTIAL ~4m25s (S57bis PE gắn WorkItem Mig 49 + all-role Pe perm + menu Cá nhân regroup + lock-14-demo-user — cross-stack BE+FE×2+Mig+test, +12 PeWorkItemGuardTests→240): 2-commit push: prev17b23a4(governance+hmw.js → Run #380 cancelled, superseded — correct, no FE/BE contract change) thendd117b7(PRODUCT, Run 381 = the deciding run). 26 files: Mig 4920260611044424_AddWorkItemToPurchaseEvaluation(3-file, PE.WorkItemId Guid? loose-Guid NO physical FK +IX_PurchaseEvaluations_WorkItemId) + DomainPurchaseEvaluation.cs+ Config + Features + DbInitializer (perm +LockDemoSampleUsersAsync+ menu regroup) + MenuKeys + 3 master controllers (write-lock Admin/CatMgr) + FE×2 (PeDetailTabs/PeHeaderForm/PeWorkspaceCreateView/menuKeys/types). Run IN-PROGRESS at first check (status=running 12:14) — polled to terminal (12:14:16→12:18:41 ≈4m25s success). ⚠️ poll-grep gotcha:"status"field sits AFTER"display_title"in tasks JSON →[^}]*"display_title"regex cut before status (showed blank all 10 iters); final FULL-object parse\{"id":381,...deploy.yml[^}]*\}confirmed status=success. Bundle ROTATE BOTH admin4SUwDLD8→CP4CB1ym+ userXdKzt9LL→BmZ3VHnm(PE in both apps ✓ shipped, verified AFTER status=success). Mig 49 applied prod (__EFMigrationsHistorytop = AddWorkItem... ✓ + WorkItemId col=1 + IX=1). sys.tables=93 (col-only, no delta). Health live/ready 200 + admin/eoffice 200. Perm seed STRONG: Pe_ CanCreate=1 = 130 rows across 13 roles* (was 3-role → all-role open landed); PeWf%=0 + AwV2%=2 (designer stays admin-only ✓ no leak). Menu regroup ✓: Personal root@30 · Off_ChamCong→Personal@1 · Hrm_Config→Master@25 (spec said keyHrmConfig, real key has underscoreHrm_Config— verify by ParentKey/Order NOT literal Key) · Contracts@31 · Hrm_Dashboard→Hrm@1. Smoke PE unauth 401 (/purchase-evaluations + /catalogs/work-items) vs control 404 (auth real). WorkItems VT/TP/MEP/TB=71. ⚠️ PARTIAL item 7 — lock-14-users is a prod NO-OP:LockDemoSampleUsersAsyncSHIPPED+RAN but its 14 hardcoded emails (bod.huynh@,pm.nguyen@,fin.do@,qs.hoang@...) DON'T EXIST in prod — real demo set uses dept.position schemebod.1@/bod.2@/pm.{nv,pp,tp}@/fin.{nv,pp,tp}@/qs.{nv,pp,tp}@(34 users ALL active, INACTIVE_TOTAL=0). Each FindByEmail→null→locked=0. Guardnv.cao/nv.truongalso absent (-1, vacuously safe); catalog.manager+admin confirmed active. NOT a deploy fail (code correct) — email list stale vs this DB seed. Escalated em main: reconcile lock-list to actual*.{nv,pp,tp}@scheme OR confirm named-person legacy users were ever seeded. LESSON: lock/deactivate-by-email assertion returning 0/-1⟹ ALWAYS dump actualUsersset before scoring FAIL — code may have run as no-op against mismatched data, NOT broken. Tag[s57bis, run381, pass-partial, mig49-pe-workitem, allrole-perm-130, lock-noop-email-mismatch]. -
2026-06-09 Run #379 (run_number 265) sha=
a20cde8PASS ~4m20s (S56 GOLIVE-HARDEN BE fixes — LeaveBalance concurrency + ItTicket authz-order + DocxRenderer null-guard + tests, ZERO FE/Mig): Pushbef5825..a20cde81 commit 13 files: 3 BELeaveOtApprovalFeatures.cs(atomic ExecuteUpdate + Serializable tx vs lost-update) +WorkflowAppsFeatures.cs(authz reorder Forbidden-before-NotFound) +DocxRenderer.cs(null-guard) + 4 test files (+12 → 216→228) + 6 agent-memory.md..cs+test present → NOT docs-skip, full pipeline RAN. Run IN-PROGRESS at first check (status=running 17:51) — correctly did NOT FAIL, polled to terminal (started 17:51:45 → updated 17:56:05 ≈4m20s status=success iter5). Bundle FROZEN admin4SUwDLD8+ userXdKzt9LL(= #378, UNCHANGED ✓ CORRECT for BE-only — NOT ship-fail, mirror Run #243/#368; verified pre-deploy + post-success + +5s re-confirm, NO transient, NO unexpected rotation). NO migration — prod__EFMigrationsHistorytop =20260609020759_AddProjectMasterFields(Mig 48) == repo latest, GIỮ NGUYÊN ✓ (BE-logic-only, schema untouched). sys.tables=93 unchanged. Health live+ready 200/200 + admin/eoffice root 200. Smoke changed-area endpoints (all gated, none crash):GET /it-tickets/assignable-staffunauth=401 ·PUT /it-tickets/{guid}/assignunauth+body=401 (authz-reorder fix live, route wired) ·GET /leave-balances/myunauth=401 (concurrency fix dll deployed) · control fake/it-tickets/zzz-not-a-route=404 (proves 401s are real auth gates not catch-all). 0 regression. Ship-proof for BE-only no-contract-change = run success + test 228 + Mig 48 unchanged + bundle frozen + health 200 (no observable API delta — fixes are internal handler logic: atomic tx / exception order / null-guard; cannot curl-assert lost-update fix, rely on +12 tests passing in CI gate). Tag[s56, run379, pass, golive-harden, be-only-bundle-frozen, no-mig]. -
2026-06-09 (S56 pre-golive verify — NO deploy, read-only audit): Re-verified prod truth at golive gate (HEAD
bef5825docs-only → prod correctly = Run #378). build SolutionErp.slnx 0-err + fe-admin/fe-user npm build 0-TS each + test 216 (58D+158I) exact. Prod health live+ready 200; admin root serves4SUwDLD8/ eofficeXdKzt9LL(== baseline, NO drift).__EFMigrationsHistorytop = Mig 48 == repo; 92 tables. Master-data prod spot: Projects=70 (62 real+8 demo), CAL01.Investor=N'Công ty TNHH Calofic' exact, WorkItems real=71 (VT16/TP30/MEP9/TB16) of 86, Suppliers 3/3. LESSON — local-vs-prod FE hash divergence is EXPECTED, not ship-fail: fresh localnpm buildproduces a DIFFERENT content-hash than CI-built prod artifact (node_modules/timestamp inputs not byte-reproducible) → load-bearing check isprod-hash == documented-baseline, NOT== my-local-rebuild. Don't false-alarm on local≠prod when HEAD unchanged. Tag [s56, pre-golive-verify, prod-truth-pass, local-vs-prod-hash-lesson]. -
2026-06-09 Run #378 (run_number 264) sha=
7feb53ePASS ~4m24s (S55 Phase-1 FE-Admin VISUAL redesign density-first design-system NAMGROUP-ref keep brand #1F7DC1 — FE-ADMIN-ONLY, ZERO BE/Mig/fe-user): Push84fa638..7feb53e1 commit 15 files: 13 fe-admin (index.cssdesign tokens + 6 ui primitives Button/Dialog/Input/Label/Select/Textarea + 6 shell DataTable/EmptyState/Layout/PageHeader/PhaseBadge/TopBar + DashboardPage) + 2 agent-memory.md(frontend-designer/reviewer). NO fe-user, NO.cs, NO Mig..tsx/.csspresent → NOT docs-skip, pipeline RAN. Run IN-PROGRESS at first check (status=running 11:51) — correctly did NOT FAIL, polled to terminal (started 11:51:06 → updated 11:55:30 ≈4m24s status=success; updated_at froze 11:55:30 across 3 poll iters = terminal signal before status field parsed). THE KEY PROOF — admin bundle ROTATEB-d6893W→4SUwDLD8(✓ redesign shipped, verified AFTER status=success; pre-success snapshot 11:51 still showed OLDB-d6893W= anti-pattern #3 timing confirmed AGAIN; re-confirm +3s post-success = stable4SUwDLD8, NO transient this run). fe-user bundle UNCHANGEDXdKzt9LL(= #377; untouched ✓ NOT ship-fail — correct, no fe-user file in commit). Admin root 200 text/html + serves<title>Solutions ERP · Admin</title>+<div id="root">(app loads ✓). NO migration — prod__EFMigrationsHistorytop =20260609020759_AddProjectMasterFields(Mig 48) == repo latest, GIỮ NGUYÊN ✓ (FE-only, BE/Domain untouched). Health live+ready 200/200 (both pre- and post-deploy). Test gate 216 (CI both proj pre-deploy ⟹ success=passed;tasksendpoint reports terminal asstatus:success,conclusionNOT populated — trust CI conclusion). 0 regression. LESSON (single-app FE redesign — asymmetric bundle verify): when ONLY fe-admin changes, the PASS criteria is asymmetric — admin hash MUST rotate (proof shipped) AND user hash MUST stay frozen (proof scope-correct, no accidental fe-user redeploy). User-unchanged is a POSITIVE signal here (mirror of BE-only Run #243/#368 where admin+user both stay frozen). Visual-only redesign (CSS tokens + className) rotates bundle exactly like logic change — Vite content-hash byte-sensitive. Status-grep gotcha: greedy.*?regex failed to isolate"status"field mid-poll → usegrep -oE '\\{"id":378,[^}]*\\}'to capture full object then sub-grep status. Tag[s55, run378, pass, fe-admin-only-redesign, asymmetric-bundle-verify, no-mig]. -
2026-06-09 Run #377 (run_number 263) sha=
69cb393PASS ~4m33s (S55 HMW-P4 real master-data seed from Excel + Project +4 cols Mig 48 — cross-stack BE+FE×2+Mig+seed): Pushf8640d6..69cb3931 commit 18 files: Mig 4820260609020759_AddProjectMasterFields(3-file) + DomainProject.cs(+Year/Investor/Location/Package nullable) +ProjectConfiguration.cs+ AppProjectFeatures.cs+DbInitializer.csNEWSeedRealMasterDataAsync(UNGATED, per-code idempotent) + FE×2master/ProjectsPage.tsx+types/master.ts+ 1 testMasterCatalogFilteredUniqueTests.cs+ 4 agent-memory .md..cs+.tsx+Mig present → full pipeline RAN. Run was IN-PROGRESS at first check (status=running 09:28) — correctly did NOT FAIL, polled to terminal (started 09:27:19 → updated 09:31:52 ≈4m33s status=success). ⚠️jqNOT in Bash-tool bash (env is bash not PS despite shell=PowerShell env-line) — parse JSON viagrep -oE '\"id\":377[^}]*'fallback; the working first call only usedhead -cnot jq. Bundle ROTATE adminDmjI8Cmn→B-d6893W+ userYxL_MljK→XdKzt9LL(BOTH changed ✓ FE shipped both apps, verified AFTER status=success; pre-success snapshot 09:28 still showed OLD DmjI8Cmn/YxL_MljK = anti-pattern #3 timing confirmed again; re-confirm +3s post-success = stable, no transient). Mig 48 applied prod (__EFMigrationsHistorytop =...AddProjectMasterFields✓ +COL_LENGTH('Projects','Investor')EXISTS). THE KEY CHECK — real master-data landed (4/4 spot PASS): Projects spot6 (APVN01/ZOTE01/CAL01/MIDEA01/SAM01/TLB01)=6 · WorkItems VT-/TP-/MEP-/TB-=71 · Suppliers (TRUONGGIANG/TANPHU/TGN)=3 ·CAL01.Investor=EXACT match N'Công ty TNHH Calofic' (console showedC<EFBFBD>ng= sqlcmd codepage mangle ofô, NOT data corruption — confirmed viaWHERE Investor=N'...'EXACT). Totals: Projects=70 (62 real + 8 demo coexist ✓ ungated seed idempotent), WorkItems=86, 5 Projects carry Investor / 23 carry Year (Excel sparse-fill, only some rows enriched — expected). Health live+ready 200/200.GET /api/projectsunauth=401 (route wired, auth gates ✓). 0 regression. LESSON (ungated prod seed verify = count spot-checks NOT schema):SeedRealMasterDataAsyncruns unconditionally on every prod startup (NOT insideif(!demoSeedDisabled)— correct per gotcha #51 INFRASTRUCTURE-seed rule); verify = sqlcmd COUNT spot-checks of real Codes + N-literal EXACT match for unicode fields (console codepage will mangle Vietnamese diacritics → always re-assert via=N'...', never trust raw sqlcmd console render). Tag[s55, run377, pass, mig48-master-fields, real-seed-ungated, sqlcmd-codepage-lesson]. -
2026-06-08 Run #376 (run_number 262) sha=
ca4b602PASS ~4m18s (S54 IT-staff self-reassign ticket — authz Admin-OR-IT + scoped capability endpoint, cross-stack, NO migration): Push18d397f..ca4b6021 commit 13 files: BEWorkflowAppsFeatures.cs(NEWGetAssignableItStaffQuerycapability +AssignItTicketHandlerauthz Admin-OR-IT) +ItTicketsController.cs(NEWGET /it-tickets/assignable-staff+/assignLOWERED Authorize-Roles) + FE×2ItTicketsPage.tsx(SHA256-identical) +workflowApps.ts×2 (+2 type) +ItTicketReassignAuthzTests.cs(+13 → 203→216) + 6 agent-memory.md..cs+.tsxpresent → NOT docs-skip, full pipeline RAN. Poll iter5 status=success (started 16:12:23 → updated 16:16:41 ≈4m18s). Bundle ROTATE adminDfCfHUE9→DmjI8Cmn+ user_3S0BPJ2→YxL_MljK(BOTH changed ✓ FE shipped, verified AFTER status=success; pre-deploy iter0 still showed OLD DfCfHUE9/_3S0BPJ2 — correct timing anti-pattern #3). NO migration — prod__EFMigrationsHistorytop =...FilterMasterCatalogUniqueIndexesByIsDeleted(Mig 47) == repo latest, GIỮ NGUYÊN ✓ (DepartmentId reuse). sys.tables=93 stable (no new table). Test gate 216 (CI both proj pre-deploy ⟹ success=passed; grep undercounts InlineData — trust CI). Health live+ready 200 + admin/eoffice root 200. Smoke NEW endpoint:GET /api/it-tickets/assignable-staffunauth=401 (route wired, [Authorize] gates) ·PUT /api/it-tickets/{guid}/assignunauth bare=411 then WITH body-d '{}'=401 (IIS demands Content-Length before auth eval; 411 is pre-auth Length-check NOT routing-miss) · control fake route/it-tickets/zzz=404 (proves 401s are real auth gates not catch-all). 0 regression. LESSON (411 vs 401 on bodyless PUT/POST): unauth bodyless PUT/POST to a JSON-body endpoint returns 411 Length Required from IIS BEFORE the [Authorize] filter runs — NOT a 404/route-miss. Re-send with-d '{}'to force auth eval → real 401. Consistent w/ Run #367PUT /adjust=411+ #364POST /approve=411(same pattern, now explained). Tag[s54, run376, pass, it-reassign-authz, no-mig, 411-precheck-lesson]. -
2026-06-08 Run #371 (run_number 257) sha=
30a99aaPASS ~4m18s (S50 HMW-Wave2 P11-C Vehicle+Driver catalogs Mig 44 + gotcha #57 filtered-unique 3 HRM catalog Mig 45 — BE+FE×2+2Mig+tests): Pushf8179c5..30a99aa1 commit 28 files: BE DomainVehicle.cs/Driver.cs+ AppHrmConfigFeatures.cs+IApplicationDbContext +HrmConfigsController+ 5 Config (Driver/Vehicle/LeaveType/OtPolicy/ShiftPattern) + DbContext + DbInitializer + MenuKeys + Mig44/45 (6 files) + FE×2 (HrmConfigsPage/Layout/menuKeys/hrm-config.ts) +HrmConfigFilteredUniqueTests.cs(+5 test → 181→186). All BE/FE/Mig, none in paths-ignore → CI ran. Poll iter3 status=success (started 10:32:58 → 10:37:16). Bundle ROTATE adminDPPTx2Kw→Cg9mvltU+ userCjoUEsoV→YgqDvsqr(BOTH changed ✓ FE shipped, verified AFTER status=success). NEW LESSON (timing trap): pre-success snapshot showed transientCVbyotwa/BBlyMlJH(intermediate FE copy mid-deploy, NOT final) → re-verify post-success gave realCg9mvltU/YgqDvsqr. Confirms anti-pattern #3 + Run #242 lesson: NEVER trust bundle hash until status=success; mid-deploy can show a 3rd transient hash. Mig 44+45 auto-applied prod (__EFMigrationsHistorytop2 = FilterHrmCatalog... + AddVehicleAndDriver...). Vehicles+Drivers tables EXIST; sys.tables=92 (was 90 +2, narrative-93 = convention diff, NOT missing). gotcha #57 LIVE — all 5 idx filtered: IX_{Vehicles,Drivers,LeaveTypes,ShiftPatterns,OtPolicies}_Code ALLis_unique=1 filter=([IsDeleted]=(0))(3 HRM ones LeaveType/Shift/OtPolicy were NULL pre-Mig45 → now filtered = proof applied). Health live+ready 200 + admin/eoffice index 200. New endpointGET /api/hrm-configs/{vehicles,drivers}unauth=401 (route wired, no crash) + admin auth=200 seed 2/catalog (vehicles XE-01/XE-02, drivers TX-01/TX-02 ✓ DbInitializer infra seed ran). 0 regression. Tag[s50, run371, pass, p11c-vehicle-driver, mig44-45, gotcha57-filtered-5idx]. -
2026-06-03 Run #369 (run_number 255) sha=
350b2bfPASS ~4m13s (S48 FE-only login subtitle a11ytext-slate-500→600, ZERO BE/Mig): Push range7bbfa5a..350b2bf2 commits:009dd94DOCS/GOVERNANCE-only (9 files: STATUS/HANDOFF + 3 adap-reports + error-ledger + session-log + frontend-designer MEMORY + session-end.md cmd — ALL.md/.claude/**) +350b2bfCODE 2 filesfe-{admin,user}/src/pages/LoginPage.tsx(1-line each, slate-500→600 subtitle contrast). Mixed push:.tsxpresent → NOT path-filter skipped, full pipeline RAN (gotcha #41 Discovery #3 — ≥1 non-ignored file in range ⟹ whole range builds; docs commit alone would skip but.tsxoverrides). Poll iter5 status=success (started 00:06:33 → 00:10:46). Bundle ROTATE adminKrjvg_3j→DPPTx2Kw+ user6sNStgxa→CjoUEsoV(BOTH changed ✓ FE shipped — verified AFTER status=success; pre-deploy snapshot iter0 still showed OLDKrjvg_3j/6sNStgxa, correct timing per anti-pattern #3). NO migration — repo 43 == prod__EFMigrationsHistory43, latest both...FilterHolidayUniqueIndexByIsDeleted(Mig 43 unchanged, BE/Domain untouched ✓). Health live+ready 200 + admin/eoffice index 200. Test gate 181 (CI both proj pre-deploy ⟹ success=passed). 0 regression. NEW LESSON: smallest possible FE change (1-line className) still rotates bundle hash — Vite content-hash sensitive to any source byte; mixed docs+tsx push is the canonical case where docs-only-skip does NOT apply. Tag[s48, run369, pass, fe-only-a11y, mixed-push-not-skipped]. -
2026-06-01 Run #368 (run_number 254) sha=
0c5a014PASS ~4m20s (S45 Mig 43 filter Holiday UNIQUE by IsDeleted + 3 HRM test gaps — BE+tests ONLY, ZERO FE): Push rangedbbed15..0c5a0142 commits:051b62bTests +27 (HrmConfigHolidayTests + EmployeeSatelliteTests + AuthorizePolicyRegressionTests-ext → baseline 154→181) +0c5a014Mig 4320260601064128_FilterHolidayUniqueIndexByIsDeleted(drops+recreatesIX_Holidays_Year_Dateas filtered UNIQUEWHERE [IsDeleted]=0, was unfiltered) + HolidayConfiguration.cs edit + Case-7 test flip. 7 files, all BE+tests, none in paths-ignore → CI ran. Poll iter4 status=success (started 13:43:47 → 13:48:07). Bundle hashes UNCHANGED adminKrjvg_3j+ user6sNStgxa(= #367) — CORRECT for BE-only push, NOT ship-fail (Run #243 precedent; ship-proof = Mig 43 applied, not bundle rotate). Mig 43 auto-applied prod (history top =...FilterHolidayUniqueIndexByIsDeleted✓). THE FIX VERIFIED prod:IX_Holidays_Year_Date | unique=1 | filter=([IsDeleted]=(0))— filter_definition non-NULL = filtered UNIQUE live (soft-deleted holidays no longer collide on UNIQUE). Health live+ready 200 Healthy.Holidaystable exists, 10 rows, 2 named idx (PK + filtered UNIQUE). Prod tables=90-by-sys.tables (index-only change, NO new table — consistent #364 delta). NEW LESSON: filtered-index migration verify = checksys.indexes.filter_definitionnon-NULL (NOT just mig-history row); index-only mig = bundle unchanged + table-count unchanged both EXPECTED. Tag[s45, run368, pass, mig43-filtered-index, be-only-bundle-unchanged]. -
2026-05-30 Run #367 (run_number 253) sha=
82d7fcfPASS ~4m08s (S42 P11-B LeaveBalance business logic, Mig 42): Code commit 22 files (4 BE: DomainLeaveBalance.cs+ AppLeaveBalanceFeatures.cs/LeaveOtApprovalFeaturesdeduction hook +LeaveBalancesController+ IApplicationDbContext + DbContext + Config + Mig42 3-file + 2 FEWorkflowAppDetailPage×2 +workflowApps.ts×2 + 2 tests + 4 agent-memory .md). Started 11:11:40 → success iter4 11:15:48. Bundle rotate adminBU8FTBRi→Krjvg_3j+ usertepE4jvR→6sNStgxa(both changed ✓ FE shipped, verified AFTER status=success — pre-deploy snapshot still showed old hash, correct timing). Mig 4220260530034336_AddLeaveBalancesauto-applied prod (tables 90→91,LeaveBalancesEXISTS). Schema ✓: UserId/LeaveTypeId/Year/EntitledDays/UsedDays/AdjustmentDays decimal + AuditableEntity soft-delete. UNIQUEIX_LeaveBalances_UserId_LeaveTypeId_Year+ FK→LeaveTypes del=NO_ACTION (=Restrict) ✓. New endpoint smoke:GET /api/leave-balances/myunauth=401 (route live not 404) + admin auth=200 lazy-default 5 LeaveTypes (ANNUAL12/COMPASSIONATE3/MATERNITY180/SICK30/UNPAID0, all Used=0,remainingDays=entitled ✓ DTO shape has remainingDays/entitledDays) +?year=2026admin route 401 unauth +PUT /adjust=411 (route reg). health live/ready 200 Healthy. NO seed gate concern (plain table, lazy DTO — Stage 4.6 N/A). 0 regression. Note: prev run #366 (ffb2062docs STATUS update) was a CODE-path push w/ status=success — NOT docs-only-skipped (commit touched only .md but Gitea still ran since prior range?); actually #366 display_title is Docs but ran full → confirms agent-memory .md NOT in paths-ignore (.claude/skills/**ignored,.claude/agent-memory/**NOT). Tag[s42, run367, pass, p11b-leavebalance, mig42]. -
2026-05-30 Run #365 sha=
75df04ePASS ~4m05s (S42 P11-A fix workflow picker 2-bug + SetWorkflow endpoint, NO mig): 11 files BE+FE×2+test. Bundle rotate adminBLA09-qv→6D4k-aRi+ userCXvejOE-→DkME-974. +4PUT /api/{leave,ot,travel,vehicle-bookings}/{id}/workflowunauth=401. Test 144. NAMING RECONCILE: use real Gitea task id (#364=e7b66cd mem-labeled "#250"). Tag[s42, run365, pass, p11a-setworkflow]. -
2026-05-30 Run #364 (mem #250) sha=
e7b66cdPASS ~4m07s (S42 P11-A wire ApproveV2+LevelOpinions 4 WorkflowApps): 1 commit BE+FE×2+Mig41+Tests. Status=success iter3. Bundle rotate admincWAXid0q→BLA09-qv+ userCX79e2kZ→CXvejOE-. Mig 41 auto-applied prod (latest=20260530021936_WireWorkflowAppsApprovalV2). Tables 84→90 (+5: Leave/Ot/Travel/VehicleRequest LevelOpinions + WorkflowAppCodeSequences — ALL EXIST). 4 new endpoint smoke 200 auth (leave/ot/travel/vehicle-requests) + unauth 401 (route exists) + POST .../approve=411 (route reg). health live/ready 200. Stage 4.6 seed gate PASS (gotcha #51): 4 WF seeded prod despite DemoSeed:Disabled — QT-NP/OT/CT/XE-V2-001 AppType=5/6/7/9, verified call-site L142-145 OUTSIDEif(!demoSeedDisabled)gate. Test gate 141 (CI runs both proj pre-deploy). Note: table count 90 vs spec-expected 89 = baseline-count diff, NOT missing table (all 5 present). Stale doc drift deploy.yml comments "54/17 test" (cosmetic, flag em main). Tag[s42, run250, pass, p11a-approvev2-workflowapps]. -
2026-05-28 Run #247 sha=
e54a22dPASS 3m25s (S38 SKELETON 5-plan combo Mig 39+40 dual): Push 1 commit megaDomain+App+Infra+Api+FE×2. ALL PASS. Bundle rotate adminCGueDk22→cWAXid0q+ userCEt0QRgX→CX79e2kZ. Mig 39+40 dual auto-applied startup (90830→90839). 6 endpoint smoke 200 (leave/ot/travel/vehicle/it-tickets/hr-dashboardtotalEmployees=33 male=17 female=16). 6 new tables + 8 menu seeded. 0 regression. Fastest S38 deploy. Tag[s38, run247, pass, skeleton-combo]. -
Archived Run #246 (S37 Proposal Mig 37+38 —
/api/proposals200 + QT-DX-V2-001 AppType=4 seed + Stage 4.6 INFRASTRUCTURE-gated correct gotcha #51) + #359/#243/#242/#241/#240 + S35/S36 startup →archive/2026-05-q4.md+ gitd2f52ba(S40 curate): Run #359 G-O2 Meeting Mig 36 · #243 HrmConfig BE 16 endpoint (BE-only bundle unchanged anti-pattern verify) · #242 FE inline forms 5 satellite · #241 Mig 35 HRM foundation · #240 satellite CRUD. Discovery #7 path-filter eval/** + #8 collectionproj_*. KEY absorbed in essentials/Stage sections above. -
[⚠️ VỊ TRÍ LẠC — entry MỚI 2026-06-11, thuộc FIFO slot giữa #384/#382 phía trên nhưng ghi lạc vào khu archive-zone này; curate-L2 ĐỪNG archive nhầm (H2 S58 P2 flagged); relocate khi curate] 2026-06-11 Run #383 (run_number 269) sha=
6c5fd26PASS ~4m25s (S58b TẠM ẨN HRM/Văn phòng số/Cá nhân khỏi non-Admin + Danh mục xuống cuối — BE-only seed, NO Mig/FE): Push2aefb31..6c5fd261 fileDbInitializer.cs(+61/-5): NEWRevokeTemporarilyHiddenModulesAsync(set 4 CRUD=false MỌI role TRỪ Admin trênHrm%+Off%+Personal, idempotent, KHÔNG xóa row) +SeedAllRolesReviewReadPermissionsAsyncscope THU HẸP còn Master/Catalogs/Pe_* + menuMasterOrder 20→80..cspresent → full pipeline RAN. Run IN-PROGRESS at first check (status=running 13:36) — correctly did NOT FAIL, polled to terminal (started 13:36:15 → success 13:40:40 iter5). Bundle FROZEN adminCP4CB1ym+ userBmZ3VHnm(= Run #382 UNCHANGED ✓ CORRECT BE-only — verified AFTER status=success, NOT ship-fail). NO Mig — prod__EFMigrationsHistorytop = Mig 49AddWorkItemToPurchaseEvaluation== repo, GIỮ NGUYÊN ✓. Health live/ready 200 + admin/eoffice root 200. 6 prod sqlcmd ALL PASS exact (DB SolutionErp, custom tablesMenuItems/Permissions/Roles): MasterOrder=80 ✓ · HiddenReadNonAdmin=0 ✓ (revoke landed,Hrm%+Off%+Personalall CRUD=false non-Admin) · HiddenReadAdmin=29 (>0, Dev est 28, Admin GIỮ ✓) · PeCreateNonAdmin=120 (Pe_* untouched ✓) · MasterReadNonAdmin=48 (Master still visible ✓). Menu-tree smoke (gotcha #44 dual-role): nv.test/api/menus/me200 → keys = Master/Suppliers/Projects/Departments + Catalogs/Catalog* + all Pe_DuyetNcc*/Pe_DuyetNccPhuongAn* — ZERO Hrm/Hrm_/Off/Off_/Personal ✓; admin counter-check VẪN CÒN Hrm/Hrm_Config*/Off/Off_*/Personal + Contracts/Budgets/System ✓ (revoke scoped non-Admin only). 0 regression. LESSON (seed-only permission-revoke verify = sqlcmd matrix + dual-role menu-tree, NOT bundle/endpoint): RevokeTemporarilyHiddenModulesAsync runs UNCONDITIONALLY on startup (NOT gated — correct, it's a permission-correction not demo-seed); verify = COUNT(CanRead non-Admin)=0 for revoked-prefix + COUNT Admin>0 (kept) + COUNT untouched-scope (Pe/Master) unchanged +/api/menus/mekey-set diff between regular-user (modules gone) and admin (modules present). FE has NO PermissionGuard per-route (commit note) → direct-URL still renders trang, mức "tạm ẩn" = menu hide + permission matrix only; acceptable pre-golive. Tag[s58b, run383, pass, revoke-hidden-modules, master-order-80, be-only-bundle-frozen, no-mig, dual-role-menutree]. -
Archived Run #232 (S29 gotcha #51 catch — SeedSampleContractWorkflowV2 nested in demoSeedDisabled → empty V2 dropdown, hoist fix) →
archive/2026-05-q4.md+ git. Smart Friend ROI 4× cumulative (S22 #44 + S25 #48 + S29 ApplicableType + S29 DemoSeed).
Curated S71 (2026-06-17) Harness-9 from L1 MEMORY.md (over-cap 65.2KB → <25KB). Appended VERBATIM below: 10 full run-records #306/#305/#304/#303/#302/#299/#298/#297/#295/#293 (newest→oldest) — byte-exact move, FIFO-trimmed from L1 hot. L1 kept #308/#307 full + stubs.
- 2026-06-17 S70 Run #306 (run_number 306, id420) sha=
c556f6cPASS ~4m42s (FE-ONLY re-skin "Văn phòng số" TOÀN MODULE 10 page PURO layout — PageHeader/KpiCard/WidgetCard apply + CSS Hồ sơ NS; presentation-only "phẫu thuật giữ nguyên logic", byte-identical logic. 20 .tsx: fe-admin 11 {AttendanceReport,InternalDirectory,ItTickets,MeetingCalendar,MeetingRooms,ProposalCreate,ProposalDetail,ProposalsList,WorkflowAppDetail,WorkflowAppsList}Page + fe-user 9 mirror (no AttendanceReport — admin-only) + 3 agent-memory .md (cicd/fe-designer/reviewer). NO BE/NO migration/NO new menu key. deploy 10/10 session after #297–#305 all PASS): Pusha8bbdae..c556f6c(1 commit, 23 files).git diff --name-only a8bbdae c556f6c -- '*Migrations*' '*Persistence*'= EMPTY ✓..tsxnon-ignored → full pipeline RAN (3.mdagent-memory match paths-ignore but co-mixed w/ 20 tsx → Discovery#3 range any-non-ignored ⟹ whole build). GITEA_TOKEN+PROD_DB_PW empty → anon Gitea API + DB pw từ prodappsettings.Production.json→ConnectionStrings.Default(pathC:\inetpub\solution-erp\api, uidvrapplen24). Run IN-PROGRESS at spawn (status=running 09:57:54→09:58:40) — pre-deploy baseline captured BEFORE poll-loop: adminBl2o_kUq(S69 #305 live) + userBImrKQNn(S69 #305 live) — both == spec baseline (still live, deploy not yet shipped, anti#3 honored no mid-flight verify). Polled status=success (started 09:57:54 → upd 10:02:36 ≈4m42s). CI gate (both proj pre-deploy ⟹ status=success ⟹ test 286 baseline (45D+241I; FE-only ⟹ 0 BE call-site risk) passed;conclusionempty —tasksendpoint terminal=status:successkhông populateconclusion, trust success; 286 INFERRED gate-passes-pre-build invariant NOT log-confirmed). ★ BUNDLE BOTH ROTATE (FE-both-app 10-page re-skin ⟹ both MUST rotate; verified AFTER status=success + re-confirm STABLE 2nd-fetch identical no-transient — anti#3): admin ROTATEBl2o_kUq→Wt54PHYl(cssCKzdDktL→BpHtX3vS) ✓ + user ROTATEBImrKQNn→B99fMU6X(csseoxUcs8v→DXRSCQW7) ✓. BOTH required → BOTH did. Asset reachable 200: admin js 1,588,241b + user js 1,493,955b (not white). ⚠️ Prod CI hashes ≠ spec local-build (fe-userC8-p69Kn/DezuRkK9, fe-adminyFhLO2Wp/Dd2WiO6n) — EXPECTED CI-rebuild content-hash divergence (only matters NEW≠baseline, BOTH rotated confirmed). Smoke 4×200 health: api/health/ready+/health/live+ admin root + eoffice root. Office-backed API live (admin bearer): GET /api/proposals 200 · /api/it-tickets 200 · /api/meeting-rooms 200 · /api/employees 200 (data layer healthy, not 500)./api/workflow-apps→404 = WRONG-route-guess NOT regression (FE-only can't change BE routing; real route differs — leave-requests/don-tu; FE uses api-client wrapper, route grep didn't land). SPA deep-link 6 Office routes admin all 200 (/proposals, /it-tickets, /workflow-apps/leave, /directory, /meeting-calendar, /office/dashboard serve index.html — not 404/white). NO migration — prod__EFMigrationsHistorytop =20260616035929_AddHoSoLinkToPurchaseEvaluation(Mig 52) == repo HEAD GIỮ NGUYÊN ✓ (prev-2 = AddDepartmentParentId Mig51 + ReplaceBudgetModuleWithPeWorkItemBudgets Mig50 chain intact). sys.tables=88 verified (sqlcmd COUNT excl mighist — unchanged). ★ OFFICE-HIDDEN CONFIRMED (non-admin Drafter=nv.test): sqlcmdOff_Dashboardperm row ONLY for Admin (1/13 roles) ✓ + Drafter has ALL 17 Off_ keys present but EVERY one CanRead=0 ✓ → menu gates on CanRead=1 ⟹ non-admin sees NO Office menu.* ⚠️ LESSON-CORRECTION vs S69: my coarseGROUP BY COUNT(*) WHERE MenuKey LIKE 'Off%'first showed "all 13 roles ~18 Off perms" — MISLEADING; those are perm-ROWS-with-CanRead=0 (existence ≠ access). Must filterCanRead=1OR inspect per-key. MenuItems.IsVisible=1 on all Off keys = admin-side global-visible flag; user-visibility = IsVisible AND role.CanRead. Office-hidden state is AMBIENT (FE-only commit cannot touch Permissions/MenuItems seed — git diff zero BE/DbInitializer) = identical pre-c556f6c. 0 regression. NO prod-data mutation (read-only curls + sqlcmd SELECT-only). Visual per-page render = anh user UAT (em chỉ confirm bundle shipped + route không 500/trắng per spec). TOOLING (re-confirmed + NEW): Bash tool = POSIX bash → ⚠️ systempython3BROKEN (ZKBioTime Python311 on PATH →SRE module mismatchfatal) ANDjqNOT installed → MUST usepowershell.exe -NoProfile -File "C:/.../tmp/x.ps1"+Invoke-RestMethodfor ALL JSON parse (Gitea tasks matchhead_sha -eq sha); poll-loop in pure-bash w/ jq SILENTLY returns empty (no error) — verify parser works before trusting loop. SSH→PS base64-EncodedCommand(UTF-16LE) for appsettings-read AND sqlcmd; sqlcmd string-LITERAL via[char]39concat (NOT doubled-quote);-U/-Pdirect (no -ConnectionString flag);Roles/Permissions(RoleId,MenuKey,CanRead)/MenuItems([Key],IsVisible). NEVER fixed code (READ-only). Tag[s70, run306, pass, fe-only-vanphongso-reskin-10page-PURO, fe-both-app-bundle-BOTH-rotate-Wt54PHYl-B99fMU6X, asset-200-reachable, office-api-200-proposals-ittickets-meetingrooms-employees, workflow-apps-404-wrong-route-not-regression, spa-deeplink-6route-200, no-mig-top-stays-mig52, tables88-verified, office-hidden-confirmed-Off_Dashboard-admin-only-drafter-canread0, lesson-canread0-rows-not-access, ambient-not-deploy-caused, jq-absent-python3-broken-use-powershell, deploy10of10, no-regression, test286-inferred]. - 2026-06-17 S69 Run #305 (run_number 305, id419) sha=
a8bbdaePASS ~4-5m (FE BOTH-APP foundation "Văn phòng số" + index.css sync + BE menu-seed NO-mig: 3 shared component mới PageHeader/KpiCard/WidgetCard + OfficeDashboardPage landing route/office/dashboard4-place-wire BOTH apps +fe-admin/src/index.cssSYNCED (Hồ sơ NS accent tokens, rotate admin mạnh) + BE menu keyOff_Dashboard(MenuKeys.cs L100 + DbInitializer.cs L1825 seed parent=OffOrder0 LayoutDashboard); deploy 9/9 session after #297–#304 all PASS): Push764fe70..a8bbdae(1 commit, 20 files). Diff: FE 14 files (2× {PageHeader,KpiCard,WidgetCard,OfficeDashboardPage,App.tsx,Layout.tsx,menuKeys.ts} + fe-admin index.css) + BE 2 (MenuKeys.cs + DbInitializer.cs) + 4 agent-memory.md..tsx/.cs/.cssnon-ignored → full pipeline RAN (the 4**/*.mdagent-memory match paths-ignore but co-mixed w/ code → Discovery#3 range any-non-ignored ⟹ whole build). GITEA_TOKEN+PROD_DB_PW empty → anon Gitea API + DB pw từ prodappsettings.Production.json→ConnectionStrings.Default(pathC:\inetpub\solution-erp\api, uidvrapp). Run IN-PROGRESS first 4 polls (running 09:26→09:29) — correctly did NOT verify-bundle-mid-flight (anti#3); pre-deploy baseline captured BEFORE poll-loop: adminCNUv1jxY(S78 #304 live) + userCpOskeS1(S78 #304 live) — both == spec S68 baseline (still live, deploy not yet shipped). Polled iter5 status=success (started ~09:25 → success 09:29:50 ≈4-5m). CI gate (both proj pre-deploy ⟹ status=success ⟹ test gate 286 baseline (45D+241I) passed;conclusionempty —tasksendpoint terminal=status:successdoesn't populateconclusion, trust success; 286 INFERRED from gate-passes-pre-build invariant NOT log-confirmed numerically). ★ BUNDLE BOTH ROTATE (FE-both-app + index.css sync ⟹ both MUST rotate; verified AFTER status=success +re-confirm STABLE 2nd-fetch identical no-transient — anti#3): admin ROTATECNUv1jxY→Bl2o_kUq✓ (Văn phòng số + index.css shipped) + user ROTATECpOskeS1→BImrKQNn✓ (foundation shipped). BOTH required → BOTH did. ⚠️ Prod CI hashes (Bl2o_kUq/BImrKQNn) ≠ spec local-build (TbkadgKd/DrxDysO7) — EXPECTED divergence (CI rebuilds w/ different Node/npm/dep-resolution → different content-hash; only matters NEW≠baseline, BOTH rotated confirmed ship). Spec assumption "prod khớp local nếu source-identical" holds ONLY if byte-identical build-env — it is NOT (S77 same lesson). Smoke 4×200: api/health/ready+/health/live+ admin root + eoffice root. NO migration — prod__EFMigrationsHistorytop =20260616035929_AddHoSoLinkToPurchaseEvaluation(Mig 52) == repo HEAD GIỮ NGUYÊN ✓ (git diff --name-only 764fe70 a8bbdae -- '*Migrations*'= EMPTY; menu-seed is runtime-idempotent DbInitializer NOT EF-migration → top did NOT advance). sys.tables=88 verified (sqlcmd COUNT excl mighist — unchanged, menu-seed inserts rows not tables). ★ MENU-SEED VERIFIED (NEW check this run — DbInitializer seed ungated reaches prod by design): sqlcmdSELECT [Key],ParentKey,Label,IsVisible FROM MenuItems WHERE [Key]='Off_Dashboard'→ 1 row Key=Off_DashboardParent=OffLabel="Bảng điều khiển Văn phòng số" IsVisible=1 ✓. ★ OFFICE-HIDDEN CONFIRMED (RevokeTemporarilyHiddenModulesAsync StartsWith("Off") scope — DbInitializer.cs L2172): sqlcmdSELECT r.Name FROM Permissions p JOIN Roles r ON r.Id=p.RoleId WHERE p.MenuKey='Off_Dashboard'→ ONLYAdmin(CanRead=1), 1 row / 13 roles total ✓ → non-admin NO Off_Dashboard perm → Office stays hidden (revoke executed, admin auto via All[]). 0 regression. NO prod-data mutation (read-only curls + sqlcmd SELECT-only). Visual "Dashboard landing render / Hồ sơ NS CSS" NOT verified (anh xem mắt) — only ship+rotate+health+mig-unchanged+tables88+menu-seed+office-hidden. LESSON: FE-both-app + index.css-sync + BE-menu-seed (NO-EF-mig) verify = both bundles MUST ROTATE + Mig-top + sys.tables MUST stay prev (menu-seed = DbInitializer runtime row-insert, NOT a migration → does NOT advance __EFMigrationsHistory NOR sys.tables; verify seed via direct MenuItems SELECT not via mig-count). Office-hidden = query Permissions-by-role: temporarily-hidden module has perm row ONLY for Admin (All[] auto-grant) after RevokeTemporarilyHidden runs. ⚠️ Prod CI bundle-hash ≠ local-build-hash is NORMAL — never FAIL on hash-mismatch-to-local, only FAIL if NOT-rotated-from-baseline. TOOLING (re-confirmed S78): Bash=POSIX ($var/$env:/''literal-quote mangle through bash→ssh→PS→sqlcmd layers) → write PS to.ps1+powershell -File "ABS/FWD/SLASH.ps1"; GiteatasksviaInvoke-RestMethod(matchhead_sha -eq sha,?limit=Nhonored URI-direct); SSH→PS base64-EncodedCommand(UTF-16LE iconv) for BOTH appsettings-read AND sqlcmd; ⚠️ sqlcmd string-LITERAL in query: doubled''x''BREAKS (PS single-quote-string closes early) → build literal via[char]39 + "x" + [char]39concat (NOT''); this sqlcmd build supports-U/-Pdirect (no -ConnectionString flag); table nameRolesNOTAspNetRoles,Permissions(RoleId,MenuKey,CanRead); CLIXML/Progress stdout-noise grep-filter out. NEVER fixed code (READ-only). Tag[s69, run305, pass, fe-both-app-vanphongso-foundation, shared-PageHeader-KpiCard-WidgetCard, index-css-sync-admin, bundle-BOTH-rotate-Bl2o_kUq-BImrKQNn, prod-ci-hash-NE-local-EXPECTED, be-menu-seed-Off_Dashboard-NO-mig, no-mig-top-stays-mig52, tables88-verified, menu-seed-verified-sqlcmd, office-hidden-confirmed-admin-only-1of13, sqlcmd-char39-literal-not-doubled-quote, deploy9of9, no-regression, test286-inferred]. - 2026-06-16 S78 Run #304 (run_number 304, id418) sha=
37752ebPASS ~4m (FE BOTH-APP 1-line CSS-precedence fix: "Hồ sơ NS" employee-detail banner NAME đen→trắng —<h2>rendered BLACK do unlayeredh1,h2,h3,h4{color:#0b1220}index.css thắngtext-white(Tailwind v4 @layer precedence); fix =text-white→text-white!important-modifier +text-xl font-extrabold→text-lg font-bold. 2 fileEmployeesListPage.tsxfe-user+fe-admin SHA256-IDENTICAL8BBAEC34…, 1-line each, NO BE/mig/index.css; deploy 8/8 session after #297–#303 all PASS): Push6983609..37752eb(1 commit). Diff 2 files: both.tsxonly → not in paths-ignore → full pipeline RAN. GITEA_TOKEN+PROD_DB_PW empty → anon Gitea API + DB pw từ prodappsettings.Production.json→ConnectionStrings.Default(pathC:\inetpub\solution-erp\api, uidvrapplen24). Run IN-PROGRESS first 7 polls (running 19:55→19:58) — correctly did NOT verify-bundle-mid-flight (anti#3); pre-deploy baseline captured BEFORE poll-loop: adminD532XZKG(S77 #303 live) + userCuFaBoWt(S77 #303 live) — both == spec baseline (still live, deploy not yet shipped). Polled iter8 status=success (started ~19:55 → success 19:59:26 ≈4m). CI gate (both proj pre-deploy ⟹ status=success ⟹ test gate 286 baseline (45D+241I; FE-only ⟹ 0 BE call-site risk) passed;conclusionempty —tasksendpoint terminal=status:successdoesn't populateconclusion, trust success; exact CI count NOT log-confirmed numerically — Gitea logs web-UI-only anon, 286 INFERRED from gate-passes-pre-build invariant). ★ BUNDLE BOTH ROTATE (the change-point — FE-BOTH-app, both EmployeesListPage changed ⟹ both bundles MUST rotate; verified AFTER status=success +re-confirm STABLE 2nd-fetch identical no transient — anti#3): admin ROTATED532XZKG→CNUv1jxY✓ (text-white!name-color fix shipped) + user ROTATECuFaBoWt→CpOskeS1✓ (same page shipped). BOTH required per spec → BOTH did (mirror S74/S75/S76/S77 pure-FE-both-app pattern — both same-SHA256 file ⟹ both rotate; frozen sibling here = ship-fail flag). Smoke 4×200: api/health/ready+/health/live+ admin root + eoffice root. NO migration — prod__EFMigrationsHistorytop =20260616035929_AddHoSoLinkToPurchaseEvaluation(Mig 52) == repo HEAD GIỮ NGUYÊN ✓ (git diff --name-only 6983609 37752eb -- '*Migrations*' '*Persistence*'= EMPTY; FE cannot alter schema → top did NOT advance; prev-2 =AddDepartmentParentIdMig51 +ReplaceBudgetModuleWithPeWorkItemBudgetsMig50 chain intact). sys.tables=88 verified (sqlcmd COUNT excl mighist — unchanged, no schema touch). 0 regression. NO prod-data mutation (read-only curls + sqlcmd SELECT-only). Visual "tên NV trắng render đúng" NOT verified (anh xem mắt) — only ship+rotate+health+mig-unchanged+tables88; SHA256-identical-between-2-apps is a SOURCE-claim (git), not runtime DOM-equality; CSS-precedence-actually-fixed is render-time, NOT provable by curling bundle (the!importanttext is in dist css per spec-claim, but visual-black-vs-white is browser-render). LESSON: pure-FE-BOTH-app 1-line CSS-fix verify (8th consecutive deploy this session, both apps same component SHA256-identical) = both bundles MUST ROTATE (≠ asymmetric one-app where sibling frozen; ≠ BE-only where both frozen). Even a 1-linetext-white→text-white!change → new content-hash both apps. Migration top + sys.tables MUST stay = prev (FE-only). No BE call-site/DTO/endpoint smoke (no API surface — render-only). TOOLING (re-confirmed S77): Bash tool is POSIX bash (Select-String/$var/$env:unavailable inline) → write PowerShell to.ps1+ invokepowershell -File "ABSOLUTE/FORWARD/SLASH.ps1"(Bash EATS backslash intmp\x.ps1→tmpx.ps1); parse GiteatasksviaInvoke-RestMethod+native object (matchhead_sha -like sha*,limit=Nignored); SSH→PS nested-quote mangles → base64-EncodedCommand(UTF-16LE) BOTH the appsettings-read AND the sqlcmd-query (no$in B64 passes bash+ssh clean); CLIXML progress-stream on stdout harmless (data rows clean below); read DB pw from prod appsettings when PROD_DB_PW empty. NEVER fixed code (READ-only). Tag[s78, run304, pass, fe-both-app-hoso-name-color-fix-text-white-important, fe-both-2files-sha256-identical-8BBAEC34, bundle-BOTH-rotate-CNUv1jxY-CpOskeS1, css-precedence-tailwind-v4-layer, no-mig-top-stays-mig52, tables88-verified, deploy8of8, ssh-encodedcommand-base64, no-regression, test286-inferred]. - 2026-06-16 S77-ARCHIVED Run #303 sha=
6983609PASS ~5m (FE BOTH-APP additive "Hồ sơ NS" banner text-polish — nametext-xl font-extrabold+drop-shadow, metatext-[13px] font-medium text-white, status badge → solid status-colored pill emerald/amber/slate. 2 fileEmployeesListPage.tsxfe-user+fe-admin SHA256-IDENTICALF013B748…, NO BE/mig/index.css; +docs 2 files{dep-audit SKILL.md gotcha 64→65, root CLAUDE.md test 263→286}no-build-impact; deploy 7/7 session after #297–#302 all PASS): Push range231a7b0..6983609(2 commits). Diff 4 files: 2.tsx+CLAUDE.md+dependency-audit-erp/SKILL.md..tsx→ not in paths-ignore → full pipeline RAN (docs alone would SKIP, but tsx present ⟹ build per Discovery#3 push-range any-non-ignored ⟹ whole build). GITEA_TOKEN+PROD_DB_PW empty → anon Gitea API + DB pw từ prodappsettings.Production.json→ConnectionStrings.Default(pathC:\inetpub\solution-erp\api). Run IN-PROGRESS first 6 polls (running 19:36→19:39) — correctly did NOT verify-bundle-mid-flight (anti#3); pre-deploy baseline captured BEFORE poll-loop: adminCcrZqfht(S76 #302 live) + userDniDFUB_(S76 #302 live) — both == spec baseline (still live, deploy not yet shipped). Polled iter6 status=success (started ~19:35 → success 19:40 ≈5m). CI gate (both proj pre-deploy ⟹ status=success ⟹ test gate 286 baseline (45D+241I; FE-only ⟹ 0 BE call-site risk) passed;conclusionempty —tasksendpoint terminal=status:successdoesn't populateconclusion, trust success; could NOT extract exact CI count — Gitea logs web-UI-only anon, 286 INFERRED from gate-passes-pre-build invariant NOT log-confirmed numerically). ★ BUNDLE BOTH ROTATE (the change-point — FE-BOTH-app, both EmployeesListPage changed ⟹ both bundles MUST rotate; verified AFTER status=success +re-confirm STABLE 2nd-fetch identical no transient — anti#3): admin ROTATECcrZqfht→D532XZKG✓ (banner polish shipped) + user ROTATEDniDFUB_→CuFaBoWt✓ (same page shipped). BOTH required per spec → BOTH did (mirror S74/S75/S76 pure-FE-both-app pattern — both same-SHA256 file ⟹ both rotate; frozen sibling here = ship-fail flag). ⚠️ Local-build hashes (fe-userSuT9mDAQ, fe-admin7ICczYiQper spec) ≠ deployed CI hashes — EXPECTED (CI rebuilds; only matters NEW≠baseline, confirmed). Smoke 4×200: api/health/ready+/health/live+ admin root + eoffice root. NO migration — prod__EFMigrationsHistorytop =20260616035929_AddHoSoLinkToPurchaseEvaluation(Mig 52) == repo HEAD GIỮ NGUYÊN ✓ (git diff --name-only 231a7b0 6983609 -- '*Migrations*' '*Persistence*'= EMPTY; FE+docs cannot alter schema → top did NOT advance; prev-2 =AddDepartmentParentIdMig51 +ReplaceBudgetModuleWithPeWorkItemBudgetsMig50 chain intact). sys.tables=88 verified (sqlcmd COUNT excl mighist — unchanged, no schema touch). 0 regression. NO prod-data mutation (read-only curls + sqlcmd SELECT-only). Visual "banner text-xl/drop-shadow/status-pill render đúng" NOT verified (anh xem mắt) — only ship+rotate+health+mig-unchanged+tables88; SHA256-identical-between-2-apps is a SOURCE-claim (git), not runtime DOM-equality. LESSON: pure-FE-BOTH-app additive verify (7th consecutive deploy this session, both apps same component SHA256-identical) = both bundles MUST ROTATE (≠ asymmetric one-app where sibling frozen; ≠ BE-only where both frozen). Docs-files in same range as tsx do NOT suppress build (range any-non-ignored ⟹ build). Migration top + sys.tables MUST stay = prev (FE-only). No BE call-site/DTO/endpoint smoke (no API surface — render-only). TOOLING (re-confirmed S76): Bash tool EATS inline$var/$env:/@{}in PS strings → write PowerShell to.ps1+ run-File; parse GiteatasksviaInvoke-RestMethod+native object (matchhead_sha -like sha*,limit=Nignored); SSH→sqlcmd base64EncodedCommand(UTF-16LE via iconv, no$in B64 passes bash clean), CLIXML progress-stream stdout harmless grep-filter out; ⚠️ thissqlcmdbuild does NOT support-ConnectionStringflag → parseUser Id/Passwordfrom conn-string via regex →-U/-P; read DB pw from prod appsettings when PROD_DB_PW empty. NEVER fixed code (READ-only). Tag[s77, run303, pass, fe-both-app-hoso-banner-textpolish, fe-both-2files-sha256-identical-F013B748, bundle-BOTH-rotate-D532XZKG-CuFaBoWt, docs-in-range-no-suppress-build, no-mig-top-stays-mig52, tables88-verified, deploy7of7, sqlcmd-no-connstring-flag-use-U-P, no-regression, test286-inferred]. - 2026-06-16 S76 Run #302 (run_number 302, id416) sha=
536dd6bPASS ~4m (FE BOTH-APP additive: PE "Link hồ sơ ổ mạng" render upgrade — đường-dẫn-ổ-mạng từ chữ+Copy →<a href=file://…>bấm-thử mở Explorer + GIỮ nút Copy dự phòng. 2 filePeDetailTabs.tsxfe-user+fe-admin SHA256-IDENTICALb415023b…, +46/−14 mỗi file, NO BE/mig/index.css/Employee-page; deploy 6/6 session after #297/#298/#299/#300/#301 all PASS): Push 2 files{fe-admin,fe-user}/src/components/pe/PeDetailTabs.tsx..tsx→ not in paths-ignore → full pipeline RAN. GITEA_TOKEN+PROD_DB_PW empty → anon Gitea API + DB pw từ prodappsettings.Production.json→ConnectionStrings.Default. Run IN-PROGRESS first 6 polls (running 14:54:20→14:56:57) — correctly did NOT verify-bundle-mid-flight (anti#3); pre-deploy baseline captured BEFORE poll-loop: adminI1fpLeYw(S75 #301 live) + userDrQYkzh0(S75 #301 live) — both == spec baseline. Polled iter7 status=success (started ~14:53 → success 14:57:13 ≈4m). CI gate (both proj pre-deploy ⟹ status=success ⟹ test gate 286 baseline (45D+241I; FE-only ⟹ 0 BE call-site risk) passed;conclusionempty —tasksendpoint terminal=status:successdoesn't populateconclusion, trust success). ★ BUNDLE BOTH ROTATE (the change-point — FE-BOTH-app, both PeDetailTabs changed ⟹ both bundles MUST rotate; verified AFTER status=success +re-confirm STABLE 2nd-fetch identical no transient — anti#3): admin ROTATEI1fpLeYw→CcrZqfht✓ (file:// link shipped) + user ROTATEDrQYkzh0→DniDFUB_✓ (same component shipped). BOTH required per spec → BOTH did (mirror S74/S75 pure-FE-both-app pattern — both same-SHA256 file ⟹ both rotate; frozen sibling here = ship-fail flag). Smoke 4×200: api/health/ready+/health/live+ admin root + eoffice root. NO migration — prod__EFMigrationsHistorytop =20260616035929_AddHoSoLinkToPurchaseEvaluation(Mig 52) == repo HEAD GIỮ NGUYÊN ✓ (git diff --name-only 536dd6b~1 536dd6b -- '*Migrations*'= EMPTY; FE cannot alter schema → top did NOT advance; prev-2 =AddDepartmentParentIdMig51 +ReplaceBudgetModuleWithPeWorkItemBudgetsMig50 chain intact). sys.tables=88 verified (sqlcmd COUNT excl mighist — unchanged, no schema touch). 0 regression. NO prod-data mutation (read-only curls + sqlcmd SELECT-only). Visual "link file:// bấm mở Explorer / nút Copy" NOT verified (anh xem mắt) — only ship+rotate+health+mig-unchanged+tables88; SHA256-identical-between-2-apps is a SOURCE-claim (git), not runtime DOM-equality (file:// scheme browsers thường block từ https-origin → click có thể no-op tùy browser, đó là lý do GIỮ Copy dự phòng — không kiểm chứng được qua curl). LESSON: pure-FE-BOTH-app additive verify (6th consecutive deploy this session, both apps same component SHA256-identical) = both bundles MUST ROTATE (≠ asymmetric one-app where sibling frozen; ≠ BE-only where both frozen). Migration top + sys.tables MUST stay = prev (FE-only). No BE call-site/DTO/endpoint smoke (no API surface — render-only). TOOLING (re-confirmed S75): Bash tool EATS inline$var/$env:inpowershell -Command→ write PowerShell to.ps1+ run-File;certutil -hashfile … SHA256for SHA256 (NOT findstr-pipe — quoting breaks); parse GiteatasksviaInvoke-RestMethod+native object (matchhead_sha -eq sha,limit=Nignored); SSH→sqlcmd base64EncodedCommand(UTF-16LE, no$in B64 passes bash clean), CLIXML progress-stream stdout harmless; read DB pw from prod appsettings when PROD_DB_PW empty (pathC:\inetpub\solution-erp\api, keyConnectionStrings.Default). NEVER fixed code (READ-only). Tag[s76, run302, pass, fe-both-app-pe-link-hoso-file-scheme, fe-both-2files-sha256-identical-b415023b, bundle-BOTH-rotate-CcrZqfht-DniDFUB_, no-mig-top-stays-mig52, tables88-verified, deploy6of6, no-regression, test286]. - 2026-06-16 S73 Run #299 (id413) sha=
bcd619dPASS ~3m (TESTS-ONLY BE: +3 filestests/SolutionErp.Infrastructure.Tests/Application/{DepartmentTreeTests,PeHoSoLinkTests,HrmProfilePermissionSeedTests}.cs= +23→286 total (45D+241I). NO prod code, NO FE, NO migration; deploy 3/3 session after #297/#298 both success):.csnot in paths-ignore → full pipeline RAN. Run IN-PROGRESS first poll (running 14:06) → polled iter4 status=success(~14:09 ≈3m);conclusionempty (terminal=success, trust). CI test gate = the focus: gate runs BOTH test projects BEFORE build ⟹ status=success ⟹ 23 new tests passed on CI (not just local 286). Could NOT extract exact CI count — Gitearuns/413/logs+jobsendpoints all 404 anon (logs web-UI-only without auth token; GITEA_TOKEN empty). Inferred 286 from success (gate-passes-pre-build invariant) — NOT log-confirmed numerically. ★ BUNDLE BOTH-FROZEN (BE-only ⟹ neither app may rotate), verified AFTER status=success +2nd-fetch STABLE no transient (anti#3): adminxkSz9BfEFROZEN ✓ + userBumgrwCJFROZEN ✓ (==pre-deploy baseline both; rotate-on-BE-only = anomaly→none). Smoke 4×200: api/health/ready+/health/live+admin root+eoffice root. NO migration — prod__EFMigrationsHistorytop=AddHoSoLinkToPurchaseEvaluation(Mig52)==repo HEAD GIỮ NGUYÊN ✓ (prev-2 Mig51AddDepartmentParentId+Mig50ReplaceBudgetModule…chain intact; commit 0 mig files). sys.tables=88 ✓ (sqlcmd excl mighist, unchanged). 0 regression, NO prod-data mutation (read curls+SELECT-only). TOOLING LESSON (supersedes S72 #298):python3/py -3on this box = BROKEN ZKBioTime embed (AssertionError: SRE module mismatchonimport re/json) — version banner works but stdlib import dies. USE PowerShellInvoke-RestMethod+native object access (NOT ConvertFrom-Json on cached temp file — temp got stale snapshot once) to parse Gitea tasks;limit=Nparam IGNORED (returns full 299, match by id anyway). SSH→sqlcmd: nested bash→ssh→PS ate$var(S42) → useiconv UTF-16LE|base64→powershell -EncodedCommand; CLIXML progress-stream noise on stdout is harmless, data lines clean. Read DB pw from prodappsettings.Production.json→ConnectionStrings.Defaultwhen PROD_DB_PW empty (pathC:\inetpub\solution-erp\api). NEVER fixed code (READ-only). Tag[s73, run299, pass, tests-only-be, +23-test-286-CI-gate-inferred-not-logcount, bundle-both-frozen-xkSz9BfE-BumgrwCJ, no-mig-top-mig52, tables88, deploy3of3, python3-broken-use-powershell-IRM, no-regression]. - 2026-06-16 S72 Run #298 (run_number 298, id412) sha=
292d64dPASS ~4m20s (FE-Admin MIRROR "Hồ sơ Nhân sự" master-detail từ fe-user: overwrite EmployeesListPage.tsx cũ 1200→1602 dòng SHA256-IDENTICAL với fe-user + index.css +4 accent palette teal/amberx/violet/greenx + utility.icon-chip/.app-gradient-brand/.card-accent/.stat-value— FE-ADMIN-ONLY 2 files, NO BE, NO migration, NO fe-user; ASYMMETRIC NGƯỢC deploy-1 S71 #297 (đó fe-user-rotate/admin-frozen, đây admin-rotate/user-frozen)): Deploy 2/2 trong session (deploy-1 =ab4e681#297 PASS fe-userBumgrwCJ; #297=success TRƯỚC push này nên KHÔNG bị cancel — confirmed in tasks list). Push 2 filesfe-admin/src/index.css(+81) +fe-admin/src/pages/hrm/EmployeesListPage.tsx(1020+/537−, 1476-line overwrite)..tsx+.css→ not in paths-ignore → full pipeline RAN. GITEA_TOKEN+PROD_DB_PW empty → anon Gitea API + DB pw từ prodappsettings.Production.json→ConnectionStrings.Default(buKL3...buKL3TGBkD0wDDbYVw65QeX9). Run IN-PROGRESS first poll (running 13:52) — correctly did NOT verify-bundle-mid-flight (anti#3); pre-deploy baseline snapshot captured BEFORE poll: adminBDwV5d0X(S68 frozen-since, must rotate) + userBumgrwCJ(S71 deploy-1 live, must stay frozen). ⚠️ POLL-PARSER BUG: mytr ','|grep -A4 '"id":412'returned empty status ×10 iter (false negative — grep -A on tr-newlined JSON misanchors) → re-queried withpython3 json.load→ status=success(started 13:51:27 → updated 13:55:47 ≈4m20s). LESSON-tooling: parse Gitea tasks JSON via python3 NOT tr/grep -A (multi-field-per-object splits wrong); always cross-check with a clean parser before trusting empty-status. CI gate (both proj pre-deploy ⟹ status=success ⟹ test gate 263 baseline (45D+218I; FE-only ⟹ 0 BE call-site risk) passed;conclusionempty — trust success). ★ BUNDLE ASYMMETRIC NGƯỢC-deploy-1 (the change-point — FE-one-app verify) ALL PASS, verified AFTER status=success +re-confirm STABLE 2nd-fetch identical no transient (anti#3): admin ROTATEBDwV5d0X→xkSz9BfE✓ (EmployeesListPage mirror + accent index.css shipped — admin un-froze from S68's BDwV5d0X frozen-streak S68→S71, now rotated correctly cho fe-admin change) + user FROZENBumgrwCJ==deploy-1-baseline ✓ (fe-user 0 files touched deploy-2 → MUST NOT rotate = correct; rotate-when-untouched = anomaly→flag). Health api live+ready 200/200 (real endpoints/health/ready+/health/live, NOT/health) + admin/eoffice root 200. NO migration — prod__EFMigrationsHistorytop =20260616035929_AddHoSoLinkToPurchaseEvaluation(Mig 52) == repo HEAD GIỮ NGUYÊN ✓ (prev-2 =AddDepartmentParentIdMig51 +ReplaceBudgetModuleWithPeWorkItemBudgetsMig50 chain intact; commit 0 mig files — FE cannot alter schema → top did NOT advance). sys.tables=88 verified (sqlcmd COUNT excl mighist — unchanged, no schema touch). 0 regression. NO prod-data mutation (read-only curls + sqlcmd SELECT-only). Visual "mirror identical / chữ xanh đậm brand-800 / accent đẹp" NOT verified (anh xem mắt) — only ship+rotate+health+mig-unchanged+tables88; SHA256-identical-with-fe-user is a SOURCE-claim (git diff), not runtime-verified by me (can't compare rendered DOM cross-app). LESSON: deploy-2 of same-session asymmetric-NGƯỢC = (a) verify prior deploy (#297) NOT cancelled by this push (it was success first — confirmed in tasks); (b) status=success ⟹ deploy ran; (c) THIS-app bundle ROTATE + OTHER-app FROZEN — direction FLIPS vs deploy-1 (deploy-1 fe-user-rotate/admin-frozen → deploy-2 admin-rotate/user-frozen); admin un-froze from multi-session frozen-streak = EXPECTED when fe-admin finally changes (frozen-streak break is normal, not anomaly); (d) migration top + sys.tables MUST stay = prev (FE-only). Re-query Gitea JSON with python3 when tr/grep parse yields empty — parser bug ≠ run-stuck. No BE call-site/DTO/endpoint smoke (no API surface). SHA256-source-identical = git-level claim, runtime-mirror-equality not provable by curl. NEVER fixed code (READ-only). Tag[s72, run298, pass, fe-admin-mirror-hoso-from-feuser, fe-admin-only-2files, bundle-asymmetric-NGUOC-admin-rotate-user-frozen, admin-unfroze-from-s68-streak, no-mig-top-stays-mig52, tables88-verified, deploy2of2-prior297-not-cancelled, poll-parser-bug-use-python3, no-regression, test263]. - 2026-06-16 S71 Run #297 (run_number 297, id411) sha=
ab4e681PASS ~4m21s (FE-User "Hồ sơ NS" cosmetic: đồng nhất font/size + chữ đentext-slate-900/800→xanh-đậmtext-brand-800cho value/tên + bỏtext-xsnhánh mono — FE-USER-ONLY 1 file, NO BE, NO migration; 4th consecutive same-page S68→S71): Push 1 filefe-user/src/pages/hrm/EmployeesListPage.tsx(className màu chữ + size, KHÔNG đụng BE/Mig/dep/fe-admin)..tsx→ full pipeline RAN. GITEA_TOKEN+PROD_DB_PW empty → anon Gitea API + DB pw từ prodappsettings.Production.json→ConnectionStrings.Default(len24). Run IN-PROGRESS first poll (running 13:43) — correctly did NOT verify-bundle-mid-flight (anti#3), pre-deploy baseline snapshot captured BEFORE poll iter0: userDbVv6rsf(S70 baseline) + adminBDwV5d0X. Polled iter5 status=success (started ~13:42 → success ~13:46:48 ≈4m21s). CI gate (both proj pre-deploy ⟹ status=success ⟹ test gate 263 baseline (45D+218I; FE-only ⟹ 0 BE call-site risk) passed;conclusionempty — trust success). ★ BUNDLE ASYMMETRIC (the change-point — FE-one-app verify) ALL PASS, verified AFTER status=success +re-confirm STABLE 2nd-fetch identical no transient (anti#3): user ROTATEDbVv6rsf→BumgrwCJ✓ (EmployeesListPage cosmetic shipped — re-rotated from S70's DbVv6rsf, 4th consecutive same-page FE-user deploy = new content-hash each, normal) + admin FROZENBDwV5d0X==baseline ✓ (fe-admin 0 files touched → MUST NOT rotate = correct; rotate-when-untouched = anomaly→flag). Health api live+ready 200/200 (⚠️/healthspec'd → 404, real endpoint/health/ready+/health/liveper iis-deploy-runbook skill — both 200, API healthy) + admin/eoffice root 200. NO migration — prod__EFMigrationsHistorytop =20260616035929_AddHoSoLinkToPurchaseEvaluation(Mig 52) == repo HEAD GIỮ NGUYÊN ✓ (commit 0 mig files —git diff --name-only | grep Migrations/= NONE; FE-only cannot alter schema → top did NOT advance; prev-2 =AddDepartmentParentIdMig51 +ReplaceBudgetModuleWithPeWorkItemBudgetsMig50 chain intact). sys.tables=88 verified (sqlcmd COUNT excl mighist — unchanged, no schema touch). 0 regression. NO prod-data mutation (read-only curls + sqlcmd SELECT-only). Visual "chữ xanh đậm/đồng nhất size" NOT verified (anh xem mắt) — only ship+rotate+health+mig-unchanged+tables88. LESSON: pure-FE-one-app cosmetic verify (4th consecutive same-page) = (a) status=success ⟹ deploy ran; (b) target-app bundle ROTATE + sibling-app FROZEN (re-rotate of same page across sessions = NORMAL, each deploy=new hash; rotate-sibling-when-untouched = anomaly); (c) migration top + sys.tables MUST stay = prev (FE cannot alter schema — advancing = bug); (d) health 200×2 (/ready+/live, NOT /health). ⚠️/health404 ≠ unhealthy — SOL uses/health/ready+/health/live(skill-doc), spec literal/healthis wrong-path; cross-check skill before flagging. No BE call-site/DTO/endpoint smoke needed (no API surface). Tokens empty: anon Gitea + prod-appsettings DB pw works. NEVER fixed code (READ-only). Tag[s71, run297, pass, fe-user-hoso-cosmetic-brand800, fe-only-1file, bundle-asymmetric-user-rotate-admin-frozen, no-mig-top-stays-mig52, tables88-verified, health-ready-not-health, no-regression, test263]. - 2026-06-16 S70 Run #295 (run_number 295, id409) sha=
456c7a7PASS ~4m21s (FE-User "Hồ sơ Nhân sự" layout 2-cột: cây tổ chức + list chồng cột trái, detail cột phải + tô màu panel chi tiết — FE-USER-ONLY 1 file, NO BE, NO migration; follow-up S69 Run #294 same page): Pushec517f7..456c7a71 filefe-user/src/pages/hrm/EmployeesListPage.tsx(309+/262−, 571-line layout rewrite)..tsx→ full pipeline RAN. Both tokens SET this run (GITEA_TOKEN+PROD_DB_PW present — unlike S65-S69 empty; DB pw still works via prodappsettings.Production.json→ConnectionStrings.DefaultbuKL3...len24). Run IN-PROGRESS first poll (running 11:36) — correctly did NOT verify-bundle-mid-flight (anti#3), polled iter6 status=success (started ~11:36 → success 11:40:46 ≈4m21s). Pre-deploy baseline snapshot iter0: userCZfo_PFZ(S69 baseline) + adminBDwV5d0X— captured BEFORE poll, correct timing. CI gate (both proj pre-deploy ⟹ status=success ⟹ test gate 263 baseline (45D+218I; FE-only ⟹ 0 BE call-site risk) passed;conclusionempty — trust success). ★ BUNDLE ASYMMETRIC (the change-point — FE-one-app verify) ALL PASS, verified AFTER status=success +re-confirm STABLE 2nd-fetch identical no transient (anti#3): user ROTATECZfo_PFZ→DbVv6rsf✓ (EmployeesListPage layout-2col shipped — re-rotated from S69's CZfo_PFZ, consecutive FE-user deploys on SAME page = new content-hash each, normal) + admin FROZENBDwV5d0X==baseline ✓ (fe-admin 0 files touched → MUST NOT rotate = correct; rotate-when-untouched = anomaly→flag). Health api live+ready 200/200 + admin/eoffice root 200. NO migration — prod__EFMigrationsHistorytop =20260616035929_AddHoSoLinkToPurchaseEvaluation(Mig 52) == repo HEAD GIỮ NGUYÊN ✓ (commit 0 mig files —git diff --name-only | grep Migrations/= NONE; FE-only cannot alter schema → top did NOT advance past S68's Mig 52; prev-2 =AddDepartmentParentIdMig51 +ReplaceBudgetModuleWithPeWorkItemBudgetsMig50 chain intact). sys.tables stays 88 by construction (no schema touch — skipped explicit count, FE-only). 0 regression. NO prod-data mutation (read-only curls + sqlcmd SELECT-only). Visual "2-cột đẹp/tô-màu" NOT verified (anh xem mắt) — only ship+rotate+health+mig-unchanged. LESSON: pure-FE-one-app verify (3rd consecutive same-page FE-user S68→S69→S70) = (a) status=success ⟹ deploy ran; (b) target-app bundle ROTATE + sibling-app FROZEN (asymmetric — re-rotate of already-rotated page across sessions is NORMAL, each deploy=new hash; rotate-sibling-when-untouched = anomaly); (c) migration top MUST stay = prev (FE cannot alter schema — advancing top on FE-only commit = bug); (d) health 200×3. No BE call-site/DTO/endpoint smoke needed (no API surface). Tokens-present vs empty: both paths work — when GITEA_TOKEN set usetokenheader, but anon also fine for public repo. NEVER fixed code (READ-only). Tag[s70, run295, pass, fe-user-hoso-2col-layout, fe-only-1file, bundle-asymmetric-user-rotate-admin-frozen, no-mig-top-stays-mig52, tables88, no-regression, test263, tokens-present]. - 2026-06-16 S68 Run #293 (run_number 293, id407) sha=
5a0aaa4PASS ~5m00s (FE-User "Hồ sơ Nhân sự" 3-panel master-detail 5-tab rewrite + PE "Link hồ sơ" hyperlink NAS + rename "Dự trù PRO"→"Ngân sách PRO" — CROSS-STACK BE Mig 52 + FE×2; 2-commit318860a(FE-User EmployeesListPage) +5a0aaa4(PE cross-stack)): Push6ce5803..5a0aaa415 files: BEPurchaseEvaluation.cs(+HoSoLink) +PurchaseEvaluationConfiguration.cs+PurchaseEvaluationFeatures.cs(Create/Update cmd +trailing optionalHoSoLink=null) +PurchaseEvaluationDtos.cs(Detail DTO +hoSoLink) + Mig 5220260616035929_AddHoSoLinkToPurchaseEvaluation(3-file) + FEEmployeesListPage.tsx(fe-user, 3-panel rewrite cây-tổ-chức+list+5-tab) + PE×2 (PeDetailTabs.tsx+PeWorkspaceCreateView.tsx+types/purchaseEvaluation.tsboth apps, mục E hyperlink + label rename)..cs+.tsx+Mig → full pipeline RAN. GITEA_TOKEN+PROD_DB_PW empty → anon Gitea API + DB pw từ prodappsettings.Production.json→ConnectionStrings.Default(buKL3...). Run IN-PROGRESS first poll (running 11:15) — correctly did NOT verify-bundle-mid-flight (anti#3), polled iter5 status=success (started 11:13:57 → success 11:18:57 ≈5m). CI gate (both proj pre-deploy ⟹ status=success ⟹ test gate 263 baseline (45D+218I; BE Create/Update +trailing optional param=null ⟹ call-sites UNBROKEN) passed;conclusionempty — trust success). ★ MIGRATION APPLIED (the KEY): prod__EFMigrationsHistorytop =20260616035929_AddHoSoLinkToPurchaseEvaluation✓ (Mig 52; ADVANCED from prev top Mig 51AddDepartmentParentId; DbInitializer auto-migrate on startup ran — api ready=200 confirms boot OK post-mig). Mig content = AddColumnHoSoLink nvarchar(1000) maxLength=1000 NULL(AddColumn-only, NO new table → sys.tables stays 88 verified). DB-level:sys.columns WHERE object_id=OBJECT_ID('PurchaseEvaluations') AND name='HoSoLink'= 1 row ✓ (column physically present prod). ★ BUNDLE BOTH ROTATE (verified AFTER status=success +re-confirm STABLE 2nd-fetch no transient — anti#3): adminDf06fmpq→BDwV5d0X✓ (PE files shipped — was FROZEN since S67, now correctly rotated) + userDxK3fCfh→DXkyUjtQ✓ (EmployeesListPage + PE files shipped). Both required to rotate per spec → BOTH did. Health api live+ready 200/200 + admin/eoffice root 200. ★ NEW-FIELD live smoke (admin bearer, route/api/auth/loginfieldaccessTokenlen468):GET /api/purchase-evaluations/{48154149...}(phiếu thậtPE/2026/A/015) = 200 ✓ JSON detail carries"hoSoLink":null(key PRESENT, null cho phiếu cũ = backward-compat ✓ — Detail DTO wired AND Create/Update signature-change did NOT break GET path) ·GET /api/departments/tree= 200 ✓ (Phase B dependency from Mig 51, still live). 0 regression. NO prod-data mutation (read-only GETs + sqlcmd SELECT-only). LESSON: nullable-AddColumn cross-stack verify = (a)__EFMigrationsHistorytop ADVANCED to new Mig (Mig 52) + api ready=200 (auto-migrate boot OK); (b)sys.tables=88 unchanged (AddColumn ≠ new table) +sys.columnsconfirms col present; (c) Detail DTO field-presence via authed GET on a REAL phiếu —"hoSoLink":nullfor old rows proves both DTO-wiring AND backward-compat (trailing-optional Create/Update param won't break GET — confirmed 200); (d) cross-stack BE+FE×2 = BOTH bundles rotate (admin un-froze from S67). Tokens empty: anon Gitea + prod-appsettings DB pw works (Bash tool is POSIX bash NOT PS despite shell=PowerShell env —$env:/jq/python3unavailable; parse viatr ','|grep+ read pw viassh→appsettings). Tag[s68, run293, pass, pe-hosolink-mig52-applied, fe-user-hoso-3panel-rewrite, pe-link-hoso-hyperlink, rename-dutru-to-ngansach, bundle-both-rotate-admin-unfroze, hosolink-null-in-dto-backward-compat, no-new-table-88, test263].