# CommitBrief'e flaky-test dedektörü: AI'ın yazdığı kararsız testleri commit'te durdurmak

> CommitBrief'e deterministik bir flaky-test süzgeci ekledim: sabit sleep'leri ve seed'siz rastgeleliği commit'te, modelsiz yakalıyor.

- Yayın: 2026-06-17
- Kategori: Günlük
- Etiketler: Test, GitHub Actions, Go, Git
- Okuma süresi: 3 dk okuma
- Kaynak: https://www.muhammetsafak.com.tr/blog/commitbrief-flaky-test-dedektoru/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
CommitBrief bugüne kadar tek bir şey yapıyordu: kendi diff'imi, kimse görmeden, bir LLM'e inceletmek. [İlk yazıda](/blog/ai-ile-self-review-neyi-hizlandiriyor-nerede-yetmiyor/) niye işe yaradığını, [summary ve remote yazısında](/blog/commitbrief-summary-ve-remote/) ekibe nasıl taşındığını anlatmıştım. Hepsinin ortak noktası: bulguyu **model üretiyordu.**

Bu yazıdaki ekleme o noktayı bilerek kırıyor. CommitBrief artık modeli çağırmadan önce, değişen test dosyalarını **deterministik bir statik süzgeçten** geçiriyor — kararsızlığa (flaky) en sık yol açan kalıpları yakalamak için. Niye bu ekleme LLM'e bir madde daha eklemekle yapılmadı; asıl yazı o.

