---
name: godx-ui-guinea-pig
description: "Bắt buộc cho mọi kho là consumer CHUỘT BẠCH của @godxjp/ui. Kích hoạt khi: dựng hay sửa bất kỳ màn hình nào, gặp một prop còn thiếu, định viết class Tailwind để lách, định tự dựng component thay cho thứ DS đã có, chạy ui-audit, hoặc thấy chú thích 'chờ upstream'. Dạy MỘT việc mà không tài liệu nào khác dạy: cách KHÉP VÒNG từ 'app thiếu gì' sang 'DS đã sửa, đã phát hành, app đã nâng, vá tạm đã gỡ'. Không dùng cho consumer thường — chuột bạch có nghĩa vụ sửa ngược lên DS, consumer thường thì không."
---

# Consumer chuột bạch của @godxjp/ui

> Bản gốc của tệp này nằm ở kho `godx-jp/godxjp-ui`. Sửa thì sửa ở đó rồi chép
> sang các consumer, đừng sửa bản chép — kho này đã hỏng đúng kiểu ấy một lần
> với catalog MCP (xem §4).

## 0. Bạn có HAI việc, không phải một

Việc thứ nhất là làm xong màn hình. Việc thứ hai là **để lại design system tốt
hơn lúc bạn gặp nó**. Chuột bạch là kho mà `@godxjp/ui` bị dùng thật lần đầu;
mọi khoảng trống lộ ra ở đây mà không được sửa ngược lên sẽ là khoảng trống
**vĩnh viễn** cho mọi consumer sau.

Vì vậy một bản vá tạm ở đây không phải là "nợ kỹ thuật của app". Nó là hai lần
thất bại: màn hình lệch chuẩn, VÀ khoảng trống bị giấu đi.

Bạn được toàn quyền sửa `@godxjp/ui`. Đó là điều kho này tồn tại để làm.

## 1. Trước khi viết dòng JSX đầu tiên

Định vị bản checkout của DS. Không có thì clone:

```bash
ls ~/Herd/godxjp-ui 2>/dev/null || git clone git@github.com:godx-jp/godxjp-ui.git ~/Herd/godxjp-ui
cd ~/Herd/godxjp-ui && pnpm install       # DS dùng pnpm, consumer thường dùng npm — đừng lẫn
```

Và hỏi MCP `godxjp-ui`, đừng đoán tên prop: `search_components`, `get_component`,
`get_tokens`. **Catalog là nguồn sự thật, không phải trí nhớ của bạn.**

## 2. Kỷ luật — thứ bị cấm kể cả khi "chỉ tạm thôi"

Mười luật ở `docs/CONSUMER-RULES.md` của DS là hàng rào, `ui-audit` cưỡng chế
chúng. Không chép lại ở đây. Ba điều cấm riêng của **chuột bạch**:

1. **Tự dựng component thay cho thứ DS đã có hoặc lẽ ra phải có.** Hộp tự vẽ
   thay `Card`, hàng tự ghép thay `ListRow`, palette tự viết thay
   `CommandPalette`.
2. **Dùng class tiện ích để lách một prop còn thiếu** — `gap-3`, `p-4`,
   `w-[240px]`, `text-muted-foreground`.
3. **Gõ mã màu hex hay số đo ngoài thang token.**

Thước đo, chạy trước mọi lần review — **quét đúng thứ bạn vừa đụng vào**:

```bash
node node_modules/@godxjp/ui/scripts/ui-audit.mjs --changed   # 0 lỗi là mức đạt
```

`--changed` lấy danh sách từ diff so với merge-base, nên nó thấy cả file sửa bằng
shell — thứ hook `PostToolUse` không thấy. Quét cả `resources/js` chỉ khi việc được
giao ĐÚNG là soát toàn bộ UI: trên một kho cũ, nó trả về hàng trăm phát hiện thuộc
mã kế thừa và chôn mất cái bạn vừa tạo ra.

Một lỗi bạn **không sửa được ở phía consumer** chính là một khoảng trống của DS.
Nó là đầu vào của §3, không phải một ngoại lệ để nới.

## 3. Vòng lặp — sáu bước, và nó phải KHÉP

Đây là phần không tài liệu nào khác có. `design-to-page` và `compose-a-screen`
đều dừng ở `report-bug`; mở issue rồi để đó là hỏng nửa vời.

