Files
kolaytercih/docs/gece-vardiyasi/2026-09-22/35-guvenlik-g3-denetimi.md
bilalgursen 2b350d0c5d content(rehber): gece vardiyasının 14 yeni yazısı + 2 düzeltme, ekip raporları ve kurallar
14 yeni rehber yazısı (hepsi editör denetiminden geçti, 46 yazıda 829 iç link
tarandı, 0 kırık hedef; 180+ DB rakamı yazıların kendi filtreleriyle yeniden
koşuldu):

  taban-siralamalari-alti-yilda-nasil-degisti · kac-net-ile-hangi-bolum ·
  siralama-bandlari-hangi-kapilar-acilir · ayni-bolum-farkli-universite-siralama-farki ·
  hangi-bolumlerin-kontenjani-azaldi · yeni-acilan-bolumler-nasil-degerlendirilir ·
  bilgisayar-mi-yazilim-muhendisligi-mi · kktc-universiteleri-okunur-mu ·
  yapay-zekaya-tercih-sordum-guvenilir-mi · ek-madde-1-puanim-yetiyor-mu ·
  universitede-ilk-hafta-ders-kaydi-muafiyet-kayit-dondurma · yks-2027-takvimi ·
  rehber-ogretmenler-icin-veri-kaynaklari · bolumumu-sevmedim-hangi-kapilar-var

İki canlı yazı düzeltildi:
- veliler-icin-tercih-rehberi: veli diliyle kapanış; altındaki "sıralamanı gir"
  kutusu artık veliye ölü uç değil
- bos-kontenjanlar-ne-anlatiyor: "birkaç yüz kişilik fark" aslında ~9 bin
  (ÖSYM 265.356 ↔ bizim 256.532); payda açıklaması tersti — ek kontenjan
  paydaya girseydi oran düşerdi. guncelleme alanı eklendi (sitemap lastmod).

Kurallar (AGENTS.md):
- Yayın sonrası arama motoru bildirimi: URL denetimi yalnız canlıda 200 dönen
  adrese; kota sayacı; mülk/hesap notu; sitemap grep kuralı
