Intro: gerbang yang menolak kata selesai
Gerbang, dalam arti yang dipakai Shopify, adalah pemeriksaan yang harus dilewati sebuah perubahan buatan AI sebelum boleh lanjut. Setelah dibangun ulang di Claude Code, hanya gerbang pertama yang terbukti berguna: tes penerimaan. Shopify memublikasikan empat gerbang tanpa satu pun angka, jadi eksperimen ini membangunnya di repositori nyata, menjalankan lima puluh permintaan dengan tiap gerbang dimatikan bergantian, lalu menghitung apa yang berhasil ditangkap masing-masing dan berapa biayanya.
Helix dirilis ke publik pada 21 September 2026. Alat ini melewatkan setiap checkpoint melalui empat gerbang secara berurutan: tes, review desain, dua reviewer, dan seorang engineer. Postingannya tidak menyebut pass rate. Para reviewer adalah bagian yang paling banyak ditiru, dan pada lima puluh run ini pass rate hanya bergerak ketika tes ikut dalam loop. Para reviewer tidak menambah apa-apa, dengan biaya kira-kira tiga kali lipat.
Dua temuan lain muncul dari bench yang sama. Sebuah hook yang katanya tidak bisa ditimpa ternyata punya tiga jalan keluar di dokumentasinya sendiri. Dan ketika tes dan reviewer tidak sepakat, agent melakukan sesuatu yang tidak direncanakan siapa pun. Penghitungan dimulai dari kasus tanpa gerbang sama sekali.
Empat gerbang, nol angka, satu bench
Repositori bench-nya adalah tinydb, database dokumen nyata, dengan 226 tes hijau pada commit yang dipakai.
Helix adalah alat internal Shopify untuk memindahkan aplikasi mobile mereka kembali ke kode native. Postingannya menyatakan aturannya dengan lugas: setiap checkpoint melewati empat gerbang, secara berurutan.
| Gerbang | Apa yang dilakukannya menurut postingan Shopify |
|---|---|
| Behaviour | Menjalankan tes yang setiap kasus relevannya harus lulus |
| Design | Meminta model kedua berperan sebagai reviewer desain yang perfeksionis |
| Review | Dua agent reviewer independen dengan konteks terisolasi; loop diulang sampai keduanya menyetujui |
| Engineer | Seorang manusia menutup loop |
Kalimat yang menjual desain ini adalah "An attempt is allowed to be wrong. It is not allowed to ship until it isn't." Gerbang desain gugur untuk sebuah library, dan postingan itu menyebut perubahan yang hanya menyangkut logika memang melewatinya.
Yang tidak pernah diberikan postingan itu adalah angka. Aplikasi Shop dibangun ulang dalam 12 minggu dan aplikasi Shopify punya lebih dari 300 layar, dan hanya itulah seluruh catatannya. Tidak ada pass rate, tidak ada biaya, dan tidak ada hitungan cacat yang lolos.
Bench-nya terdiri dari lima permintaan yang diketik seperti developer mengetik: operasi push dan pull, query between, penggantian nama tabel, pencarian terurut, dan ukuran write cache. Di balik tiap permintaan ada lima syarat tersembunyi yang tidak pernah dilihat agent, hal-hal yang diharapkan seorang senior engineer yang teliti. Totalnya 25 syarat.
Worker-nya adalah Sonnet 5.5, dijalankan headless. Lima konfigurasi kali lima tugas kali dua pengulangan menghasilkan 50 run. Satu catatan berlaku: versi eksplisit dari permintaan ini lulus dalam sekali jalan, jadi prompt dipersingkat menjadi apa yang benar-benar diketik orang.
Tanpa gerbang sama sekali, 35 dari 50 syarat lulus. Itu 70%, dengan tiga run dari sepuluh yang bersih sepenuhnya.
Tugas sort menunjukkan seperti apa kegagalannya. Agent mengindeks setiap dokumen dengan field pengurutan, dan begitu satu dokumen tidak memiliki field itu, seluruh pencarian crash. Lima syarat, nol lulus, satu key yang hilang.
Hook yang menolak selesai, dan tiga jalan keluarnya
Stop hook adalah skrip yang dijalankan Claude Code setiap kali agent mencoba mengakhiri gilirannya. Panduan resmi Anthropic menyebut masalah yang ditanganinya: Claude berhenti ketika pekerjaan tampak selesai. Tanpa pemeriksaan yang bisa dijalankan, "tampak selesai" adalah satu-satunya sinyal yang tersedia, dan kamu sendiri yang menjadi loop verifikasinya.
Hook ini berada di file settings proyek, di bawah hooks, Stop, sebagai satu command dengan timeout. Di sini bentuknya wrapper satu baris yang memanggil skrip gerbang. Kontraknya adalah exit code: 0 membiarkan giliran berakhir, 2 memblokirnya, dan apa pun yang dicetak skrip ke standard error dikirim kembali ke agent sebagai alasannya.
Dalam praktik, jalannya begini. Agent merangkum perubahannya lalu berhenti. Hook menjalankan tes penerimaan, satu tes gagal, dan giliran tidak berakhir. Agent membaca kegagalan itu sebagai feedback dari hook, dalam kasus ini sebuah tes yang mengharapkan type error dan tidak mendapatkannya, memperbaiki kodenya, lalu berhenti lagi. Kali ini gerbangnya diam.
Aturan di dalam prompt hanyalah saran. Hook bukan saran. Namun halaman dokumentasi yang sama mencantumkan tiga jalan keluar.
| Jalan keluar | Apa kata dokumentasi |
|---|---|
| Batas (cap) | Setelah stop hook melanjutkan giliran 8 kali berturut-turut, Claude Code menimpa blokir berikutnya dan mengakhiri giliran. Hitungan di-reset setiap kali agent memanggil tool, dan sebuah environment variable bisa menaikkannya. |
| Timeout | Default 600 detik untuk command hook, dan hook yang kena timeout tidak menghasilkan keputusan. Test suite yang lambat membuka gerbang dengan sendirinya. |
| Flag | Input hook membawa stop_hook_active, bernilai true ketika sesi sedang berlanjut karena sebuah stop hook. Dokumen menyuruh memeriksanya agar kamu tidak pernah memblokir karena kondisi yang tidak bisa terselesaikan. |
Gerbang di sini mencatat flag itu dan mengabaikannya. Ia menyerah sendiri setelah enam blokir, sehingga loop tetap bisa dihitung.
Ada satu jalan keluar lagi yang tidak didokumentasikan siapa pun. Setelah diblokir sekali, agent mencari tesnya: ia menjalankan cat pada skrip hook dan menyisir seluruh disk untuk menemukan file tes. Hanya allow list yang menghentikannya. Karena itu tes penerimaan disimpan di luar pohon repositori, settings menolak pembacaan hook dan tes tersembunyi, dan agent melihat kegagalannya tetapi tidak pernah melihat file-nya.
Gerbang 1: tes penerimaan
Gerbang tes penerimaan adalah sekumpulan tes, ditulis terpisah dari implementer, yang mendefinisikan arti "selesai". Gerbang pertama Shopify bukan tes milik agent sendiri. Sebuah subagent menghasilkan test case bergaya integrasi untuk setiap checkpoint, mendorong edge case di luar happy path, dan tes-tes itulah yang mendefinisikan arti terbukti.
Karena itu writer di sini adalah Sonnet headless terpisah. Ia melihat permintaan dan repositori sebelum perubahan, hanya-baca, tidak bisa menjalankan kode, dan diminta menulis satu tes untuk setiap perilaku yang diharapkan seorang senior yang teliti: field yang hilang, tipe yang salah, batas nilai, persistensi, cache, dan perilaku lama yang tidak berubah. Ia menulis antara 23 dan 53 tes, rata-rata 39, dalam sekitar 40 detik. File-nya mendarat di luar repositori, sehingga ketika sebuah tes gagal, agent membaca assertion-nya dan tidak pernah membaca tesnya.
Tes saja sudah menaikkan pass rate dari 70% ke 84%: 42 dari 50 syarat, dan enam run bersih sepenuhnya, bukan tiga.
| Tugas | Tanpa tes | Dengan tes |
|---|---|---|
| Pencarian terurut | 5 dari 10 | 10 dari 10 |
| Ukuran write cache | 8 dari 10 | 10 dari 10 |
| Push dan pull | 4 dari 10 | 4 dari 10 |
Crash field yang hilang pada tugas sort persis jenis kasus yang ditanyakan writer. Untuk tugas cache, writer menuntut value error untuk ukuran cache di bawah satu.
Tugas push dan pull tidak bergerak. Tiga syaratnya meleset, dua di antaranya soal dokumen tanpa field itu, dan permintaannya tidak pernah menyinggungnya. Writer berspekulasi persis di tempat prompt diam: tesnya menerima raise atau no-op diam-diam, sehingga gerbang tidak bisa memulihkan syarat yang tidak pernah dinyatakan siapa pun.
Dari 30 run bergerbang dengan tes, gerbang mengirim agent kembali 28 kali dengan syarat nyata yang gagal, dan lima kali ketika semuanya lulus. Salah satu dari lima itu adalah tugas rename: writer menginginkan nama yang bukan string ditolak, tidak ada yang memintanya, dan agent kehilangan satu ronde karenanya.
Versi pertama gerbang ini menjalankan ulang tes milik implementer sendiri. Ia mengizinkan setiap run pada stop pertamanya padahal sebuah syarat tersembunyi gagal, karena tes milik agent menguji apa yang ia bangun.
Harga gerbang ini $0.20 per run, bukan $0.085, dan 60 detik, bukan 33.
Gerbang 3: dua reviewer, seperti tulisan Shopify
Gerbang review adalah sepasang agent reviewer, masing-masing bekerja di konteks baru. Kata-kata Shopify: dua agent reviewer independen dengan konteks terisolasi memeriksa kode baru, setiap temuan harus diperbaiki, dan loop diulang sampai kedua reviewer menyetujui. Panduan Anthropic menjelaskan mengapa isolasi penting: reviewer di konteks baru hanya melihat diff dan kriteria yang kamu berikan, bukan penalaran yang menghasilkan perubahan itu.
Setiap reviewer di sini adalah Sonnet headless dengan tool hanya-baca dan paling banyak 15 giliran. Ia mengembalikan vonis sebagai JSON terstruktur: flag approve dan temuan, masing-masing dengan severity blocking atau minor. Dua reviewer berjalan paralel pada permintaan dan diff lengkap, temuan mereka digabung, dan gerbang memblokir ketika salah satunya menemukan sesuatu yang blocking. Sepasang reviewer berbiaya antara 3 dan 6 sen per stop.
| Konfigurasi | Syarat lulus | Pass rate | Blokir |
|---|---|---|---|
| Tanpa gerbang | 35 dari 50 | 70% | tidak ada |
| Reviewer saja | 34 dari 50 | 68% | 1 dari 10 run |
| Tes saja | 42 dari 50 | 84% | 6 dari 10 run (3 tepat, 3 salah) |
| Reviewer setelah tes | 41 dari 50 | 82% | 2 dari 10 run, keduanya tepat |
Reviewer saja berada dua poin di bawah tanpa gerbang sama sekali, dan reviewer setelah tes berada dua poin di bawah tes saja.
Tugas sort menjelaskan angkanya. Dalam satu run kedua reviewer memblokir: pengurutan mengindeks dokumen, satu dokumen tanpa field itu memunculkan key error, dan seluruh pencarian gagal. Agent memperbaikinya dan lulus lima dari lima.
Di dua run lain kedua reviewer melihat crash yang persis sama dan menilainya minor. Dokumen tanpa field pengurutan memunculkan key error polos, dan docstring-nya mendokumentasikan hal itu. Kedua reviewer menyetujui, dan kedua run berakhir di nol dari lima. Model sama, diff sama, bug sama, dua severity berbeda. Para reviewer tidak pernah salah ketika memblokir. Masalahnya ada pada apa yang mereka loloskan.
Sebuah paper bulan Agustus mengukur pola yang sama pada benchmark publik. Zero-shot mencetak 77%, satu reviewer 77%, dan dua reviewer independen 75%, semuanya dalam noise dibanding tanpa review sama sekali.
| Setup di LiveCodeBench | Skor |
|---|---|
| Zero-shot | 77% |
| Satu reviewer | 77% |
| Dua reviewer independen | 75% |
| Reviewer plus critic (adversarial review) | 87% |
Reviewer plus critic, dan loop yang tak pernah berakhir
Adversarial review adalah reviewer yang dipasangkan dengan critic yang mengaudit review itu lewat ketidaksepakatan terstruktur sebelum agent utama mengedit. Paper yang sama menemukan bahwa inilah bentuk yang berhasil. Skornya 87% di mana dua reviewer mendapat 75%, dan mengalahkan baseline lima agent terkuat di angka 82%.
| Benchmark | Zero-shot | Dengan adversarial review | Biaya |
|---|---|---|---|
| SWE-bench | 71.6 | 75.2 | sekitar 4.5x token |
Maka critic dimasukkan ke dalam gerbang. Reviewer A mereview. Critic B menerima temuan beserta diff dan menandai tiap temuan sebagai upheld atau rejected. Hanya temuan blocking yang upheld yang menutup gerbang.
Pada repositori ini hasilnya 39 dari 50, atau 78%. Pasangan critic memblokir nol kali dalam sepuluh run. Saat validasi, ia memang menolak satu temuan palsu yang akan diblokir oleh bentuk dua reviewer.
Yang tidak bisa diperbaiki critic adalah loop-nya. Tiga run mencapai enam blokir pada tes penerimaan yang sama, dan gerbang menyerah dengan syarat nyata yang masih gagal di setiap stop. Pada dua di antaranya tes yang memblokir adalah kesalahan writer sendiri, dan agent mengatakannya: ia tidak akan mengubah kode demi tes yang tidak bisa ia baca.
Yang ketiga adalah yang paling perlu diingat. Tes penerimaan menginginkan update yang gagal tidak menyentuh dokumen mana pun, jadi agent menulis ulang method update milik tabel. Para reviewer memblokirnya, karena perubahan pada method update tidak diminta dan tugasnya hanya dua fungsi baru. Agent mengembalikannya. Pesan terakhirnya mengatakan bahwa tes penerimaan dan code review meminta hal yang berlawanan, sehingga ia berhenti tanpa mengubah apa pun. Enam blokir kemudian, counter melepaskannya.
Titik menyerah di sini ada di enam. Cap milik Claude Code sendiri adalah delapan blokir berturut-turut dan di-reset pada setiap pemanggilan tool, sehingga ia tidak pernah menyala sekali pun dalam lima puluh run.
Gerbang 4 adalah kamu: apa yang sampai ke manusia, biayanya, kit-nya
Gerbang keempat adalah engineer, dan postingan Shopify menjanjikan bahwa gerbang tidak menjadi lebih longgar ketika tidak ada yang mengawasi. Yang sampai ke engineer adalah apa pun yang diloloskan gerbang lainnya. Ledger di bawah ini menjawab gerbang mana yang sepadan dengan harganya.
| Konfigurasi | Lulus dari 50 | Pass rate | Biaya per run | Tersisa untuk manusia |
|---|---|---|---|---|
| Tanpa gerbang | 35 | 70% | $0.085 | 15 |
| Tes penerimaan | 42 | 84% | $0.20 | 8 |
| Reviewer saja | 34 | 68% | $0.15 | 16 |
| Tes dengan reviewer | 41 | 82% | $0.28 | 9 |
| Tes dengan reviewer plus critic | 39 | 78% | $0.28 | 11 |
Setiap baris bergerbang memakan 54 sampai 86 detik per run, dibanding 33 untuk tanpa gerbang.
Blokir di seluruh lima puluh run terurai sebagai berikut. Tes mengirim agent kembali 28 kali dengan tepat dan lima kali dengan salah. Para reviewer memblokir tiga kali, tidak pernah salah, dan tiga loop menyerah. Seluruh matriks menghabiskan $9.95 pengeluaran API harga list seperti yang dilaporkan CLI, dan sekitar 3,136 detik, atau 52 menit, total waktu worker.
Vonis untuk repositori seperti ini: pertahankan gerbang tes, dengan writer yang bukan implementer dan tes yang tidak bisa diedit agent. Perkirakan sekitar satu blokir salah dari enam, dan beri loop sebuah counter. Gerbang dua reviewer seperti yang ditulis Shopify tidak menggerakkan pass rate di sini, dalam kedua bentuknya. Kalau tetap dipakai, pakai sebagai saran di dalam feedback, bukan sebagai syarat lulus.
Satu jebakan di jalan. Dokumentasi menjelaskan sebuah flag bare yang melewati hook, skill, plugin, dan memori untuk panggilan terskrip. Pada login langganan, flag itu hanya membaca API key dan mengembalikan error, sehingga tiap reviewer diisolasi dengan sumber settings kosong dan hook dimatikan.
Batasannya nyata: satu repositori, satu model worker, lima tugas, dua pengulangan per sel. Selisih dua poin antar baris adalah noise. Empat belas poin antara tanpa gerbang dan gerbang tes bukan noise. Gerbang desain tidak dibangun, dan waktu engineer tidak diukur.
Dari empat gerbang Shopify, pada repositori ini, hanya tes yang terbukti berguna. Seluruh kit ada di paket gratis: file settings dengan hook dan deny list, skrip stop, gerbang, command writer dan reviewer beserta skemanya, dan template ledger.
AIDive