23 KiB
CICD-K7: PARTIAL — K7 ĐÃ LIVE & verify sạch (gate 639=45+594 · Mig GIỮ 71 · bundle rotate ×2 marker 8/8 · seeder 0 added + 2 upgraded + COUNT 1→3 · endpoint mới 400-không-403). 2 lệch: run aaed699 bị CANCELLED (ship qua #442, đề-xuất gotcha #86) + restart×2 không quan-sát-được trong cửa-sổ.
sub-cicd-verify-k7 — deploy verify K7 (bridge KHKK→HĐ), S167 2026-08-01
Artifact chống-#53: ghi TỪNG MỤC ngay khi có bằng chứng. Return chỉ là tóm tắt. Baseline PRE đọc từ
sub-cicd-verify-k6.md(post-#440): admin jsIg1R5tAh· eoffice jsDFOCQ2ks· gate 622=45+577 · Mig 71 · sys.tables 97.
M0 — Push + path-filter (ĐÃ ĐO)
- HEAD =
aaed699228f418fa31ecba07414709de465502fcsubject:[CLAUDE] Contract: K7 SP-002 bridge KHKK->HD hoi-2-khoa + Line.ContractId + grant seeder (gate PASS-WITH-FLAGS 10, F-8 fixed)committed: 2026-08-01 13:04:49 +0700 git log origin/main..HEAD= RỖNG ⇒ đã push thật.- Range
1c6d77a..aaed699= 7 commit (6 ×wal: flush+ 1 code). - File code trong range (13/13 khớp đề bài):
- BE (4):
Api/Controllers/ContractSigningPlansController.cs·Application/ContractSigningPlans/ContractSigningPlanFeatures.cs·Application/ContractSigningPlans/CreateContractFromSigningPlanFeatures.cs·Infrastructure/Persistence/DbInitializer.cs - FE mirror ×2 app (6):
fe-{admin,user}/src/hooks/usePipelineStages.ts·.../pages/khkk/KhkkDetailPage.tsx·.../types/khkk.ts - Test (3):
Api/AuthorizePolicyRegressionTests.cs·Api/KhkkBridgeAuthzTests.cs·Application/CreateContractFromSigningPlanTests.cs - (còn lại 14 file đều
.claude/**= paths-ignore, KHÔNG trigger)
- BE (4):
- M3-phần-1 (git):
git diff --name-only 1c6d77a..aaed699 -- '*Migrations*'= 0 file ⇒ 0-mig chứng-minh-được từ git. - Path-filter: có file
src/**+fe-*/**⇒ CI PHẢI chạy (không skip).
M1 — Run Gitea 🔴 PHÁT HIỆN LỚN: run của aaed699 bị CANCELLED, KHÔNG success
Đo lúc 13:09-13:10 qua actions/tasks?limit=3 (khớp theo head_sha, KHÔNG theo run_number):
| run | head_sha | status | started | updated | ghi chú |
|---|---|---|---|---|---|
| #441 (id 554) | aaed699 |
🔴 cancelled | 13:04:57 | 13:07:59 | sống ~3m02s rồi bị huỷ giữa chừng |
| #442 (id 555) | 628132c |
running → (đo tiếp) | 13:08:26 | … | run-2 lead báo |
| #440 (id 553) | 1c6d77a |
success | 11:54:38 | 12:01:07 | mốc PRE (K6) |
Cơ chế: push 628132c lúc 13:08:26 ⇒ Gitea cancel-in-progress giết run #441 đang chạy.
⇒ Đề bài mục 1 ("run aaed699 success") KHÔNG THOẢ THEO NGHĨA ĐEN — aaed699 chưa bao giờ tự deploy xong.
Nhưng K7 VẪN ship, chứng minh bằng ancestry (không phải suy đoán):
git merge-base --is-ancestor aaed699 628132c= YES ⇒ cây của run #442 CHỨA trọn 13 file K7.git diff --name-only aaed699..628132c= 6 file (docs/STATUS.md·fe-{admin,user}/…/KhkkWorkflowPanel.tsx· 3scripts/*.ps1) — không đụng file K7 nào. ⇒ run #442 = phương-tiện ship DUY NHẤT của K7. Mọi phép đo POST vì thế quy về #442.
🔴 GOTCHA MỚI đề-xuất (#86) — "push-đè nuốt deploy im lặng"
Push code A rồi push B ngay sau (<run-time của A) ⇒ run A cancelled. Nếu B rơi trọn vào paths-ignore
(docs/memory-only) thì run B skip ⇒ code A KHÔNG BAO GIỜ deploy, mà bảng Actions trông vẫn "lành"
(1 cancelled + 0 run) và git log thì đã push. Lần này thoát NHỜ MAY: 628132c có chạm fe-*/src/**
nên mới đẻ run. Luật rút ra: sau 2 push liên tiếp, PHẢI xác minh run cuối cùng có head_sha là TIP và
tip ⊇ commit code, chứ không được đọc "có run xanh" là xong.
Hệ quả cho mục 5 (restart-proof): giả định "run-1 + run-2 = restart ×2 miễn phí" của lead KHÔNG còn đúng — run #441 bị giết TRƯỚC bước deploy nên không hề restart pool. Trong cửa sổ này chỉ có 1 lần restart thật (từ #442). Idempotent-qua-2-restart do đó phải hạ cấp thành "chưa chứng-minh-được trong cửa-sổ" (xem M5) — tao KHÔNG tự restart pool prod (READ-only, cấm #6).
M4-PRE — mốc bundle + tập marker (ĐO TRƯỚC khi deploy đè, 13:06-13:07)
Bundle PRE (live, khớp đúng baseline K6 post-#440):
| app | js | css | Last-Modified (GMT) | size js |
|---|---|---|---|---|
| admin | index-Ig1R5tAh.js |
index-DqXm5Hpn.css |
Sat 01 Aug 2026 04:59:40 (=11:59:40 +07) | 1.758.853 B |
| eoffice | index-DFOCQ2ks.js |
index-DlN3yZ5L.css |
Sat 01 Aug 2026 05:00:41 (=12:00:41 +07) | 1.676.752 B |
Kiểm-tra-tiền-đề cho marker (bài K6 nâng cấp — kiểm ĐƠN-VỊ-MÃ-HOÁ + ĐỊA-CHỈ trước khi tin 0-hit):
filebáo bundle "ASCII text" ⇒ nghi non-ASCII bị\u-escape. Bác bỏ bằng đo:grep -c -F 'Hợp đồng'= 4,'Kế hoạch'= 3 trên bundle PRE ⇒ tiếng Việt nằm thô (UTF-8),grep -Fđọc được. ✅- Nghi code-split (nếu
KhkkDetailPagenằm chunk lazy riêng thì marker 0-hit là vô nghĩa): index.html chỉ trỏ đúng 2 asset (index-*.js+index-*.css), và bundle 0 ref dạng"./xxx-<hash>.js"⇒ single-bundle, không lazy-chunk. ✅ - CONTROL DƯƠNG (chứng
KhkkDetailPageTHẬT nằm trongindex-*.js+ grep có răng) — 4 chuỗi bản tiền-K7 của chính file đó, đo trên cả 2 bundle PRE:Phiếu nguồn chưa có NCC trúng thầu.=1/1 ·Chưa khai căn cứ nào.=1/1 ·Giá tham chiếu (phiếu NCC)=1/1 ·Tên TVGS xử lý=1/1 ⇒ 4/4 hiện diện. ✅
CONTROL ÂM / tập marker K7 — 0-hit PRE trên CẢ 2 bundle (8/8, grep -c -F):
| marker (chuỗi UI nút bridge) | adm PRE | eof PRE |
|---|---|---|
Đưa dòng kế hoạch vào Hợp đồng |
0 | 0 |
Chọn dòng đưa vào hợp đồng |
0 | 0 |
Loại hợp đồng |
0 | 0 |
Quy trình duyệt hợp đồng |
0 | 0 |
Phiếu chưa có dòng nhà cung cấp nào. |
0 | 0 |
khkk-bridge-line |
0 | 0 |
Tạo hợp đồng |
0 | 0 |
Chưa có giá chốt |
0 | 0 |
⇒ marker có tín hiệu thật (0→≥1 mới là bằng chứng ship). Diff FE đối xứng: fe-admin và fe-user
mỗi bên +308 dòng KhkkDetailPage.tsx, +61 usePipelineStages.ts, +49 types/khkk.ts (796 insert/40 delete tổng)
⇒ kỳ vọng element-wise 8/8 ở CẢ 2 app.
M3 — Migration (phần git, ĐÃ CHỐT) + prod PRE
- git:
1c6d77a..aaed6990 file dưới*Migrations*⇒ 0-mig. (Range mở rộng tới628132ccũng 0 — 6 file kia không có Migrations.) - prod PRE (sqlcmd 13:06):
MIG_TOP=20260731085624_AddKhkkApprovalGroupCatalogAndFinalizeRuntime= Mig 71 ✅ ·sys.tables(is_ms_shipped=0)= 97 ✅ ·MenuItems=200 ·Permissions=2221 (khớp anchor #438/#440).
M5-PRE — trạng thái quyền Contracts TRƯỚC deploy (mấu chốt K7)
SELECT COUNT(*) FROM Permissions WHERE MenuKey='Contracts' AND CanCreate=1 → PRE = 1 ✅ (đúng dự-đăng-ký của gate-K7)
SELECT COUNT(*) … WHERE MenuKey='Contracts' (mọi cờ) → PRE = 13 row (13 role, mỗi role 1 row)
Bảng đầy đủ 13 role PRE — CanRead=1 toàn bộ, CanCreate=1 duy nhất Admin:
| role | create | read | role | create | read | |
|---|---|---|---|---|---|---|
| Accounting | 0 | 1 | Equipment | 0 | 1 | |
| Admin | 1 | 1 | Finance | 0 | 1 | |
| AuthorizedSigner | 0 | 1 | HrAdmin | 0 | 1 | |
| CatalogManager | 0 | 1 | Procurement | 0 | 1 | |
| CostControl | 0 | 1 | ProjectManager | 0 | 1 | |
| DeptManager | 0 | 1 | ||||
| Director | 0 | 1 | ||||
| Drafter | 0 | 1 |
⇒ Drafter + Procurement ĐÃ CÓ row sẵn (read=1, create=0) ⇒ seeder phải UPGRADE 2 row, KHÔNG add row nào
⇒ acceptance 0 added + 2 upgraded nhất-quán với trạng thái prod đo được, và kỳ vọng POST:
CanCreate=1 → 3 đồng thời tổng row Contracts GIỮ NGUYÊN 13 (nếu thành 15 ⇒ seeder add trùng ⇒ FAIL).
Đọc code seeder xác nhận khuôn UPGRADE-ONLY: SeedContractCreateAccessAsync gọi sau revoker, chỉ nâng cờ, không hạ.
M1-bis — run #442 (628132c) = phương-tiện ship K7: SUCCESS
- start 13:08:26 → settle 13:14:51 = 6m25s (nằm dải bình thường 5m3x-6m3x, không escalate).
- status
success(fieldconclusion= None như thường lệ vớitasks— tinstatus, đã biết từ trước). - URL:
https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp/actions/runs/442
M2 — Test gate: 639 = 45 Domain + 594 Infra ✅ KHỚP TUYỆT ĐỐI
Đo LIVE ở web-UI log (/actions/runs/442/jobs/0/logs, 18.695 B) — tách phần, không chỉ khớp tổng:
Passed! - Failed: 0, Passed: 45, Skipped: 0, Total: 45, Duration: 169 ms - SolutionErp.Domain.Tests.dll (net10.0)
Passed! - Failed: 0, Passed: 594, Skipped: 0, Total: 594, Duration: 1 m 47 s - SolutionErp.Infrastructure.Tests.dll (net10.0)
| PRE (#440) | POST (#442) | Δ | |
|---|---|---|---|
| Domain | 45 | 45 | 0 |
| Infrastructure | 577 | 594 | +17 |
| tổng | 622 | 639 | +17 |
| Failed / Skipped | 0 / 0 | 0 / 0 | — |
Δ+17 phân rã khớp TỪNG ĐỒNG với nguồn (đếm attr trong repo — an toàn vì 0 [Theory]/[InlineData], hết rủi ro undercount):
CreateContractFromSigningPlanTests.cs= 12[Fact]KhkkBridgeAuthzTests.cs= 5[Fact]AuthorizePolicyRegressionTests.cs(file sửa, không thêm test) = 0 attr test mới trong diff- ⇒ 12 + 5 + 0 = 17 = đúng Δ Infra đo được. ✅
Flaky đã khai: DeleteContractSigningPlan_SoftDeletes_KeepsChangelog_ThenCreateAgainSucceeds
(ContractSigningPlanCrudTests.cs:482) — gate báo Skipped 0 / Failed 0 ⇒ XANH lần thứ 4 liên tiếp
(#439, #440, local, #442). Chưa có cớ hô hồi-quy.
M3 — Migration GIỮ 71 + sys.tables 97 ✅ (2 nguồn ⟂)
| nguồn | kết quả |
|---|---|
git 1c6d77a..aaed699 -- '*Migrations*' |
0 file ⇒ 0-mig |
git mở rộng tới 628132c |
0 file (6 file của run-2 không có Migrations) |
sqlcmd __EFMigrationsHistory TOP |
20260731085624_AddKhkkApprovalGroupCatalogAndFinalizeRuntime = Mig 71 (PRE == POST) |
sqlcmd COUNT(*) __EFMigrationsHistory |
71 (khớp tên-đỉnh — 2 cách đọc độc lập cùng chốt) |
sqlcmd sys.tables (is_ms_shipped=0) |
97 (PRE == POST) |
⇒ NO-Mig proof đủ 2 chân: history KHÔNG tiến + range git rỗng file Migrations.
Anchor phụ giữ nguyên: MenuItems 200 · Permissions 2221 · control revoker Hrm*/Off* CanRead=1 = 29.
M4 — Bundle rotate ×2 app ✅ + marker 8/8 element-wise ✅
Hash + LM + size (curl LIVE sau status=success):
| app | PRE js | POST js | LM POST (GMT / +07) | size PRE→POST | Δ |
|---|---|---|---|---|---|
| admin | Ig1R5tAh |
RgvHZkPH |
06:13:22 / 13:13:22 | 1.758.853 → 1.765.720 | +6.867 B |
| eoffice | DFOCQ2ks |
D65FJPGd |
06:14:22 / 13:14:22 | 1.676.752 → 1.683.623 | +6.871 B |
- LM cả 2 nằm trong cửa-sổ deploy (13:08:26 → 13:14:51) ⇒ ship THẬT, không phải FROZEN-kẹt.
- Δ size 2 app gần bằng nhau (+6.867 / +6.871) — nhất quán với diff FE mirror đối xứng (+308/+61/+49 dòng mỗi app). Nhân chứng ⟂ grep.
- CSS giữ nguyên cả 2 (
DqXm5Hpn/DlN3yZ5L) — hợp lý: class mới đều là utility Tailwind đã tồn tại, không sinh CSS mới. Không phải cờ đỏ. - NHÂN CHỨNG THỨ 4 — log CI in thẳng hash+size, khớp curl tới từng byte:
dist/assets/index-RgvHZkPH.js 1,765.72 kB↔ curlContent-Length: 1765720✅dist/assets/index-D65FJPGd.js 1,683.62 kB↔ curlContent-Length: 1683623✅ - Chống SPA-fallback trap (#S72):
Content-Type: application/javascriptcho cả 2 hash mới; hash BỊAindex-ZZZZfake9.js→text/html900 B ✅ discriminator còn răng; hash CŨIg1R5tAh→text/html900 B ⇒ bundle cũ đã biến khỏi đĩa = chứng ghi-đè (control âm tầng-2) ✅
Marker (đo bằng grep -c -F — KHÔNG regex-neo-quote, đúng recipe K6):
| marker | ADM pre→post | EOF pre→post |
|---|---|---|
Đưa dòng kế hoạch vào Hợp đồng |
0 → 1 | 0 → 1 |
Chọn dòng đưa vào hợp đồng |
0 → 1 | 0 → 1 |
Loại hợp đồng |
0 → 1 | 0 → 1 |
Quy trình duyệt hợp đồng |
0 → 1 | 0 → 1 |
Phiếu chưa có dòng nhà cung cấp nào. |
0 → 1 | 0 → 1 |
khkk-bridge-line |
0 → 1 | 0 → 1 |
Tạo hợp đồng |
0 → 1 | 0 → 1 |
Chưa có giá chốt |
0 → 1 | 0 → 1 |
CONTROL DƯƠNG Phiếu nguồn chưa có NCC trúng thầu. |
1 → 1 | 1 → 1 |
CONTROL DƯƠNG Chưa khai căn cứ nào. |
1 → 1 | 1 → 1 |
CONTROL ÂM ĐỘC-QUYỀN …vào Hợp đồng XYZ (bịa) |
0 → 0 | 0 → 0 |
CONTROL ÂM ĐỘC-QUYỀN khkk-bridge-line-nonexistent |
0 → 0 | 0 → 0 |
⇒ 8/8 × 2 app, control dương giữ, control âm câm ⇒ tín hiệu sạch cả 2 chiều.
TRÍCH BYTE THẬT (bắt buộc trước khi kết luận — bài K6):
ADMIN idx=1667835 : …title:`Đưa dòng kế hoạch vào Hợp đồng`,footer:(0,N.jsxs)(N.Fragment,…
ADMIN idx=1669865 : …jsx)(`input`,{type:`radio`,name:`khkk-bridge-line`,checked:a===e.id,…
EOFFICE idx=1501164 : …title:`Đưa dòng kế hoạch vào Hợp đồng`,footer:(0,F.jsxs)(F.Fragment,…
EOFFICE idx=1503194 : …jsx)(`input`,{type:`radio`,name:`khkk-bridge-line`,checked:a===e.id,…
🔑 Xác nhận lại bài K6: rolldown/Vite-8 minify literal thành BACKTICK (title:`…`) — regex neo ["']
sẽ 0-hit GIẢ. grep -F là đúng công cụ. 2 app chỉ khác tên biến module (N vs F) = minifier, code y hệt.
Kiểm-tra-tiền-đề đã làm TRƯỚC khi tin marker (ghi lại vì suýt sập bẫy mới):
file báo bundle "ASCII text" ⇒ nghi \u-escape ⇒ bác bỏ bằng đo (grep -c -F 'Hợp đồng'=4 trên bundle PRE);
và nghi code-split ⇒ bác bỏ bằng đo (index.html chỉ 2 asset, 0 ref "./x-<hash>.js"). Cả 2 nghi ngờ đều SAI,
nhưng nếu không đo mà kết luận "0-hit = chưa ship" thì đã FAIL-OAN như K6.
M5 — 🎯 K7-đặc-thù: seeder grant restart-proof (#75/#76)
(a) Log API — dòng acceptance CHỐT bởi gate-K7: CÓ, verbatim ✅
2026-08-01 13:14:40.592 +07:00 [INF] Seeded Contracts.Create access: 0 added + 2 upgraded
(K7 cầu KHKK→HĐ - Drafter/Procurement/Admin; câu III default chờ ratify)
(C:\inetpub\solution-erp\api\logs\solution-erp-20260801.log; ký tự Việt hiển thị lỗi do console codepage, không phải log hỏng.)
Chứng restart THẬT (3 chân kiềng, không đọc số lúc running):
- Dòng ĐẦU TIÊN của log =
13:14:33.867 [INF] Applying migrations...⇒ tiến trình khởi động mới. w3wpPID 7008,StartTime = 13:14:27— và map PID→pool bằngappcmd list wpxác nhậnWP "7008" (applicationPool:SolutionErp-Api). ⚠️ Map này KHÔNG thừa: PID 7680 cũng start 13:13:09 nhưng làvietreport-admin(dự án khác) — đọc vội sẽ tưởng pool ta restart 2 lần.- Cả 3 mốc (13:14:27 / :33 / :40) nằm gọn trong cửa-sổ run #442 (13:08:26 → 13:14:51). ✅
(b) sqlcmd đếm row — PRE 1 → POST 3 ✅
| phép đo | PRE (13:06) | POST (13:16) | phán |
|---|---|---|---|
COUNT(*) Permissions WHERE MenuKey='Contracts' AND CanCreate=1 |
1 | 3 | ✅ đúng kỳ vọng |
COUNT(*) Permissions WHERE MenuKey='Contracts' (mọi cờ) |
13 | 13 | ✅ KHÔNG đẻ row ⇒ chứng "0 added" |
COUNT(*) Permissions (toàn hệ) |
2221 | 2221 | ✅ hệ quả tất yếu của 0-added |
3 role có CanCreate=1 POST + hình-dạng cờ (không chỉ đếm):
Admin | create=1 read=1 upd=1 del=1 ← vốn đã đủ từ trước (không bị đụng)
Drafter | create=1 read=1 upd=0 del=0 ← NÂNG, và KHÔNG tràn sang Update/Delete
Procurement | create=1 read=1 upd=0 del=0 ← NÂNG, và KHÔNG tràn sang Update/Delete
⇒ vừa đúng số, vừa đúng hình dạng (không over-grant) — thứ mà đếm-tổng đơn thuần không thấy được.
(c) Idempotent — ⚠️ PARTIAL: chứng ở tầng-row (ĐO ĐƯỢC), chưa chứng ở tầng restart×2 (không quan-sát-được)
Đo được (mạnh): STARTUP_COUNT('Applying migrations...') trong log = 1 ⇒ trong cửa-sổ này pool
SolutionErp-Api restart đúng 1 lần. Giả định của lead "run-1 + run-2 = restart ×2 miễn phí" KHÔNG thành
vì run #441 bị giết trước bước deploy (M1) ⇒ không có lần restart thứ hai để đo.
Tao KHÔNG tự recycle pool prod (READ-only, anti-pattern #6/#8).
Nhưng nhánh no-op ĐÃ CHẠY THẬT và đo được — nhờ Admin đóng vai control sẵn có:
code đếm (DbInitializer.cs, diff K7):
if (existing.TryGetValue(role.Id, out var row)) {
var changed = false;
if (!row.CanRead) { row.CanRead = true; changed = true; }
if (!row.CanCreate) { row.CanCreate = true; changed = true; }
if (changed) upgraded++; // ← CHỈ tăng khi cờ THỰC SỰ lật
continue; // ← row có sẵn ⇒ KHÔNG BAO GIỜ Add
}
Seeder chạy trên 3 role nhưng báo 2 upgraded (không phải 3) ⇒ Admin bị bỏ qua vì đã đúng sẵn ⇒ đó chính là nhánh idempotent, đã thực thi trên prod và đo được, chứ không phải suy từ code.
Suy (chưa đo — khai rõ): restart lần 2, cả 3 row đã ở trạng thái đích ⇒ added=0, upgraded=0 ⇒ do guard
if (added > 0 || upgraded > 0) bọc câu log, sẽ KHÔNG in gì cả (IM LẶNG) — chứ không in 0 added + 0 upgraded.
🔎 Ai verify lần sau: đừng đọc "log không có dòng seeder" thành hồi-quy — đó là hình-dạng ĐÚNG của lần 2.
Phép đo rẻ nhất cho (c) ở deploy kế: đếm lại COUNT (phải vẫn 3) + STARTUP_COUNT (phải ≥1 ở log mới).
M6 — Smoke ✅ (kể cả endpoint MỚI)
Bearer admin OK (accessToken len=468, roles=['Admin']).
| phép thử | kỳ vọng | thực đo | |
|---|---|---|---|
POST /api/auth/login (admin) |
200 + token | 200, token 468 ký tự | ✅ |
GET /api/contracts |
200 | 200 | ✅ |
GET /api/purchase-evaluations |
200 | 200 | ✅ |
GET /api/menus |
200 | 200 | ✅ |
GET /api/contract-signing-plans |
200 | 200 | ✅ |
control âm GET /api/contracts KHÔNG token |
401 | 401 | ✅ |
control âm GET /api/bogus-route-xyz + admin |
404 | 404 | ✅ |
🎯 MỚI POST /api/contract-signing-plans/{0-guid}/create-contract + admin |
404/400, KHÔNG 403 | 400 | ✅ |
| 🎯 MỚI cùng endpoint, không token | 401 | 401 | ✅ |
Wire-proof mạnh hơn cả mã số — thân 400 là lỗi bind/validate của chính command K7, tức request đã đi xuyên authz vào tới FluentValidation:
{"status":400,"errors":{"PlanId":["'Plan Id' must not be empty."],
"ApprovalWorkflowId":["'Approval …"]}}
⇒ endpoint đã wire thật, policy không chặn nhầm Admin. (403 mới là FAIL — không xảy ra.)
⚠️ Hạn chế trung thực — tầng-2 (#82) của Drafter CHƯA đo LIVE được
nv.test@solutions.com.vn / TestUser@123456 → 401 "Email hoặc mật khẩu không đúng" (tài khoản UAT không
đăng nhập được ở thời điểm đo). Vì vậy:
- Đã đo: row
Contractscủa Drafter/Procurement cóCanCreate=1(tầng dữ-liệu/hiển-thị). - CHƯA đo: một principal Drafter thật gọi endpoint và KHÔNG bị 403 (tầng API-authz).
- Bằng chứng thay thế (CI, không phải prod):
KhkkBridgeAuthzTests5/5 PASS trong gate #442. - ⚠️ Admin qua được không tách bạch được công của row vs admin-bypass (chính comment seeder cũng nói vậy)
⇒ không dùng kết quả Admin để tuyên bố grant Drafter đã ăn ở tầng API. Đề nghị: UAT đăng nhập 1 tài khoản
Drafter thật bấm nút, hoặc cấp lại mật khẩu
nv.testrồi đo lại 1 lệnh curl.
📌 UAT — NÓI TRƯỚC (ghi theo yêu cầu lead)
Nút "Đưa vào Hợp đồng" chỉ sáng cho Drafter / Procurement SAU KHI USER ĐĂNG XUẤT & ĐĂNG NHẬP LẠI. Lý do: menu/permission được client cache sau lần login trước; quyền
Contractsvừa được nâng ở phía server lúc 13:14:40 hôm nay nên phiên đang mở vẫn mang bộ quyền CŨ. Không phải lỗi deploy, không cần báo bug — chỉ cần re-login. (Admin không thấy khác biệt vì vốn đã có quyền.)
Tổng kết 6 mục
| # | Mục | Phán | Bằng chứng gọn |
|---|---|---|---|
| 1 | Run aaed699 success |
🔴 LỆCH | #441 cancelled 13:07:59 (bị push 628132c giết); K7 ship qua #442 success 6m25s, ancestry aaed699 ⊂ 628132c đã verify |
| 2 | Gate 639 = 45+594 | ✅ PASS | log web-UI tách phần; Δ+17 = 12+5 [Fact] khớp từng đồng; 0 fail/0 skip; flaky xanh lần 4 |
| 3 | Mig 71 + tables 97 | ✅ PASS | git 0-file Migrations ⟂ sqlcmd TOP + COUNT=71 + sys.tables=97 |
| 4 | Bundle rotate ×2 + marker | ✅ PASS | Ig1R5tAh→RgvHZkPH, DFOCQ2ks→D65FJPGd; LM in-window; hash+size khớp log CI; marker 8/8 × 2 app + 2 control dương + 2 control âm |
| 5 | Seeder restart-proof | 🟡 PARTIAL | (a) log verbatim 0 added + 2 upgraded ✅ (b) COUNT 1→3, row 13→13 ✅ (c) restart×2 không quan-sát-được (chỉ 1 startup) — nhánh no-op vẫn chứng được qua Admin |
| 6 | Smoke + endpoint mới | ✅ PASS | 4 GET 200 · 401/404 control · endpoint mới 400 (không 403) + 401 khi thiếu token; ⚠️ Drafter-live chưa đo (login UAT hỏng) |
Việc cần lead xử:
- Gotcha #86 (đề xuất mới) — push-đè nuốt deploy: nếu commit thứ 2 rơi trọn
paths-ignorethì code commit thứ 1 im lặng không bao giờ lên prod. Lần này thoát nhờ may. Luật: sau 2 push liên tiếp phải kiểm "run cuối có head_sha = TIP" và "TIP ⊇ commit code". - Mở lại tài khoản UAT
nv.test(hoặc cấp 1 Drafter khác) để đóng nốt tầng-2 #82 cho K7. - Deploy kế: đếm lại
Contracts/CanCreate=1(phải vẫn 3) để khép mục 5(c).
END cicd-k7 — VERDICT=PARTIAL (K7 ĐÃ LIVE, 4/6 PASS sạch + 1 LỆCH-cơ-chế run bị huỷ + 1 PARTIAL restart×2 không quan-sát-được; 0 defect trong code K7)