Haruskah Tim yang Separuhnya Agen Pindah dari Git ke Jujutsu?
Catatan hari kedua memakai Jujutsu (jj) di repo trunk-based dengan beberapa agen AI yang ikut menulis. Satu sore berantakan jadi delapan MR, dan op log jadi kotak hitam pesawat.
Catatan ini saya tulis di hari kedua. Saya mulai pakai Jujutsu (jj) di repo internal core kami tanggal 25 September 2026 jam 13:07, dan sekarang tanggal 26. Jadi jangan baca ini sebagai testimoni “setahun pakai jj dan hidup saya berubah”. Anggap saja Patch Notes v0.1 dari orang yang baru install, yang kebetulan install-nya di repo produksi, dengan beberapa agen AI yang ikut ngetik perintah di terminal yang sama.
Kalau kamu sudah biasa git rebase -i dan paham kenapa history yang rapi itu penting, loncat saja ke Bagian Dua. Kalau kamu penasaran kenapa saya repot-repot ganti alat di tengah sprint, baca dari sini.
Bagian Satu: kenapa saya peduli
Tim Founderplus kerja dengan trunk-based development. Satu main, branch umurnya pendek, MR kecil, merge cepat. Aturannya sederhana di atas kertas: satu perubahan, satu MR, satu hal yang bisa direview tanpa reviewer harus menebak niat penulisnya.
Masalahnya, saya tidak kerja dalam garis lurus. Saya buka satu tugas, ketemu bug di sebelahnya, benerin, lalu ingat ada docs yang basi, lalu ada yang minta tolong cek deploy staging. Sore hari working copy saya jadi laci dapur: semuanya ada di situ, tidak ada yang bisa dipakai.
Itu masalah lama. Yang baru adalah agen. Sekarang di satu repo bisa ada beberapa agen yang jalan paralel, masing-masing pegang tiket sendiri. Kalau saya saja bisa bikin laci dapur dalam satu sore, coba hitung kalau penulisnya enam yang tidak pernah capek, tidak pernah ragu, dan tidak pernah bilang “eh bentar, aku lagi pakai branch ini”.
Di git, jawaban standarnya adalah disiplin: stash, branch baru, add -p, jangan lupa pindah branch sebelum mulai. Disiplin itu mahal buat manusia dan rapuh buat agen. Saya butuh alat yang membuat bentuk kerja yang benar jadi jalur paling murah.
History yang bersih bukan soal estetika. Itu antarmuka antara penulis dan reviewer, dan sekarang penulisnya bukan cuma manusia.
Bagian Dua: apa yang beda di jj
Ada empat hal yang menurut saya mengubah cara main.
Pertama, tidak ada staging area. Tidak ada git add. Working copy kamu sendiri adalah sebuah commit, namanya @. Setiap kali kamu menjalankan perintah jj, isi disk di-snapshot ke commit itu. Kamu tidak mungkin “lupa commit”, karena kamu memang selalu sedang berada di dalam commit.
Kedua, commit bisa berubah. Tiap commit punya change ID yang stabil, walaupun isinya ditulis ulang berkali-kali. Mengedit commit lama bukan operasi berbahaya yang butuh upacara, tinggal jj edit atau jj squash ke sana. Anak-anaknya ikut di-rebase otomatis.
Ketiga, jj undo. Semua operasi tercatat di jj op log, dan hampir semuanya bisa dibatalkan. Ini yang bikin berani. Rebase yang salah bukan lagi alasan buat buka Stack Overflow jam sebelas malam.
Keempat, efek dari tiga hal di atas: menulis ulang history jadi rutin, bukan ritual. Di git, rebase -i itu seperti operasi bedah. Di jj rasanya seperti merapikan meja.