### Bước 1 — Chứng minh đó là khoảng trống, đừng cảm thấy

Viết ra đúng đoạn mã bạn **muốn** viết, rồi chạy `ui-audit` lên nó.

Audit xanh nghĩa là **không có gì CẤM** bạn — chưa phải là bạn có nước đi. Nước
đi chỉ có thật khi bạn MỞ TRANG và thấy nó đổi pixel. Đo được: consumer viết
`modifiers` + `modifiersClassNames` cho màu cuối tuần, audit xanh, tsc xanh,
build xanh, và **số màu chữ trên cả lưới vẫn là 1** — class rơi vào `<td>` còn
`<button>` tự đặt màu. Một API chết im lặng trông y hệt một API đang chạy.

Chỉ khi mọi prop và token hiện có đều không nói được điều cần nói, VÀ mọi đường
còn lại đều bị audit chặn, thì mới là bất khả.

### Bước 2 — Ba câu hỏi, phải đủ cả ba

Đọc `docs/WHAT-BELONGS-HERE.md` của DS. Tóm tắt không thay thế nó:

1. Consumer có thật sự **không có nước đi hợp lệ** nào không? (không phải "bất tiện")
2. Nó thuộc về **hình dạng** của component, hay **nội dung** của một màn?
3. Consumer **khác** có cần không?

Trượt bất kỳ câu nào → dựng ở consumer, và ghi rõ TRONG MÃ vì sao nó không
thuộc về DS.

### Bước 3 — Sửa trong DS, và sửa đủ bốn chỗ

```
src/…            mã
src/…/__tests__/ test (§5)
mcp/src/data/    catalog (§4 — chỗ hay quên nhất, và tốn kém nhất)
docs/            nếu đổi hợp đồng công khai
```

Thứ tự ưu tiên, chỉ tiến khi bước trước thật sự không diễn đạt nổi:
**dùng → ghép → thêm prop vào component đã có → tạo component mới.**
Một prop nữa hơn một component nữa.

**Và TÊN không phải của bạn — nó là của antd.** `docs/DESIGN-AUTHORITY.md` (mục
"The PROP SURFACE of a component is antd's too"): **nơi antd đặt tên cho một năng
lực, kho này lấy nguyên tên và nguyên ngữ nghĩa của antd. Một năng lực còn thiếu
được port từ antd 100% TRƯỚC — tên, prop, ngữ nghĩa — rồi mới cải tiến. Không
thiết kế lại trước, và không port một nửa.**

Đây là luật mới nhất và là luật hay bị bỏ qua nhất, vì nó nghe như lời khuyên.
Cái giá của việc bỏ qua đã đo được: `DataTable` mọc `pin: "end"` nơi antd có
`fixed`, `sortable: true` nơi antd có `sorter`, và không có câu trả lời nào cho
filter/expandable — mỗi lần một người quyết một kiểu. Cùng hình dạng lỗi:
`Dialog` + `AlertDialog` từng là 26 export với **12 cặp trùng tên** và **0** phần
chỉ `AlertDialog` mới có, trong khi antd chỉ có MỘT `Modal` (nguy hiểm là một
PROP, `okType`). Nay là một họ, `variant` là lối chuẩn, 12 export cũ ở lại vì gỡ
là breaking. Và cả họ Ant Design X từng tới nửa vời — thiếu `Conversations`,
`Attachments`, `ThoughtChain`, `Welcome`, `Actions`, nay đã đủ.

Ba chỗ cố ý lệch khỏi antd (logical thay `left/right`, từ vựng giá trị của kho
này, `density` thay `size`) đều **ghi lý do trong DESIGN-AUTHORITY**. Lệch thêm
thì phải viết vào đó, không lệch lặng lẽ. Và đọc bề mặt antd từ **type đã cài ở
một checkout khác**, không từ trí nhớ — `antd` đã bị gỡ khỏi devDependencies của
kho này từ 20.0.0 và `check:no-antd-runtime` canh cho nó không quay lại.

### Bước 4 — Kiểm bằng tarball TRƯỚC khi phát hành

Đây là bước làm cho "vừa làm vừa trải nghiệm" thành thật. Đừng phát hành rồi
mới biết mình sửa trượt.

```bash
cd ~/Herd/godxjp-ui
pnpm build && npm pack                    # ra @godxjp-ui-<version>.tgz
cd <kho consumer>
npm install ~/Herd/godxjp-ui/godxjp-ui-<version>.tgz
```

