Files
solution-erp/.claude/agent-memory/cicd-monitor/MEMORY.md
pqhuy1987 8f780b6237 [CLAUDE] Docs: S76 closeout — PE ngan sach ma tran 3 cot + bang luoi + badge quyen-NS
STATUS/HANDOFF (Mig 55->56, test 339->344, gotcha 69->70, bundle jOqxW4-p/DbsznVvR
Run #319, Phase +S76, In Progress->Recently Done) + gotcha #70 (FE absolute-set echo
stale-echo data-loss -> useIsFetching gate) + ef-core skill Mig 56 row + session log
2026-06-19-S76 + agent-memory harvest (impl-FE stray->canonical + 4 sub diary).
Curate-debt carry: reviewer 45KB + inv-codebase 35KB keep-floor-hit manual-condense.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 11:44:34 +07:00

95 lines
27 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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

# CI/CD Monitor Agent — Persistent Memory
> **Persistent diary cross-session.** Auto-injected first ~200 lines at spawn (L1 HOT).
> Update BEFORE every stop. Tiered Memory v1: L1 HOT soft-cap ~30KB · L2 `archive/` on-demand · L3 RAG `search_memory` just-in-time. Keep entry ≤ 1.5K chars (gotcha #53).
> Full verbatim run history pre-S40 → git `d2f52ba` + `archive/2026-05-{runs,q2,q3,q4}.md` + `archive/2026-06.md`. 🗺️ **Lookup map (Harness-9 S70): `archive/_INDEX.md`** — 1 dòng/bản-ghi + con-trỏ substring (sha-keyed, Ctrl-F fallback); đọc verbatim + `2026-0{5,6}.gist.md` (nén 4-field) theo nhu cầu.
---
## 🎯 Role baseline
Read-only CI/CD + post-deploy verifier SOLUTION_ERP. Polls Gitea Actions API, verifies test gate + deploy ship + prod health. Tools: Read, Grep, Glob, Bash, WebFetch + 5 RAG MCP. Output: PASS/FAIL + evidence <500 words. Skills: `iis-deploy-runbook` + `dependency-audit-erp` + `ef-core-migration`. Spawn ~150K trade-off catch fail tự động.
---
## 🚨 Recurring CI/CD bug patterns (catch priority)
- **#39 act_runner github.com TCP timeout** run hang "Set up job" 21s. Log `dial tcp github.com:443 i/o timeout`. Fix: manual checkout bypass hardcoded `.gitea/workflows/deploy.yml` (pass #110). KHÔNG revert.
- **#40 npm cache `tsc not found`** `build_fe_admin` fail post `cache: npm`. DISABLED rolled back `a21790d`. KHÔNG re-enable.
- **#41 paths-ignore docs-only skip** code commit không trigger CI? Check `git diff --name-only HEAD~1 HEAD` vs `paths-ignore: ['docs/**','**/*.md','.claude/skills/**']`. Discovery #3: Gitea evaluates push *range* commits nếu 1 commit non-ignored file toàn range build (BENEFICIAL).
- **#25 IIS WebSocket** `notification-hub/negotiate` 401/404 prod. Fix: WebSocket module enable `web.config` site api (skill `iis-deploy-runbook`).
- **#48 SQLite tie-break** `OrderByDescending(CreatedAt).First()` pick wrong khi 2+ `.Add()` cùng frozen-clock. Fix: discriminator filter `.Where(Summary.Contains("Chuyển phase"))` BEFORE OrderBy.
- **Bundle hash unchanged = ship FAIL** push+action success nhưng prod không đổi. Verify via INDEX.HTML ref (`curl -s https://admin.solutions.com.vn/ | grep -oE '/assets/index-[A-Za-z0-9_-]+\.(js|css)'`), NOT by GETting a hash-named asset directly. Fix: SSH `Restart-WebAppPool`. Bundle hash verify MUST sau status=success (Run #242 false-positive lesson: check khi "running" stale hash).
- **🔴 #69 (S72 Run #312 OVERTURNS prior invariant) FE bundle hash is NON-DETERMINISTIC per rebuild. `deploy.yml` `Remove-Item fe-{admin,user}\* -Exclude web.config` + `Copy-Item dist\*` runs UNCONDITIONALLY every run (path-filter gates whole-workflow trigger, NOT per-step). Identical FE source DIFFERENT hash each deploy (Vite/rolldown non-reproducible chunk-id). PROVEN: governance-only `18fced6` (0 files in fe-*/src) rotated BOTH bundles admin `BgNCjwsG→fc_xkNpJ` (+717b) + user `CBvh0vtf→DP-tBcg0`, index.html `Last-Modified` matched deploy window. "BE-only/governance bundle MUST stay frozen" is FALSE; rotation is EXPECTED on every deploy. Bundle-rotate alone is NOT proof of FE content change to detect real FE change, diff `fe-*/src` in commit/range, NOT hash delta.**
- **⚠ SPA-fallback 200 trap (S72):** server rewrites `/*``/index.html` so GET `/assets/index-<ANYTHING>.js` returns **200** even for non-existent/fake hash (control `ZZdoesnotexist0.js`200 confirmed). Old-hash-still-200 is MEANINGLESS as persistence signal. RELIABLE bundle signal = parse index.html `<script src>`/`<link href>`, then GET that exact path + check `size_download` large (not white-screen) + `Last-Modified` in deploy window.
- **Migration drift prod vs repo** compare `ls .../Persistence/Migrations/*.cs` vs `sqlcmd __EFMigrationsHistory`. Fix: check `Program.cs` `app.MigrateDatabase()` + app pool recycle.
---
## 📋 5-stage checklist (EVERY run)
- **Stage 0 RAG infra:** `Get-Service Qdrant` Running + `http://localhost:6333/healthz`. Collection `proj_solution_erp` (prefix `proj_*` 7 project Discovery #8).
- **Stage 1 Push+filter:** `git log -1 --format='%H %s'` + `git log origin/main..HEAD` empty + diff vs paths-ignore (docs-only SKIPPED-DOCS return).
- **Stage 2 Gitea poll** (max 10 iter × 60s): API `.../actions/tasks?limit=5` (NOT `/runs` 404). Match `head_sha`. task table `updated_at` stale ~2min (gotcha #46) cross-check VPS mtime.
- **Stage 3 Test gate:** baseline **130 PASS** (58 Domain + 72 Infra). Phase 9 UAT exception lower OK (`feedback_uat_skip_verify`).
- **Stage 4 Post-deploy** (if SUCCESS): auth login bearer (admin + nv.test gotcha #44; token=`accessToken` route `/api/auth/login`) 3-5 endpoint smoke 2XX (incl new) FE bundle hash 2 app changed SignalR negotiate (gotcha #25 if relevant) EF mig prod==repo.
- **Stage 4.6 (S29 CRITICAL):** sqlcmd seed sample verify post-deploy (NOT chỉ schema). `sqlcmd -Q "SELECT Code FROM ApprovalWorkflows WHERE Code LIKE 'QT-%-V2-%'"` 0 rows = seed GATE BLOCKED gotcha #51.
- Discovery #4: ASP.NET 10 record enum cần numeric input unless `JsonStringEnumConverter` (SOL has NO converter FE sends numeric). #5: sqlcmd ssh Windows-auth cần `\\\\SQLEXPRESS` 4-backslash. #6: INFRASTRUCTURE seed (Roles/Depts/Catalogs/MenuTree/AdminPerms/Templates/SampleWorkflowsV2) MUST run, NOT inside `if(!demoSeedDisabled)`; DEMO seed (DemoUsers/Contracts/PE) OK gated gotcha #51.
- **Stage 5 Report** PASS/FAIL + evidence + MEMORY update.
---
## ⚠️ Anti-patterns (DO NOT)
1. Push fix code READ only, escalate em main · 2. Speculate fail without log · 3. Skip post-deploy bundle hash (biggest catch) · 4. Skip MEMORY · 5. Poll forever (max 10 iter) · 6. Auto-rollback (escalate + recommend) · 7. Verify docs-only (SKIPPED-DOCS return ngay)
---
## 🧠 SOLUTION_ERP CI/CD essentials (S40 verified)
- **Gitea:** `git.baocaogiaoduc.vn/vietreport-admin/solution-erp` · workflow `.gitea/workflows/deploy.yml` · paths-ignore `['docs/**','**/*.md','.claude/skills/**']`
- **Prod:** api/admin/eoffice `.solutions.com.vn` · SSH `ssh vietreport-vps` (Administrator, id_ed25519) · IIS site phys paths (S42 verified): API `C:\inetpub\solution-erp\api` · admin `\fe-admin` · user `\fe-user` (3 sites Started). DB `.\SQLEXPRESS`/`SolutionErp`/`vrapp` SQL-auth. **Conn string key = `ConnectionStrings.Default` (NOT `DefaultConnection`!)** read pw from prod appsettings.Production.json when `$env:PROD_DB_PASSWORD` empty.
- **SSHPS quoting (S42 lesson):** nested bashsshpowershell mangles `$var`/`\"`. Use `iconv UTF-16LE | base64` `powershell -EncodedCommand $B64`. Single-quote literal paths.
- **Tests baseline:** **263 PASS** (S62 Run #286 sha 7926c21 spec; 45 Domain + 218 Infra em-main supplied; supersedes prev 228/240/256). CI gate runs both test projects BEFORE build/deploy status=success test gate passed (`tasks` endpoint reports terminal as `status:success`, `conclusion` field NOT populated). Local grep undercounts (Theory/InlineData) trust CI conclusion. Phase 9 UAT mode skip per chunk OK.
- **Mig latest repo:** **Mig 55 `20260618105426_AddCcmNoteToPeWorkItemBudget`** (S74-bis Run#315 sha 8655ebf; PeWorkItemBudgets +`CcmNote nvarchar(1000) nullable` 1 AddColumn no new table VERIFIED APPLIED PROD: history-top==repo, sys.columns `nvarchar maxlen=2000 null=1` [2000=byte-len 1000char×2], tables88). Prev Mig 54 `AddPeSuggestedAndApprovedPrice` (S73 #313, 5 PE price cols) + Mig 53 `20260617060207_AddPeUrgentAndCeoApprovalThreshold` (S71 Run#308; PE +IsUrgentByPro/+IsUrgentByCcm bit-default-0 + ApprovalWorkflows +CeoApprovalThreshold decimal(18,2)-nullable, 3 AddColumn no new table VERIFIED APPLIED PROD). Prev Mig 52 `AddHoSoLinkToPurchaseEvaluation` (S65 PE HoSoLink hyperlink-NAS) + Mig 51 `AddDepartmentParentId` (S65 org-tree loose-Guid) + Mig 50 `ReplaceBudgetModuleWithPeWorkItemBudgets` (S61 net-reduce). Path `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/` (53 mig .cs non-designer total). Prod check `sqlcmd __EFMigrationsHistory ORDER BY MigrationId DESC TOP 5`. Table-count: `sys.tables` (is_ms_shipped=0, excl mighist) = **88** (S62 Run #286 verified S61 Budget-replace DROPPED tables 9388). Narrative-93 is STALE pre-S61 when commit touches no schema, 88 is correct, don't FAIL on 8893. Always cross-ref COMMIT scope vs ambient count.
- **Bearer:** admin `admin@solutions.com.vn/Admin@123456` (full) · UAT `nv.test@solutions.com.vn/TestUser@123456` (Drafter CCM, gotcha #44 check)
- **Bundle hash live S77:** admin `jOqxW4-p` + css `DyECY611` · user `DbsznVvR` + css `JQfATaQB` (Run #319 sha 21d1f4e FE-only PeDetailTabs Block-A table-grid; index.html `Last-Modified` admin 2026-06-19T04:31:53Z + user 04:32:45Z = deploy window; admin css ALSO rotated [Block-A real style] but user css `JQfATaQB` FROZEN = asymmetric-ok gotcha#69). Prev #318: admin `BhFDF9IJ`/css `DuqjXB6Y` · user `BAkuRl3C`/css `JQfATaQB`. Prev #315: admin `BYF5vIMJ`/user `CB-tiRxd`. **PER #69 (S72): bundle hash ROTATES on EVERY deploy regardless of FE change (non-deterministic rebuild). So "live hash" value is only a SNAPSHOT for THIS run — do NOT treat it as a frozen-baseline to compare next run against.** To detect a real FE ship, diff `fe-*/src` in commit/range NOT hash delta. ASYMMETRIC-deploy "anomaly" framing (S66) is RETIRED by #69: both apps rebuilt+rotated every deploy, asymmetry impossible to read from hashes alone. S50 mid-deploy transient lesson still holds: re-confirm hash AFTER status=success ALWAYS (anti-pattern #3).
- **DB pw (S42, when `$PROD_DB_PASSWORD` empty):** `vrapp/buKL3TGBkD0wDDbYVw65QeX9` read from `C:\inetpub\solution-erp\api\appsettings.Production.json``ConnectionStrings.Default`. Skill-doc path `C:\inetpub\apps\SolutionErp\Api` is STALE real path `C:\inetpub\solution-erp\api`. sqlcmd over SSH works direct (no UTF-16 encode needed). sys-catalog string-concat queries hit collation conflict (`Latin1_General_CI_AS_KS_WS` vs `SQL_Latin1_General_CP1_CI_AS`) add `COLLATE DATABASE_DEFAULT` per concatenated column.
## 🔑 Critical config (flag commit nếu tái xuất)
Node CI `20.x` (`feedback_node_cicd`) · MediatR `12.4.1` (gotcha #1, flag `Version="14`) · Swashbuckle `6.9.0` (gotcha #2) · act_runner manual checkout (#39) · npm cache DISABLED (#40, flag `cache: npm`)
---
## 🎯 Per-NV admin opt-in wire — 10-point checklist (cumulative S22→S23)
Cross-ref `feedback_per_nv_permission_scope`. Per-NV/per-Level refactor MUST verify: 1 Domain field · 2 EF `HasDefaultValue(false)` · 3 Mig 3-file · 4 Service read · 5 Domain+App DTO mirror · 6 Designer FE checkbox · 7 AwLevelDto+ToDto · 8 CreateAwLevelInput+Update mutation · 9 **Lookup discrimination** (`FirstOrDefault` ADD `ApproverUserId==actorId` + admin fallback) · 10 **Controller body record count == Command record count**. Bug latency 2-3 days prod silent khi miss 9-10. Scan `grep -n "FirstOrDefault.*Order.*==" *.cs` after OR-of-N refactor.
## 📊 Run stats baseline
BE (test+build) ~90s · FE × 2 ~60s/app · deploy ~30s · **total ~3min code / 0s docs-only**. >5min → escalate.
---
## 📅 Recent runs (FIFO — older → archive/git)
- _(S72 Run #312 sha=`18fced6` PASS ~5m16s [governance-only 26-md+hmw.js+memory-budget.json triggers-CI, ZERO prod code, Mig53-unchanged-VERIFIED-prod, tables88, health-4x200, test306-inferred, **BUNDLE-BOTH-ROTATE-but-BENIGN fc_xkNpJ/DP-tBcg0** → NEW gotcha #69 (FE-hash NON-deterministic per-rebuild, deploy.yml Remove+Copy fe-* UNCONDITIONAL every run, "frozen-on-BE-only" invariant OVERTURNED, SPA-200-trap control-test, real-FE-change = `git diff fe-*/src` NOT hash-delta), 0-fe-src-diff-confirms-no-accidental-ship] → gotcha #69 promoted to recurring-patterns §top; FIFO-trimmed, full verbatim git `18fced6`+`39d55d4`)_
- _(S71 Run #308 sha=`ebd7e1c` PASS ~4m41s [FULL-STACK PE-urgent+CCM-threshold, **Mig53 AddPeUrgentAndCeoApprovalThreshold VERIFIED-APPLIED-PROD** 3-cols-sys-columns (IsUrgentByPro/Ccm bit0 + CeoApprovalThreshold decimal18-2-null), history-top-advance-53, tables88-addcolumn-only, bundle-BOTH-rotate BgNCjwsG/CBvh0vtf, endpoint-urgent-401-not-404, test306] → 4-axis full-stack pattern (BE+FE-both-rotate · history-top-advance + N-cols sys.columns type-verify · tables-stay-88 · endpoint-401-not-404); FIFO-trimmed, full verbatim git `ebd7e1c`)_
- **2026-06-19 S76 Run #318 (run_number 318, id=432) sha=`e33481e` PASS ~4m58s (FULL-STACK: BE Mig 56 `AddProBudgetSplitToPeWorkItemBudget` — PE ngân-sách MA-TRẬN 3 cột Dự-án|PRO|CCM, PRO split số ban-hành + điều-chỉnh + badge quyền NS theo role; anh Kiệt FDC continuation Mig 50/53/54/55. 19 files: BE 9 {Controller, App ApprovalWorkflowV2AdminFeatures+PurchaseEvaluationDtos+PeWorkItemBudgetFeatures+PurchaseEvaluationFeatures, Domain PeWorkItemBudget.cs, Config PeWorkItemBudgetConfiguration} + 3 Mig-file + FE 6 {PeDetailTabs+PeWorkflowPanel+types ×2-app, admin +ApprovalWorkflowsV2Page} + 1 test PeWorkItemBudgetTests + .gitignore. deploy 17/17 session after #297#315 all PASS):** Push HEAD=`e33481e` (nothing unpushed). `git diff --name-only e33481e~1 e33481e -- '*Persistence/Migrations*'` = 3 files (Mig 56 .cs/.Designer.cs/Snapshot) — REAL EF mig. Mig56 Up() read = **2× `AddColumn<decimal>`: `ProAdjustmentAmount` + `ProInitialAmount` both decimal(18,2) precision18 scale2 nullable** + `migrationBuilder.Sql(UPDATE PeWorkItemBudgets SET ProInitialAmount=ProEstimateAmount WHERE ProEstimateAmount IS NOT NULL AND ProInitialAmount IS NULL)` data-migrate (phiếu cũ 1-cột giữ số PRO ở cột ban-hành); Down() 2× DropColumn — matches spec exactly, AddColumn-only no new table. `.cs/.tsx` non-ignored → full pipeline RAN. GITEA_TOKEN+PROD_DB_PW BOTH absent env → anon Gitea API (worked, public repo) + DB pw read prod `appsettings.Production.json``ConnectionStrings.Default` (`vrapp`/`buKL3TGBkD0wDDbYVw65QeX9`, `.\SQLEXPRESS`/`SolutionErp`, path `C:\inetpub\solution-erp\api`). Run IN-PROGRESS at spawn (running 11:03:00, exact head_sha match #318 deterministic; NOTE prompt said "Run #316" but #316=CANCELLED `ae957c4` — actual run for `e33481e` = #318 matched by head_sha NOT run_number). **★ PRE-DEPLOY DB SNAPSHOT captured BEFORE poll-loop (proves deploy hadn't shipped): prod history-top = Mig 55 `AddCcmNoteToPeWorkItemBudget`, ProInitial+ProAdjustment cols ABSENT (only ProEstimateAmount present), tables88.** Pre-poll bundle baseline (anti#3): admin `BYF5vIMJ`/css `X_45M1jX` + user `CB-tiRxd`/css `Bbbo0M5H` = still #315 live. Poll-loop iter6 status=success (created 11:03:00→success 11:07:58 ≈4m58s; iter1-5 running). CI gate (both proj pre-build ⟹ status=success ⟹ test **344** expected (45D+299I; +1 PeWorkItemBudgetTests vs #315's 339) passed; `conclusion` empty — `tasks` terminal=`status:success` doesn't populate conclusion, trust success; 344 INFERRED gate-passes-pre-build NOT log-confirmed numeric). **🔑★ MIG 56 VERIFIED APPLIED PROD (sqlcmd-over-SSH ground-truth, POST-deploy re-query): history-top advanced Mig55→Mig56 `20260619024427_AddProBudgetSplitToPeWorkItemBudget` == repo HEAD ✓ (DbInitializer auto-migrate-on-boot ran on app-pool recycle). BOTH cols EXIST sys.columns: `ProAdjustmentAmount` decimal len9 prec18 scale2 null=1 ✓ + `ProInitialAmount` decimal len9 prec18 scale2 null=1 ✓ — match Mig 56 spec. sys.tables=88 UNCHANGED — 2 AddColumn no new table ✓. ★ DATA-MIGRATE VERIFIED: 4 rows `ProEstimateAmount IS NOT NULL AND ProInitialAmount=ProEstimateAmount` (backfill ran on prod data) + 0 violation rows `estimate-set-but-initial-null` ✓ — UPDATE-Sql executed on real prod data per gotcha #64.** **★ BUNDLE BOTH ROTATE (FE 2-app PeDetailTabs+PeWorkflowPanel + admin ApprovalWorkflowsV2Page ⟹ both EXPECTED per gotcha #69; verified AFTER status=success + STABLE 2nd-fetch no-transient per anti#3): admin JS ROTATE `BYF5vIMJ→BhFDF9IJ` + css `X_45M1jX→DuqjXB6Y`** ✓ (css rotated too — admin had real style change ApprovalWorkflowsV2Page) **+ user JS ROTATE `CB-tiRxd→BAkuRl3C` + css `Bbbo0M5H→JQfATaQB`** ✓. Asset reachable 200 + LARGE: admin js 1,603,616b + user js 1,507,842b (control fake `/assets/index-ZZfakehash0.js`→200 size 919b/895b SPA-fallback ⟹ real JS shipped, gotcha #69 SPA-200-trap distinguished by size). index.html `Last-Modified` admin 04:06:36Z + user 04:07:28Z (=11:06-11:07VN deploy window) ✓. Smoke **4×200** health: api `/health/ready`+`/health/live` + admin root + eoffice root. 0 regression. NO prod-data mutation (read-only curls + sqlcmd SELECT-only; mig-apply+backfill-UPDATE+FE-copy = expected boot/deploy side-effects). Behavioral PRO budget-matrix 3-col + role-badge = anh Kiệt UAT (em confirm Mig56+2cols+backfill+bundles shipped per spec). **VERDICT PASS: green CI + test 344 + Mig 56 applied (ProInitial+ProAdjustment decimal(18,2) sys.columns ground-truth, history advanced 55→56) + tables 88 + backfill 4-rows-0-violation + bundle BOTH rotate (css too) + health 4×200. LESSON: full-stack-2-AddColumn-WITH-DATA-MIGRATE = S74-bis 4-axis + 5th-axis BACKFILL-VERIFY (count rows where new-col=source-col AND 0 violation) — gotcha #64 prod-data-UPDATE-runs-first-time confirmed real. PRE-deploy snapshot at spawn (Mig55+cols-absent) → POST-deploy (Mig56+2cols+4-backfill) = unambiguous proof, best-practice capture-baseline-before-poll when run still building. admin css ALSO rotated (real ApprovalWorkflowsV2Page style) — distinguishes from S74-bis css-frozen (tab-logic-only). Mig-applied proof = `__EFMigrationsHistory` top==repo-HEAD; if stuck Mig 55 ⟹ app pool didn't recycle ⟹ FAIL even if status=success+bundle-rotated. TOOLING: bash POSIX → write PS to `$LOCALAPPDATA/Temp/x.ps1` + `powershell.exe -File $(cygpath -w ...)`; Gitea `tasks` via Invoke-RestMethod `-Body @{limit=N}` hashtable; poll-loop backgrounded + grep-until FINAL; SSH→PS base64 `-EncodedCommand` UTF-16LE for appsettings-read AND sqlcmd; sqlcmd pw inline + `-W -h -1`. NEVER fixed code (READ-only).** Tag `[s76, run318, pass, full-stack-pe-budget-matrix-3col, mig56-AddProBudgetSplitToPeWorkItemBudget-VERIFIED-APPLIED-PROD, 2-cols-sys-columns-ProInitialAmount-ProAdjustmentAmount-decimal18-2-prec18-scale2-null, history-top-advance-55-to-56, tables88-unchanged-addcolumn-only, DATA-MIGRATE-backfill-4rows-ProInitial-eq-ProEstimate-0-violation-gotcha64-prod-data-update-first-time, bundle-BOTH-rotate-BhFDF9IJ-BAkuRl3C, css-BOTH-rotate-DuqjXB6Y-JQfATaQB-admin-ApprovalWorkflowsV2Page-real-style, asset-200-large-1.6MB-vs-fake-919b-spa-trap, last-modified-deploy-window-04-06-04-07z, health-4x200, test344-inferred-plus1-PeWorkItemBudgetTests, deploy17of17, pre-deploy-db-snapshot-mig55-then-post-mig56-unambiguous, prompt-said-316-but-actual-318-matched-by-head-sha-316-cancelled, anon-gitea-api-both-token-absent, behavioral-anh-kiet-uat, 5th-axis-backfill-verify-new]`.
- _(S74-bis Run #315 sha=`8655ebf` PASS ~4m54s [FULL-STACK Mig 55 `AddCcmNoteToPeWorkItemBudget` CcmNote nvarchar(1000) VERIFIED-APPLIED-PROD sys.columns maxlen2000-bytelen, history-advance-54→55, tables88-addcolumn-only, bundle-BOTH-rotate Bv3jUCNo→BYF5vIMJ/BWlMBQz6→CB-tiRxd css-frozen, asset-200-large-vs-fake-919b, health-4x200, test339, pre-deploy-snapshot-mig54-unambiguous, prompt-said-314-actual-315-head-sha] → 4-axis full-stack (BE+FE-both-rotate · history-advance+col-type-verify · tables88 · health-4x200); FIFO-trimmed, full verbatim git `8655ebf`)_
- **2026-06-19 S77 Run #319 (run_number 319, id=433) sha=`21d1f4e` PASS ~4m46s (FE-ONLY: PE ngân-sách Block A → bảng lưới `<table border-collapse>` viền ô giống Excel [cột Khoản mục|Dự án|PRO|CCM], anh Kiệt FDC phản-hồi. 2 files ONLY: `fe-admin/src/components/pe/PeDetailTabs.tsx` + `fe-user/.../PeDetailTabs.tsx` — identical 287-line change each app. deploy 18/18 session after #297#318 all PASS):** Push HEAD=`21d1f4e` (nothing unpushed). `git diff --name-only HEAD~1 HEAD -- '*Persistence/Migrations*'` = EMPTY + `-- 'src/Backend/**/*.cs'` = EMPTY ⟹ confirmed FE-only NO-migration NO-BE. `.tsx` non-ignored → full pipeline RAN (path-filter gates whole-workflow not per-step, gotcha #69). GITEA_TOKEN absent env → anon Gitea API (worked, public repo). Real-FE-change detection per gotcha #69 = `git diff fe-*/src` POSITIVE here (genuine `<table>`/`<thead>`/`<tr>` grid replacing prior layout — NOT just hash-delta noise). Run IN-PROGRESS at spawn (running, exact head_sha match #319; baseline #318=`e33481e` confirmed in tasks-list). **Pre-poll bundle baseline captured BEFORE poll-loop (anti#3): admin `BhFDF9IJ`/css `DuqjXB6Y` + user `BAkuRl3C`/css `JQfATaQB` = still #318 live (matches prompt-supplied prev exactly).** Poll-loop iter5 status=success (created 11:28:28→success 11:33:14 ≈4m46s; iter1-4 running). CI gate (both test proj pre-build ⟹ status=success ⟹ test **344** [45D+299I, unchanged from #318 — FE-only no test delta] passed; `conclusion` empty — `tasks` terminal=`status:success` doesn't populate conclusion, trust success; 344 INFERRED gate-passes-pre-build NOT log-confirmed numeric). **★ BUNDLE BOTH JS ROTATE (FE 2-app PeDetailTabs change ⟹ both EXPECTED per gotcha #69; verified AFTER status=success + STABLE 2nd-fetch no-transient per anti#3): admin JS ROTATE `BhFDF9IJ→jOqxW4-p`** ✓ (css ALSO rotate `DuqjXB6Y→DyECY611` — Block-A table has real style) **+ user JS ROTATE `BAkuRl3C→DbsznVvR`** ✓ (css `JQfATaQB` UNCHANGED — user-app style untouched; asymmetric css fine per gotcha #69). Asset reachable 200 + LARGE: admin js 1,604,226b + user js 1,508,452b (control fake `/assets/index-ZZfakehash0.js`→200 size 919b/895b SPA-fallback ⟹ real JS shipped, gotcha #69 SPA-200-trap distinguished by SIZE not status). index.html `Last-Modified` admin 04:31:53Z + user 04:32:45Z (=11:31-11:32VN deploy window) ✓. Smoke **4×200**: api `/health/ready`+`/health/live` + admin root + eoffice root. 0 regression. NO migration/DB check (FE-only, per spec). NO prod-data mutation (read-only curls only; FE-copy = expected deploy side-effect). **VERDICT PASS: green CI + test 344 + bundle BOTH JS rotate (real-FE-source-diff confirmed) + real-JS-size 1.6MB/1.5MB vs fake-919b + health 4×200. LESSON: textbook FE-ONLY = 2-axis (BE-skip · BOTH-JS-rotate + real-size-vs-SPA-trap + Last-Modified-deploy-window). Real-FE-ship proof per gotcha #69 = `git diff fe-*/src` non-empty (here genuine `<table>` grid) — NOT hash-delta alone (hash rotates every deploy regardless). admin css ALSO rotated (Block-A real style) but user css frozen (user-app PeDetailTabs Block-A changed too BUT user css chunk unaffected) — asymmetric css normal, don't FAIL. Pre-poll baseline capture BEFORE running-run completes = best practice (matched prompt prev #318 exactly = clean diff). TOOLING: bash POSIX → write PS to `/tmp/x.ps1` + `powershell.exe -File $(cygpath -w ...)`; Gitea `tasks` via Invoke-RestMethod `-Body @{limit=N}` hashtable; poll-loop backgrounded + grep-until FINAL/POLL-DONE. NEVER fixed code (READ-only).** Tag `[s77, run319, pass, FE-ONLY-2file-PeDetailTabs, pe-budget-blockA-table-grid-excel, NO-migration-NO-BE-confirmed-empty-diff, bundle-BOTH-JS-rotate-jOqxW4-p-DbsznVvR, admin-css-ALSO-rotate-DyECY611-blockA-real-style, user-css-frozen-JQfATaQB-asymmetric-ok-gotcha69, real-JS-size-1.6MB-1.5MB-vs-fake-919b-spa-200-trap-by-size, last-modified-deploy-window-04-31-04-32z, health-4x200, test344-inferred-unchanged-FE-only-no-delta, deploy18of18, baseline-318-e33481e-matches-prompt-prev, real-FE-ship-proof-git-diff-fe-src-not-hash-delta-gotcha69, anon-gitea-api-token-absent, pre-poll-baseline-before-running-run-completes, no-DB-check-FE-only-per-spec]`.
- _(S74 Run #314 sha=`6aa4dcb` PASS ~5m12s [FE-ONLY empty-candidates guard PeWorkflowPanel ×2-app, no-mig-stays-54, bundle-BOTH-js-rotate OlNyG9OD→Bv3jUCNo/DSzSLVtL→BWlMBQz6, css-frozen, asset-200-large-vs-fake-919b, health-4x200, test334, no-DB-query-needed-FE-only] → LESSON: FE-only verify lightweight, skip Mig/sqlcmd when 0 migration in diff; FIFO-trimmed, full verbatim git `6aa4dcb`)_
- _(S69b Run #307 sha=`1f8947e` PASS ~4m33s [BE-ONLY GOLIVE security "Văn phòng số" public Read+Create — NEW `SeedAllRolesOfficeModulePermissionsAsync` boot-after-revoke, allow-list 16 Office key upgrade-only; **GOLIVE-DB-VERIFIED-16of16-across-13-roles** sqlcmd prod Permissions (ALLOW-16 R∧C all roles · EXCLUDED-3 only-Admin leak0 · HRM/Personal Drafter still-0 · admin-not-downgraded), bundle-BOTH-frozen Wt54PHYl/B99fMU6X (BE-only no-rotate), no-mig-top-mig52, tables88, test292] → LESSON: BE-only golive-seed verify CORE-proof = prod Permissions DB query NOT bundle-frozen alone (seed=runtime-row-insert NOT EF-mig → no history/tables advance); FIFO-trimmed, full verbatim git `1f8947e`)_
- _(S77 #303 sha `6983609`, S76 #302, S75/S74 … pre-S78 verbatim → git `764fe70` + archive — FIFO trimmed to keep L1 under soft-cap)_
- _(S75 Run #301 sha=`6df1b2d` PASS ~2.5m [FE-both-app PE Link-hồ-sơ auto-detect render, bundle-BOTH-rotate I1fpLeYw/DrQYkzh0, no-mig-mig52, tables88, test286, fastest-streak] → FIFO-trimmed, full verbatim git + `archive/2026-06.md`)_
- _(S73 Run #313 sha=`1d86abc` PASS ~5m22s [FULL-STACK Mig 54 `AddPeSuggestedAndApprovedPrice` PE giá-đề-xuất PRO/CCM + CEO chọn giá-chốt + CCM duyệt-done; **5 cols VERIFIED-APPLIED-PROD sys.columns** ProSuggestedMin/Max+CcmSuggested+ApprovedPriceAmount decimal(18,2)+ApprovedPriceSource nvarchar all-null, history-advance-53→54, tables88-addcolumn-only, bundle-BOTH-rotate fc_xkNpJ→OlNyG9OD/DP-tBcg0→DSzSLVtL +css, asset-200-large-vs-fake-919b, **NEW-ENDPOINT-PROBE PUT /suggested-price/pro unauth→401-NOT-404 route-exists class-Authorize** +ccm→401 +list→401, health-4x200, test334, prompt-said-N-actual-313-head-sha] → 4-axis full-stack + endpoint-401-not-404-probe; FIFO-trimmed, full verbatim git `1d86abc`)_
- _(S74 Run #300 sha=`91aaf05` PASS [FE-both-app list-redesign flex-row, bundle-BOTH-rotate PxiZQkaw/B36hGoKd, user-unfroze-from-s71-streak, no-mig-mig52, tables88, test286, TOOLING: ps1-file-not-inline-dollar + ssh-encodedcommand-base64] → FIFO-trimmed, full verbatim git + `archive/2026-06.md`)_
- _(S67 Run #292 [dept-parentid Mig51-applied-after-#291-fail, retry-chain CS7036-fix, bundle-asymmetric, tree-endpoint-200] + S65 #289 [hrm-hoso public-readonly seed-only, asymmetric read200/write403] → FIFO-trimmed, full verbatim git `d2f52ba` + `archive/2026-06.md`)_
- **Older runs (S66 #290 ← S62 #286 06-13 ← … → S29 #232) full verbatim archived → git `d2f52ba` + `archive/2026-06.md`** (incl #291 06-16 FAIL forensic [lesson=gotcha #65 + #292-inline] + #383 ex-VITRILAC); pre-S38 → `archive/2026-05-{runs,q2,q3,q4}.md`.
---
## 🔄 Curate trigger
- >~30KB → archive recent runs → L2 `archive/<period>.md`. Dup failure patterns → merge. Stale >3mo → remove.
- **Last curate: 2026-06-17 S70 Harness-9 (em-main + Stage-B `wf_a58e0d15-beb`)** (65.2→23.2KB): L2 dark-matter recovery — 10 oldest run-records → `archive/2026-06.md` (ADDITIVE, original 58378B prefix byte-exact) + `archive/_INDEX.md` (mục-lục substring sha-keyed, Ctrl-F fallback, no line-hint) + `2026-0{5,6}.gist.md` (4-field, distill-gen:1). 0-byte-loss git `+13 -0` + sha (Stage C `wf_9520d8cd-4fe` + em-main self-gate, 2 reviewer no-return). Kept foundation + 2 newest full #308/#307 + stubs.
- **Prev curate: 2026-06-16 S66 em main** (86.8→29.2KB sed Run #286#232 incl #291 forensic) · S40 q4.
- **Prev curate: 2026-05-29 S40 em main proxy** (35.3→~21KB): archived Run #359/#243/#242/#241/#240 + S35/S36 startup → q4 + git d2f52ba; refreshed stale 120→130 test + Mig 34→40. Prev: S34 q3 · S32 q2 · S22 runs.