[CLAUDE] Docs: S87 closeout — PE multi-winner + budget=0 + cấp-KẾT-THÚC (Mig 58) + Section B 3-cột (Mig 59)
STATUS + HANDOFF S87 narrative + count flush (Migrations 57->59, test 377->402, gotcha 73 unchanged) + session log 2026-06-24-S87 + agent-memory harvest (cicd-monitor #344 / implementer-backend / investigator-codebase / reviewer / test-specialist — spawn-record on-disk). State THẬT: Mig 59 · 88 bảng (cicd flag 89 drift, re-ground monthly) · 402 test (45D+357I) · gotcha 73 · menu 54 · bundle admin CXCKlNay/user Di40ewrF (Run #344; #345 cicd-verifying Section B). Docs-only -> CI skip. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -32,7 +32,7 @@ Read-only CI/CD + post-deploy verifier SOLUTION_ERP. Polls Gitea Actions API, ve
|
||||
- **Stage 0 RAG infra:** `Get-Service Qdrant` Running + `http://localhost:6333/healthz`. Collection `proj_solution_erp`.
|
||||
- **Stage 1 Push+filter:** `git log -1 --format='%H %s'` + `git log origin/main..HEAD` empty + diff vs paths-ignore (docs-only → SKIPPED-DOCS return).
|
||||
- **Stage 2 Gitea poll** (max 10 iter × 60s): API `.../actions/tasks?limit=5` (NOT `/runs` 404). Match `head_sha` (NOT run_number — cancelled runs shift numbering). ⚠️ `updated_at` stale ~2min (gotcha #46) → cross-check VPS mtime. Foreground-sleep BLOCKED Windows → Monitor-style busy-wait-curl-spin ~30s/iter.
|
||||
- **Stage 3 Test gate:** baseline **354 PASS** (45 Domain + 309 Infra). CI runs both proj BEFORE build/deploy → status=success ⟹ test gate passed (`tasks` terminal=`status:success`, `conclusion` NOT populated — trust success NOT log-numeric). Local grep undercounts (Theory/InlineData). Phase 9 UAT skip OK.
|
||||
- **Stage 3 Test gate:** baseline **402 PASS** (45 Domain + 357 Infra; S88 #345 — 395 + 7 new `PeCcmBudgetPeriodSetterAuthzTests`; prior S87 #344 395, S86 377). CI runs both proj BEFORE build/deploy → status=success ⟹ test gate passed (`tasks` terminal=`status:success`, `conclusion` NOT populated — trust success NOT log-numeric). Local grep undercounts (Theory/InlineData). Phase 9 UAT skip OK.
|
||||
- **Stage 4 Post-deploy** (if SUCCESS): auth login bearer (admin + nv.test gotcha #44; token=`accessToken` route `/api/auth/login`) → 3-5 endpoint smoke 2XX (incl new — NEW-endpoint bodyless-POST returns **411 IIS Length-Required pre-auth NOT 404**; re-probe WITH body → 401 confirms wired) → FE bundle hash 2 app (per #69 = real-size-vs-fake not hash-delta) → SignalR negotiate (gotcha #25) → EF mig prod==repo.
|
||||
- **Stage 4.6 (S29 CRITICAL):** sqlcmd seed sample verify post-deploy (NOT chỉ schema). Seed=runtime-row-insert (no history/tables advance) → verify via DB-query (e.g. Permissions count), NOT bundle-frozen-alone.
|
||||
- Discovery: ASP.NET 10 record enum needs numeric input unless `JsonStringEnumConverter` (SOL has NO converter → FE sends numeric). sqlcmd ssh Windows-auth `\\\\SQLEXPRESS` 4-backslash. INFRASTRUCTURE seed MUST run (NOT inside `if(!demoSeedDisabled)`); DEMO seed gated → gotcha #51.
|
||||
@ -50,9 +50,9 @@ Read-only CI/CD + post-deploy verifier SOLUTION_ERP. Polls Gitea Actions API, ve
|
||||
- **Gitea:** `git.baocaogiaoduc.vn/vietreport-admin/solution-erp` · workflow `.gitea/workflows/deploy.yml` · paths-ignore `['docs/**','**/*.md','.claude/skills/**']`. Anon API works (public repo) when GITEA_TOKEN absent.
|
||||
- **Prod:** api/admin/eoffice `.solutions.com.vn` · SSH `ssh vietreport-vps` (Administrator, id_ed25519) · IIS phys paths: API `C:\inetpub\solution-erp\api` · admin `\fe-admin` · user `\fe-user`. DB `.\SQLEXPRESS`/`SolutionErp`/`vrapp` SQL-auth. **Conn key = `ConnectionStrings.Default` (NOT `DefaultConnection`)**. pw `vrapp/buKL3TGBkD0wDDbYVw65QeX9` read from prod `appsettings.Production.json` when `$env:PROD_DB_PASSWORD` empty. ⚠️ skill-doc path `C:\inetpub\apps\SolutionErp\Api` STALE.
|
||||
- **SSH→PS quoting (S42):** nested bash→ssh→powershell mangles `$var`/`\"` → use `iconv UTF-16LE | base64` → `powershell -EncodedCommand $B64`; OR write ps1-file + `powershell -File $(cygpath -w)`. sqlcmd over SSH direct `-W -h -1` pw-inline. sys-catalog string-concat → `COLLATE DATABASE_DEFAULT` (collation conflict).
|
||||
- **Tests baseline:** **354 PASS** (45 Domain + 309 Infra · 0 fail/skip; S77 Run #329 sha e823694). Phase 9 UAT skip per chunk OK.
|
||||
- **Mig latest repo:** **Mig 57 `20260619070051_AddPeSuggestedPriceNotes`** (S77 #323; PE +2 note-cols nvarchar(1000) — VERIFIED-APPLIED-PROD). Path `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/`. Prod check `sqlcmd __EFMigrationsHistory ORDER BY MigrationId DESC TOP 5`. ⚠️ **`sys.tables` (is_ms_shipped=0) = 88** (S61 Budget-replace DROPPED 93→88 — narrative-93 STALE; when commit touches no schema, 88 correct, don't FAIL on 88. Always cross-ref COMMIT scope vs ambient count). enum-additive (e.g. `ApprovalAttachment=5`) = int-stored NO-mig → expect history NOT-advance + empty `git diff -- '*Migrations*'`.
|
||||
- **Bundle hash live (S85 Run #341/#342):** admin js `aKuNip19`/css `BKB2NsgQ` · user js `BUsL0HeQ`/css `BaqeI4iG` (FE frozen since #339, BE/docs-only deploys after). ⚠️ **PER #69: hash MAY rotate per deploy OR stay frozen if 0 fe-src diff (byte-identical) — SNAPSHOT for THIS run, NOT a frozen baseline.** Real FE ship = diff `fe-*/src` in commit/range + real-size-vs-fake (1.6/1.5MB vs ~919b SPA-trap), NOT hash delta. ASYMMETRIC-deploy framing (S66) RETIRED by #69.
|
||||
- **Tests baseline:** **402 PASS** (45 Domain + 357 Infra · 0 fail/skip; S88 Run #345 sha 9b1ee0c — 395 + 7 CCM-budget-period setter authz; prior S87 #344 395). Phase 9 UAT skip per chunk OK.
|
||||
- **Mig latest repo:** **Mig 59 `20260624084608_AddCcmBudgetPeriodToPurchaseEvaluation`** (S88 #345; PE +1 AddColumn `CcmBudgetPeriodAmount` decimal(18,2) NULL no-backfill — VERIFIED-APPLIED-PROD history-HEAD + col-exists 0-nonnull). Prev Mig 58 `AddPeMultiWinnerAndLevelFinalize` (S87 #344). Path `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/`. Prod check `sqlcmd __EFMigrationsHistory ORDER BY MigrationId DESC TOP 5`. ⚠️ **`sys.tables` (is_ms_shipped=0) = 88** (S61 Budget-replace DROPPED 93→88 — narrative-93 STALE; when commit touches no schema, 88 correct, don't FAIL on 88. Always cross-ref COMMIT scope vs ambient count). enum-additive (e.g. `ApprovalAttachment=5`) = int-stored NO-mig → expect history NOT-advance + empty `git diff -- '*Migrations*'`.
|
||||
- **Bundle hash live (S88 Run #345):** admin js `C8lWxEQK`/css `BFfMO5l8` · user js `Kz5cSW2u`/css `B11h2RoV` (BOTH js+css ROTATED from #344 `CXCKlNay`/`BKB2NsgQ` + `Di40ewrF`/`BaqeI4iG` by real fe-src PE Section-B 3-col rewrite — css rotated too = new utility classes for table grid). ⚠️ **PER #69: hash MAY rotate per deploy OR stay frozen if 0 fe-src diff (byte-identical) — SNAPSHOT for THIS run, NOT a frozen baseline.** Real FE ship = diff `fe-*/src` in commit/range + real-size-vs-fake (1.6/1.5MB vs ~919b SPA-trap), NOT hash delta. ASYMMETRIC-deploy framing (S66) RETIRED by #69.
|
||||
- **Bearer:** admin `admin@solutions.com.vn/Admin@123456` (full) · UAT `nv.test@solutions.com.vn/TestUser@123456` (Drafter CCM, gotcha #44 check).
|
||||
|
||||
## 🔑 Critical config (flag commit nếu tái xuất)
|
||||
@ -70,6 +70,9 @@ BE (test+build) ~90s · FE × 2 ~60s/app · deploy ~30s · **total ~3min code /
|
||||
|
||||
## 📅 Recent runs (compressed — full verbatim → `archive/2026-06.md` via `archive/_INDEX.md`)
|
||||
|
||||
- **S88 #345 `9b1ee0c` PASS ~4m (FULL-STACK PE Section-B 3-col + Mig 59, anh Kiệt FDC/CCM)** — Section B (THỰC HIỆN) rewrite 1-col-list → 3-col table (per-col kỳ-này) + new setter endpoint `PATCH /purchase-evaluations/{id}/budget/ccm-period` (`SetPeCcmBudgetPeriodCommand`, fail-closed Admin|CostControl else Forbidden) + DTO field `ccmBudgetPeriodAmount`. Files 14: BE 4 (Controller/DTO/Domain/Config) + 2 App-features (PeSuggestedPriceFeatures + PurchaseEvaluationFeatures) + FE **both apps** 4 (PeDetailTabs + types ×2) + Mig 3-file + 1 new test. **Mig 59 `20260624084608_AddCcmBudgetPeriodToPurchaseEvaluation`** = single AddColumn `PurchaseEvaluations.CcmBudgetPeriodAmount` decimal(18,2) NULL **no backfill** — VERIFIED-APPLIED-PROD (history top==`AddCcmBudgetPeriod`==repo HEAD; sys.columns COL=1 type `decimal(18,2) null=1`; NONNULLrows=0 as designed). Test gate=success ⟹ passed (**baseline 402** = 45 Domain + 357 Infra = 395 + **7 `PeCcmBudgetPeriodSetterAuthzTests`**: Admin-set/CostControl-set/Drafter-forbidden/Procurement-forbidden/Zero-valid/Null-clears/Unknown-NotFound). **Bundle BOTH js+css ROTATE (real fe-src Section-B rewrite): admin js `CXCKlNay`→`C8lWxEQK` css `BKB2NsgQ`→`BFfMO5l8`, user js `Di40ewrF`→`Kz5cSW2u` css `BaqeI4iG`→`B11h2RoV`** (css rotated = new table-grid utility classes, NOT logic-only). Real-ship proven: admin js 1,627,505b Last-Modified 09:10:47Z=16:10+07 in-window (16:08→16:12) · user js 1,531,406b 09:11:40Z in-window · SPA-trap control `ZZfake0.js`→200 but 919b text/html (size-check meaningful). Endpoint live: `PATCH .../budget/ccm-period` unauth→**401** bodyless AND with-json-body (wired: NOT 404=route-exists, NOT 500=no-crash; PATCH auth short-circuits before IIS Length-check so 401 not 411). Health api ready/live + admin + eoffice 4×200. GET /api/purchase-evaluations + /api/menus both 401. ⚠️ ambient sys.tables=88 (NOT 89 as run-spec hedged — sqlcmd direct COUNT WHERE is_ms_shipped=0 AND name<>'__EFMigrationsHistory' = 88; Mig 59 AddColumn-only 0-new-table, consistent w/ S62 re-ground baseline). conn key `ConnectionStrings.Default`, prod path `C:\inetpub\solution-erp\api`. gotcha #69 inverse re-confirmed (real fe-src ⟹ hash rotates).
|
||||
- **S87 #344 (id=458) `f1b10e9` PASS ~5m11s (FULL-STACK PE CCM 3-fix + Mig 58, anh Kiệt FDC)** — multi-winner SUM (SelectWinner→SupplierIds[] array body) + budget=0 valid-submit + per-level auto-finalize (`ApproveV2Async if matchingLevel.AllowApproverFinalize`). Files: BE 13 (Controller/App/Domain/Infra/Service + Mig 3-file) + FE **BOTH apps** 7 (PeDetailTabs/PeWorkflowPanel/types ×2 + fe-admin ApprovalWorkflowsV2Page Designer) + 6 test. **Mig 58 `20260624071509_AddPeMultiWinnerAndLevelFinalize`** = 2 AddColumn bit (`PurchaseEvaluationSuppliers.IsWinner` default0 + `ApprovalWorkflowLevels.AllowApproverFinalize` default0) + 1 backfill `UPDATE ... SET IsWinner=1 WHERE pes.SupplierId=pe.SelectedSupplierId` — VERIFIED-APPLIED-PROD (history top==`AddPeMultiWinnerAndLevelFinalize`==repo HEAD; both cols sys.columns=1; **backfill ran: winners=13 == pe_with_SelectedSupplierId=13 / 30 total supplier rows** — exactly 1 winner per pre-existing selected PE). Test gate=success ⟹ passed (**baseline 395** = 45 Domain + 350 Infra per spec; was 397 −2 because per-level-finalize moved runtime-flag→auto-config, NOT regression). **Bundle BOTH ROTATE (real fe-src ⟹ new content-hash, contrasts #69-REFINE BE-only): admin js `aKuNip19`→`CXCKlNay` (css FROZEN `BKB2NsgQ`, logic-only diff no new chunk), user js `BUsL0HeQ`→`Di40ewrF` (css FROZEN `BaqeI4iG`).** Real-ship proven: admin js 1,621,660b Last-Modified 08:26:32Z=15:26+07 in-window · user js 1,525,569b 08:27:26Z in-window · control fake-hash `ZZfakehash0.js`→200 but 919b text/html (SPA-trap confirms 200-alone meaningless). Endpoint live: `POST /api/purchase-evaluations/{id}/select-winner` bodyless→411 (IIS Length-Required pre-auth), +json-body→401 (wired, NOT 404/400/500; `SelectWinnerBody(IReadOnlyList<Guid>? SupplierIds)`→`SelectPurchaseEvaluationWinnerCommand` array contract live). Health api ready/live + admin + eoffice 4×200. ⚠️ **ambient sys.tables=89 (was 88) — NOT from Mig 58** (AddColumn-only, 0 new table; newest table `PeWorkItemBudgets` 06-13 Mig 50, none created 06-24); 88 baseline last re-grounded S62, +1 pre-existing drift in Mig 50-57 range, harmless. ⚠️ prod path `C:\inetpub\solution-erp\api` (skill-doc `\apps\SolutionErp\Api` STALE — re-confirmed); conn key `ConnectionStrings.Default`; base64 EncodedCommand + 1 path-retry. gotcha #69-REFINE inverse confirmed (real fe-src ⟹ hash DOES rotate).
|
||||
- **S86 #343 (id=457) `ae4f106` PASS ~5m00s (BE-ONLY notify-filter)** — PE "cần duyệt" notify EXCLUDE drafter + 1 new test (`PeApproverNotifyExcludesDrafterTests`) + STATUS + agent-memory. Only-BE-src: 1 file `PurchaseEvaluationWorkflowService.cs`, ZERO `fe-*/src`, ZERO `*Migrations*`. status=success ⟹ test gate passed (**baseline 377** = 45 Domain + 332 Infra per spec; S85 374 +3; reviewer's 2 transient parallel-collection flake did NOT surface red in CI — gate green). **Ship-proof #69-REFINE (BE-only) CONFIRMED via dll Last-Modified in run-window (16:37:38→16:42:35+07 = 09:37:38→09:42:35Z):** Api.dll 09:40:04Z · **Application.dll 09:38:12Z · Infrastructure.dll 09:38:34Z** (PurchaseEvaluationWorkflowService lives in Infrastructure → recompiled fresh) — all 3 in-window. **FE bundle FROZEN (correct, 0 fe-src diff ⟹ byte-identical): admin `aKuNip19`/`BKB2NsgQ`, user `BUsL0HeQ`/`BaqeI4iG` = S85 baseline.** Mig FROZEN 57 (`AddPeSuggestedPriceNotes`, prod head==repo) + tables 88. Prod 4×200 (api ready/live, admin, eoffice) + PE wired (GET /api/purchase-evaluations + /api/menus both 401 unauth, NOT 404/500). Notify-filter BE-internal no-new-endpoint → test 377 covers behavior (no curl-probe of notify logic). gotcha #69-REFINE reaffirmed (BE-only: dll Last-Modified = sole ship-proof, hash-delta N/A). ⚠️ SSH transient drop ×3 mid-session (`Connection closed by 103.124.94.38`) — base64 EncodedCommand + retry-loop recovered; not a deploy issue.
|
||||
- **S85 #342 `d4f6021` PASS ~5m00s (DOCS+SQL NO-OP, INVERTED criteria)** — 6×.md + `scripts/s85-wipe-uat-data.sql` (only non-ignored path → CI triggers; whole-workflow still builds FE+publish). **PASS-when-FROZEN, NOT rotate.** Verified: status=success ⟹ test gate passed (baseline **374** = 45 Domain + 329 Infra per STATUS.md canonical, logs API 404 anon so count not log-scraped — trusted gate+baseline) + **bundle FROZEN admin `aKuNip19`/user `BUsL0HeQ` (correct: 0 fe-src diff ⟹ byte-identical ⟹ same hash, gotcha #69-REFINE)** but DID ship (admin js Last-Modified 08:56:34Z=15:56+07 IN-window 15:53→15:58, real 1.62MB/user 1.52MB vs SPA-trap 919b, 2nd-fetch byte-stable). Prod 4×200 (ready/live/admin/eoffice) + endpoint smoke 4×200 (PE/awf-v2/menus/projects). **Prior S85 deploys still live: R6 TZ fix `49ac086` → `createdAt:"…7331140Z"` trailing-Z CONFIRMED; R5 UpdateDraft `1263c0d` → both ancestors of d4f6021, shipped via #341 (#340 cancelled but #341 success built later commit incl TZ).** SignalR negotiate 411 (bodyless-POST IIS, wired, notif untouched). gotcha #73 (TZ) + #69-REFINE reaffirmed.
|
||||
- **S83 #333 `fc1f19d` PASS ~4m59s** — BE-only PE-guard (require ApprovalWorkflowId V2 at create-validator + submit-branch ConflictException; close FE-only-validation gap → phiếu reached "Đã gửi duyệt" w/ null workflow = V1-legacy-banner trap). +2 validator tests → **test 356** (gate=success ⟹ pass). NO-mig (frozen 57, prod head `AddPeSuggestedPriceNotes`==repo, no-pending) + ZERO fe-*/src diff. **Bundle hash UNCHANGED (admin `CsJetgZH`/user `BVS0ApIm` = #330) BUT shipped — Last-Modified 17:10:35/17:11:27 in-window** (real js 1.62/1.52MB vs SPA-trap 919b) → **gotcha #69-REFINE: 0-FE-src ⟹ byte-identical ⟹ same-hash; Last-Modified is true ship-proof.** Guard BE-only no-endpoint → no curl-probe (356 tests cover RED→GREEN). Health api/ready+live 200, admin/eoffice 200, api-root 404 (no `/` route, expected).
|
||||
- **S78 #330 `7886fd0` PASS ~4m56s** — cross-stack NO-MIG enum `ApprovalAttachment=5` (attach-on-approve) + FE 2-app PeWorkflowPanel modal-upload. Verified: empty-migrations-diff + Mig-frozen-57 + tables88 + bundle BOTH rotate js+css (real 1.6/1.5MB vs fake-919b) + endpoint bodyless-POST-411→re-probe-body-401-wired + health 4×200. → _INDEX.
|
||||
|
||||
Reference in New Issue
Block a user