Strategi uji AI coding assistant diperlukan ketika model, prompt sistem, atau tool executor berubah dan mulai menghasilkan patch, command, atau output terstruktur yang berbeda dari sebelumnya. Masalah utamanya bukan hanya “hasil berubah”, tetapi bagaimana membedakan perubahan yang valid dari regresi yang merusak workflow engineering.

Jika tim Anda memakai Codex atau workflow serupa untuk membuat patch, menjalankan command, atau menulis test, maka pendekatan paling aman adalah memisahkan uji deterministik dari uji nondeterministik, memvalidasi hasil berbasis diff dan kontrak output, lalu menaruh semua itu di pipeline CI dengan ambang kegagalan yang jelas. Tujuannya bukan memaksa model selalu identik, melainkan menjaga perilaku tetap dapat diprediksi, dapat diaudit, dan aman saat terjadi upgrade model/tool.

Mengapa upgrade model AI mudah memicu flaky test

Pada AI coding assistant, sumber perubahan tidak hanya berasal dari kode aplikasi. Perilaku dapat berubah karena:

  • model baru memiliki preferensi berbeda dalam penamaan, struktur fungsi, atau gaya patch,
  • tool invocation berubah urutan atau argumennya,
  • prompt sistem diperbarui,
  • konteks yang dikirim ke model berubah,
  • runtime command menghasilkan output yang tidak stabil, misalnya timestamp, path temporer, atau urutan file.

Akibatnya, test yang terlalu ketat akan sering gagal walau hasil masih benar, sedangkan test yang terlalu longgar akan meloloskan regresi nyata. Di sinilah desain strategi uji menjadi penting: ukur hal yang benar, dengan toleransi yang benar.

Bedakan uji deterministik dan nondeterministik sejak awal

Uji deterministik

Uji deterministik cocok untuk bagian yang seharusnya tidak berubah bila input sama. Contohnya:

  • normalisasi prompt input,
  • parser output JSON,
  • validator schema,
  • perhitungan skor patch,
  • aturan sandbox command,
  • transformasi diff menjadi AST atau metrik statis.

Bagian ini sebaiknya diuji dengan unit test biasa. Jika komponennya murni dan tanpa I/O acak, tidak ada alasan untuk menerima flaky behavior.

Uji nondeterministik

Uji nondeterministik dipakai untuk interaksi yang melibatkan model generatif, misalnya:

  • model menghasilkan patch untuk bug tertentu,
  • model memutuskan command apa yang harus dijalankan,
  • model menyusun respons terstruktur dari konteks yang besar,
  • model memilih strategi refactor yang masuk akal namun tidak tunggal.

Pada area ini, jangan mengharapkan kesetaraan string penuh. Gunakan invariant yang benar-benar penting:

  • patch harus menyentuh file yang diharapkan,
  • test target harus lulus,
  • command tidak boleh keluar dari sandbox,
  • output JSON harus valid dan lengkap,
  • kode tidak boleh memperkenalkan pola berbahaya tertentu.

Kesalahan umum adalah memperlakukan output generatif seperti hasil compiler. Model boleh menghasilkan solusi berbeda, tetapi kontrak perilakunya tetap harus diuji.

Membangun golden test dengan toleransi terukur

Golden test tetap berguna untuk AI coding assistant, tetapi formatnya harus dirancang agar tidak rapuh. Jangan simpan seluruh output mentah bila yang Anda butuhkan hanya bagian penting dari hasil.

Apa yang layak dijadikan golden

  • daftar file yang berubah,
  • jenis operasi patch: tambah, hapus, modifikasi,
  • struktur output JSON,
  • potongan diff yang kritis,
  • ringkasan keputusan model yang telah dinormalisasi.

Apa yang sebaiknya dinormalisasi

  • timestamp, UUID, path temporer,
  • urutan field yang tidak bermakna,
  • whitespace non-esensial,
  • komentar otomatis yang sering berubah,
  • formatting hasil tool eksternal yang tidak stabil.

Contoh pendekatan: alih-alih menyimpan seluruh patch sebagai teks mentah, simpan representasi yang sudah dinormalisasi.