Hidup berdampingan dengan git
Repo kami colocated: ada .jj dan .git di folder yang sama. jj 0.45.1 menulis ke git di belakang layar, jadi GitLab, CI, dan rekan yang masih pakai git tidak merasakan bedanya. Aturan kami sederhana: mutasi hanya lewat jj, git cuma boleh dipakai untuk baca.
Ada satu pengecualian yang harus diingat. Kalau ada tool luar yang bikin commit lewat git (IDE, atau agen yang masih kebiasaan pakai git), jalankan dulu:
jj git import
Kalau tidak, jj belum tahu commit itu ada, dan kamu akan bingung sendiri kenapa log-nya beda.
Contoh nyata: satu sore berantakan jadi delapan MR
Kondisi awal saya jelek. Working copy berisi campuran beberapa tugas: tool MCP baru di apps/api, perubahan deploy staging, docs untuk agen, dan artefak sesi tldraw. Semua di satu tumpukan.
Yang saya lakukan, berurutan:
- Kasih nama dulu biar jelas ini sampah sementara (13:27):
jj describe -m "wip(triage): ..." - Pecah per fileset (13:38):
jj split -m "..." apps/api/app/Mcp apps/api/tests apps/api/config - Ulangi
splituntuk tiap kelompok file sampai tumpukan habis. - Pasang bookmark ke tiap potongan (14:21):
jj bookmark create feat/api-mcp-lead-invoice-tools -r <change> - Push semuanya:
jj git push
Jam 14:21 delapan bookmark lahir sekaligus: chore/jj-colocate-contract, fix/api-lead-payment-renewal, feat/api-mcp-lead-invoice-tools, feat/ci-staging-e2e-whitelabel, feat/learner-mentor-card-tokens, docs/revenue-diagram-beszel-nda, chore/cli-changelog-gate, docs/session-maps-tldraw. Satu sore berantakan jadi delapan MR yang masing-masing bisa direview sendiri. Beberapa commit-nya sekarang sudah duduk di history main, misalnya “feat(api): tool MCP create lead invoice dan renewal payment link” dan “chore(cli): changelog 0.27.1 dan gerbang cli”.
Jam 15:01 main maju. Saya rebase, lalu enam bookmark dipindah serentak ke commit hasil rebase. Anak-anaknya ikut turun sendiri. Di git, itu enam kali checkout dan enam kali berdoa.

Di git, merapikan pekerjaan itu hukuman. Di jj, itu langkah biasa sebelum push.
Revset: query untuk history
jj punya bahasa kecil bernama revset untuk memilih commit. Yang paling sering saya pakai adalah trunk(), yang di repo kami menunjuk ke main@origin. Change baru selalu dimulai dari sana:
jj new main
Untuk melihat semua pekerjaan yang belum masuk trunk:
jj log -r 'trunk()..'
Praktiknya, jj log jadi papan kerja. Nanti saya jelaskan kenapa itu penting buat agen.
Bagaimana kelihatannya dari luar
Reviewer di GitLab tidak tahu ada jj, dan tidak perlu tahu. Yang mereka lihat branch biasa, commit biasa, MR biasa lewat glab. Alur sebelum push kami seperti ini:
jj rebase -d main@originjj bookmark set <nama> -r @-jj git push -b <nama>- Buka MR dengan
glabseperti biasa.
Bookmark itu padanan branch di git. Bedanya, bookmark tidak ikut maju sendiri waktu kamu bikin commit baru. Kamu yang menaruhnya. Awalnya terasa repot, lama-lama terasa jujur: bookmark cuma ada di tempat yang memang mau kamu tunjukkan ke orang lain.
Bagian Tiga: agen yang pakai jj
Ini bagian yang bikin saya menulis catatan ini.
Kontrak kerja agen kami tinggal di .agents/skills/jj/references/founderplus-workflow.md, dan dimuat otomatis tiap agen mulai kerja di repo. Isinya pendek, dan tiap barisnya lahir dari sesuatu yang hampir rusak:
- Satu tugas = satu change.
- Board hidup di
jj log. Change tanpa bookmark berarti masih lokal. Bookmark yang sudah di-push berarti in-review di GitLab. Change yang hilang dari log berarti selesai. - Change baru selalu
jj new main. - Mutasi hanya lewat jj. Menulis lewat git dilarang.
- Satu writer per working copy. Agen read-only (scout, reviewer) boleh duduk di checkout utama, tapi tidak boleh mutate.
- Jangan abandon change yang punya bookmark ter-track.
- Jangan
jj undooperasi agen lain tanpa cekjj op logdulu. wip(triage)tidak boleh di-push.
Aturan soal board itu yang paling saya suka. Saya tidak butuh dashboard terpisah untuk tahu agen sedang mengerjakan apa. Status kerja dibaca dari struktur graph-nya sendiri. Sumber kebenarannya satu.
Workspace: satu agen, satu meja
Malam harinya, antara 22:10 sampai 00:25, tiap agen dapat workspace sendiri:
jj workspace add ../fm-agent-leads-filter
Ada lead-invoice, lead-invoice-needs-action, leads-filter, fm-triage, foun-591, dan foun-1043. Workspace itu mirip git worktree, tapi semuanya berbagi satu graph. Agen tidak sinkron lewat file di disk, tapi lewat change yang sudah di-describe. Kalau agen A selesai, change-nya langsung kelihatan di jj log workspace mana pun.

