Sepuluh agen menjalankan type check yang sama sekaligus. Solusinya sebuah folder.
Tujuh belas agen coding dalam satu checkout, sepuluh proses tsc bersamaan, load average 37 dan sisa RAM 87 MB. Type check yang biasanya sembilan puluh detik memakan 7 menit 36 detik. Ini hasil pengukurannya, alasan mesin sebenarnya tidak sedang menghitung, dan kunci bersama kecil yang menyelesaikannya. Bisa disalin ke repositori mana pun.
Pada 7 September, mesin pengembangan kami berhenti merespons dengan benar. Bukan crash, bukan hang. Semuanya hanya jadi sepuluh kali lebih lambat, termasuk hal-hal yang tidak ada hubungannya dengan kode.
Tersangka yang paling jelas ternyata salah semua. Laptopnya tidak kepanasan: tidak ada throttling yang tercatat, baterai di 30,6 C. Tidak ada proses liar yang melahap CPU. Tidak ada yang baru dideploy. Satu-satunya hal tidak biasa adalah tujuh belas CLI agen hidup di repositori yang sama, dan itu hari kerja normal di tempat kami.
Ini yang sebenarnya terjadi, diukur, bukan ditebak.
Pengukurannya
Mesin 16 GB, 8 core, menyala sejak lima setengah jam, tujuh belas agen bekerja:
| Yang kami ukur | Nilai |
|---|---|
| CLI agen yang hidup | 17 |
Proses tsc --noEmit bersamaan, terlihat dalam dua menit | 3, lalu 10 |
| Load average | 37 sampai 41 |
| RAM bebas / kompresor memori | 87 MB / 7,2 GB |
| Satu type check di aplikasi desktop, mesin jenuh | 7 min 36 waktu dinding untuk 26 s CPU |
| Type check yang sama, mesin tenang | 33 s |
Baris yang menentukan adalah yang kedua dari bawah. Dua puluh enam detik CPU yang tersebar di tujuh setengah menit berarti utilisasi sepuluh persen. Type check itu tidak sedang menghitung. Ia sedang menunggu memori.
Dan salah satu proses itu dibunuh sistem operasi di tengah jalan. tsc yang dibunuh keluar dengan kode bukan nol dan output kosong, yang tidak bisa dibedakan dari error tipe sungguhan. Jadi selain lambat, mesinnya juga memproduksi vonis yang tidak bisa dipercaya siapa pun.
Tidak ada yang berbuat salah
Bagian ini yang layak direnungkan, karena inilah yang membuat kegagalannya begitu sulit dilihat datang.
Setiap agen itu mengikuti aturan. Masing-masing telah mengedit TypeScript. Masing-masing diminta memverifikasi tipenya sebelum menyerahkan pekerjaan. Masing-masing menjalankan tsc --noEmit. Tidak satu pun melihat yang lain. Tidak ada papan bersama tempat seorang agen menulis "aku sedang mengerjakan yang mahal, tunggu sebentar".
Lalu keadaannya memberi makan dirinya sendiri. Type check melambat karena mesinnya jenuh. Agen yang mengawasinya menyimpulkan bahwa prosesnya macet. Maka ia membunuhnya dan menjalankan satu lagi. Refleks itu benar kalau sendirian dan bencana kalau berkelompok, dan ini keluarga kegagalan yang sama dengan yang kami dokumentasikan sebulan sebelumnya ketika agen meninggalkan proses pencarian yang macet: Process Guard adalah jaring yang menemukan apa yang sudah terlanjur jalan, di sini kami mencegah jalannya sejak awal.
Tiga jawaban yang tidak kami ambil
Jalankan lebih sedikit agen. Ini membelah gejalanya dan menyisakan bugnya. Dua type check bersamaan di mesin yang sarat tetap lebih lambat daripada satu, dan memangkas armada berarti membayar masalahnya dengan hal yang justru membuat pekerjaan jadi cepat.
Satu type check di akhir. Menggoda, dan keliru karena alasan yang sama sekali bukan soal performa. Error tipe yang ditemukan sepuluh tiket kemudian jadi yatim: agen yang menulisnya sudah ditutup, konteksnya hilang, dan seorang manusia harus membuka lagi seluruh topik untuk memperbaiki satu baris. Kami tidak mau menunda verifikasinya.
Kompilasi inkremental. Sudah dicoba dan dibuang. Keuntungannya meragukan dalam mode --noEmit, dan proses yang berjalan bersamaan merusak .tsbuildinfo yang dipakai bersama. Ia menyelesaikan separuh masalah dengan memperburuk separuh sisanya.
Yang kami lakukan sebagai gantinya: satu pemeriksaan, dibagi bersama
Aturannya bukan "periksa lebih jarang". Aturannya satu type check pada satu waktu, per proyek, untuk semua orang. Sebuah skrip pembungkus mengganti N pemeriksaan dengan satu, dan menjawab tiga kasus:
- Tidak ada yang berubah sejak eksekusi terakhir, jadi kembalikan hasilnya.
- Sebuah eksekusi sudah berjalan, jadi tunggu dan ambil hasilnya.
- Kalau tidak, ambil kuncinya dan jadi satu-satunya
tscdi mesin ini.
Dari sudut pandang agen tidak ada yang berubah: ia mengetik yarn typecheck, ia mendapat error tipenya. Ia juga tidak pernah menunggu lebih lama dari sebelumnya, karena eksekusi yang ia tunggu adalah eksekusi yang dimulai sebelum eksekusinya sendiri. Mesin membayar satu, bukan sepuluh.
Itu saja seluruh idenya. Yang menarik, kedua mekanisme yang dibutuhkannya jauh lebih kecil daripada yang kamu bayangkan.
Kuncinya adalah sebuah folder
Bukan file, bukan basis data, bukan daemon. Sebuah folder.
try {
mkdirSync(lockDir); // berhasil: kuncinya milik kita
} catch (err) {
if (err.code === 'EEXIST') { /* orang lain memegangnya, kita tunggu */ }
}
mkdir entah membuat foldernya atau gagal dengan EEXIST, dan ia melakukannya secara atomik di macOS, Windows, dan Linux, tanpa dependensi dan tanpa panggilan native. Menulis file lalu memeriksa apakah file itu ada akan jadi dua operasi, dan dua operasi persis di situlah agen kedua menyelinap masuk.
Di dalam folder itu kami menaruh owner.json berisi pid, hostname, dan waktu mulai. File itu untuk diagnosis dan untuk mendeteksi kunci yang mati. Bukan file itu yang mengeksklusi.
Kunci yang mati diambil alih otomatis dalam dua kasus: proses pemiliknya sudah lenyap (dicek hanya bila hostname-nya cocok, karena pid tidak berarti apa-apa antar mesin), atau kuncinya sudah lebih tua dari lima belas menit.
Satu jebakan yang membuat kami membayar sebuah bug. Di antara mkdir dan penulisan owner.json ada jendela waktu di mana pemiliknya tidak terbaca. Menyatakan kunci itu mati di dalam jendela tersebut sama dengan mencurinya dari yang baru saja mengambilnya, yaitu tepat balapan yang ingin dicegah oleh file tadi. Jadi ketika tidak ada pemilik yang terbaca, kami menilai dari umur foldernya, bukan dari file yang hilang.
Sidik jarinya adalah sebuah tanggal dan sebuah hitungan
Kasus 1 perlu tahu apakah ada yang berubah sejak eksekusi terakhir. Jawaban yang jelas adalah menghitung hash file sumbernya. Kami tidak melakukannya.
Sidik jarinya adalah <mtime paling baru>:<jumlah file> pada akar-akar yang diturunkan dari include di tsconfig, ditambah tsconfig itu sendiri.
Pada 2.300 file, membaca setiap byte lebih mahal daripada pemeriksaan yang dihematnya. Tanggal saja tidak melihat penghapusan. Hitungan saja tidak melihat pengeditan. Bersama-sama keduanya menutup dua-duanya. Negatif palsu yang kami terima adalah dua pengeditan dalam milidetik yang sama yang menyisakan jumlah file identik, dan kasus terburuknya di situ adalah hasil cache yang basi beberapa detik, tidak pernah error tipe yang lolos diam-diam, karena verifikasi yang benar-benar memblokir tetap verifikasi build.
Aturan tertulis ternyata tidak cukup, jadi kami menambahkan hook
Instruksinya ada di AGENTS.md sejak hari pertama: jangan pernah menjalankan tsc langsung, selalu lewat skrip bersama. Itu tidak cukup, dan alasannya layak dikatakan dengan jujur.
Memverifikasi tipe setelah mengedit adalah refleks yang tertanam dalam. Di bawah tekanan, seorang agen mengetik npx tsc --noEmit tanpa membaca ulang instruksinya. Dan cukup satu agen menyimpang dari aturan untuk menciptakan lagi penumpukan yang ingin dicegah oleh kunci itu. Instruksi bisa didebat. Hook tidak.
Maka kami memasang hook PreToolUse pada tool Bash, yang menolak tsc langsung dan menyebutkan perintah yang benar di dalam penolakannya:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "node \"$CLAUDE_PROJECT_DIR/scripts/hooks/block-direct-tsc.mjs\"" }
]
}
]
}
}
Hook itu membaca panggilan tool sebagai JSON di stdin, keluar dengan kode 2 beserta alasannya di stderr untuk menolak, dan keluar dengan 0 untuk meloloskan. Dua detail membuat perbedaan antara hook yang berguna dan hook yang menyebalkan.
Ia mengenali tsc di posisi perintah, bukan di sembarang tempat dalam string. Mencari tiga huruf itu di mana saja akan ikut menolak grep -rn tsc AGENTS.md. Jadi polanya menuntut tsc ada di awal baris atau sesudah ;, &&, ||, | atau (, boleh didahului sebuah package runner dan sebuah path. Ia juga meloloskan tsc --version: tidak ada alasan menolak flag yang cuma memberi informasi.
Ia menolak satu hal kedua yang tidak kami antisipasi. Kami melihat seorang agen menunggu di belakang kunci, memutuskan setelah beberapa saat bahwa kunci itu pasti sudah mati, lalu menghapus folder kuncinya supaya dirinya tidak terhalang lagi. Itu memulai proses berat kedua di samping yang masih hidup, yaitu jalan pintas sempurna yang membatalkan semua yang dilindungi kunci tersebut. Maka menghapus folder kunci atau folder cache juga ditolak, dengan penjelasan bahwa kunci yang mati diambil alih dengan sendirinya.
Penolakan kedua itu tidak akan pernah kami tulis di awal. Ia lahir dari mengamati apa yang benar-benar dilakukan agen ketika mereka terhalang, dan itu sumber pagar pengaman yang lebih baik daripada membayangkan apa yang mungkin mereka lakukan.
Sampai mana ini berhenti
Hook-nya khas Claude Code. CLI agen lain di armada hanya melihat aturan tertulis. Itu lubang yang kami tahu dan kami terima: pagar pengaman yang menutup sebagian besar armada lebih baik daripada tidak ada pagar sama sekali sambil menunggu standar hook yang dibaca semua CLI.
Skrip bersamanya sendiri netral terhadap penyedia, karena ia cuma sebuah perintah. CLI mana pun yang bisa menjalankan yarn typecheck ikut menikmati kuncinya, entah ada yang memaksanya atau tidak.
Yang perlu kamu bawa pulang
Type check adalah kasus kami yang paling berisik, bukan kasus khusus. Polanya berlaku untuk setiap perintah yang mahal, idempoten dalam jendela waktu pendek, dan dijalankan setiap agen dengan alasan bagus yang sama: memasang dependensi, menjalankan seluruh rangkaian tes, membangun untuk produksi, menyalakan server dev di port tetap.
Tiga pertanyaan, dalam urutan ini, dan kamu punya seluruh rancangannya:
- Bisakah aku memakai ulang hasil yang masih segar?
- Bisakah aku menumpang eksekusi yang sudah berjalan?
- Kalau tidak, akukah yang memulainya, sendirian?
Kalau beberapa agen berbagi mesinmu, yang layak diukur bukan berapa banyak yang sedang berjalan. Melainkan berapa banyak dari mereka yang memulai perintah yang sama di menit yang sama. Angka itulah yang benar-benar dirasakan mesinmu, dan selama kamu tidak melihatnya, kamu akan menyalahkan panasnya.
Kalau kamu mau gambaran yang lebih luas tentang bagaimana kami menjalankan beberapa agen di satu repositori tanpa mereka saling menginjak, itu ada di Menjalankan agen coding secara paralel, dan jaring pengaman untuk proses yang memang sudah terlanjur jalan adalah Process Guard. Skrip bersama dan hook-nya sama-sama tinggal di repositori AgentsRoom, yaitu tempat ketujuh belas agen itu bekerja sore itu.
Unduh AgentsRoom
Jalankan semua agen AI Anda, di semua proyek Anda, dari satu jendela.
Aplikasi pendamping: pantau agen Anda saat bepergian
Gunakan Claude, Codex, Antigravity CLI, atau penyedia AI lainnya.
Kirim bug dan permintaan langsung ke backlog publik Anda.
Lanjutkan membaca
Cara menskalakan agen coding AI di seluruh tim developer
Satu developer dengan satu agen coding adalah cerita produktivitas. Lima developer dengan dua puluh agen adalah masalah koordinasi. Inilah yang rusak lebih dulu ketika sebuah tim menskalakan, dan setup yang bertahan: file konteks yang di-commit, kepemilikan file yang jelas, review sesuai blast radius, dan biaya yang benar-benar terlihat.
Baca artikelAI Agent Loop: Cara Agen Coding yang Mengoreksi Diri Sendiri Menuntaskan Pekerjaan
AI agent loop mengubah prompt-lalu-perbaiki menjadi siklus yang mengoreksi diri sendiri: agen menulis rencana, membangunnya, mereview hasil kerjanya sendiri terhadap rencana, dan terus berputar sampai selesai. Begini cara loop bekerja di Claude Code, Codex, Antigravity CLI, Cursor, dan Ralph loop.
Baca artikelPelatih lari AI saya adalah sebuah repo Git dan satu agen Claude
Saya selesai berlari, jam tangan saya menyinkron, dan tiga menit kemudian analisisnya sudah tertulis di repositori saya, program pekan ini sudah disesuaikan, dan pelatih saya sudah meninggalkan komentar di bawah aktivitas Strava. Tanpa aplikasi yang dibangun, tanpa server yang ditulis, tanpa tagihan API per token: satu langganan Claude, AgentsRoom, dan berkas Markdown. Ini rakitan lengkapnya, bisa Anda tiru.
Baca artikel