function normalizePatch(diffText) {
  return diffText
    .replace(/\/tmp\/[^\s]+/g, '/tmp/REDACTED')
    .replace(/index [a-f0-9]+\.\.[a-f0-9]+/g, 'index HASH..HASH')
    .replace(/[ \t]+$/gm, '')
    .trim();
}

function summarizePatch(diffText) {
  const files = [...diffText.matchAll(/^\+\+\+ b\/(.+)$/gm)].map(m => m[1]);
  return {
    changedFiles: files.sort(),
    addedConsoleLog: /\+.*console\.log\(/.test(diffText),
    touchedTests: files.some(f => /test|spec/i.test(f))
  };
}

Dengan cara ini, golden test memeriksa elemen yang relevan tanpa terikat ke detail yang tidak penting.

Toleransi harus terukur, bukan “pokoknya longgar”

Toleransi yang baik memiliki batas eksplisit, misalnya:

  • maksimal 3 file berubah untuk task sederhana,
  • tidak boleh mengubah file di luar direktori target,
  • harus menambah atau memperbarui minimal 1 test untuk task bugfix tertentu,
  • rasio baris yang berubah tidak boleh melewati ambang review otomatis,
  • patch tetap harus membuat command verifikasi lulus.

Dengan ambang seperti ini, perbedaan output masih diperbolehkan, tetapi ruang geraknya tetap terkontrol.

Verifikasi patch berbasis diff lebih kuat daripada membandingkan teks final

Untuk workflow Codex, unit pengamatan yang paling berguna biasanya bukan file hasil akhir, melainkan diff. Diff lebih mudah diaudit dan lebih dekat ke tindakan nyata model terhadap codebase.

Yang sebaiknya diperiksa dari diff

  • file mana yang disentuh,
  • apakah ada file sensitif yang ikut berubah,
  • berapa besar perubahan per file,
  • apakah patch menyentuh area yang relevan dengan issue,
  • apakah patch menghapus test atau bypass validasi,
  • apakah ada pola berisiko seperti menonaktifkan auth, logging, atau error handling.

Contoh aturan verifikasi sederhana:

rules:
  allowed_paths:
    - "src/**"
    - "tests/**"
  forbidden_paths:
    - ".github/workflows/**"
    - "infra/production/**"
  max_changed_files: 5
  forbid_patterns:
    - "skip_auth"
    - "process.env ="
    - "chmod 777"
  require_tests_for:
    - issue_type: "bugfix"
      paths: ["tests/**"]

Pendekatan ini bekerja karena model boleh menulis solusi yang berbeda, tetapi patch tetap harus patuh pada kebijakan perubahan yang bisa diverifikasi mesin.

Sandbox wajib untuk command execution

Jika AI coding assistant dapat menjalankan command, risiko terbesar bukan hanya regresi kualitas, tetapi juga command yang tidak aman atau tidak reproduktif. Karena itu, command execution harus diisolasi.

Prinsip sandbox yang praktis

  • jalankan di container atau VM terpisah,
  • batasi network jika tidak diperlukan,
  • gunakan filesystem sementara,
  • batasi CPU, memori, dan waktu eksekusi,
  • sediakan allowlist command atau shell wrapper,
  • rekam stdout, stderr, exit code, dan durasi.

Tujuannya ada dua: keamanan dan reproducibility. Tanpa sandbox, sulit membedakan apakah hasil gagal karena model, karena environment, atau karena command merusak state di luar skenario uji.

Contoh wrapper eksekusi

{
  "command": ["npm", "test", "--", "--runInBand"],
  "working_directory": "/workspace",
  "timeout_seconds": 300,
  "network_access": false,
  "writable_paths": ["/workspace", "/tmp"],
  "env_allowlist": ["CI", "NODE_ENV"]
}

Jangan berikan shell bebas tanpa kebijakan. Banyak flaky outcome muncul karena model mencoba command yang valid secara sintaks, tetapi tidak stabil dalam CI, bergantung pada network, atau mengubah lingkungan secara tidak sengaja.

Snapshot yang tidak rapuh

Snapshot sering dipakai untuk memantau perubahan output model, tetapi snapshot mentah sangat mudah pecah. Gunakan snapshot hanya setelah normalisasi dan pemilahan field penting.

Praktik snapshot yang lebih tahan perubahan

  • snapshot struktur, bukan seluruh narasi,
  • hapus ID, timestamp, hash acak, dan path lokal,
  • urutkan array jika urutan tidak penting,
  • simpan hanya field yang mempengaruhi keputusan sistem,
  • pisahkan snapshot tool call dari snapshot patch.

Contohnya, untuk output perencanaan tool call, snapshot seperti ini lebih stabil:

{
  "intent": "edit_code",
  "tool_calls": [
    { "tool": "read_file", "target": "src/auth.js" },
    { "tool": "run_tests", "target": "tests/auth.spec.js" },
    { "tool": "apply_patch", "target": "src/auth.js" }
  ]
}

Bukan seluruh respons model lengkap dengan alasan panjang yang bisa berubah walau keputusan intinya sama.

Contract test untuk output terstruktur

Bila AI coding assistant mengembalikan JSON, YAML, atau format terstruktur lain, gunakan contract test. Fokusnya adalah memastikan field wajib selalu ada, tipe data benar, dan semantik minimum terpenuhi.

Contoh kontrak output

{
  "type": "object",
  "required": ["action", "reason", "targets"],
  "properties": {
    "action": {
      "type": "string",
      "enum": ["answer", "edit", "run_command", "ask_human"]
    },
    "reason": { "type": "string", "minLength": 1 },
    "targets": {
      "type": "array",
      "items": { "type": "string" }
    },
    "commands": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["argv"],
        "properties": {
          "argv": {
            "type": "array",
            "items": { "type": "string" }
          }
        }
      }
    }
  },
  "additionalProperties": false
}