Jam 23:53, tujuh branch fix di-push dalam satu perintah jj git push, termasuk fix/checkout-payment-verified-dobel, fix/foun-1090-exception-stacktrace, dan fix/foun-1114-utm-purchase-completed. Tujuh branch dari beberapa workspace, satu perintah.
jj op log adalah kotak hitam pesawat
Pertanyaan yang paling sering saya dapat soal agen: “gimana kamu tahu mereka kerja dengan benar?” Jawaban saya sekarang: saya baca jj op log.
Semua angka di catatan ini saya ambil dari sana. Dalam kurang lebih satu setengah hari ada sekitar 167 operasi. Rinciannya: 26 snapshot, 16 point bookmark, 14 new, 12 split, 12 push, 12 describe, 10 rebase, 10 add workspace, 4 reconcile divergent, 3 abandon, 2 restore.
Git punya reflog, tapi reflog itu per ref dan gampang tertimbun. Op log jj mencatat operasi terhadap seluruh repo, lengkap dengan perintah yang memicunya. Kalau ada agen yang bertingkah aneh jam dua pagi, jejaknya ada, berurutan, dan bisa di-undo per operasi.
Agen yang tidak bisa diaudit itu bukan rekan kerja. Itu tebakan yang kebetulan bisa ngetik.
Jebakan yang sudah saya injak
Dua hari, dan daftarnya sudah lumayan.
Agen menggantung karena editor. jj split atau jj squash tanpa -m membuka editor untuk menulis deskripsi. Manusia tinggal ketik lalu simpan. Agen cuma diam menunggu selamanya. Aturannya sekarang: semua perintah jj dari agen wajib non-interaktif.
Dua proses split di satu @. Jam 13:38, dalam satu menit, log mencatat tiga kali “reconcile divergent operations”. Dua batch split jalan bersamaan di working copy yang sama. jj merekonsiliasi sendiri dan tidak ada yang rusak, yang jujur saja bikin saya kaget. Tapi “bisa selamat” bukan alasan menjadikannya kebiasaan. Dari situ lahir aturan satu writer per working copy.
squash --into ke revisi yang sudah di-rebase. Kontennya tidak pindah. Saya pulihkan dengan jj restore --from <commit lama>. Pelajarannya: setelah rebase, ID commit berubah, jadi pakai change ID, bukan hash yang kamu salin lima menit lalu.
Hook git tidak dipanggil jj. Pre-push hook yang biasa menjaga kami tidak jalan saat jj git push. Jadi /ci-preflight wajib dijalankan manual sebelum push. Ini yang paling gampang terlupa, karena tidak ada yang menegur.
Workspace dibuat, di-forget, dibuat lagi. Workspace foun-591 saya bikin, saya forget, lalu saya bikin ulang dalam sepuluh menit. Murni trial and error soal struktur folder. Tidak ada korban, cuma malu.
Dan yang paling lucu: saat saya menulis paragraf ini, workspace default saya sendiri berstatus stale. jj bilang “The working copy is stale… Run jj workspace update-stale”, dan change saya divergent. Penyebabnya workspace lain menulis ulang graph sementara saya sibuk menulis tentang betapa rapinya graph ini. Yang bikin aturan satu writer per working copy justru yang working copy-nya ketinggalan.
Kalau kamu mau mulai
Saran saya praktis saja, dan berlaku untuk repo tim, bukan repo mainan:
- Mulai colocated.
jj git init --colocatedi repo yang sudah ada. Tidak ada yang perlu migrasi, dan kamu bisa mundur kapan saja. - Tulis kontrak agennya dulu sebelum kasih agen akses. Minimal: non-interaktif, satu writer per working copy, mutasi hanya lewat jj.
- Satu agen, satu workspace. Jangan pelit folder.
- Biasakan
jj op logsebelumjj undo, apalagi kalau ada agen lain yang jalan. - Pasang ulang pagar yang dulu dijaga hook git. jj tidak akan menjalankannya untukmu.
- Jalankan
jj workspace update-stalebegitu lihat peringatan stale, jangan ditunda sampai selesai nulis blog.
Apakah tim yang separuhnya agen harus pindah ke jj? Setelah dua hari, jawaban saya: kalau kamu kerja trunk-based dan agenmu lebih dari satu, coba satu minggu di repo yang sungguhan. Biaya masuknya kecil karena git tetap ada di bawahnya. Yang kamu dapat adalah history yang bisa dibentuk ulang tanpa takut, dan catatan lengkap tentang apa yang dilakukan setiap penulis di repo itu, manusia maupun bukan.
Saya akan tulis v0.2 kalau sudah sebulan. Semoga waktu itu workspace default saya sudah tidak stale.