Rồi chạy màn hình thật với bản sửa: `ui-audit` phải sạch **và** đoạn mã bạn
muốn viết ở Bước 1 phải chạy đúng. Nếu kho có bộ trình duyệt, chạy nó.

**Xong việc thì hoàn nguyên `package.json` về bản registry** — đừng để một
tarball đường dẫn máy bạn lọt vào commit. `main` phải `npm ci` được từ registry.

### Bước 5 — Cổng của DS

**KHÔNG chạy `pnpm verify:ci` / `pnpm test` của kho DS khi chưa được chủ dự án cho
phép trong chính lượt trao đổi ấy.** Đó là bộ đầy đủ của một kho KHÁC — hàng nghìn
test cộng các cổng trình duyệt, hàng chục phút máy mỗi lượt. Chạy đúng test bạn vừa
viết cho thay đổi này, đẩy nhánh, rồi đọc cổng đỏ trên Actions: `ci.yml` chạy y hệt
những thứ ấy trên PR. **Tới được bước này KHÔNG phải là được phép.**

**Không nới, không tắt, không thêm ngoại lệ để lấy màu xanh.** Một cổng đỏ là
một câu hỏi, không phải một chướng ngại. Nếu bạn tin cổng ấy sai thì nói ra và
đưa số đo, đừng lặng lẽ sửa nó.

**VÀ ĐỪNG NGỒI CHỜ CI.** Đẩy nhánh, mở PR, rồi đi làm việc khác. Không có vòng lặp
`until … gh pr checks … sleep` nào cả. CI chạy là việc của CI; nếu cần theo dõi thì
mở một agent nền, đừng chặn người đang điều phối. Đo được ngày 12/09/2026: một lượt
ngồi poll bốn shard đã ăn hơn một tiếng đồng hồ của chủ dự án để nhìn một thanh tiến
trình, trong khi có việc khác đang xếp hàng.

**Và full suite thì chạy theo LỊCH, không nằm trong vòng lặp sửa code của ai.** Cùng
ngày, tôi thêm bốn shard vitest vào làn PR của kho DS và đặt chúng thành required
check, để bịt một khoảng trống có thật (hai commit vào `main` đỏ qua một làn nhanh
xanh). Khoảng trống là thật và phép đo trung thực — nhưng nó chỉ định giá **máy**.
Máy chưa bao giờ là phần đắt. Từ lúc ấy mọi PR phải chờ bốn shard mới merge được.
Đã hoàn nguyên. Một hàng rào làm người điều phối phải chờ không rẻ hơn một `main`
đỏ; nó chỉ dời chi phí sang con đường duy nhất không song song hoá được.

### Bước 6 — Khép vòng

Phát hành → nâng gói ở consumer → **gỡ vá tạm** → **gỡ mọi chú thích "chờ
upstream"** → **đóng issue**.

Vòng chưa khép thì việc chưa xong. Đợt 07–08/09/2026 mở 18 issue cho godxjp-ui;
#401/#412 và #402 đã vá trên nhánh nhưng issue vẫn mở và consumer vẫn chờ —
đó là hình dạng của thất bại này.

## 4. Nghĩa vụ catalog — chỗ tốn kém nhất khi quên

**Một prop có trong mã nhưng không có trong catalog MCP là một prop KHÔNG TỒN
TẠI** với agent tiếp theo.

Đây không phải suy đoán. Đo được trong phiên 08/09/2026: một agent mới, làm
đúng mọi hướng dẫn (hỏi MCP, không đoán), kết luận _"Flex chỉ có gap"_ trong khi
`pad` và `padRaw` đã nằm trong gói đã cài — vì catalog đã phát hành chưa có
chúng. Nó làm đúng và vẫn ra sai.

Nên sau mỗi lần thêm hay đổi prop:

```bash
node scripts/gen-component-api-manifest.mjs
pnpm check:mcp-sync && pnpm check:mcp-prop-sync && pnpm check:component-api-manifest
```

Và kiểm ví dụ trong `mcp/src/data/{patterns,components}.ts` có **biên dịch được
và qua nổi ui-audit** không. Catalog đã từng dạy: `<Dialog mode="confirm">`
(không có API ấy), `variant="success"` (bị chính audit cấm),
`<Input onValueChange>` (không có prop ấy). Ví dụ sai trong catalog không phải
lỗi chính tả — nó là mã mà agent sau sẽ chép.