- Yıkıcı git komutları yasak: reset/checkout --/restore/clean/stash
- Tarayıcı açma: tasarımcı ve UX gerekli gördüğünde açabilir (yetenek için
  .claude/agents/tasarimci.md'ye tarayıcı araçları eklendi)

docs/gece-vardiyasi/2026-09-22/ — 34 rapor: CEO kararları ve kapanış turu,
marka/ödeme analizi, sıfır ödeme teşhisi, SEO konu planı, veri kalitesi,
editör denetimi, CTO şartnamesi, yazılımcı ve güvenlik raporları, SABAH-OZETI.

docs/ekip/YAYIN-KUYRUGU.md — Search Console bildirim kuyruğu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 23:25:34 +03:00

123 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 35 — Güvenlik & uyum denetimi: G3 · `odeme/paket-vaadi` (merge öncesi zorunlu denetim)
> Rol: `guvenlik-uyum` · Gece vardiyası, 22 Eylül 2026 sabah turu
> Denetlenen: dal `odeme/paket-vaadi`, commit'ler `4acfd27` + `43b6cec`, taban `9bd448b` (`main`)
> Kaynaklar: `34-yazilimci-g3.md`, `31-guvenlik-satici-kimligi.md` §2, `28-cto-sartname.md` §G3
> Yöntem: **yalnız okuma.** Kod değiştirilmedi, dal değiştirilmedi, commit/`git add` yok, yıkıcı git komutu yok. Diff `git show`/`git diff` ile okundu. Proje DB'sine yalnız `sqlite3 -readonly` ile bakıldı.
> İşaret: **[D]** doğrulandı (komut çıktısı / dosya:satır) · **[Ç]** koddan çıkarım (koşturulmadı)
---
## Özet (5 madde)
1. **Kritik bulgu yok; üç koşulun üçü de kodda karşılanmış.** İade dalları yerinde ve tutar koşullu (`rapor-actions.ts:223-228`, `:358-363` → `delta: maliyet`), dönen bakiye koşullu (`:254`, `:382`), `hasPaket` tek yerden — `getCurrentUser()`'ın istek başına React-cache'lenmiş **tek satır okumasından** geliyor, yeni DB sorgusu eklenmemiş [D].
2. **Denetimin en kritik sorusu — `amount: 0` idempotency çapasını kaybettirir mi — CEVAP: HAYIR, ve bu artık [Ç] değil [D].** Projenin kendi sürücüsüyle (`@libsql/client@0.17.4`) izole bir scratch DB'de koşturdum: bakiyesi 0 olan kullanıcıda `UPDATE … credit_balance - 0 … WHERE credit_balance >= 0` → **`rowsAffected = 1`** (hiçbir kolon değeri değişmese bile 1; SQLite MySQL gibi "changed rows" saymıyor). `delta = -0` satırı tabloya **integer 0** olarak yazılıyor ve aynı `(reason, ref_id)` ile ikinci insert `SQLITE_CONSTRAINT: UNIQUE constraint failed` ile reddediliyor. Yani satır gerçekten yazılıyor, UNIQUE çapası duruyor, `INSUFFICIENT` yanlış alarmı yok.
3. **Sızıntı yolu kapalı, paketsiz davranış bit-bit aynı.** Paketlinin başarısız üretimi/revizyonu `delta 0` `refund` satırı yazıyor, bakiye artmıyor; taze `requestId` ile sınırsız tekrar da kredi üretmiyor. Paketsizde `maliyet = RAPOR_KREDI` olduğu için harcama, iade, hata metni ve `krediBittiIsaretle` yolu değişmemiş [D].
4. **Yeni ve gerçek olan tek risk kredi değil, maliyet:** G3'ten sonra paketli kullanıcının liste üretiminde **hiçbir üst sınır kalmıyor** (eski tavan 60 kredi ÷ 3 ≈ 20 üretimdi). Üstelik `listeOlusturKilitli` `reports`'u `revisionCount: 0` ile üzerine yazdığı için (`rapor-actions.ts:247`) MAX_REVIZYON=2 tavanı da "yeniden üret → 2 revizyon → yeniden üret" döngüsüyle sıfırlanabiliyor. Repoda hiçbir rate limit yok (`grep -rln "rateLimit\|rate-limit\|ratelimit"` src/ → **0 dosya** [D]); tek fren kullanıcı başına üretim kilidinin serileştirmesi. **Merge engeli değil** (ücretli kullanıcı yüzeyi, CEO kararının doğal sonucu) ama kayda geçmeli — F1.
5. **Uyum tarafında G3 bir açığı kapatıyor:** `/paket` "60 Yapay Zeka danışman sorusu" diyordu (`paket-satinal.tsx:39-40`), gerçekte liste (3) + 2 revizyon (6) düşünce elde **51** soru kalıyordu. G3 sonrası 60 gerçekten 60. Mesafeli Sözleşmeler Yön. m.5/1-a (hizmetin temel nitelikleri) bakımından "ön bilgilendirme × fiilî ifa" uyumsuzluğu kapanıyor — **G3 uyumu bozmuyor, iyileştiriyor.** *(Hukuki değerlendirme taslaktır: avukat teyidi gerekir.)*
---
## 1 · Üç koşul: doğrulama
| Koşul (`31-…` §2) | Hüküm | Kanıt |
|---|:--:|---|
| **1.** İade dalları yerinde + tutar koşullu | **KARŞILANDI** | `rapor-actions.ts:223-228` ve `:358-363`: `grantCredits({ … delta: maliyet, reason: "refund", refId })` — çağrı kaldırılmamış, `refId` aynı. DUPLICATE dalındaki "önceki deneme iade edilmişti" sorgusu (`:179-186`, `:320-327`) yalnız `(reason='refund', ref_id)` bakıyor, **delta'ya bakmıyor** → `delta 0` satırı bu çıkarımı aynen besliyor [D] |
| **2.** Dönen bakiye koşullu | **KARŞILANDI** | `:254` ve `:382` → `kredi: user.creditBalance - maliyet`. `grep -c "creditBalance - RAPOR_KREDI"` = 0 [D] |
| **3.** `hasPaket` tek yerden, yeni DB okuması yok | **KARŞILANDI** | `:158` ve `:300` → `const maliyet = user.hasPaket ? 0 : RAPOR_KREDI;`. `user` nesnesi `getCurrentUser()`'dan geliyor: `session.ts:27-35` **tek `SELECT *`**, `react/cache` ile istek başına bir kez. `hasPaket` ile `creditBalance` **aynı satır anlık görüntüsünden** okunuyor → harcama ile iade arasında yırtık okuma imkânsız. Diff'te yeni `appDb.select`/`query` yok [D] |
Ek doğrulamalar:
- `hasPaket` şemada `integer("has_paket", { mode: "boolean" })` (`appdb/schema.ts:20`) → drizzle gerçek `boolean` döndürüyor; yazılımcının `=== true` yazmaması **doğru** tercih, SQLite 0/1 temsilinde de truthiness doğru çalışır [D].
- Kapsam: her iki commit toplam **3 dosya**, `credits.ts`/`liste-uretici.tsx`/`huni-semasi.tsx` yok; çalışma ağacındaki başkalarına ait `M`/`??` dosyalar hâlâ commit'siz duruyor [D].
---
## 2 · `spendCredits(amount: 0)` / `grantCredits(delta: 0)` — koşturulmuş hüküm
Şartname de yazılımcı raporu da bu noktayı **[Ç]** bırakmıştı. İzole scratch DB'de (proje DB'sine **yazılmadı**), projenin kendi sürücüsüyle koşturdum:
```
A) amount=0, balance=0 -> rowsAffected = 1 → INSUFFICIENT DÖNMEZ
B) hiçbir kolon değeri değişmiyor -> rowsAffected = 1 → SQLite matched-rows sayar
C) delta=-0 insert rowsAffected = 1
D) yazılan delta = {"delta":0,"t":"integer"} → -0 değil, temiz integer 0
E) aynı (reason, ref_id) ile ikinci insert: SQLITE_CONSTRAINT: UNIQUE constraint failed
(code SQLITE_CONSTRAINT, rawCode 2067) → idempotency ÇAPASI YERİNDE
F) refund delta=0 sonrası bakiye = 0 → kredi üretilmiyor
G) balance=-1, amount=0 -> rowsAffected = 0 → yalnız negatif bakiyede INSUFFICIENT
```
**Hüküm:**
- **Soru 2 (idempotency):** bozulmadı. `delta = 0` satırı gerçekten yazılıyor; `uniqueIndex("ledger_reason_ref").on(reason, refId)` (`schema.ts:133`) aynı `requestId` ile ikinci üretimi `DUPLICATE`'e düşürmeye devam ediyor. `uniqueIhlaliMi` bu hatayı yakalıyor (mesaj "UNIQUE" içeriyor, `credits.ts:22-40`) [D]. `refId` zinciri değişmedi (`refId: requestId` × 4, satır 165/227/306/362).
- **Soru 4 (`INSUFFICIENT`):** bakiyesi 0 olan paketli kullanıcı liste üretebiliyor. **Paketin vaadi ayakta.** Tek istisna satır G: bakiye **negatifse** `INSUFFICIENT` dönerdi — ama negatif bakiye ulaşılamaz: bakiyeyi yazan her yol `gte` ile korunuyor (`credits.ts:69`, `:110`), `grantCredits` yalnız ekliyor, dev paneli `Math.max(0, …)` uyguluyor (`lib/dev/actions.ts:83`, zaten `NODE_ENV === "production"`'da fırlatıyor) [D]. Lokal DB'de de min bakiye 5 ve 34 (`sqlite3 -readonly data/app.db`) [D].
---
## 3 · Bulgu tablosu
| # | Dosya:satır | Sorun | Somut senaryo | Önem | Düzeltme | Kesinlik |
|---|---|---|---|:--:|---|:--:|
| **F1** | `rapor-actions.ts:158` + `:247` (`revisionCount: 0` upsert); rate limit yok | Paketlide liste üretiminin **kredi freni kalktı**, yerine hiçbir sınır konmadı | 299 TL ödemiş kullanıcı (ya da hesabı ele geçiren biri) `/listem`'de sihirbaz tercihini değiştirip "Listemi güncelle"ye basar; her basış tam bir rapor LLM çağrısı, bedeli 0 kredi. Üretim kilidi yalnız **seri**leştirir, saymaz; bayat kilit eşiği 180 sn (`uretim-kilidi.ts:13`). Ayrıca her yeniden üretim `revisionCount`'u 0'a çekiyor → "2 revizyon hakkı" tavanı da döngüyle sıfırlanıyor. Eski tavan 60÷3 ≈ 20 üretimdi; şimdi tavan yok | **orta** | Bu dalda **değil** (kapsam dışı). Ayrı iş: `report_generate` ledger satırları zaten sayıyor → `spendCredits`'ten önce "son 24 saatte `reason='report_generate'` satır sayısı ≥ N ise `code: "SINIR"`" kapısı (N'i CEO belirler; 10 tartışma başlatır, 30 kimseyi rahatsız etmez). Alternatif ucuz fren: üretim kilidini başarıdan sonra 60 sn tutmak | DOĞRULANDI |
| **F2** | `credits.ts:174-176` (`krediBittiAt: null`) ↔ `rapor-actions.ts:223-228` | `delta = 0` iade de "kredi geldi" sayılıp bekleyen kredi-bitti hatırlatmasını iptal ediyor | Paketli kullanıcının 60 kredisi sohbette biter → `/api/soru` `krediBittiIsaretle` yazar (`route.ts:180`), 48 saat sonra top-up e-postası kuyruğa girer. Kullanıcı o arada bir liste üretir, üretim hata verir → `grantCredits(delta: 0, reason:"refund")` `krediBittiAt`'ı **null**'lar. Kredi gelmediği hâlde hatırlatma iptal olur; `krediHatirlatmaGonderildiAt` da hâlâ null olduğu için e-posta ancak kullanıcı bir kez daha sohbette duvara toslarsa yeniden kurulur. G3 öncesi bu doğruydu (iade gerçekten 3 kredi getiriyordu), G3 sonrası yanlış | **düşük** (gelir kaybı, güvenlik değil) | `credits.ts:174` → `...(opts.delta > 0 ? { krediBittiAt: null } : {})`. `credits.ts` bu dalın kapsamı dışında (CTO yasak listesi) → backlog | DOĞRULANDI |
| **F3** | `tercih-degisti-modali.tsx:254` + `:336-347` (`KrediYetersiz`) | Paketli için ölü dal; render edilirse "Güncelleme **0 kredi** ve bakiyen yetmedi" yazar | `durum = "kredi-yetersiz"` yalnız `code === "KREDI" \| "PAKET"` ile kuruluyor (`:115`). Paketlide `KREDI` artık imkânsız (bkz. §2), `PAKET`'i `listeOlustur` hiç döndürmüyor (yalnız `listeRevize` döndürür) → dal **bugün ulaşılamaz**. Ama `ucretsiz` bayrağı bu dala bağlanmadığı için, ileride bakiye negatife düşebilen bir yol açılırsa ekranda anlamsız cümle çıkar | **düşük** | Bu dalda şart değil. İstenirse tek satır: `:254`'teki üçlüye `!ucretsiz &&` eklemek ya da `KrediYetersiz` içinde `raporKredi === 0` dalını yazmak. Aynı sınıf ölü kod `liste-uretici.tsx:263-285`'te de var (backlog #B2, Bilal'in dosyası) | DOĞRULANDI |
| **F4** | `rapor-actions.ts:104` (`getCurrentUser`) → `:158` | `hasPaket` okuması ile harcama arasında paket satın alma yarışı | Kullanıcı `/listem`'de üretimi başlatır; tam o saniyede ikinci sekmede iyzico callback'i paketi yazar. Sunucu eski anlık görüntüyü gördüğü için 3 kredi düşer — paketli olmasına rağmen. Ters yön (paketliyken ücrete düşme) imkânsız, `hasPaket` true→false dönmüyor | **düşük** | Düzeltme önermiyorum: pencere milisaniyeler, zarar 3 kredi, G3 öncesinde de aynıydı (o zaman **her** paketliden düşüyordu). Kayda geçsin yeter | OLASI |
| **F5** | `credit_ledger` (veri, kod değil) | Ölçüm serisi kırılması | `report_generate` satırının `delta`'sı paketlide −3 → 0 oldu. Satır **sayısına** dayanan sorgular (ör. `04-uretim-engelleri.md` §5'teki deneme/iade kırılımı) etkilenmiyor; `SUM(delta)` ile "kaç kredi yandı/iade edildi" soran gelecekteki bir sorgu 22 Eylül'den öncesi ve sonrası için farklı şey ölçer | **bilgi** | Merge commit mesajında ya da `docs/odeme/iyzico.md` kenar notunda tek cümle | DOĞRULANDI |
| **F6** | süreç — çalışma ağacı | `HEAD` şu an **`odeme/paket-vaadi`** üzerinde; orkestratör beni "`funnel/sonuc-ilk-ekran` dalındasın" diye bilgilendirdi | G1 dalına devam eden bir sonraki ajan kendini G3 dalında sanmadan commit atarsa G1 işi G3'ün üstüne biner ve merge sırası bozulur. `git reflog`: `HEAD@{2}: checkout: moving from funnel/sonuc-ilk-ekran to odeme/paket-vaadi` — yazılımcı dönmemiş. (Aynı reflog'da `HEAD@{5}: reset: moving to HEAD~1` **G1 dalında**, G3 dalında yıkıcı komut **yok** — yazılımcının bu iddiası doğru) | **orta** (süreç) | Bir sonraki kod ajanı işe başlamadan orkestratör dalı açıkça söylesin/doğrulasın. Ben dal değiştirmedim (kurallar gereği) | DOĞRULANDI |
### Sızıntı yolları — tek tek (denetim sorusu 3)
| Yol | Sonuç |
|---|---|
| Paketli, başarısız üretim | `refund` satırı `delta 0` → bakiye **değişmez**. Kredi eklenmiyor [D: kod + scratch DB] |
| Paketli, başarısız revizyon, N kez tekrar | Her denemede taze `requestId` → her denemede yeni `(report_revision, id)` + `(refund, id)` çifti, **hepsi 0**. Sınırsız tekrar sınırsız kredi üretmiyor. `revisionCount` yalnız başarıda artıyor (`:374-380`) — bu da yalnız LLM maliyeti bırakıyor (F1) |
| Paketli, bakiyesi 0, liste üretimi | Çalışır (`INSUFFICIENT` dönmez) — paketin vaadi ayakta |
| Paketsiz | `maliyet = 3`: harcama, `KREDI` hatası + `krediBittiIsaretle`, iade, dönen bakiye, modal metinleri — **hiçbiri değişmedi**. Diff'te paketsiz yolu etkileyen tek satır yok [D] |
| Başkasının kredisiyle LLM | G3 yeni bir yetkilendirme deliği açmıyor: `listeOlustur`/`listeRevize` oturumu doğruluyor, `refId` tahmini başkasının raporunu **okutmuyor** (DUPLICATE dalı `kullanicininRaporu(user.id)` ile kendi raporuna bakıyor). Maliyet riski yalnız kullanıcının **kendi** hesabında ve F1'de anlatıldığı gibi |
---
## 4 · Muhasebe / mutabakat (denetim sorusu 5)
Bu gece verdiğim "bozmaz" hükmü kod yazıldıktan sonra **hâlâ geçerli** [D]:
- Bakiyenin tek kaynağı `user.credit_balance` kolonu; defteri toplayan hiçbir okuyucu yok (`grep -rn "creditLedger\|credit_ledger" src scripts` → tüm okumalar `findFirst`/`select id`, `SUM` yok).
- iyzico mutabakat sorgusu yalnız `l.reason IN ('purchase','topup') AND l.ref_id = o.id` (`docs/odeme/iyzico.md:84`). `report_generate`/`report_revision`/`refund` satırlarının `ref_id`'si istemcinin ürettiği `requestId`, `orders.id` değil → bu sorguya **hiç girmiyor**. 0 TL'lik hareket iyzico tarafında görünmüyor.
- `krediKaydiVarMi` de `reason`'ı şart koşuyor (`credits.ts:205-211`) → `delta 0` `refund` satırı bir siparişi "kredisi yazılmış" göstermiyor.
- Tek kenar etkisi F2 (kredi-bitti işaretinin silinmesi) ve F5 (ölçüm serisi).
---
## 5 · Arayüz ↔ kod uyumu (denetim sorusu 6)
| Yüzey | Gösterilen | Sunucuda olan | Uyum |
|---|---|---|:--:|
| Modal açıklaması (`tercih-degisti-modali.tsx:193-197`, paketli) | "…yeni liste bunun yerine geçer; güncelleme paketine dâhil, kredi düşmez." | `maliyet = 0`, bakiye sabit | ✔ birebir |
| Modal buton (`:290-292`, paketli) | "Listemi güncelle" (bedelsiz), ok ikonu metnin sonunda | 0 kredi | ✔ (AGENTS.md CTA deseni korunmuş) |
| Modal bakiye satırı (`:263-267`) | `!ucretsiz && !krediYeter` → paketliye hiç çıkmaz | — | ✔ |
| Modal, **paketsiz** (`:198-202`, `:292`, `:265`) | "…3 kredi düşer", "Listemi güncelle — 3 kredi", "Güncelleme 3 kredi; bakiyen N kredi" | 3 kredi düşer | ✔ değişmedi |
| `listem-icerik.tsx:167-172` | `raporKredi = user.hasPaket ? 0 : RAPOR_KREDI` — modalın tek kredi kaynağı, `raporKredi`'yi başka tüketen yok (`listem-govde.tsx:399` sadece geçiriyor) | aynı ifade sunucuda `:158`/`:300` | ✔ tek kaynak |
| Başlıktaki kredi pill'i (`user-nav.tsx:69`) | `creditBalance` | paketlide düşmüyor | ✔ artık tutarlı (G3 öncesi düşüyordu) |
| `KrediYetersiz` (`:336-347`) | paketliye "Güncelleme 0 kredi ve bakiyen yetmedi" derdi | dal ulaşılamaz | F3 — bugün görünmez |
| `liste-uretici.tsx:274` "Liste üretimi 3 kredi… +30 kredi yükle" | paketli dalı | dal ulaşılamaz | Bilinen ölü kod (backlog #B2), G3'ün değil |
| `paket-satinal.tsx:141-144` "liste 3 kredi" | yalnız **girişsiz** ziyaretçiye gösteriliyor | paketsiz için doğru | ✔ |
**Metin × kod uyumsuzluğu (yasal/pazarlama):** G3'ten sonra `/paket`'teki "60 Yapay Zeka danışman sorusu (her mesaj 1 kredi)" ilk kez **doğru**. Kalan tek uyumsuzluk kod değil e-posta: `kredi-hatirlatma.ts:36` paketliyi de `credit_balance < 3` filtresiyle yakalıyor ve `eposta.ts:175-190`'daki metin "listenin tamamını açmak… için kredi yükleyebilirsin" diyor — paketlinin listesi zaten tam açık. G3 öncesinden var (backlog #B3), G3 onu büyütmüyor ama F2 ile aynı dosyada çözülmeli.
---
## 6 · HÜKÜM
**Merge edilebilir.** Üç koşulun üçü de kodda karşılanmış, idempotency çapası (`UNIQUE(reason, ref_id)`) `amount/delta = 0` ile **fiilen** ayakta (koşturarak doğrulandı), paketliye kredi sızıntısı yok, bakiyesi 0 olan paketli liste üretebiliyor, paketsiz davranış bit-bit aynı, iyzico mutabakatı ve bakiye hesabı etkilenmiyor, arayüz sunucunun yaptığını söylüyor. Bu dalda düzeltilmesi gereken bir satır bulamadım.
Merge'e **engel olmayan**, ayrı iş olarak kuyruğa girmesi gereken üç şey (yazılımcıya şartname netliğinde):
1. **F1 — üretim tavanı (yeni dal, `credits.ts` + `rapor-actions.ts`):** `rapor-actions.ts`'te `spendCredits` çağrısından **önce**, `credit_ledger`'da `reason = 'report_generate' AND user_id = ? AND created_at > now-24h` sayısı ≥ N ise `{ ok:false, code:"SINIR", error:"Bugünlük liste üretim hakkın doldu; yarın devam edebilirsin." }` dön. N değeri CEO'nun; kilit/bayat-kilit mantığına dokunma, `revisionCount` upsert'ünü (`:247`) değiştirme.
2. **F2 — `credits.ts:174`:** `krediBittiAt: null` yalnız `opts.delta > 0` iken yazılsın (`...(opts.delta > 0 ? { krediBittiAt: null } : {})`). Aynı commit'te `kredi-hatirlatma.ts:36`'ya `eq(user.hasPaket, false)` eklenirse backlog #B3 de kapanır.
3. **F6 — süreç:** sabah entegrasyonuna geçmeden `HEAD`'in `odeme/paket-vaadi`'de durduğu bilinsin; G1'e devam eden ajan dalını açıkça doğrulasın.
---
## Bilal'den istenen
1. **Yok** — G3 denetimden temiz çıktı; merge kararı zaten senin kuyruğunda, bu rapor onun için ek bir soru üretmiyor. *(F1'in tavan sayısı CEO'ya sorulacak bir ürün kararı, sana değil.)*