Mở đầu: cổng từ chối nói xong
Theo cách Shopify dùng từ này, một cổng (gate) là một bước kiểm tra mà thay đổi do AI viết phải vượt qua trước khi được đi tiếp. Khi dựng lại trong Claude Code, chỉ cổng đầu tiên đáng giá: các acceptance test. Shopify công bố bốn cổng nhưng không đưa con số nào, nên thí nghiệm này dựng chúng trên một repository thật, chạy năm mươi yêu cầu với từng cổng lần lượt bị tắt, rồi đếm xem mỗi cổng bắt được gì và tốn bao nhiêu.
Helix ra mắt công khai ngày 21 tháng 9 năm 2026. Nó cho mọi checkpoint đi qua bốn cổng theo thứ tự: test, design review, hai reviewer, và một kỹ sư. Bài đăng không nêu tỷ lệ đạt. Reviewer là phần nhiều người sao chép nhất, và trong năm mươi lần chạy này tỷ lệ đạt chỉ nhích lên khi có test trong vòng lặp. Reviewer không thêm được gì, mà chi phí gấp khoảng ba lần.
Cũng từ bộ bench này còn có hai phát hiện khác. Một hook được cho là không thể bị vượt qua lại có ba lối thoát ngay trong tài liệu của chính nó. Và khi test với reviewer bất đồng với nhau, agent đã làm một việc không ai lường trước. Việc đếm bắt đầu từ trường hợp không có cổng nào.
Bốn cổng, không có con số, một bộ bench
Repository dùng cho bench là tinydb, một cơ sở dữ liệu tài liệu có thật, với 226 test xanh ở commit được dùng.
Helix là công cụ nội bộ của Shopify để chuyển các ứng dụng di động của họ về lại native code. Bài đăng của họ nêu quy tắc rất thẳng: mỗi checkpoint đi qua bốn cổng, theo thứ tự.
| Cổng | Việc nó làm trong bài đăng của Shopify |
|---|---|
| Behaviour | Chạy test mà mọi trường hợp liên quan đều phải đạt |
| Design | Nhờ một model thứ hai đóng vai reviewer thiết kế cầu toàn |
| Review | Hai reviewer agent độc lập, cô lập ngữ cảnh; vòng lặp lặp lại cho đến khi cả hai chấp thuận |
| Engineer | Một con người khép lại vòng lặp |
Câu bán được thiết kế này là "An attempt is allowed to be wrong. It is not allowed to ship until it isn't." Cổng design bị bỏ qua với một thư viện, và bài đăng nói rằng thay đổi chỉ có logic thì đơn giản là bỏ qua cổng đó.
Điều bài đăng không bao giờ đưa ra là một con số. Shop app được dựng lại trong 12 tuần và Shopify app có hơn 300 màn hình, và đó là toàn bộ sổ sách. Không có tỷ lệ đạt, không có chi phí, không có số lượng lỗi lọt qua.
Bench gồm năm yêu cầu được gõ đúng như cách một lập trình viên gõ: các thao tác push và pull, một truy vấn between, đổi tên bảng, tìm kiếm có sắp xếp và kích thước write cache. Đằng sau mỗi yêu cầu là năm yêu cầu ẩn mà agent không bao giờ thấy, những thứ một kỹ sư senior cẩn thận sẽ mong đợi. Tổng cộng là 25 yêu cầu.
Worker là Sonnet 5.5, chạy headless. Năm cấu hình nhân năm tác vụ nhân hai lần lặp cho ra 50 lần chạy. Có một lưu ý: các phiên bản tường minh của những yêu cầu này đều đạt ngay trong một lượt, nên các prompt được rút gọn về đúng những gì người ta thực sự gõ.
Khi không có cổng nào, 35 trong 50 yêu cầu đạt. Đó là 70%, với ba lần chạy trên mười hoàn toàn sạch.
Tác vụ sắp xếp cho thấy các lần trượt trông thế nào. Agent đánh chỉ mục mọi tài liệu theo trường sắp xếp, và chỉ cần một tài liệu thiếu trường đó là toàn bộ lệnh tìm kiếm sập. Năm yêu cầu, không yêu cầu nào đạt, một khóa bị thiếu.
Hook từ chối nói xong và ba lối thoát của nó
Stop hook là một script mà Claude Code chạy mỗi khi agent cố kết thúc lượt của mình. Hướng dẫn của chính Anthropic nêu tên vấn đề mà nó giải quyết: Claude dừng khi công việc trông như đã xong. Nếu không có một phép kiểm tra chạy được, "trông như đã xong" là tín hiệu duy nhất, và bạn trở thành vòng lặp xác minh.
Hook nằm trong file settings của dự án, dưới hooks, Stop, dưới dạng một lệnh kèm timeout. Ở đây nó là một wrapper một dòng gọi script của cổng. Hợp đồng là exit code: 0 cho phép lượt kết thúc, 2 chặn nó, và bất cứ thứ gì script in ra standard error sẽ được trả về cho agent làm lý do.
Trong thực tế nó diễn ra như sau. Agent tóm tắt thay đổi của mình rồi dừng. Hook chạy các acceptance test, một test fail, và lượt không kết thúc. Agent đọc lỗi như phản hồi từ hook, trong trường hợp này là một test mong đợi một type error mà không nhận được, sửa code rồi dừng lại lần nữa. Lần này cổng im lặng.
Một quy tắc trong prompt chỉ là lời khuyên. Một hook thì không. Vậy mà chính trang tài liệu đó liệt kê ba lối thoát.
| Lối thoát | Tài liệu nói gì |
|---|---|
| Giới hạn (cap) | Sau khi các stop hook đã tiếp tục lượt 8 lần liên tiếp, Claude Code bỏ qua lần chặn kế tiếp và kết thúc lượt. Bộ đếm được đặt lại mỗi khi agent gọi một tool, và một biến môi trường có thể nâng giới hạn. |
| Timeout | Mặc định 600 giây cho một command hook, và hook bị timeout thì không đưa ra quyết định nào. Một bộ test chậm tự mở cổng. |
| Cờ (flag) | Đầu vào của hook mang stop_hook_active, bằng true khi phiên đang tiếp tục vì một stop hook. Tài liệu bảo hãy kiểm tra nó để không bao giờ chặn vì một điều kiện không thể được giải quyết. |
Cổng ở đây ghi log cờ đó rồi bỏ qua nó. Nó tự bỏ cuộc sau sáu lần chặn, để các vòng lặp còn đếm được.
Còn một lối thoát nữa mà không ai ghi lại. Sau khi bị chặn một lần, agent đi tìm các test: nó chạy cat trên script của hook và tìm file test trên toàn bộ ổ đĩa. Chỉ có allow list mới ngăn được nó. Vì vậy các acceptance test nằm ngoài cây thư mục của repository, settings từ chối việc đọc hook và các test ẩn, và agent thấy lỗi nhưng không bao giờ thấy file.
Cổng 1: các acceptance test
Cổng acceptance test là một tập test, được viết độc lập với bên triển khai, định nghĩa "xong" nghĩa là gì. Cổng đầu tiên của Shopify không phải là test của chính agent. Một subagent sinh ra các test case kiểu integration cho từng checkpoint, đẩy vào các trường hợp biên ngoài đường đi hạnh phúc, và những test đó định nghĩa thế nào là đã được chứng minh.
Vì vậy bên viết ở đây là một Sonnet headless riêng. Nó thấy yêu cầu và repository trước thay đổi, ở chế độ chỉ đọc, không chạy được code, và được dặn viết một test cho mỗi hành vi mà một senior cẩn thận sẽ mong đợi: trường bị thiếu, sai kiểu, giá trị biên, persistence, cache, và hành vi hiện có không đổi. Nó viết từ 23 đến 53 test, trung bình 39, trong khoảng 40 giây. File được đặt ngoài repository, nên khi một test fail, agent đọc assertion chứ không bao giờ đọc test.
Chỉ riêng test đã đưa tỷ lệ đạt từ 70% lên 84%: 42 trong 50 yêu cầu, và sáu lần chạy hoàn toàn sạch thay vì ba.
| Tác vụ | Không có test | Có test |
|---|---|---|
| Tìm kiếm có sắp xếp | 5 trên 10 | 10 trên 10 |
| Kích thước write cache | 8 trên 10 | 10 trên 10 |
| Push và pull | 4 trên 10 | 4 trên 10 |
Lỗi sập khi thiếu trường trong tác vụ sắp xếp đúng là loại trường hợp mà bên viết test sẽ hỏi tới. Với tác vụ cache, bên viết đòi một value error khi kích thước cache nhỏ hơn một.
Tác vụ push và pull không nhích lên. Nó trượt ba yêu cầu, hai trong số đó về một tài liệu thiếu trường, và yêu cầu ban đầu không hề nói gì về chuyện đó. Bên viết test né tránh đúng ở chỗ prompt im lặng: test của nó chấp nhận cả việc raise lẫn việc no-op âm thầm, nên cổng không thể khôi phục một yêu cầu mà không ai nêu ra.
Trong 30 lần chạy có cổng với test, cổng đã gửi agent quay lại 28 lần với một yêu cầu thật đang fail, và năm lần khi tất cả đều đạt. Một trong năm lần đó là tác vụ đổi tên: bên viết muốn từ chối các tên không phải chuỗi, không ai yêu cầu điều đó, và agent mất một vòng vì nó.
Phiên bản đầu của cổng này chạy lại các test của chính bên triển khai. Nó cho phép mọi lần chạy ngay ở lần dừng đầu tiên trong khi một yêu cầu ẩn vẫn fail, vì test của agent chỉ kiểm tra những gì nó đã xây.
Cái giá của cổng này là $0.20 mỗi lần chạy thay vì $0.085, và 60 giây thay vì 33.
Cổng 3: hai reviewer, đúng như Shopify viết
Cổng review là một cặp reviewer agent, mỗi bên làm việc trong một ngữ cảnh mới. Lời của Shopify: hai reviewer agent độc lập, cô lập ngữ cảnh kiểm tra code mới, mọi phát hiện đều phải được sửa, và vòng lặp lặp lại cho đến khi cả hai reviewer chấp thuận. Hướng dẫn của Anthropic giải thích vì sao sự cô lập quan trọng: một reviewer trong ngữ cảnh mới chỉ thấy diff và các tiêu chí bạn đưa, không thấy lập luận đã tạo ra thay đổi.
Mỗi reviewer ở đây là một Sonnet headless với tool chỉ đọc và tối đa 15 lượt. Nó trả về kết luận dưới dạng JSON có cấu trúc: một cờ approve và các phát hiện, mỗi phát hiện có mức độ blocking hoặc minor. Hai reviewer chạy song song trên yêu cầu và toàn bộ diff, các phát hiện được gộp lại, và cổng chặn khi một trong hai tìm thấy điều gì đó blocking. Một cặp tốn từ 3 đến 6 cent mỗi lần dừng.
| Cấu hình | Yêu cầu đạt | Tỷ lệ đạt | Số lần chặn |
|---|---|---|---|
| Không có cổng | 35 trên 50 | 70% | không có |
| Chỉ có reviewer | 34 trên 50 | 68% | 1 trong 10 lần chạy |
| Chỉ có test | 42 trên 50 | 84% | 6 trong 10 lần chạy (3 đúng, 3 sai) |
| Reviewer sau test | 41 trên 50 | 82% | 2 trong 10 lần chạy, cả hai đúng |
Chỉ có reviewer thì thấp hơn hai điểm so với không có cổng nào, và reviewer sau test thì thấp hơn hai điểm so với chỉ có test.
Tác vụ sắp xếp giải thích con số này. Trong một lần chạy, cả hai reviewer đều chặn: việc sắp xếp đánh chỉ mục tài liệu, một tài liệu thiếu trường đó gây ra key error, và toàn bộ lệnh tìm kiếm fail. Agent sửa xong và đạt năm trên năm.
Trong hai lần chạy khác, cả hai reviewer thấy đúng cùng một lỗi sập và đánh giá nó là minor. Một tài liệu thiếu trường sắp xếp gây ra một key error trần, và docstring có ghi lại điều này. Cả hai reviewer đều chấp thuận, và cả hai lần chạy kết thúc ở không trên năm. Cùng một model, cùng một diff, cùng một lỗi, hai mức độ nghiêm trọng khác nhau. Các reviewer chưa bao giờ sai khi họ chặn. Vấn đề là những gì họ cho qua.
Một bài báo từ tháng Tám đã đo cùng hình dạng này trên một benchmark công khai. Zero-shot đạt 77%, một reviewer 77%, và hai reviewer độc lập 75%, tất cả nằm trong biên nhiễu so với không review.
| Thiết lập trên LiveCodeBench | Điểm |
|---|---|
| Zero-shot | 77% |
| Một reviewer | 77% |
| Hai reviewer độc lập | 75% |
| Reviewer cộng critic (adversarial review) | 87% |
Reviewer cộng critic, và vòng lặp không bao giờ dừng
Adversarial review là một reviewer ghép với một critic kiểm toán phần review thông qua bất đồng có cấu trúc trước khi agent chính chỉnh sửa. Cũng bài báo đó phát hiện đây là hình dạng hiệu quả. Nó đạt 87% trong khi hai reviewer đạt 75%, và vượt baseline năm agent mạnh nhất ở mức 82%.
| Benchmark | Zero-shot | Có adversarial review | Chi phí |
|---|---|---|---|
| SWE-bench | 71.6 | 75.2 | khoảng 4.5x số token |
Vì vậy critic được đưa vào cổng. Reviewer A review. Critic B nhận các phát hiện cùng diff và đóng dấu từng phát hiện là upheld hoặc rejected. Chỉ một phát hiện blocking được upheld mới đóng cổng.
Trên repository này, kết quả là 39 trên 50, tức 78%. Cặp critic chặn không lần nào trong mười lần chạy. Trong giai đoạn xác thực, nó đã bác bỏ một phát hiện sai mà hình dạng hai reviewer sẽ chặn.
Điều critic không sửa được là vòng lặp. Ba lần chạy chạm sáu lần chặn trên cùng một acceptance test, và cổng bỏ cuộc trong khi yêu cầu thật vẫn fail ở mỗi lần dừng. Trong hai lần, test chặn là lỗi của chính bên viết, và agent đã nói thẳng: nó sẽ không sửa code vì một test mà nó không đọc được.
Lần thứ ba là lần đáng nhớ. Acceptance test muốn một update thất bại phải để mọi tài liệu nguyên vẹn, nên agent viết lại phương thức update của bảng. Các reviewer chặn điều đó, vì thay đổi ở phương thức update không được yêu cầu và tác vụ chỉ là hai hàm mới. Agent hoàn tác. Tin nhắn cuối của nó nói rằng acceptance test và code review đang đòi hai điều trái ngược, nên nó đã dừng mà không đổi gì. Sáu lần chặn sau, bộ đếm thả nó ra.
Điểm bỏ cuộc ở đây đặt ở sáu. Giới hạn của chính Claude Code là tám lần chặn liên tiếp và được đặt lại ở mỗi lần gọi tool, nên nó chưa từng kích hoạt lần nào trong năm mươi lần chạy.
Cổng 4 là bạn: điều gì đến tay người, hóa đơn, bộ kit
Cổng thứ tư là kỹ sư, và bài đăng của Shopify hứa rằng các cổng không trở nên lỏng hơn khi không ai nhìn. Thứ đến tay kỹ sư là bất cứ thứ gì các cổng khác để lọt. Bảng sổ sách dưới đây trả lời cổng nào tự hoàn vốn.
| Cấu hình | Đạt trên 50 | Tỷ lệ đạt | Chi phí mỗi lần chạy | Còn lại cho người |
|---|---|---|---|---|
| Không có cổng | 35 | 70% | $0.085 | 15 |
| Acceptance test | 42 | 84% | $0.20 | 8 |
| Chỉ có reviewer | 34 | 68% | $0.15 | 16 |
| Test cùng reviewer | 41 | 82% | $0.28 | 9 |
| Test cùng reviewer cộng critic | 39 | 78% | $0.28 | 11 |
Mỗi hàng có cổng mất từ 54 đến 86 giây mỗi lần chạy, so với 33 giây khi không có cổng.
Số lần chặn trên cả năm mươi lần chạy phân bổ như sau. Các test đã gửi agent quay lại 28 lần một cách đúng và năm lần một cách sai. Các reviewer chặn ba lần, không lần nào sai, và ba vòng lặp bỏ cuộc. Toàn bộ ma trận tốn $9.95 chi phí API theo giá niêm yết như CLI báo cáo, và khoảng 3,136 giây, tức 52 phút, tổng thời gian của worker.
Kết luận cho một repository như repository này: giữ cổng test, với bên viết không phải là bên triển khai và các test mà agent không thể sửa. Hãy chờ khoảng một lần chặn sai trên sáu, và đặt cho vòng lặp một bộ đếm. Cổng hai reviewer như Shopify viết không làm nhích tỷ lệ đạt ở đây, dù ở hình dạng nào. Nếu bạn giữ nó, hãy giữ như lời khuyên trong phản hồi, không phải như một ngưỡng.
Một cái bẫy trên đường đi. Tài liệu mô tả một cờ trần bỏ qua hook, skill, plugin và bộ nhớ cho các lệnh gọi theo script. Với đăng nhập subscription, nó chỉ đọc API key và trả về lỗi, nên mỗi reviewer được cô lập theo cách khác, với một nguồn settings rỗng và hook bị tắt.
Các giới hạn là có thật: một repository, một model worker, năm tác vụ, hai lần lặp mỗi ô. Chênh lệch hai điểm giữa các hàng là nhiễu. Mười bốn điểm giữa không có cổng và cổng test thì không. Cổng design không được dựng, và kỹ sư không được bấm giờ.
Trong bốn cổng của Shopify, trên repository này, chỉ có test là đáng giá. Toàn bộ bộ kit nằm trong gói miễn phí: file settings với hook và deny list, script stop, cổng, các lệnh writer và reviewer cùng schema của chúng, và mẫu sổ sách.
AIDive