## 5. Kỷ luật test của DS — luật, không phải gợi ý

- **Test theo TỪNG component**, và chỉ những component **liên quan** tới nó.
  `Dropdown` = menu + list + button + icon → chỉ quanh chừng đó. Không test lan man.
- **Test của story/example KHÔNG nằm trong CI.** Chúng ở `tests/manual/`, chạy
  bằng tay. CI không tiêu thời gian vào thứ vô bổ.
- Test bám vào **role và nhãn**, không bám class Tailwind — có cổng
  `check:no-tailwind-class-assertions` chặn.

Vì sao role/nhãn: 138 trên 160 selector của bộ Playwright ở consumer bám vào
`getByRole`/`getByLabel`. Chúng không phải nghi thức a11y — chúng là cái cân cho
biết một lần đổi nền thư viện có làm vỡ CẤU TRÚC hay không. Gỡ chúng là mất cân.

Nhưng chúng **không đo được màu, bo góc, hay phần tử nào đang được tô** — cả một
đợt lỗi calendar đi qua chúng mà không cái nào đỏ. Cấu trúc và diện mạo là hai
thước khác nhau; xem §5c.

## 5b. Một cổng canh được đúng thứ nó CHẠY QUA

Một cổng viết ra mà không nối vào CI là một cổng không tồn tại — y hệt một prop
không có trong catalog (§4). Nhưng "đã nối" chưa đủ, và đợt 08/09/2026 đo được
cả hai nửa của bài học này.

**Nửa thứ nhất — đừng đo bằng cái thước sai.** Bản trước của mục này viết rằng
"25 cổng `check:*` nằm ngoài `verify:ci`", và kết luận từ đó rằng chúng không
chạy. Kiểm lại bằng workflow thì 22 trong 25 cổng ấy VẪN chạy mỗi lần merge hoặc
mỗi đêm — chỉ là ở lane khác. Lý do rất đơn giản và rất dễ vấp: **không workflow
nào chạy `verify:ci`.** `ci.yml` chạy `verify:ci:static` + `check:frame-contracts`

- `pnpm test` chia shard; `ci-browser.yml` chạy `verify:browser` (gồm
  `check:contrast`) và `check:frame-axe`; `ci-browser-full.yml` chạy các sweep rộng
  theo lịch đêm; `release-integrity.yml` chạy `check:release-plan`. Hỏi "cổng này
  có trong `verify:ci` không" là hỏi một script không ai gọi.

Nên đừng tự viết đoạn `node -e` so với `verify:ci`. Hỏi thẳng cổng canh-cổng, nó
đọc workflow rồi mới trả lời:

```bash
pnpm check:gate-coverage --report   # in ra: mỗi check:* chạy ở workflow nào
pnpm check:gate-coverage            # đỏ nếu có cổng không ai chạy và không khai miễn trừ
```

Cổng này nằm trong `verify:ci:static`, nên thêm một `check:` mới mà quên nối vào
đâu là CI đỏ ngay. Muốn để một cổng ngoài lề thì phải khai TƯỜNG MINH kèm lý do
trong `EXEMPT` của `scripts/check-gate-coverage.mjs` — hiện có đúng hai cái, và
cả hai đều là "cần người", không phải "chạy lâu": `check:voiceover-capture` (cần
VoiceOver thật do người bật) và `check:frame-runtime` (chỉ là alias gọi tám cổng
đã chạy riêng trong lane đêm). "Chạy lâu" không phải lý do để miễn trừ — đó là
lý do để nằm trong lane đêm, và lane đêm VẪN được tính là có chạy.

**Nửa thứ hai, và là nguyên nhân thật của lỗi đã lọt.** Một toast
`data-type="success"` với tương phản **1,02:1** — chữ gần như vô hình — phát hành
trong `@godxjp/ui@19.5.0` và bị bắt bởi **test trình duyệt của một consumer**.
Kho DS có sẵn hai thứ đáng lẽ phải bắt được nó, và **cả hai đều đã chạy trong
CI**:

- `scripts/check-contrast.mjs` chạy mỗi lần merge trong job "Contrast + visual
  audit" (55–59s, và tên job nằm trong `REQUIRED_CI_CHECK_RUNS` nên bản phát
  hành không đi qua nổi nếu nó đỏ). Nó xanh — vì danh sách `ROUTES` của nó có 11
  route và **không route nào render một cái toast**.
