21 KiB
Test-Specialist Agent — Persistent Memory
-
S164-K3 (07-31) KHKK level-finalize + pin-đúng-nhóm — 5 test, return SẠCH
[baseline đo tươi 609/0 = 45D+564I KHỚP đề bài · sau: 614/0, Δ+5, 0 hồi quy]: +4 vàoContractSigningPlanApprovalTests.cs(T-a/b/c finalize + T-e giữ-cờ), +1 vàoContractSigningPlanGroupAndCatalogTests.cs(T-d rào (v)) — chọn file thứ 3 thay "file mới" để KHÔNG nhân bản 70 LOCSeedPeForCreateAsync+CreateAsyncđã có sẵn ở đó. Kỹ thuật chính = MA TRẬN cùng-ruột-khác-1-biến thay cho fault-inject (không được chạm prod, mà prod đang mang diff lane khác): 1SeedFinalizeScenarioAsync→ T-a (cờ+apply=true ⇒ DaDuyet+chốt giá) / T-b (cùng seed, đổi ĐÚNGapplyLevelFinalize:false⇒ (2,1) advance thường) / T-c (Cấp không cờ + default true ⇒ (0,2)). Bỏ qua cờ ⇒ T-a đỏ · bỏ qua param ⇒ T-b đỏ ·||thay&&⇒ T-c đỏ. Điều kiện SỐNG CÒN: đặt cờ ở Bước-2-Cấp-2 của quy trình 3 Bước — cờ ở cấp cuối thì "kết thúc" xảy ra ở CẢ HAI thế giới ⇒ T-a xanh mà chứng minh 0 (assertStepCountAsync==3để chốt). T-d: CÙNG workflowKHKK-N5cho 2 kết quả trái ngược (phiếu nhóm 3 = 409 · nhóm 5 = qua) + không-đốt-mã (rào (v):409-420đứng TRƯỚC codegen:428) có control-dương 001/002/003 tăng thật. Kiểm dữ-liệu chứ không chỉ đọc regex:DbInitializer.cs:609sinh đúng$"KHKK-N{n}"⇒ rào neo thật. T-e: 3 vế (tick⇒true · không-tick⇒false · đối chứng type-1 cũng true ⇒ ECHO chứ không phải đổi ép-false thành ép-true). CỐ Ý KHÔNG pinAllowApproverSkipToFinal==truedù mã đang echo: service KHKK 0 lần đọc cờ đó (:47khai "không port") ⇒ đó là ứng-viên config-lie #78 CÒN SÓT; pin nó = biến khả-năng-lỗi thành yêu cầu → REPORT lead thay vì test. 0 bug prod. Tag[s164-k3, level-finalize, ma-tran-cung-ruot-1-bien, co-o-cap-cuoi-lam-rong-phep-do, khong-pin-config-lie] -
S161-W2 (07-29) KHKK CRUD 8 test
[hmw wf_7490179a-70b · chết #53 sau khi file LAND (sub-md dừng §0.1) · lead chạy suite tươi 574/0 = 566+8, cả 8 tên đúng]:Application/ContractSigningPlanCrudTests.cs(7) + extendApi/AuthorizePolicyRegressionTests.cs(PIN reflection). 2 PIN spec + 6 acceptance: happy-path Lines=winners + SUM IsSelected đúng NCC (bẫyw.Idvsw.SupplierId) · second-create 409_AndDoesNotBurnCode(mã không đốt khi Conflict — phép đo sequence side-effect) · workflow-guard 2 nhánh Conflict/NotFound · no-winner · soft-delete-then-recreate + changelog giữ · owner/phase guard. Phát hiện đắt (sub-md §0.1): TestApplicationDbContext KHÔNG wire AuditingInterceptor ⇒Remove()= xoá CỨNG ⇒ đo "xoá-tạo-lại" trên xoá-cứng = ĐO NHẦM CƠ CHẾ (rào 1-PE-1-KH dựa global-filter che row soft-deleted, không phải row biến mất) → dựng fixture riêng CÓ interceptor cho nhóm delete-test. Baseline đo tươi trước khi viết (không tin số đề bài). Tag[s161-w2, khkk-8-test, khong-dot-ma-khi-409, fixture-interceptor-cho-soft-delete, do-nham-co-che-xoa-cung] -
S161 (07-29) W1 KHKK: 4 test schema+codegen
[hmw wf_a95c9909-14b · chết #53 LÚC ĐỢI full-suite (việc thật đã xong) → sub-md 10KB ghi-từng-mục cứu trọn; lead chạy full-suite tươi 566/0 + điền §2/§4 on-behalf]: File MỚICommon/ContractSigningPlanSchemaTests.cs— 4 test: (1) PIN-specBayBang_MoiBang_CoAuditFields_VaQueryFilter(quét EF model theo NAMESPACE →BeEquivalentTođúng bộ 7 tên [bảng #8 lọt = ĐỎ] + audit-fields + soft-delete assert 2-CHIỀU [BaseEntity phải KHÔNG có IsDeleted] + seed IsDeleted=true chứng filter là BIỂU-THỨC-THẬT); (2) PINSigningPlanCodeGenerator_Format_KhkkYearSeqD3— regex + tuyệt-đối 001→002 + row LastSeq==2 + đẩy đồng hồ 2027 → KHKK/2027/001 reset (thiếu vế này\d{4}xanh cả khi năm hardcode); (3) #57 xoá-mềm-thêm-lại KHÔNG nổ UNIQUE + vế đối chứng 2-dòng-sống PHẢI DbUpdateException (không có = xanh cả khi index chưa từng tạo); (4) pin slot 10 enum. Fault-inject 2 chiều trên bảng-kỳ-vọng rồi revert — chứngGetDeclaredQueryFilters()phân biệt 2 thế giới. Learned: (1) prompt lead ≠ đĩa — LevelOpinion là AuditableEntity nhưng CỐ Ý KHÔNG filter (lý-do ghi 2 chỗ: site orphan-purge phải thấy row soft-deleted) ⇒ test theo CODE + assert cái-cố-ý thành invariant có răng; (2) EF Core 10:GetQueryFilter()obsolete-prone →(IReadOnlyEntityType).GetDeclaredQueryFilters(); (3) Serializable trên SQLite = graceful-map, tiền lệSqliteDbFixture.cs:36-42+ PE codegen test — KHÔNG chế fixture mới; (4) KHÔNGRemove()đo xoá mềm (TestCtx không wire AuditingInterceptor → xoá CỨNG, bài S155). Tag[s161, w1-4-test, faultinject-2-chieu, test-theo-code-khong-theo-prompt, clock-2027-reset, 53-luc-doi-suite] -
S155 (07-27) ĐỢT 2 PE xóa-khi-đang-duyệt + màn "Đã xóa" [run 2026-07-27-S155-pe-delete-approver · sub-d2-test-4, diary 13.9KB ghi-trong-lúc-làm]: +10
PeDeleteByApproverTests.cs(T21 lũy kế 4 HƯỚNG KHÁC NHAU · T22/T22b/T22c màn Đã xóa · T23/T24/T24b cờ+lượt · T25 authz reflection · T25b chiều thuận · T26 changelog). Full 561 = 560 PASS / 1 FAIL (45D + 516I); 0 hồi quy (Infra cũ 506 nguyên vẹn). 1 FAIL = T26 GIỮ ĐỎ = BUG PROD:PeSoftDeleteFeatures.csAdd changelog (:126) →Remove(pe)(:142) →CascadeDeleteTiming.Immediatecascade ngay tại Remove; dependent đangAddedbị cascade ⇒ Detached (không phải Deleted) ⇒ 0 row;AuditingInterceptorchạy ở SavingChanges = SAU điểm mất ⇒ không cứu. Đo bằng ChangeTracker diag (Added→Detached,rowsInDb=0), KHÔNG suy đoán. Grep cùng-lớp: site DUY NHẤT có hình "Add con → Remove cha → 1 SaveChanges". REPORT lead, KHÔNG tự sửa. Hạ tầng MỚI:SoftDeleteFixture(ngay trong file test, ~90 LOC) = fixture ĐẦU TIÊN của repo wireAuditingInterceptor— BẮT BUỘC khi đoRemove()=xóa-mềm, vìTestApplicationDbContextKHÔNG wire ⇒ mặc định XÓA CỨNG ⇒ màn "Đã xóa" rỗng ⇒ assert đúng ở CẢ HAI thế giới (đúng bẫy vacuous). Số học T21 nhớ kỹ: xóa phiếu ChoDuyet ⇒ PrevSubmitted↓ · PendingSelected↓ · PrevSelected + PendingSubmitted BẤT ĐỘNG (seed thêm peer DaDuyet + TraLai để 2 số "không đổi" ≠ 0 ở cả 2 phía). Tag[s155, pe-delete-by-approver, cascade-detach-added-child, softdelete-fixture, 4-huong] -
S153 (07-26) T4 ctx-adopt FAULT-INJECT máy
mind-check#8-#17+#21 [run 2026-07-26-S153-ctx-adopt · lane trước đứt sau #7, file đĩa nguyên → tiếp được][engine: opus-5]: 18/18 PASS 0 FAIL. KHÔNG phải test C# (baseline 509 KHÔNG đổi) — đối tượng =scripts/session_ctx.py mind-check, mọi mutation trong cây tạm qua seam--root, repo read-only. Bắt 1 GAP THẬT: ở closed-mode XOÁ con-trỏ → cờ/exit 0 còn GIỮ con-trỏ đang-chờ trung thực → TRƯỢT/exit 1 ⇒ đường-rẻ-nhất-để-xanh là đường SAI (chữ luật cấm, gradient thưởng) → REPORT em main, KHÔNG tự sửa. Bài học mới quan trọng nhất: fixturedraft-tsMALFORMED ra exit 0 —inject_Aneo## MIND-1sau khirenum(...,2)đã đổi heading ⇒str.find=-1 ⇒ chèn GIỮA heading ⇒ts:không ở đầu dòng ⇒ máy ĐÚNG mà vẫn xanh. Fixture-hỏng cho-xanh trông Y HỆT máy-đúng cho-xanh ⇒ phải soi RUỘT fixture (cat -A/reprtừng dòng), KHÔNG chỉ đọc exit code. 2 kỹ thuật xài lại: (i) cặp CÙNG-RUỘT khác 1 biến — cap=32 vs cap=1 trên cùng tệp = chứng đọc-config-LIVE;_endgiả_pause=9vs không = chứng nguồn-đếm-là-ĐĨA, dùngdiff2 output để chứng dòng đo BYTE-IDENTICAL; (ii) đo wall-clock phải gồm nhánh ĐẮT — lệnh literal đi nhánh early-exit (repo chưa có_mind) = tự đo nhánh rẻ rồi gọi là chi phí thật; đo 55-64ms/lượt vs ước 140-300ms ⇒ lệch 2.2-5.4×, giao lead điền lại. Tag[s153, t4-fault-inject, mind-check, fixture-bug-vacuous-pass, cung-ruot-1-bien] -
S134 (07-17) PE lũy-kế TẠM TÍNH TEST-C [wf_97d6a582-b7e · return sạch 0-garble]
[engine: opus-worker]: +8PePendingAccumulationTests.csC1-C8 (file MỚI, mirror seed PeBudgetFreezeTests :277-336): C1 ChoDuyet+winner→delta2+PriorPes · C2 TuChoi/DaDuyet loại sạch · C3 nháp list-only/TraLai delta1 · C4 window CreatedAt · C5 delta âm signed · C6 complementary-no-double-count: gọi CẢ ComputeAsync + ComputePendingAsync cùng seed, assert EXACT-NUMBERS 2 phía (PrevSelected 90tr/1 bất động ⟂ PendingSelected 50tr/1) · C7 frozen→0/null qua query handler · C8 D4 changelog positive+contrast (filterContextNote.Contains("[S134 pending-prior]")#48). Full 528/0 (45D+483I, +8). Learned: (1) direct-call accumulator = synthetic curPeId + CreatedAt muộn, KHÔNG cần row phiếu-đang-xét (chỉ filter Id!=/CreatedAt<); (2) regression "bất động" mạnh nhất = C6-style exact-numbers 2 hàm cùng seed; (3) FluentAssertionsContainInOrder(params T[])+ because-string overload-clash khi T=Guid → bọcnew[]{...}trước. Surprise: D4 no-SaveChanges-in-helper → row chỉ persist sau TransitionAsync. Tag[s134, pependingaccumulation, c6-exact-numbers-complementary, 8-test] -
S133 INV-C T1-T9 [wf_75c6bfd3-754 · #53 return-fail lần 3 → disk + lead tự chạy suite; entry on-behalf]: +9 test vào PeBudgetFreezeTests (
:426T1 đủ-11-cột ·:480T2 CCM-threshold ·:524T3 all-steps ·:555T4 V1-legacy assert KHÔNG-ApprovedPrice ·:638T5 guard CCM+Admin Conflict + ChoDuyet-allow ·:687T6 AdjustBudget DaDuyet+TuChoi ·:730T7 live-vs-frozen flags/editability ·:783T8 null-safe no-WorkItem + no-pair ·:821T9 overwrite + SnapshotAt refresh). FULL suite lead-verified exit-0: Domain 45 + Infra 474 = 519, 0 fail (464+10 khớp). ⚠️ Lesson mới: INV-C ghi đèsub-test-specialist-0.mdcủa INV-A — cùng role + cùng task-index qua 2 invocation chung run-folder = sub-file collision; INV-A content sống nhờ journal + diary. Multi-invocation cùng run → cần label/index phân biệt (báo lead khi gặp lại). Tag[s133, t1-t9, subfile-collision-multi-invocation] -
S133 (07-17) PE budget-freeze T0 REPRODUCE [wf_d9639faa-97a INV-A, return sạch]: RED thật
Failed 1/1— expected ProInitial 100tr, found 999tr @PeBudgetFreezeTests.cs:184. Flow chuẩn: đưa phiếu tới DaDuyet QUAsvc.TransitionAsync(V2 1-Bước-1-CấpAllowApproverFinalize=true+ giá chốt), KHÔNG seed thẳng Phase (post-fix snapshot CHỈ set qua transition); mutate pair qua UpdatePeBudgetPro/Ccm (2 handler KHÔNG phase-guard = đúng cơ chế bug); 2 sanity-assert chống vacuous (Phase==DaDuyet + liveRec đổi thật) TRƯỚC assert chính. Bắt được lỗi spec: query THẬT =GetPurchaseEvaluationQuery(bundle.BudgetSummary), spec ghi nhầm GetPurchaseEvaluationDetailQuery → lead sửa spec. Sau INV-B: T0 GREEN trong 44/44 filter. Tag[s133, pe-budget-freeze, t0-red-green, anti-vacuous-sanity-assert]
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 RAGsearch_memoryjust-in-time. Keep entry ≤ 1.5K chars (gotcha #53). NEW agent S39 (2026-05-29) — dedicated test layer (tách khỏi implementer Case 3).
🎯 Role baseline
WRITE specialist độc quyền tests/**. xUnit + FluentAssertions 7.2 + EF SQLite TestApplicationDbContext + IdentityFixture. Tools: Read, Edit, Write, Bash, Grep, Glob + 5 RAG. Skills: contract-workflow + permission-matrix.
🚫 Split boundary
- ✅ MINE:
tests/SolutionErp.{Domain,Infrastructure}.Tests/** - ❌ NOT: production code
src/Backend/**+fe-*/**→ test reveal bug → REPORT em main, KHÔNG fix - ❌ NOT: decide WHAT to test (test plan) → em main + reviewer chốt priority
📊 Baseline: 509 tests = 509 PASS (45 Domain + 464 Infra) as of S118b (was 505@S118; +4 Infra ReportsController company-wide-report authz-regression, filtered-run 19/19, full 509/509)
- Count history + verbose per-session test-design notes (S54→D2): archive/baseline-history.md (L2, on-demand).
- Latest (D2): +6 PE Mig 58 multi-winner —
CreateContractFromEvaluationMultiWinnerTests.cs(5, 1→N HĐ per IsWinner, GiaTri per-winner=quote-total / single=legacy-budget-sum) +PeListWinnerNamesProjectionTests.cs(1, EF collection-projection translation-smoke). No prod bug.
Pattern S67: private-static seed/init → invoke qua REFLECTION (
GetMethod(name, NonPublic|Static)+Invoke(null, [db, roleManager, NullLogger.Instance])); seed MenuItem rows TRƯỚC Permission (FK MenuKey→MenuItem.Key Cascade, SQLite Error 19 nếu thiếu). Cycle-guard test: SqliteDbFixture đủ (no User); rollup-count test cần IdentityFixture. Run:dotnet test SolutionErp.slnx --nologo --verbosity minimal -p:BuildInParallel=false -maxcpucount:1(MSBuild OOM → serialize build)
⚠️ Pattern: deduction hook FK → seed LeaveType cho terminal test (S43)
LeaveBalance→LeaveType Restrict FK. ApproveLeaveRequest terminal (DaDuyet) insert LeaveBalance → test tới DaDuyet PHẢI seed 1 LeaveType + LeaveRequest.LeaveTypeId=type.Id (random Guid→FK fail SQLite 19). Non-terminal (advance/reject/return/OtRequest) KHÔNG cần. BuildLeave optional leaveTypeId default random. Year=StartDate.Year. Negative OK (no quota guard). Query lazy-synth Entitled=DaysPerYear khi 0 row.
⏱️ Timing rules (docs/rules.md §7)
- Feature mới = test-after (UAT ổn → viết, Phase 9 skip per
feedback_uat_skip_verify) - Bug fix = test-before BẮT BUỘC (reproduce → fix)
- Critical algo = test-before merge (codegen/guard/financial/security)
- Skip: DTO mapping, CRUD master, FE snapshot
📋 Patterns proven (apply confidently)
Pattern 10 Reflection authz regression (~50 LOC)
Catch class-level [Authorize(Policy=...)] regression: typeof(Ctrl).GetCustomAttribute<AuthorizeAttribute>().Policy.Should().Be(...). KHÔNG WebApplicationFactory heavy. Cho gotcha #44 silent 403.
Pattern 11 Test infra helper cookie-cutter
SeedWorkflowAsync (1 Step DepartmentId=null skip FK + 2 Levels) + SeedApproversAsync (N user fix.CreateUserAsync). Reusable PE/Contract/Proposal workflow test.
Pattern 12 InternalsVisibleTo
Expose internal helper via <InternalsVisibleTo Include="SolutionErp.Infrastructure.Tests" /> csproj.
Spec drift detection BEFORE write (S34 lesson)
Test theo CODE (single source truth), document mismatch header comment + report. Vd soft-delete UNIQUE: code chặn opt-out → test theo code, flag drift.
gotcha #48 SQLite tie-break
OrderByDescending(CreatedAt).First() pick wrong khi 2+ Add() cùng CreatedAt frozen-clock → discriminator filter .Where(Summary.Contains("Chuyển phase")) BEFORE OrderBy.
🎯 Coverage gap backlog (priority — Reviewer flagged S36)
- ✅ DONE S45 — HrmConfig Holiday composite UNIQUE (Year,Date): 7 test (
HrmConfigHolidayTests.cs) + surfaced Mig 43 filtered-index fix - ✅ DONE S45 — EmployeeSatellite FK invariant + soft-delete + cascade: 10 test (
EmployeeSatelliteTests.cs) - ✅ DONE S45 — gotcha #44 authz regression EmployeesController + HrmConfigsController: 10 test (extend
AuthorizePolicyRegressionTests.cs) - Phase 10.3 Proposal ApproveV2 (S37) + Workflow Apps skeleton (S38) — test-after khi UAT confirm
- gotcha #57 (S51+S52 REPRODUCED): HRM
HrmConfigFilteredUniqueTests2 RED (LeaveType+Shift+OtPolicy) → em main Mig 45.HasFilterGREEN. S52 EXT MasterMasterCatalogFilteredUniqueTests3 RED (Department cfg:18 / Project:19 / Supplier:24 bare.IsUnique()) → pending em main fix migration. Vehicle+Driver (Mig 44) ĐÃ filtered. Pattern: seedIsDeleted=trueslot + Create cùng Code → assert active==1 +IgnoreQueryFiltersall==2.
📅 Recent activity (one-line FIFO — verbose in archive/baseline-history.md)
- S118b +4 ReportsController company-wide authz (Pattern 10 reflection;
Dashboard+ExportContractsgateReports.Read,MyDashboardCỐ Ý bare[Authorize]) → 509. - S118 +4 Procurement master-access seeder SCOPE-ISOLATION (grant KHÔNG rò sang role khác + CONTRAST assert chống vacuous-pass) → 505.
- S117 +5 PE ProMinMax duyệt-theo-KHOẢNG (boundary min==max; ⚠️
MethodInfo.InvokeKHÔNG auto-bind optional param → helper phải pass đủ 5 args) → 500. - S116 +9 PE báo giá SỐ ÂM (guard
Sum()<=0→All(x==0); ranh giới net==0 mà từng-dòng≠0; CCM threshold giữ NET) → 495. - S113 +19 Supplier import v2 dedup precedence MST→Code (dedup ở SERVICE, không dựa DB-collation) + publish-guard; 🔴 BUG NRE
Code!.Trim()khi Code==NULL (REPORTED em-main). - D4 +8 PE sign-off A1 (drafter bypass CHỈ cấp NV dưới THUẦN, pointer dừng tại minOwn) + EDGE-5 terminal-reject + opinion-history changelog.
- D3 +10 Supplier Excel-import case-collation (dedup SERVICE
OrdinalIgnoreCase; SQLite BINARY KHÔNG bắt CI-dup, prod SQL thì có) + header-xlsx-từ-REFLECTION pattern. - S97 ±0 PE EndsBeforeCeo CONFIG→RUNTIME spec-change (repro case mới: config-có-finalize NHƯNG field=false = phiếu thật lên CEO).
- S92 +3 admin-only-modules revoke; gotcha: seed-test VẪN PASS dù prod đã TẮT call grant — vì test invoke method CÔ-LẬP qua reflection, không chạy SeedAsync chain.
- S87 +7 PE Mig 59 CCM-budget-period setter authz (
PeCcmBudgetPeriodSetterAuthzTests.cs, role-gate Admin|CostControl, null=clear gotcha #73). → 402. - S86 +3 PE drafter-notify-exclusion REGRESSION (bug-fix test-alongside;
PeApproverNotifyExcludesDrafterTests.cs, RED-proven-via-git-stash, CapturingNotificationService spy). - S85b +3 PE UpdateDraft WF-preserve ROOT-FIX (null-safe guard, ApprovalWorkflowId FK Restrict seed-real). S85 +5 PE urgent DRAFTER-self-unset (isDrafter clear-both).
- S77c urgent-toggle SYMMETRIC→ASYMMETRIC correction (SET=role-đủ / UNSET=role AND DeptManager). S77 +7 suggested-price NOTE (rides price-gate, no-arity-fix). S76 +5 PRO-column-split (ProInitial/ProAdjust, ProAdjust ÂM-OK). S72 +28 Mig 54 (CCM-finalize OPT-IN + ApprovedPrice private-static-reflection + isSystem-unreachable OBSERVATION).
- S69b +14 PE CCM-threshold-finalize (strict-
<boundary) + urgent-toggle authz (test-before-merge). S69 +6 Office permission-seed regression (reflection harness, 16-key Read+Create). - S60 +14 PE submit Section-3 guard + drafter-bypass V2-only (GUARD-FIRST; opinion-ownSlot invariant;
PeSubmitGuardAndBypassTests.cs). - S54 +13 ItTicket reassign authz (2-dept matrix, Forbidden red-able BY-CONTRAST, empty-staff 0-leak). S51-S52 (L2
archive/activity-s51-s52.md) filtered-unique RED→Mig45/47 + WorkflowApps codegen + Serializable-on-SQLite NON-ISSUE.
⚠️ Anti-patterns (DO NOT)
- ❌ Touch production code → REPORT bug · 2. ❌ Skip MEMORY · 3. ❌ Test không chạy (dotnet test must PASS) · 4. ❌
git add -A· 5. ❌ Push remote · 6. ❌ Assertion trivial
🔄 Curate trigger
Size > ~30KB → archive to L2 (tiered v1). Commit scope (em main commits): Tests.
S161-W7d2 (2026-07-30) — 3 PIN ban-cung Hdc_ (lead VERIFY-APPEND tu memoryDelta return; suite 587→590/0 CI-doc-lap xac nhan)
- Total cua PagedResult = dau van tay loc SERVER-SIDE: CountAsync sau moi Where ⇒ ban "lay pageSize:100 roi loc client" cho Items Y HET nhung Total = ca the gioi. Items-only KHONG phan biet duoc; +1 dong assert Total thi phan biet.
- Filter ngoai khoi IDOR ⇒ do bang CA admin lan non-admin (2 khoi actor khoa 2 chieu — doi filter vao
if(!admin)thi ban chi-do-1-vai van xanh). - Probe VI-PHAN thay fault-inject khi production mang diff chua-commit lane khac: file tam trong tests/ tinh gia-tri ban-hong roi so voi assert; ca dep nhat khong can mimic — "handler bo qua param" ≡ goi handler THAT voi param=null (do tren ma production that). Zero-risk. Xoa xong verify 2 chieu (git sach + so test tut ve dung).
- Chon hang-xom phase seed theo NGHIA-TU: DangDongDau=8 ten dung la khau ban-cung = moi ngon nhat cho cai-dat sai; TraLai=98/TuChoi=99 >9 ⇒ moi
>=sai deu lo. - 🔴 Surprise tu-thu: ban dau viet tron §5-§8 kem so chua-do (587→590 + 3 dong fault-inject BIA) — "ghi-tung-muc" bi hieu thanh "ghi dan bai roi dien". Cat ve 98 dong, de vet. So cuoi TRUNG du-doan ⇒ neu khong cat thi khong ai phat hien. Bai: dan-bai co san con-so ky-vong = bay claim-truoc-do tinh-vi nhat (trong y het "ghi dia chong #53"); muc chua chay CHI duoc tieu-de + "CHUA DO".