# Aynı hata, başka başlık: bir LLM bulgusuna kimlik vermek

> CommitBrief'te bir bulgunun 'aynısı' sayılması başlıktan kural kimliğine taşındı; baseline, policy ve PR thread'leri bu kararın üstüne kuruldu.

- Yayın: 2026-11-11
- Kategori: Günlük
- Etiketler: Yapay Zekâ, Git, Go, CI/CD
- Okuma süresi: 4 dk okuma
- Kaynak: https://muhammetsafak.com/tr/blog/commitbrief-bir-bulgunun-kimligi/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
> **Kısaca:** CommitBrief ilk baseline'ında bulguyu dosya, severity ve başlıkla tanıyordu; model başlığı değiştirince bulgu geri geliyordu. v1.19.0'da kimlik dosya, kapalı taksonomiden bir kural kimliği ve symbol oldu. Baseline, policy, SARIF ve PR yorum senkronu aynı kimliğe yaslanıyor.

Baseline'a aldığım bir bulgu, hata aynı kaldığı hâlde geri geliyordu. Değişen tek şey modelin bulguya verdiği başlıktı.

CommitBrief'te bir bulgunun "aynısı" olmanın neye bağlı olduğunu uzun süre sormadım. Bu yazı, o sorunun cevabını bulgunun kimliği olarak yazılı hâle getirmenin ve bu tek kararın arkasından gelen özelliklerin hikâyesi. Aracı [neden yazdığımı](https://muhammetsafak.com/tr/blog/kendi-diffimi-bir-tool-a-soruyorum-commitbrief/) ve [PR akışına nasıl taşıdığımı](https://muhammetsafak.com/tr/blog/commitbrief-summary-ve-remote/) önceki yazılarda anlatmıştım.

## v1: kimlik başlıktı

v1.8.0'da (2026-06-20) baseline geldi. Mevcut bir depoda bugünkü bulguları bir kez kabul ediyorsunuz, sonraki koşularda yalnız yeni olanları görüyorsunuz. Bir bulguyu tanımak için şu parmak izini kullanıyordum:

```text
sha256(File + Severity + normalize(Title))
```

Satır kaymasına dayanıklıydı: bulgu dosyada yukarı aşağı gitse de baseline'da kalıyordu. Ama başlığa bağlıydı, ve başlığı yazan ben değil, modeldi. Model aynı hatayı bir sonraki koşuda başka bir başlıkla yazınca, baseline'a aldığım bulgu "yeni" sayılıp geri geliyordu. Modelin severity'yi yeniden derecelendirmesi de aynı sonucu veriyordu.

Aynı tavanı v1.10.0'daki policy gate'de de görmüştüm. Changelog'a o gün şunu yazmışım: "Rule-id-scoped allow/deny lists are deferred (findings carry no stable rule id)." Kural bazlı izin ve yasak listesi yapamıyordum, çünkü bulgunun kararlı bir kural kimliği yoktu.

## "Aynı"yı tanımlamak

v1.19.0'da (2026-10-18) kimliği başlıktan çıkarıp iki parçaya böldüm.

