Dua Konstanta, Satu Kebohongan Diam: Deteksi Drift Pakai ast Tanpa Import
Konstanta turunan drift di hari sumbernya berubah. ast.parse membandingkannya tanpa mengeksekusi satu baris pun.
Ringkasan
Tambah satu nilai di CLOSE_ACTIONS ternyata bikin job lama masih dianggap aktif, soalnya STATE_ACTIONS di kernel nggak kebagi update, dua konstanta itu drift diam-diam. Solusinya skrip check-close-actions-drift.sh yang baca dua file sebagai teks pakai ast Python, tanpa import. Skrip ngitung proyeksi STATE_ACTIONS minus EXTRACT_TIME dan NONSTATE, lalu bandingin sebagai set; beda dikit aja langsung exit 1.
Satu nilai baru, nol error, job tertutup yang masih aktif
Saya menambahkan satu nilai string baru ke konstanta CLOSE_ACTIONS di file wrapper. Layar bersih. Nggak ada error, nggak ada warning, pipeline lewat mulus. Tiga minggu kemudian laporan aneh masuk: job yang sudah lama ditutup masih muncul sebagai aktif di dashboard. Biang keroknya diam saja: STATE_ACTIONS di file kernel tidak pernah tahu tentang nilai baru itu. Dua konstanta sudah drift, dan tidak ada satu pun mekanisme yang dirancang untuk menyadarinya.
Insting pertama saya salah: import modul wrapper lalu baca konstantanya. Efek samping import-time ikut kebawa ke tempat yang seharusnya terisolasi. Insting kedua, copy-paste daftarnya ke komentar di sebelah konstanta kernel, itu pasti busuk: di hari seseorang mengubah kode dan lupa mengubah komentarnya, komentar berubah jadi kebohongan yang terlihat resmi.
Baca filenya, jangan jalankan
Perbaikan di commit 0bac516 (check-close-actions-drift.sh) memperlakukan kedua file sebagai teks. Tanpa import, tanpa eksekusi. Modul ast Python yang kerja: ast.parse(source) mengubah teks sumber jadi tree tanpa mengeksekusi modulnya [1].
Menelusuri tree-nya lanjut di ast.walk(node), dan dokumentasinya tegas: descendant di-yield tanpa urutan yang dijamin [1]. Urutan bukan jaminan di sini, jadi hasil ekstraksi dikumpulkan sebagai set, dan perbandingan di ujung adalah kesamaan set, bukan list.
Ekstraksinya bertumpu pada ast.literal_eval, yang hanya mengevaluasi struktur literal: string, angka, tuple, list, dict, set [1]. Taruh function call di dalam konstanta, dia raise alih-alih menjalankan apa pun. Restripsi itulah cerita keamanannya: skrip menarik STATE_ACTIONS dari kernel dan CLOSE_ACTIONS dari wrapper sebagai data murni. Set display Python, bentuk literal kurung kurawal, menghasilkan mutable set baru [3], persis bentuk yang diekstrak skrip.
Proyeksi, bukan duplikasi
Kedua konstanta ini memang tidak boleh sama. Set close milik wrapper adalah proyeksi dari set state milik kernel, dan skrip mengkodekan aturan itu secara eksplisit:
# ide dari check-close-actions-drift.sh
# expected = STATE_ACTIONS - EXTRACT_TIME - NONSTATE
# drift -> print extra/missing, exit 1
# input tak terbaca -> exit 2
# sinkron -> exit 0
Kernel melacak semua transisi state; wrapper hanya peduli pada aksi yang mengakhiri job. Kurangi aksi extract-time dan non-state, sisanya persis set yang boleh dipakai wrapper untuk menutup. Kalau CLOSE_ACTIONS asli beda satu nilai saja dari proyeksi itu, skrip mencetak yang extra dan yang missing, lalu exit 1. Input tak terbaca exit 2, sejalan dengan kebiasaan Unix yang memesan 2 untuk error usage dan 1 untuk sisanya [2].
Run jujur pertama si checker langsung menemukan drift skip-extra yang memang dikenal, dan itu inti semuanya. Alat ini membayar dirinya sendiri di hari pertama, dan sejak itu aturan proyeksi tinggal di satu tempat yang bisa dieksekusi, bukan di ingatan seseorang tentang dua file.