Tantangan Regresi pada Autonomous Coding Agent

Coding agent berbasis LLM (seperti Claude Code, Devin, atau implementasi kustom berbasis LangGraph) kerap menyelesaikan masalah lokal namun memicu regresi pada modul lain. Fenomena ini memburuk ketika agent menghadapi hallucinated patches: kode yang secara sintaksis valid dan lulus verifikasi prompt internal, namun mematahkan edge case historis yang tidak terdokumentasi dalam konteks aktif.

Untuk mengatasinya, agent membutuhkan Memory Test Harness. Sistem ini menggabungkan basis data solusi masa lalu (recipes/fixes) dengan pipeline verifikasi deterministik sebelum agent diizinkan menjalankan perintah git commit.

Arsitektur Penyimpanan Memori: Vector Store vs Local File

Penyimpanan memori solusi masa lalu dapat diimplementasikan menggunakan dua strategi utama:

  • Vector Store (ChromaDB / Qdrant): Efektif untuk repositori besar dengan ribuan solusi modular. Memetakan error stack trace dan diff deskriptif ke dalam representasi vektor numerik untuk pencarian semantik berkecepatan tinggi.
  • Local File Store (Markdown / JSONL under .agent/memory/): Pendekatan zero-dependency yang disimpan langsung di git tree. Tepat untuk proyek monorepo berukuran menengah karena langsung terlacak versi konfigurasinya bersama commit kode.

Struktur artefak memori harus menyatukan deskripsi error, diff solusi, dan suite pengujian verifikasi:

{
  "id": "fix-null-pointer-auth-header",
  "error_signature": "TypeError: Cannot read properties of undefined (reading 'split')",
  "context_path": "src/middleware/auth.ts",
  "patch_diff": "--- a/src/middleware/auth.ts\n+++ b/src/middleware/auth.ts\n@@ -12,2 +12,3 @@\n-  const token = req.headers.authorization.split(' ')[1];\n+  const authHeader = req.headers.authorization;\n+  const token = authHeader ? authHeader.split(' ')[1] : null;",
  "test_cases": [
    "tests/unit/auth-null-header.test.ts"
  ]
}

Implementasi Verification Workflow Sebelum Commit

Harness verifikasi bekerja sebagai gerbang terisolasi (sandbox gatekeeper). Agent tidak diperbolehkan langsung melakukan push atau commit otomatis sebelum harness memvalidasi patch terhadap riwayat pengujian masa lalu.

import subprocess
import json
from pathlib import Path

class MemoryVerificationHarness:
    def __init__(self, memory_index_path: str):
        self.memory_index = Path(memory_index_path)

    def retrieve_relevant_tests(self, modified_file: str) -> list[str]:
        if not self.memory_index.exists():
            return []
        
        with open(self.memory_index, 'r') as f:
            data = json.load(f)
            
        # Ambil daftar tes terkait file yang diubah
        matched_tests = []
        for entry in data.get("recipes", []):
            if entry.get("context_path") == modified_file:
                matched_tests.extend(entry.get("test_cases", []))
        return list(set(matched_tests))

    def verify_patch(self, modified_file: str) -> bool:
        tests = self.retrieve_relevant_tests(modified_file)
        if not tests:
            # Ponytail: default ke suite pengujian standar jika tidak ada memori spesifik
            cmd = ["pytest", "-q", "tests/base/"]
        else:
            cmd = ["pytest", "-q"] + tests
            
        result = subprocess.run(cmd, capture_output=True, text=True)
        
        if result.returncode != 0:
            print(f"[HARNESS FAILURE] Regresi terdeteksi:\n{result.stdout}")
            return False
        
        print("[HARNESS SUCCESS] Seluruh tes regresi dan test harness lulus.")
        return True

# Eksekusi sebelum agent finalize commit
harness = MemoryVerificationHarness(".agent/recipes.json")
is_safe = harness.verify_patch("src/middleware/auth.ts")
assert is_safe, "Patch agent ditolak: mematahkan invariant memori masa lalu."

Mitigasi Flaky Test Akibat Hallucinated Patch

Agent sering memodifikasi file test suite alih-alih memperbaiki kode inti saat menghadapi kegagalan eksekusi. Terapkan batasan deterministik berikut pada test runner harness:

  1. File Test Read-Only: Pasang aturan pada file sandbox di mana agent hanya memiliki izin write pada direktori src/ dan dilarang mengubah file di dalam tests/.
  2. Deterministik Timeout & Mock Injection: Gunakan mocking strict pada API eksternal dan batas eksekusi deterministik (misal 5000ms per test runner) untuk mencegah agent terjebak dalam infinite loop logic.
  3. Double-Pass Validation: Jalankan test harness pada base tree (sebelum patch) untuk memastikan test memang valid, lalu jalankan pada patched tree untuk memvalidasi fix.

Assert Deterministik untuk Edge Cases Lama

Pengujian regresi memory-driven harus bersifat black-box dan bebas dari side-effects global state. Gunakan deterministic assert tanpa random input:

import pytest
from app.parser import parse_custom_payload

def test_parser_memory_regression_issue_402():
    # Memori kasus: Payload kosong menyebabkan IndexError
    payload_kosong = ""
    result = parse_custom_payload(payload_kosong)
    assert result == {}, "Harus mengembalikan dict kosong jika payload kosong"

def test_parser_memory_regression_issue_488():
    # Memori kasus: Encoding malformed tidak boleh melempar unhandled exception
    malformed_payload = "\x00\xFF--invalid"
    result = parse_custom_payload(malformed_payload)
    assert result.get("status") == "error"
    assert "encoding_failure" in result.get("error_code")

Trade-offs dan Praktik Operasional

Menerapkan memory test harness menambah runtime overhead saat iterasi agent berjalan. Menjalankan seluruh riwayat pengujian untuk setiap modifikasi kecil memperpanjang siklus eksekusi agen secara signifikan.

Gunakan filtering relevansi: jalankan hanya pengujian yang beririsan dengan file path atau dependensi AST dari kode yang diubah oleh agent. Lakukan eksekusi test suite lengkap (full integration test) hanya satu kali sebelum branching commit dinaikkan menjadi pull request final.