Birincisi **kural kimliği** (`rule_id`). 30 kimlikli kapalı bir taksonomi: `sec.injection`, `correctness.error-handling`, `test.flaky` ve `general.other` gibi. Model bunu kendisi seçiyor; OpenAI, Anthropic ve Gemini şemalarında `rule_id` zorunlu ve değerleri taksonomiyle sınırlı. Boş ya da bilinmeyen bir değer gelirse tekrar denenmiyor, doğrudan `general.other`'a çekiliyor. Etiket konusunda gevşek olan bir model, geçerli bir review'ı bozmasın istedim. Deterministik üreticiler kimliği kendileri veriyor: [flaky süzgeci](https://muhammetsafak.com/tr/blog/commitbrief-flaky-test-dedektoru/) `test.flaky`, `leaks` ise `sec.hardcoded-secret` yazıyor.

İkincisi **symbol**. Modelden gelmiyor, diff'ten yerelde türetiliyor: bulgunun satırını içeren hunk'ta o satıra kadar, git'in varsayılan funcname kuralına uyan son satır, yani ilk karakteri harf, `_` ya da `$` olan satır. Parantez içleri `()`'ye indirildiği için parametre listesi değişince symbol değişmiyor. Bu yüzden kimlik şu oldu:

```text
fingerprint = sha256(file, rule_id, symbol)
```

Başlık, severity ve satır numarası formülde yok. Kimlik her koşuda yerelde hesaplanıyor, cache'e yazılmıyor; cache'ten gelen bulgu için de güncel diff'ten yeniden hesaplanıyor. Formül bir gün değişirse eski kimlikler cache'te kalıp yanlış eşleşmesin diye böyle yaptım.

## Aynı kimlikle gelenler

Kimlik bir kere kararlı olunca, bekleyen özellikler tek tek yerine oturdu.

- **Baseline v2** artık kimlikleri saklıyor. Başlığı değişen bulgu baseline'dan düşmüyor.
- **Policy** `rules.deny` ve `rules.allow` kazandı. `deny` listesindeki bir kuraldan tek bir bulgu kapıyı kapatıyor; `allow` listesindekiler eşiklerden ve toplamdan muaf tutuluyor. Girdi, taksonomideki bir kimlik ya da `<category>.*`.
- **Satır içi işaretler** kural kapsamı aldı: `commitbrief-ignore[sec.injection]: gerekçe` yalnız o kuralın bulgusunu susturuyor.
- **SARIF** çıktısı kimliği `partialFingerprints["commitbrief/v1"]` altında taşıyor. SARIF'in başarısız olduğunda neden dosya yazmadığı ayrı bir konu; burada yalnız anıyorum.

Zincirin en görünür halkası v1.20.0'daki `remote pr` değişikliği. Eskiden her koşu yorumları yeniden atıyordu. Şimdi her satır içi yorumun sonunda gizli bir marker var:

```text
<!-- commitbrief:fp=<fingerprint> v=1 -->
```

Bir sonraki koşu kendi thread'lerini bu marker'dan tanıyor. Durmaya devam eden bulguya dokunmuyor (metin değiştiyse yorumu yerinde düzenliyor), yeni bulguyu bir kez yazıyor, kaybolan bulgunun thread'ini kapatıyor, geri dönen bulgunun thread'ini yeniden açıyor. Bir insanın çözdüğü ya da yeniden açtığı thread'lere hiç dokunmuyor. Aynı kimliğin tekrarları tek thread'de `Also at:` altında toplanıyor. Thread'i kapatma yetkisi yoksa yanıt olarak `No longer present as of <sha>` yazıyor.

Aynı sürümde `--since-last-review` da geldi: her tam PR review'ının sonuna `<!-- commitbrief:reviewed=<head sha> -->` marker'ı ekleniyor, bayrakla yalnız yeni commit aralığı inceleniyor. Force-push, merge commit ya da aralık güvenle belirlenemezse tam review'a dönüyor. v1.21.0'dan beri `remote pr` satır içi susturmaya da uyuyor; baseline `remote pr`'a uygulanmıyor.

## Bedeli

- v1'den v2'ye geçiş kayıplı. Başlık hash'lerinden kural kimliği ve symbol geri çıkarılamıyor; `--update-baseline` ile yeniden almak gerekiyor.
- v1.20 öncesi yorumlarda marker yok. Önceden review edilmiş bir PR'da ilk v1.20 koşusu bulguları bir kez daha yazıyor.
- Taksonomi bloğu prompt'a changelog'daki tahminle yaklaşık 690 token ekliyor. System prompt cache anahtarının parçası olduğu için mevcut cache girdileri yükseltmeden sonra bir kez ıskalıyor.

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

- **Kimlik hâlâ kısmen modele bağlı.** `rule_id`'yi model seçiyor ve bulgunun satırını o veriyor; satır da hangi symbol'e düşeceğini belirliyor. Aynı hata iki koşuda farklı kuralla gelirse kimlik değişir. Bu kararlılığın ne kadar tuttuğuna dair burada vereceğim bir ölçümüm yok; modelleri review kalitesi ekseninde ölçtüğüm [önceki yazı](https://muhammetsafak.com/tr/blog/commitbrief-hangi-model-daha-iyi-review-yapiyor/) `rule_id` kararlılığını ölçmedi.
- **Kapalı taksonomi kaba.** `general.other`'a düşen bulgular ayırt edilemiyor; baseline'daki kapsamı da geniş.
- **Aynı kuralın aynı symbol'deki iki bulgusu çakışır.** Baseline'a alınan bir bulgu, aynı symbol'de aynı kuraldan gelecek bulguları da gizler.
- **Girintili satır symbol olmaz.** Python sınıf metotlarında symbol sınıf satırına ya da boş değere düşebilir; kardeş metotlar aynı symbol'ü paylaşır.
- **Symbol, diff'in türüne bağlı.** Tarama önce yeni taraftaki satırlara bakıyor, bulamazsa git'in eski koddan yazdığı hunk başlığına düşüyor. Aralıkta yeniden adlandırılan bir fonksiyon için `--staged` ile `main..HEAD` farklı symbol, dolayısıyla farklı kimlik verebilir. Baseline'ı aldığınız türden diff'le karşılaştırın. Dosya adı değişince de kimlik değişir.
- **CLI tabanlı sağlayıcılar düz metin ürettiği için `rule_id` ve fingerprint yok.**

## Denemek için

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

Kendi aracınızda ya da CommitBrief'te bir bulguyu baseline'a alıp bir sonraki koşuda geri gelip gelmediğine bakın. Geliyorsa, aracınızın "aynı bulgu"yu neye bağladığını yazılı hâle getirmek için iyi bir gün bugün.
