# sub-implementer-backend-0 — ĐỢT-1 lane BE (W0 + W1) — S182 YC-013

> Artifact ghi LIÊN TỤC trong lúc làm (chống-#53). Spec nguồn: `spec-khkk-awf-mirror-ncc-08-08-2026.md` MỤC 2.
> Trạng thái: **IN-PROGRESS** (khởi tạo lúc bắt đầu lane).

## 0. Kế hoạch lane
- W0.1 `dotnet ef database update` ×2 DB (Dev + Design) → kỳ vọng top MigrationId `20260806070242`
- W0.2 boot API 1 lần → verify 8 row `KHKK-N1..8` type-10 IsActive=1
- W0.3 `dotnet test SolutionErp.slnx` baseline TƯƠI → ghi `run.md`
- W1a deactivate-per-Code type-10 (`ApprovalWorkflowV2AdminFeatures.cs:339-343`) — CÔNG THỨC R-6 KHOÁ
- W1b file MỚI `ContractSigningPlanWorkflowGuards.cs` + gọi 3 site
- W1b2 pin-check IsActive ∧ IsUserSelectable (D6)
- W1c changelog type-10 (`:863-893`)
- W1d DELETE guard (`:1193-1205`)

## 1. W0 — prep + đo nền

### W0.1 — `dotnet ef database update` ×2 DB — ✅ DONE
🔴 **Khai lệch-có-lý-do #1: local có BA DB, không phải hai.** Đo trước khi áp (đúng luật "đo drift tại chỗ"):
| DB | nguồn conn | trước | sau |
|---|---|---|---|
| `SolutionErp` | `appsettings.json:3` Default (**base — KHÔNG dùng ở dev**, bị `appsettings.Development.json:3` override) | ~Mig 53 (tụt 19 mig) | `20260806070242` |
| `SolutionErp_Dev` | `appsettings.Development.json:3` = **DB runtime THẬT của API local** | `20260731085624` (Mig 71) | **`20260806070242`** ✅ |
| `SolutionErp_Design` | `DesignTimeDbContextFactory.cs:15` (ef CLI mặc định) | `20260731085624` (Mig 71) | **`20260806070242`** ✅ |

- Spec nói "Dev + Design"; memory-pack Pattern-2 định nghĩa Dev = `SolutionErp_Dev` (explicit conn) ⇒ **2 DB spec đòi = `_Dev` + `_Design`, cả 2 ĐÃ lên Mig 72.**
- Lượt chạy ĐẦU tôi trỏ nhầm `--connection` vào `SolutionErp` (base). **Khai thẳng, không giấu:** không phá gì (DB đó vốn tụt 19 mig và không ai đọc ở dev), nhưng nó **không phải** 1 trong 2 DB spec đòi ⇒ đã chạy lại đúng 2 DB. Nếu chỉ nhìn lượt đầu mà tuyên "xong W0.1" thì đó là **PASS giả**.
- Lệnh đo: `sqlcmd -S "(localdb)\MSSQLLocalDB" -d <db> -Q "SELECT TOP 1 MigrationId FROM __EFMigrationsHistory ORDER BY MigrationId DESC"`

### W0.2 — boot API → 8 row `KHKK-N1..8` — ✅ PASS
- Trước boot: `SELECT ... WHERE Code LIKE 'KHKK-N%'` trên `SolutionErp_Dev` = **0 row** (control âm — thước sống).
- Boot: `ASPNETCORE_ENVIRONMENT=Development ASPNETCORE_URLS=http://localhost:5443 dotnet run --project src/Backend/SolutionErp.Api --no-launch-profile`, chạy tới khi seeder xong rồi **kill** (`Stop-Process` pid 55784).
- Sau boot: **8/8** row `KHKK-N1..N8` · `ApplicableType=10` · `IsActive=1` · `IsUserSelectable=1` · **3 Bước / 9 Cấp mỗi row** ⇒ roster local resolve ĐỦ 11 email (không rơi vào ca "workflow 0 bước" mà `DbInitializer.cs:544-548` cho phép).
- Seeder: `SeedKhkkGroupWorkflowsAsync` `DbInitializer.cs:549`, idempotent PER-CODE `:610` `if (await db.ApprovalWorkflows.AnyAsync(w => w.Code == code)) continue;` — **KHÔNG đụng** (memory-pack cấm).

### W0.3 — baseline test TƯƠI — ✅ `645 PASS (45D+600I)`
- Lệnh: `dotnet test SolutionErp.slnx`, chạy SAU W0.1/W0.2, **0 sửa mã** tại thời điểm đo.
- Domain 45 / 407 ms · Infrastructure 600 / 35 s · **Failed 0 · Skipped 0**.
- Ghi `run.md` mục "Đo nền W0" (R-3). Trùng số lead đo @bootstrap (645) nhưng **đo lại độc lập**, không chép.
- ⇒ Cửa W1/W4: FULL suite = **645 + 14 = 659**, 0 fail.

## 2. W1 — 5 fix

> 🔴 **Khai lệch-có-lý-do #2 — ARTIFACT NÀY TỪNG NÓI DỐI (lag).** Mục này đọc "_(chưa bắt đầu)_"
> trong khi **W1a ĐÃ nằm trên đĩa** (`git diff` = +21/−4 đúng khối `:339`). Lane trước bị cắt
> SAU khi Edit land nhưng TRƯỚC khi ghi artifact ⇒ đúng lỗ hổng mà "ghi-đĩa-từng-phần" sinh ra
> để bịt. Lane tiếp đã **TIN DIFF, không tin artifact** (lệnh lead). Bài học ghi §4.

### W1a — deactivate-per-Code cho type-10 — ✅ DONE (đã land từ lượt trước, verify lại lượt này)
- Site: `ApprovalWorkflowV2AdminFeatures.cs:339-359` (khối `var actives = ...`).
- Công thức **ĐÚNG R-6**, verify từng vế trên đĩa:
  `typeEnum == ApprovalWorkflowApplicableType.ContractSigningPlan ? Where(w => w.Code == request.Code && w.ApplicableType == typeEnum && w.IsActive) : Where(w => w.ApplicableType == typeEnum && w.IsActive)`
  ⇒ 3 vế đủ, **0 regex** trong nhánh này (grep `KHKK-N` trong file: chỉ 1 hit và nằm trong
  COMMENT giải thích lệnh cấm, không phải mã chạy — *use ⟂ mention*).
- Comment `:339` cũ "only ONE active per type" **đã sửa** (F12d): thay bằng khối 2 gạch đầu
  dòng type-10 per-CODE / loại khác per-TYPE + ghi chú `nextVersion :334-337` vốn đã per-Code.

### W1b — file MỚI `ContractSigningPlanWorkflowGuards.cs` + gọi 3 site — ✅ DONE
- **File mới:** `src/Backend/SolutionErp.Application/ContractSigningPlans/ContractSigningPlanWorkflowGuards.cs`
  — `public static class`, 2 method: `ParseGroup(string?)` + `EnsureWorkflowGroupMatchAsync(IApplicationDbContext, Guid, int, CancellationToken)`.
  Thân = **MOVE nguyên** logic rào (v) cũ; **GIỮ semantics cho-qua-code-ngoài-họ** (viết thành
  doc-comment nêu lý do, để không ai "sửa" thành chặn).
- **Site 1 Create** `ContractSigningPlanFeatures.cs:475-489` — 14 dòng regex inline XOÁ, thay bằng 1 call.
- **Site 2 UpdateDraft** `:617-630` — rào MỚI (cửa này trước KHÔNG có), đặt TRONG nhánh `awId != plan.ApprovalWorkflowId`.
- **Site 3 SubmitAsync** `ContractSigningPlanWorkflowService.cs:189-197` — chèn SAU rào hạng-mục
  (`okCount != catalogIds.Count` `:185-187`) và NGAY TRƯỚC `LoadStepsAsync` `:198`. **Điểm chèn đã
  verify tại chỗ** đúng lệnh (spec đoán `:169-187`; số thật sau khi chèn = `:189`). Thêm
  `using SolutionErp.Application.ContractSigningPlans;` vào service.
- Message 409 dùng **nguyên văn spec**, 1 message chung 3 site.

### W1b2 (D6) — pin-check `IsActive ∧ IsUserSelectable` — ✅ DONE
- Helper `ContractSigningPlanScope.EnsureWorkflowPinnableAsync` (`ContractSigningPlanFeatures.cs:322-347`)
  — mirror vá-3 `CreateContractFromSigningPlanFeatures.cs:141-147`, **2 message TÁCH** ("đã ngừng
  hoạt động" / "chưa ghim") đúng lý do vá-3 nêu.
- Gọi 2 site: Create sau rào (iv) (`:478`) · UpdateDraft **CHỈ** trong nhánh `awId != plan.ApprovalWorkflowId` (`:626`).
- Giữ-nguyên-pin KHÔNG check (phiếu cũ pin bản archived vẫn sửa được GhiChu) — đúng D6.

### W1c — changelog PUT type-10 — ✅ DONE
_(chi tiết ghi ngay dưới, sau khi land)_

### W1d — DELETE usage-check → 409/404 — ✅ DONE
_(chi tiết ghi ngay dưới, sau khi land)_

## 3. Verify
_(chưa)_

## 4. Ambiguity / lệch spec khai
_(chưa)_