- `src/components/feedback/__tests__/toast-tone-contrast.test.tsx` chạy trong
  `pnpm test`. Nó xanh — vì nó đọc token trong `src/tokens/**` rồi tính tỉ số
  trên giấy; nó chạy trong jsdom, mà jsdom **không tô màu**, nên nó không nhìn
  thấy màu đã render thật.

Không cổng nào bị tắt. Không cổng nào bị bỏ quên. Cả hai đều xanh và cả hai đều
đúng với thứ chúng đo — chỉ là **không cái nào đo cái đã hỏng**. Đây là dạng
hỏng đắt hơn hẳn dạng "quên nối cổng", vì bảng CI toàn xanh trông y hệt như một
kho thật sự an toàn.

`check:contrast` đã dính đúng dạng này một lần rồi, và vết sẹo còn nằm trong
chú thích của chính nó: danh sách route từng trỏ vào `tiximax-*` sau khi các
route đó bị đổi tên, nên chúng render ra "Showcase not found" và sweep báo trang
rỗng ấy là AA sạch. Lần đó người ta thêm một guard chặn not-found. Lần này là
cùng một hình dạng ở một trục khác: route tồn tại, nhưng bề mặt cần soi thì
không có route nào chạm tới.

Nên khi bạn thêm hay sửa một cổng, hỏi HAI câu chứ không phải một:

1. **Nó có chạy không?** → `pnpm check:gate-coverage --report`.
2. **Nó có đi qua bề mặt tôi vừa đụng không?** → mở chính danh sách đầu vào của
   cổng (`ROUTES` của `check-contrast.mjs`, danh sách frame của `check:frame-axe`,
   `include` của `vitest.config.ts`) và tìm bề mặt ấy trong đó. Cổng xanh trên
   một danh sách không chứa thứ bạn vừa sửa thì nó chưa nói gì về bản sửa của bạn.

Và một hệ quả cho phía consumer: **bộ test trình duyệt của bạn là lớp lưới cuối
cùng của DS.** Hai lỗi tương phản trên do `php artisan test` của consumer bắt
được, không phải do CI thư viện. Đừng bỏ axe ra khỏi bộ trình duyệt chỉ vì "hệ
thống nội bộ" — ở đây nó không đo tuân thủ, nó đo xem DS có phát ra chữ đọc được
hay không.

## 5c. CSS hỏng IM LẶNG theo ba cách — và cách duy nhất thấy được

Một luật CSS sai không báo lỗi, không cảnh báo, và đọc lên vẫn thuyết phục. Ba
cơ chế, cả ba đo được trong một ngày:

1. **Nhắm vào class không tồn tại.** `weekdays: cn("flex", …)` — không có class
   DS, nên mọi luật viết cho `.ui-calendar-weekdays` chưa từng khớp lần nào.
2. **Thua tầng khác.** `buttonVariants` đặt `rounded-[var(--button-radius)]` như
   một Tailwind **utility**, mà `utilities` sau `components` — nên không luật
   components nào đổi được bo góc của một Button. Phải trỏ lại chính biến đó.
   Cùng lớp: CSS bên thứ ba nhập KHÔNG layer thắng mọi thứ; nhập sai vị trí
   layer thì thua cả reset. Thứ tự đúng: `theme, base, vendor, components, utilities`.
3. **Class ở phần tử này, sơn ở phần tử kia.** RDP đặt `day-selected` lên `<td>`,
   còn nền/bo góc ở `<button>` bên trong → ngày chọn ra hình vuông sắc trong khi
   hover thì tròn. Quy tắc: **một phần tử sở hữu bề mặt**, mọi trạng thái tô lên nó.

Cách duy nhất phát hiện: **mở trang, `getComputedStyle`, rồi CHỤP MÀN HÌNH.** Đo
đúng thuộc tính vừa sửa là chưa đủ — ba lần liên tiếp tôi báo "xong" trong khi
khối đó đang vỡ ở chỗ khác.

Token màu có HAI TẦNG: `--success/--warning/--info/--destructive` là màu **TÔ**;
chữ phải đọc `--text-success/-warning/-info/-error`. Đo: `Text tone="warning"`
đọc nhầm tầng cho **1,74:1**, đúng tầng cho **5,90:1**.

