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>
123 lines
17 KiB
Markdown
123 lines
17 KiB
Markdown
# 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.)*
|