> Kararsızlığın *neden* AI çağında patladığını ve neden retry/quarantine'in bir koşu bandı olduğunu sade.dev'de ayrı yazdım: [Kararsız Testler: Retry Çözüm Değil](https://sade.dev/tr/journal/kararsiz-testler-ve-determinizm). Bu yazı o tezin araç tarafı.

## Kolay yol LLM'di — onu seçmedim

CommitBrief baştan sona LLM-güdümlü bir araç. En kolay yol belliydi: prompt'a "test dosyalarında sabit sleep, kırılgan selector, seed'siz random ararsan işaretle" diye bir rubrik daha eklemek. Bir akşamlık iş.

Sorun, bu çözümün **olasılıksal** olması. Modele bağlı, çalıştırmadan çalıştırmaya kayabilir ve — daha önemlisi — *farklılaşmıyor*: herhangi bir LLM review aracı aynı prompt'la aynı şeyi yapabilir. Oysa kararsızlığın asıl can sıkıcı tarafı, çözümün **deterministik** olmasını istemen: aynı diff her zaman aynı sonucu versin, model olmadan da çalışsın, commit aşamasında bir saniyede dönsün.

O yüzden statik yolu seçtim. Yeni bir paket (`internal/flaky`), değişen test dosyalarının **yalnızca eklenen satırlarını** tarıyor, bilinen anti-pattern'leri muhafazakâr regex'lerle yakalıyor ve standart bir bulgu üretiyor. Aynı diff → aynı bulgu. Model yok, network yok.

## Ne yakalıyor

İlk sürümde iki yüksek-isabetli kural var. İsabet, recall'dan önce geliyor: commit aşamasındaki gürültülü bir kapı yok sayılır, yok sayılan kapı da hiç olmamasından kötüdür.

- **Sabit bekleme / sleep** (`medium`): `time.Sleep`, `Thread.sleep`, `Task.Delay`, `asyncio.sleep`, `*.waitForTimeout`, sayısal `cy.wait(2000)`, `usleep`, `sleep(<n>)` — Go, Java/Kotlin, C#, Python, JS/TS, Cypress, PHP.
- **Seed'siz rastgelelik** (`low`): `Math.random`, Python `random.*`, Go `math/rand` globalleri (`rand.Intn`, …).

Test dosyasını yoldan tanıyor (`*_test.go`, `*.test.*`/`*.spec.*`, `test_*.py`, `*Test.java`, `*_spec.rb`, `__tests__`/`e2e`/`cypress` dizinleri…). Bulgu metni en/tr yerelleşiyor. Çıktı, modelin bulgularıyla aynı kart:

```text
● medium  worker/job_test.go:41
  Test senkronizasyon için sabit bir bekleme (sleep) kullanıyor
  Sabit beklemeler testi zamana bağımlı kılar: hızlı makinede geçer, CI
  yükü altında patlar; süite olan güveni aşındıran flaky sonuçlar üretir.
  → Sabit gecikmeyi koşula dayalı bir beklemeyle değiştir…
```

## Modelin bulgularıyla nasıl birleşiyor

Statik bulgular, filtreden sonra hesaplanıp LLM bulgularıyla **merge** ediliyor — render, `--fail-on` ve `--copy`'den önce. İki kural:

- **Her LLM bulgusu korunur.** Statik süzgeç hiçbir şeyi bastırmıyor.
- **Aynı `file:line`'da çakışma olursa** statik bulgu eklenmiyor — modelin o satır için zaten söyleyecek sözü varsa, ikinci kez söylemiyoruz.

Güzel bir yan etki: model bozuk JSON döndürüp degrade olsa bile statik bulgular hayatta kalıyor. Deterministik kısım, olasılıksal kısma bağımlı değil.

Ve **JSON şeması v1 değişmedi.** Statik bulgular mevcut `render.Finding` şeklini kullanıyor; yeni alan yok, yeni exit-code yüzeyi yok. `--fail-on=medium` artık sabit bir sleep'te de kapıyı kapatıyor — ekstra bir flag öğrenmene gerek kalmadan.

## Kullanımı

API/mock sağlayıcılarda varsayılan **açık**. Kapatmak istersen:

```bash
commitbrief                              # flaky pre-pass açık (varsayılan)
commitbrief --no-flaky                   # bu çalıştırmada atla
commitbrief config set review.flaky false  # kalıcı kapat
```

Öncelik `--no-flaky` > `review.flaky` > yerleşik (`true`). CLI-tabanlı (claude-cli/gemini-cli) sağlayıcılar düz metin ürettiği için bu yolda şimdilik devre dışı — onu ayrı bir adımda ele alacağım.

## "Yerini almak" değil, yine "önünden gitmek"

Bu ekleme de serinin aynı cümlesinin etrafında: *üretim ucuzladıysa, üretileni denetlemek pahalı kalmamalı.* AI test yazmayı bedavalaştırdı; o testlerin bariz kararsızlığını yakalamak da bedava olmalı — bir model çağrısı, bir CI dakikası harcamadan, commit'in olduğu yerde.

Burada model bir lükse dönüşüyor, zorunluluğa değil: bariz olanı deterministik kısım topluyor, modelin dikkatini gerçekten muhakeme isteyen şeylere saklıyorum.

## Sınırlar — yine dürüst liste

- **İsabet-öncelikli.** Bariz anti-pattern'leri yakalar; kendi kodunun içine gömülü bir race condition'ı değil. Akıllı kararsızlığı kaçırır ve tahmin etmesine izin vermiyorum.
- **CLI sağlayıcı yolu kapsam dışı.** Düz metin üreten sağlayıcılarda merge henüz yok; takip adımı.
- **Statik, AST değil (henüz).** Regex tabanlı; kapsamı/semantiği görmüyor. Brittle selector, over-mock ve zaman-bağımlı assertion kuralları sıradaki additive eklemeler.
- **Tespit emniyet ağı, çözüm değil.** Asıl çözüm testi en baştan deterministik yazmak — onu [sade.dev yazısında](https://sade.dev/tr/journal/kararsiz-testler-ve-determinizm) anlattım.

## Denemek için

- [github.com/CommitBrief/commitbrief](https://github.com/CommitBrief/commitbrief)
- [commitbrief.com](https://commitbrief.com)

Bir test dosyasına sabit bir `sleep` koyup `commitbrief`'i çalıştırın — modelden önce, deterministik olarak yakalandığını görün. Hangi anti-pattern'i bir sonraki sürümde görmek istediğinizi yazarsanız, sıraya koyarım.