Kenapa ini penting? Karena regresi AI sering muncul bukan dalam logika besar, melainkan dalam detail format: field hilang, enum berubah, atau struktur nested tidak lagi sesuai parser downstream.

Tambahkan validasi semantik

Schema saja tidak cukup. Tambahkan aturan seperti:

  • jika action=run_command, maka commands wajib terisi,
  • jika target file berada di area sensitif, sistem harus mengembalikan ask_human,
  • jika issue bertipe dokumentasi, command build produksi tidak boleh dijalankan.

Pipeline verifikasi sebelum merge

Upgrade model/tool tidak boleh langsung diaktifkan ke seluruh repositori. Gunakan pipeline verifikasi bertahap sebelum merge atau sebelum rollout default.

Contoh alur pipeline

  1. Replay dataset: jalankan sekumpulan task historis terhadap model/tool baru.
  2. Validasi kontrak: periksa output terstruktur dan tool call.
  3. Analisis diff: cek path, ukuran patch, dan pola terlarang.
  4. Sandbox execution: jalankan command verifikasi di lingkungan terisolasi.
  5. Project tests: jalankan unit/integration test yang relevan.
  6. Skor stabilitas: bandingkan failure rate, retry rate, dan kebutuhan human override.
  7. Manual review: wajib untuk task berisiko tinggi atau hasil borderline.

Contoh pseudo-config CI gating

stages:
  - replay
  - contract
  - diff_check
  - sandbox_verify
  - project_tests
  - review_gate

review_gate:
  rules:
    - fail_if_contract_invalid: true
    - fail_if_forbidden_paths_touched: true
    - fail_if_failure_rate_above_baseline: true
    - require_human_review_if_retry_count_above_threshold: true
    - require_human_review_if_sensitive_files_changed: true

Intinya, jangan hanya bertanya “apakah patch bisa dibuat?”, tetapi “apakah patch ini lolos semua guardrail yang memang dirancang untuk workflow AI?”.

Metrik yang perlu dipantau setelah upgrade

Tanpa metrik, tim akan menilai upgrade model secara subjektif. Pilih metrik yang langsung berkaitan dengan stabilitas workflow.