## 5d. Tra catalog TRƯỚC khi tự dựng — bốn lần trong một ngày

`CardContent flush` (đường kẻ chạm mép), `CardHeader banded` (header có kẻ khi
thân là danh sách flush), `ListRow asChild` (hàng LÀ liên kết, thay cho một nút
rời), `Calendar bordered` — cả bốn **đã có sẵn** và tôi vẫn tự dựng bằng thứ
khác, vì không hỏi. Lỗi không phải "đoán sai tên prop" mà là **cho rằng thứ đó
không tồn tại nên không hỏi**.

Trước khi viết bất kỳ bố cục nào: `search_components` + `get_component`. Rẻ hơn
mọi lần sửa sau.

**Catalog từng chở PROP mà không chở LUẬT BỐ CỤC — và ví dụ ấy nay đã được vá,
nên đừng trích nó nữa.** Bản trước của mục này viết: `CardBar` trong manifest có
đúng một prop (`extra`), không dòng nào nói nó tự lấy đường kẻ theo VỊ TRÍ. Đo
lại hôm nay: `CardBar` có **6 prop**, trong đó `border` (`"none" | "block-start"
| "block-end" | "both"`) ép được đường kẻ khi xếp chồng, và luật vị trí (đầu: kẻ
dưới · cuối: kẻ trên · giữa: cả hai) nằm **trong chính catalog** — ở `usage` của
`CardBar` lẫn `description` của `Card`.

Bài học còn lại vẫn thật, chỉ đổi hình: một luật bố cục **có thể** chỉ sống
trong chú thích CSS, và không có cổng nào bắt nó phải lên catalog. Nên:

1. Hỏi `get_component` trước — nay nó thường trả lời cả hình dạng lẫn luật.
2. `usage`/`description` im lặng về bố cục → mở `src/styles/*-layout.css` của
   component ấy ra đọc (chỗ định nghĩa hai nhịp `section` và `band` chẳng hạn).
3. Là chuột bạch, khi bước 2 phải dùng tới, đó là **khoảng trống catalog** — đưa
   luật ấy lên `usage` trong cùng PR, theo §4. Đó là cách `CardBar` được vá.

## 6. Thứ KHÔNG đẩy lên DS

- Bố cục của một trang cụ thể ("dashboard cần bốn thẻ ngang").
- Số đo của một màn ("cột vai trò rộng 8rem").
- Bất cứ thứ gì biết về miền nghiệp vụ của app này.

DS sở hữu **hình dạng**. Màn hình sở hữu **nội dung**. Đẩy nhầm hướng làm DS
phình ra thành thứ không ai nhớ nổi — cũng hỏng như để nó quá hẹp.

## 7. Năm cách hỏng đã đo được — đừng lặp lại

1. **Chẩn đoán bằng mắt rồi sửa.** Một lần đổ lỗi lệch header cho DS; hoá ra là
   heuristic `onChat` của chính consumer. Đo trước, sửa sau.
2. **Làm tròn số đo cho sạch lint.** Thiết kế cần 12px, thang bậc tên có 8 và
   16 — "gần nhất" là một phép đoán, và mỗi khe lệch 4px × n phần tử là cả khối
   trôi. Giữ nguyên literal và mở đường cho DS.
3. **Đọc nhầm nguồn rồi kết luận chắc nịch.** Một lần đọc `.ui-inline-*` trong
   khi thứ đang chạy là `.ui-flex-gap-*`, rồi tuyên bố "đã sửa rồi". Trích đúng
   dòng đang chạy, không phải dòng trông giống.
4. **Chạy audit sai chỗ.** `ui-audit` chỉ báo lỗi khi chạy TRONG cây consumer;
   chạy nó ở `/tmp` ra 0 lỗi và ru ngủ.
5. **Phép thử đột biến không thật sự đột biến.** Một lượt
   `perl -0pi -e 's/data-slot="x"/BROKEN/'` thiếu cờ `/g` chỉ thay lần khớp ĐẦU
   TIÊN — mà lần đầu lại nằm trong một dòng chú thích, nên mã chạy không hề đổi
   và phép kiểm "không đỏ". Suýt kết luận rằng assertion là rỗng. Sau khi phá,
   hãy XÁC NHẬN mình đã phá đúng chỗ (`git diff` một dòng) trước khi tin vào kết
   quả màu.