Metrik inti

  • failure rate: persentase task yang gagal memenuhi kontrak atau verifikasi.
  • retry rate: berapa kali sistem harus meminta model mengulang karena hasil awal tidak valid.
  • patch acceptance rate: persentase patch yang lolos review/gating.
  • test pass rate after patch: persentase task yang membuat test target lulus.
  • human override rate: seberapa sering reviewer harus membatalkan keputusan otomatis.
  • unsafe command attempt rate: frekuensi model mengusulkan command di luar kebijakan.

Cara membaca metrik dengan benar

Misalnya, patch acceptance rate naik tetapi retry rate juga melonjak. Itu bisa berarti model akhirnya mencapai hasil baik, namun hanya setelah beberapa kali koreksi otomatis. Dalam workflow produksi, kondisi ini tetap mahal dan dapat memperlambat CI. Jadi, jangan melihat satu metrik saja.

Selain itu, bandingkan metrik per kategori task: bugfix kecil, refactor, penulisan test, perubahan dokumentasi, atau tugas linting. Model yang stabil pada dokumentasi belum tentu stabil pada patch backend yang sensitif.

Kapan human review wajib dipertahankan

Otomasi pengujian penting, tetapi tidak menggantikan reviewer manusia untuk semua kasus. Human review tetap wajib jika:

  • patch menyentuh autentikasi, otorisasi, kriptografi, billing, atau kontrol akses,
  • AI mengubah migrasi database, skema, atau query yang berdampak besar,
  • patch memodifikasi pipeline deploy, infrastruktur, atau secret handling,
  • model harus memilih trade-off arsitektur, bukan sekadar memperbaiki bug lokal,
  • hasil verifikasi berada di area abu-abu: lolos kontrak, tetapi pola patch tidak biasa,
  • retry terlalu tinggi, menandakan model tidak benar-benar memahami task.

Human review di sini bukan tanda sistem gagal. Justru itu bagian dari desain kontrol risiko yang sehat.

Kesalahan umum yang membuat pengujian AI rapuh

  • membandingkan output generatif secara tekstual penuh,
  • mencampur test deterministik dan nondeterministik dalam satu ambang yang sama,
  • tidak menormalkan data yang memang tidak stabil,
  • membiarkan command berjalan tanpa sandbox,
  • hanya memeriksa apakah file berubah, tanpa memeriksa apa yang berubah,
  • tidak menyimpan dataset replay dari task historis,
  • menilai upgrade model hanya dari demo sukses, bukan dari distribusi kegagalan nyata.

Checklist implementasi strategi uji AI coding assistant

  1. Petakan workflow menjadi komponen deterministik dan nondeterministik.
  2. Buat dataset replay dari task nyata yang mewakili pola kerja tim.
  3. Tentukan invariant utama: file target, test pass, schema output, command policy.
  4. Bangun golden test dari hasil yang sudah dinormalisasi.
  5. Terapkan verifikasi patch berbasis diff dengan allowlist dan denylist path.
  6. Jalankan command hanya di sandbox terisolasi dengan batas resource.
  7. Gunakan snapshot untuk struktur penting, bukan narasi mentah.
  8. Tambahkan contract test untuk semua output terstruktur.
  9. Pasang gating CI berbasis kontrak, diff, dan hasil verifikasi runtime.
  10. Pantau failure rate, retry rate, acceptance rate, dan human override rate.
  11. Pertahankan human review untuk area sensitif dan hasil borderline.
  12. Evaluasi ulang ambang toleransi setelah ada upgrade model atau perubahan tool.

Penutup

Strategi uji AI coding assistant yang baik tidak berusaha membuat model selalu identik, tetapi memastikan hasil Codex tetap stabil dalam batas yang dapat diterima. Cara paling efektif adalah memisahkan apa yang harus deterministik dari apa yang memang probabilistik, lalu memagari perilaku model dengan golden test bertoleransi, verifikasi diff, contract test, sandbox command, dan gating CI.

Saat model atau tool di-upgrade, fokus utama Anda seharusnya bukan mengejar output yang sama persis, melainkan menjaga agar perubahan tetap aman, relevan, terukur, dan bisa direview. Dengan begitu, peningkatan kemampuan model tidak berubah menjadi sumber flaky test dan regresi yang sulit dilacak.