Files
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

426 lines
38 KiB
Markdown
Raw Permalink 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.
# 28 — CTO şartnamesi: G1, G3, G2+G5
Hazırlayan: CTO ajanı · Taban commit: **`9bd448b`** (`main`, push edilmiş; `origin/main == main`).
Kaynak kararlar: `05-ceo-kararlari.md` §3, `24-ceo-kapanis.md` §A/§D, `04-sifir-odeme-teshisi.md` Ö1–Ö5, `00-kurucu-brief.md` ("sade ve basit, over-engineering yok").
Yöntem: **yalnız okuma**. Kod değiştirilmedi, commit atılmadı, `pnpm install`/`build`/`typegen`/`tsc`/`lint` **koşulmadı** (disk kısıtı — orkestratör talimatı). `git status` bu raporun yazımı dışında değişmedi.
İşaret: **[D]** bu gece açıp satırını doğruladığım kod · **[Ç]** koddan çıkarım (çalıştırılmadı).
---
## Özet (5 madde)
1. **CEO'nun satır numaralarının çoğu tuttu, ikisi tutmadı.** `rapor-actions.ts` bu gece `9bd448b` ile değişti: liste harcaması artık `98-99/118-124` değil **`153-158`**, iade **`212-217`**, revizyon harcaması **`285-290`**, revizyon iadesi **`339-344`**. Ayrıca `giris-karti.tsx` `src/features/auth/` altında **değil**, `src/features/kullanici/components/giris-karti.tsx`. Aşağıdaki tüm atıflar `9bd448b` üzerinde tek tek açılarak yazıldı. [D]
2. **G3'ün gizli dördüncü satırı var.** `spendCredits`/`grantCredits` koşullansa bile `rapor-actions.ts:243` ve `:363`'teki `kredi: user.creditBalance - RAPOR_KREDI` paketliye **düşmemiş krediyi düşmüş gibi gösterir**; ayrıca `tercih-degisti-modali.tsx` paketliye "Listemi güncelle — 3 kredi" yazmaya devam eder. Tek bir `maliyet` değişkeni dört yeri birden kapatıyor — kapsam bu kadar büyütülmeli, daha fazlası değil. [D]
3. **G1'in CTA'sı için `?kaynak=` allowlist'i şart.** `/listem?uret=1&kaynak=tadimlik` yazmadan önce `uretim-basladi-olayi.tsx:25-41`'deki `IZINLI_KAYNAKLAR`'a `"tadimlik"` eklenmezse değer `"bilinmiyor"`a düşer ve bu değişikliğin ölçümü kaybolur. Dosyanın kendi notu bunu zaten emrediyor. [D]
4. **İki sessiz build tuzağı var ve ikisi de G1'in metin işinde patlar:** `rapor-maske.ts` `import "server-only"` (satır 6), `credits.ts` `appdb`'yi import ediyor (satır 2) — **ikisi de `"use client"` olan `tadimlik-satiri.tsx`'e import EDİLEMEZ**. Yeni CTA cümlesi `ACIK_SATIR`/`DENEME_KREDISI` sabitlerine uzanamaz; sayı gerekiyorsa cümle sayısız yazılır. [D]
5. **`/meraklisina` şeması üç pakette de değişmiyor.** `huni-semasi.tsx`'teki `SEMA` yalnızca sihirbaz adımları, aday havuzu, 24'lük iskelet, filtre gevşetme ve havuz-dışı kuralını belgeliyor (satır 11-41). G1 sayfa sırası + CTA hedefi, G3 kredi muhasebesi, G5 metin değiştiriyor — hiçbiri bu beş şeyden biri değil. **Yazılımcı `huni-semasi.tsx`'e dokunmayacak.** [D]
---
## Dosya sahipliği tablosu
Paketler **teker teker** koşacak (disk), yine de kümeler ayrık tutuldu — dal sırası bozulsa bile çakışma olmaz.
| Dosya | G1 `funnel/sonuc-ilk-ekran` | G3 `odeme/paket-vaadi` | G2+G5 `odeme/kasa-ve-metin` |
|---|:--:|:--:|:--:|
| `src/app/sonuc/page.tsx` | ✅ | | |
| `src/features/liste/components/manuel-harita.tsx` | ✅ | | |
| `src/features/rapor/components/tadimlik-satiri.tsx` | ✅ | | |
| `src/features/sihirbaz/sihirbaz-sabitler.ts` | ✅ | | |
| `src/features/sihirbaz/components/sihirbaz-cagri-karti.tsx` | ✅ | | |
| `src/features/rapor/components/uretim-basladi-olayi.tsx` | ✅ | | |
| `src/features/rapor/rapor-actions.ts` | | ✅ | |
| `src/features/rapor/components/listem-icerik.tsx` | | ✅ | |
| `src/features/rapor/components/tercih-degisti-modali.tsx` | | ✅ | |
| `src/features/odeme/odeme-actions.ts` | | | ✅ |
| `src/features/odeme/components/satin-al-form.tsx` | | | ✅ |
| `src/features/odeme/components/paket-satinal.tsx` | | | ✅ |
| `src/features/kullanici/components/giris-karti.tsx` | | | ✅ |
| `src/lib/analitik.ts` | | | ✅ |
**Hiçbir pakette yok (yasak listesi):** `src/features/rapor/components/liste-uretici.tsx` (Bilal'in dosyası — `9bd448b` ile commit'li ama gece kuralı gereği dokunulmaz), `docs/ekip/BACKLOG.md`, `docs/ekip/KARARLAR.md`, `AGENTS.md`, `content/rehber/**`, `data/**`, `src/features/pazarlama/components/huni-semasi.tsx`, `src/lib/credits.ts`.
### Çakışma kararları
1. **`src/lib/analitik.ts` → G2'nin.** G1'in `tadimlik_goruntulendi` olayında bir **seri kırılması** yaratacağı doğru (olay bugün ~2.200 px aşağıdaydı, yarın ilk ekranda olacak; 30 günlük değer 0 → `01-analitik-bulgular`), ve bu repoda seri kırılmasını `analitik.ts`'in `OlayAdi` yorumuna yazma geleneği var (`program_eklendi`, satır 29-31). Buna rağmen G1 bu dosyaya **dokunmaz**: G2 aynı birliğe `odeme_hata` ekliyor, iki dal bitişik satırlarda çakışır. G1 seri kırılmasını (a) `tadimlik-satiri.tsx:100-102`'deki `useGorunumOlayi` çağrısının üstündeki yoruma, (b) commit mesajına yazar. Kalıcı notun `analitik.ts`'e taşınması backlog önerisi #B1.
2. **`paket-satinal.tsx` ve `tercih-degisti-modali.tsx` → G5/G3'ün, A2'nin değil.** CEO'nun A2 paketi (`odeme/paket-adi`, "Tercih Dönemi Paketi" → "KolayTercih Paketi") bu iki dosyaya da girmek zorunda: ad **8 dosyada** geçiyor ve ikisi bu dalların içinde (`paket-satinal.tsx:64`, `tercih-degisti-modali.tsx:329`) [D]. A2 CEO'nun sırasında **10.** ve kesme sırasının başında; bu yüzden A2 en sonda koşar, G1/G3/G5 dalları yazıldıktan **sonra** taban alır. G1/G3/G5'teki hiçbir yazılımcı paket adına dokunmaz.
3. **`liste-uretici.tsx` kimsenin değil.** G3 uygulandığında o dosyadaki `hasPaket` dalı ("+30 kredi yükle") liste üretimi için **ölü koda** dönüşür (paketli artık hiç `KREDI` kodu alamaz, bkz. G3 adım 1). Bu doğru ama zararsız bir tutarsızlıktır; dosya Bilal'e ait olduğu için **düzeltilmez**, backlog önerisi #B2 olarak kaydedilir.
---
## Tüm yazılımcılar için ortak kurallar
1. Yalnız orkestratörün verdiği **worktree + dalda** lokal commit. Push yok, merge yok, `main`'e commit yok.
2. **`git add -A` / `git add .` yasak.** Yalnız kendi dosya kümenden, yol vererek ekle. Commit öncesi `git status --porcelain` çıktısında kümen dışında bir şey görünüyorsa dur ve raporla.
3. **Doğrulama sırası:** `pnpm exec next typegen && pnpm exec tsc --noEmit && pnpm lint`. Dev sunucusu yok, tarayıcı yok, `pnpm build` **yok** (disk ~5 GB; tam build'i Bilal sabah C3 komut bloğunda tek seferde alır).
4. **Taze worktree'de `next typegen` olmadan `tsc` baseline'da kırıktır** — `PageProps` gibi global tipler `.next/types`'tan gelir. Kırmızı çıktıyı kendi değişikliğine yorma; önce typegen koş.
5. **Worktree'de `node_modules` symlink'i Turbopack'te patlar** — klon kullanılır (`cp -Rc ../kolaytercih/node_modules node_modules`). Kurulumu orkestratör yapar; `pnpm install` çalıştırma.
6. `next build` env'siz koşar: modül yüklenirken env zorunlu kılan kod yazma. Bu üç pakette yeni env yok.
7. **Next'e özgü bir API'ye dokunmadan önce ilgili dokümanı oku** (her paketin altında yolu yazılı). Eğitim verisindeki Next bu sürüm değil.
8. AGENTS.md: rozet gerekiyorsa `SectionEyebrow`, CTA'da ikon **metnin sonunda** `<ArrowRight className="size-4" aria-hidden />`, `Sparkles` yok; modal kapatma çarpısı `DialogContent` varsayılanı. Sihirbaz/liste **mimarisi** değişirse `/meraklisina` şeması aynı commit'te — bu üç pakette **değişmiyor** (bkz. Özet #5), dokunma.
9. Uydurma sayı yok: ekranda görünen her rakam ya tek kaynak sabitten (`URUNLER`, `RAPOR_KREDI`, `ACIK_SATIR`) gelir ya da hiç yazılmaz.
10. Rapor: `docs/gece-vardiyasi/2026-09-22/` altına, orkestratörün verdiği numarayla; kabul kriterlerinin her birinin çıktısını yapıştır.
---
# G1 · `funnel/sonuc-ilk-ekran`
**Amaç:** Ödeme öncesi tek gerçek önizleme (tadımlık) `/sonuc`'un ilk ekranında görünsün ve kullanıcıyı giriş sonrası listenin üretildiği yere göndersin.
**Dal:** `funnel/sonuc-ilk-ekran` · **Taban:** `9bd448b` · **Öncelik: 1 (kesilmez).**
### Dokunulacak dosyalar (tam liste)
| Dosya | Satır | Ne |
|---|---|---|
| `src/features/liste/components/manuel-harita.tsx` | 67-80 | Boş placeholder yerine `null` |
| `src/app/sonuc/page.tsx` | 100-118 | Tadımlık tablodan önce + yorum düzeltmesi |
| `src/features/sihirbaz/sihirbaz-sabitler.ts` | ~165 (`sonucHref` yanı) | `listemUretHref` export edilir (yeni fonksiyon, taşınma) |
| `src/features/sihirbaz/components/sihirbaz-cagri-karti.tsx` | 20-26, 195, 231, 260 | Yerel `listemUretHref` silinir, import edilir |
| `src/features/rapor/components/uretim-basladi-olayi.tsx` | 25-41 | `IZINLI_KAYNAKLAR`'a `"tadimlik"` |
| `src/features/rapor/components/tadimlik-satiri.tsx` | 28, 100-102, 111, 114, 186 | CTA hedefi, seri-kırılması yorumu, `first:mt-0`, yanlış iddia |
### Kapsam dışı (yazılımcı taşmasın)
- Maskenin kaç satır açtığı (`ACIK_SATIR = 3`) — **değişmiyor** (24-ceo-kapanis R5).
- `/sonuc` tablosunun sıralaması, filtreleri, `<h1>` eksikliği (A3 ayrı dal), Suspense fallback'inin iskeleti (`sonuc/page.tsx:45-54`) — fallback bugün de haritayı temsil etmiyor (pill + başlık + `h-36` + tablo satırları = `ProgramTablosu`'nun başlığı), boş harita kalkınca yanlışlığı **artmıyor**. [Ç]
- Tadımlığın başka yüzeylere (`/`, `/paket`) taşınması.
- Profil çerezi olmayan ziyaretçiye özel CTA dalı yazmak — **bilerek yapılmayacak**, gerekçesi aşağıda (Riskler #2).
- `huni-semasi.tsx`, `analitik.ts`, `liste-uretici.tsx`.
### Adım adım
**1) Boş harita ilk ekranı işgal etmesin** — `manuel-harita.tsx:67-80`
`if (liste.length === 0) return null;` Yorum da düzeltilir: bugünkü not "197 KB'lık il geometrisi inmesin diye placeholder alanı rezerve eder" diyor; yeni not, boş durumda hiç yer tutulmadığını ve ilk eklemede haritanın belireceğini söylesin. `TercihHaritasiLazy` zaten `dynamic` — ölçü davranışı değişmez. [D]
**2) Tadımlık tablodan önce** — `sonuc/page.tsx:103-118`
Yeni sıra: `<ManuelHarita />` → `<TadimlikBolumu … />` → `<SonucProgramTablosu … />` → `<SonucSihirbazi … />` → dipnot. Satır 100-102'deki "Sayfa hiyerarşisi: önce Türkiye haritası, sonra liste" yorumu yeni sıraya göre yeniden yazılır (neden: tadımlık ödeme öncesi tek gerçek önizleme, ilk ekranda durur; harita yalnız manuel liste doluyken görünür). Props değişmez, bileşenlerin içine dokunulmaz.
**3) Tadımlık ilk eleman olduğunda tepede boşluk bırakmasın** — `tadimlik-satiri.tsx:114`
`className="mt-16 min-w-0"` → `className="mt-16 min-w-0 first:mt-0"`. Gerekçe: `ManuelHarita` `null` döndüğünde bu `<section>` `main`'in **gerçek ilk DOM çocuğu** olur (JSX yorumları DOM düğümü üretmez), `:first-child` eşleşir ve `main`'in `py-12`'si tek başına yeterli üst boşluğu verir; harita görünürken `mt-16` aynen çalışır. Tek utility, koşullu prop yok. [Ç]
**4) `listemUretHref` tek kaynağa** — `sihirbaz-sabitler.ts` + `sihirbaz-cagri-karti.tsx`
`sihirbaz-cagri-karti.tsx:25-27`'deki yerel fonksiyon `sihirbaz-sabitler.ts`'e (`sonucHref`'in hemen altına) taşınır ve export edilir; üstündeki "`uret=1` HER ZAMAN ilk parametre kalmalı — `giris-karti.tsx` funnel metnini `startsWith("/listem?uret=1")` ile seçiyor" yorumu da birlikte taşınır. Çağrı yerleri (`:195`, `:231`, `:260`) import'a çevrilir, davranış birebir aynı kalır.
*Neden tek satırlık kopyala-yapıştır değil:* bu sözleşmenin kopyası bugün **üç** dosyada yorum olarak yaşıyor (`listem-icerik.tsx:48-50`, `sihirbaz-cagri-karti.tsx:22-24`, `giris-karti.tsx:28-31`) [D]; dördüncü bir literal kopya, funnel metnini sessizce kaybettiren bir hata sınıfı açar. Taşıma 2 satır, riski sıfır.
**5) Ölçüm yüzeyi allowlist'e** — `uretim-basladi-olayi.tsx:25-41`
`IZINLI_KAYNAKLAR`'a `"tadimlik"` eklenir + tek satır yorum ("/sonuc'taki girişsiz tadımlık CTA'sı"). Dosyanın kendi kuralı: "Yeni bir üretim yüzeyi eklerken değerini önce buraya ekle, sonra href'e koy."
**6) CTA doğru yere gitsin** — `tadimlik-satiri.tsx:111`
`const girisUrl = \`/giris?callback=${encodeURIComponent(listemUretHref("tadimlik"))}\`;`
Import satırı 28 düzeltilir: `sonucHref` **kaldırılır** (başka kullanımı yok → `no-unused-vars` lint hatası verir), `listemUretHref` eklenir; `PROFIL_DEGISTI_EVENT` kalır. `onClick`'teki `olay("giris_cta_tiklandi", { kaynak: "tadimlik" })` **aynen kalır** (mevcut seri korunur).
Doğrulanmış zincir: `/giris` callback'i `/^\/(?![/\\])/` kalkanından geçer (`giris-karti.tsx:18-19`) ✅ · `startsWith("/listem?uret=1")` funnel metni korunur ✅ · dönüşte `listem/page.tsx:29-31` → `ListemIcerik uret` → `ListeUretici` üretimi kendiliğinden başlatır (`liste-uretici.tsx:243-256`) ✅. [D]
**7) Seri kırılması yorumu** — `tadimlik-satiri.tsx:100-102`
`useGorunumOlayi("tadimlik_goruntulendi", …)` çağrısının üstüne 2-3 satır: olayın bu değişiklikten önce sayfanın ~2.200 px altında yaşadığı, sonrasında ilk ekranda olduğu, iki dönemin karşılaştırılamayacağı. (`analitik.ts`'e **yazılmaz** — çakışma kararı #1.)
**8) Teslim edilmeyen iddia kalkar** — `tadimlik-satiri.tsx:186`
`24 satırın tamamı için giriş yap — 5 deneme kredin hazır` → **`Giriş yap ve listeni oluştur`** (+ mevcut `<ArrowRight className="size-4" aria-hidden />` aynen kalır — AGENTS.md CTA deseni).
Gerekçe: maske paketsize 24 satırın **3'ünün** analizini açıyor (`rapor-maske.ts:12, 28-46`); "tamamı" teslim edilmiyor. Bu commit **yeni vaat eklemez**, yalnız yanlış olanı kaldırır. Üstteki `<p>`'deki "Bu, 24 satırlık Yapay Zeka listenden yalnızca bir örnek." cümlesi **doğrudur ve kalır** (üretilen liste gerçekten 24 satır).
**9) `[Bilal]` commit'i — CTA'nın yeni vaadi** (ayrı, en son commit)
Önerilen: buton `Kendi listemi oluştur`, alt satır (`:174-176`) `Kredi kartı gerekmez · Google ya da e-posta ile 30 saniyede · Giriş sonrası liste hemen üretilir`.
**Kısıt:** bu dosya `"use client"`. `ACIK_SATIR` (`rapor-maske.ts`, `import "server-only"` satır 6) ve `DENEME_KREDISI`/`RAPOR_KREDI` (`credits.ts`, satır 2'de `./appdb` import ediyor) **import edilemez** — build'i kırar. Sayı gerekiyorsa cümle sayısız yazılır; elle "3"/"5" yazmak yasak (tek kaynak kuralı). Commit en sona konur ki Bilal `git reset --hard HEAD~1` ile tek hamlede düşürebilsin.
### Kabul kriterleri (tarayıcısız, komutla)
```sh
# 1) Tadımlık tablodan önce (ilk sayı küçük olmalı)
grep -n "<TadimlikBolumu\|<SonucProgramTablosu$" src/app/sonuc/page.tsx
# 2) Boş harita hiç render edilmiyor
grep -n "liste.length === 0" src/features/liste/components/manuel-harita.tsx # 1 sonuç
grep -c "haritada belirir" src/features/liste/components/manuel-harita.tsx # 0
grep -c "haritada belirir" -r src/ # 0
# 3) CTA doğru hedefe gidiyor, eski hedef kalmadı
grep -n "listemUretHref" src/features/rapor/components/tadimlik-satiri.tsx # 1
grep -c "sonucHref" src/features/rapor/components/tadimlik-satiri.tsx # 0
grep -n "export function listemUretHref" src/features/sihirbaz/sihirbaz-sabitler.ts # 1
grep -c "function listemUretHref" src/features/sihirbaz/components/sihirbaz-cagri-karti.tsx # 0
# 4) Ölçüm yüzeyi allowlist'te
grep -n '"tadimlik"' src/features/rapor/components/uretim-basladi-olayi.tsx # 1
# 5) Teslim edilmeyen iddia yok
grep -rc "24 satırın tamamı" src/ # 0
# 6) Şema ve yasak dosyalar el değmemiş
git diff --name-only HEAD~N | grep -E "huni-semasi|analitik.ts|liste-uretici|credits.ts" # boş
# 7) Tipler ve lint
pnpm exec next typegen && pnpm exec tsc --noEmit && pnpm lint
```
Ek el kontrolü (dosya:satır okuyarak): `TadimlikBolumu` girişli kullanıcıda ve havuz boşken **hâlâ `null` dönüyor** (`tadimlik-bolumu.tsx:18-21` değişmedi) · `ManuelHarita` liste doluyken haritayı aynen render ediyor.
### Riskler ve tuzaklar
1. **`sonucHref` import'unu silmeyi unutmak** → lint kırmızısı. (Adım 6.)
2. **Profil çerezi olmayan ziyaretçi.** `/listem?uret=1`, seçim yoksa "profil-yok" → boş durum + sihirbaz CTA'sı gösterir (`liste-uretici.tsx:243-251`); **kredi harcanmaz, hata ekranı çıkmaz** [D]. Bu durum yalnızca eski `?sira=` linkiyle gelen ziyaretçide oluşabilir, çünkü geçerli bir profil `tercihProfiliDogrula` gereği **en az 1 ilgi alanı içermek zorundadır** (`sihirbaz-sabitler.ts:90, 143-145`) [D]. Bu yüzden CTA'ya koşullu ikinci bir dal **yazılmayacak** — kurucunun "over-engineering yok" kısıtı.
3. **Giriş dönüşünde onaysız 3 kredi.** `giris-linki.tsx:13-16` navbar butonu için bunu bilerek yasaklıyor; burada sorun değil çünkü kullanıcı "listemi oluştur" diyen bir CTA'ya **açıkça tıklıyor** ve bu, `hazir_cta` (`sihirbaz-cagri-karti.tsx:260`) ile birebir aynı, onaylı desen. [D] CTA metni bu yüzden üretimi söylemeli (adım 8-9).
4. **Layout kayması:** boş harita `null` iken ilk "+" tıklamasında harita belirir ve tablo aşağı iner. Bugün de placeholder→harita geçişi var; kaybı kabul edilen bilinçli takas (CEO §3 G1-b).
5. `tadimlik_goruntulendi` sayısı bir gecede 0'dan yüksek bir sayıya fırlayacak — **kırık ölçüm değil, kazanılan payda**. Adım 7 bunu koda yazar.
### Commit planı
1. `fix(sonuc): boş harita ilk ekranı işgal etmez` — `manuel-harita.tsx`
2. `fix(sonuc): tadımlık tablonun üstüne alınır` — `sonuc/page.tsx`, `tadimlik-satiri.tsx` (`first:mt-0`)
3. `refactor(sihirbaz): listemUretHref tek kaynağa taşınır` — `sihirbaz-sabitler.ts`, `sihirbaz-cagri-karti.tsx`
4. `fix(tadimlik): CTA girişten dönüşte üretimi başlatır` — `tadimlik-satiri.tsx`, `uretim-basladi-olayi.tsx`
5. `content(tadimlik): teslim edilmeyen "24 satırın tamamı" iddiası kalkar` — `tadimlik-satiri.tsx`
6. `[Bilal] content(tadimlik): CTA'nın yeni vaadi` — `tadimlik-satiri.tsx` *(en son, tek başına düşürülebilir)*
### Okunacak Next dokümanı (kod yazmadan önce)
- `node_modules/next/dist/docs/01-app/01-getting-started/05-server-and-client-components.md` — client sınırı ve `server-only` (Adım 9'un tuzağı).
- `node_modules/next/dist/docs/01-app/01-getting-started/04-linking-and-navigating.md` — `<Link href>` ve sorgu parametresi.
---
# G3 · `odeme/paket-vaadi`
**Amaç:** Paket sayfasındaki söz kodla eşitlensin — paketli kullanıcıdan liste ve revizyon için kredi düşmesin, "60 danışman sorusu" gerçekten 60 olsun.
**Dal:** `odeme/paket-vaadi` · **Taban:** `9bd448b` · **Öncelik: 2 (kesilmez).** · **Merge öncesi `guvenlik-uyum` denetimi zorunlu.**
### Dokunulacak dosyalar
| Dosya | Satır | Ne |
|---|---|---|
| `src/features/rapor/rapor-actions.ts` | 152-158, 212-217, 243, 285-290, 339-344, 363 | `maliyet` değişkeni; 4 harcama/iade + 2 bakiye satırı |
| `src/features/rapor/components/listem-icerik.tsx` | 167 | `raporKredi` prop'u paketliye 0 iner |
| `src/features/rapor/components/tercih-degisti-modali.tsx` | 129, 188, 250, 275 | 0 bedelde "kredi" dili yerine "pakete dâhil" |
### KRİTİK KISIT (CEO kararı, tartışmaya kapalı)
`spendCredits` ve `grantCredits` çağrıları **KALDIRILMAZ**, yalnız **tutarları koşullanır**. Gerekçe koddan doğrulandı:
- `credit_ledger` üzerinde `uniqueIndex("ledger_reason_ref").on(reason, refId)` var (`src/lib/appdb/schema.ts:133`) [D]. Üretimin tek idempotency çapası bu. Çağrı kaldırılırsa aynı `requestId` ile ikinci üretim `DUPLICATE` dönmez → BACKLOG #1'deki çifte üretim kapısı yeniden açılır.
- `amount: 0` ile `spendCredits` **INSUFFICIENT dönmez**: `gte(creditBalance, 0)` her zaman eşleşir ve `updatedAt` değiştiği için `rowsAffected = 1` olur (`credits.ts:65-74`) [Ç — SQLite `rowsAffected` semantiği; QA'nın DB duman testiyle teyit edilmeli].
- İade dalındaki `grantCredits(delta: 0, reason: "refund")` satırı **silinmemeli**: `rapor-actions.ts:171-184` ve `:303-316` "önceki deneme başarısız mıydı" sorusunu tam olarak bu `refund` satırının varlığıyla cevaplıyor. Satır yazılmazsa paketlinin başarısız denemesi `SURUYOR` ("Listen zaten hazırlanıyor") gibi görünür — yanlış mesaj. [D]
- `delta`'yı koşullamazsak paketliye her başarısız üretimde **bedava 3 kredi** yazılır. (CEO'nun uyardığı sızıntı.)
### Kapsam dışı
- Fiyat (299 TL), kredi adedi (60/30), top-up kuralları, maske (`ACIK_SATIR`) — hepsi R5 ile kapalı.
- Sohbet kredisi (`spendCreditForMessage`, mesaj başına 1) — **doğru davranış, dokunulmaz**; G3'ün tüm amacı bu 60 sorunun gerçekten 60 kalması.
- `credits.ts`, `liste-uretici.tsx`, paket adı (A2), `kredi-hatirlatma.ts`.
- `/paket`, `/` ve `listem-govde.tsx`'teki "60 danışman sorusu" metinleri: **üçü de `URUNLER.paket.credits`'ten türüyor** (`app/page.tsx:201`, `paket-satinal.tsx:40`, `listem-govde.tsx:345`) [D] — G3 sonrası hiçbir metin değişikliği gerektirmeden doğru hâle gelirler. Dokunma.
### Adım adım
**1) `listeOlustur` yolu** — `rapor-actions.ts:152` civarı (fonksiyon: `listeOlusturKilitli`)
`spendCredits` çağrısının hemen üstüne:
```ts
// Paket vaadi (docs/urun/vizyon.md): liste ve revizyon pakete dâhil; krediler
// yalnız danışman sorusu için. Çağrı BİLEREK duruyor — ledger'daki
// UNIQUE(reason, ref_id) (appdb/schema.ts:133) üretimin tek idempotency
// çapası; paketliye delta 0 satır yazılır, aynı requestId ikinci kez
// DUPLICATE döner.
const maliyet = user.hasPaket ? 0 : RAPOR_KREDI;
```
Kullanım yerleri:
- `:155` `amount: RAPOR_KREDI` → `amount: maliyet`
- `:165` hata metni `${RAPOR_KREDI} kredi gerekli` → **değişmez** (bu dal yalnız `maliyet > 0` iken erişilebilir).
- `:214` `delta: RAPOR_KREDI` → `delta: maliyet`
- `:243` `kredi: user.creditBalance - RAPOR_KREDI` → `kredi: user.creditBalance - maliyet`
**2) `listeRevize` yolu** — `rapor-actions.ts:285` civarı
Aynı `maliyet` satırı (`user.hasPaket ? 0 : RAPOR_KREDI`) `spendCredits`'ten önce tanımlanır; `:287 amount`, `:341 delta`, `:363 kredi` aynı şekilde bağlanır. Not olarak yazılır: `:256`'daki `if (!user.hasPaket) return PAKET` guard'ı yüzünden burada `maliyet` **her zaman 0**'dır; ifade guard değişirse davranışın bozulmaması için koşullu bırakılıyor.
**3) Sunucu davranışı dışına taşma yasak.** `krediBittiIsaretle`, `uretimKilidiAl/Birak`, `reports` yazımı, maskeleme dalları (`user.hasPaket ? sonuc : raporMaskele(sonuc)`) **aynen kalır**.
**4) Arayüz aynı şeyi söylesin** — `listem-icerik.tsx:167` + `tercih-degisti-modali.tsx`
`raporKredi={RAPOR_KREDI}` → `raporKredi={user.hasPaket ? 0 : RAPOR_KREDI}`.
Modalda tek türetilmiş bayrak: `const ucretsiz = raporKredi === 0;` ve üç metin yeri:
- `:188` "…geçer ve {raporKredi} kredi düşer." → ücretsizse "…geçer; güncelleme paketine dâhil, kredi düşmez."
- `:250` "Güncelleme {raporKredi} kredi; bakiyen {kredi} kredi." → ücretsizse bu satır **gösterilmez**.
- `:275` buton "Listemi güncelle — {raporKredi} kredi" → ücretsizse "Listemi güncelle" (ok ikonu deseni aynen).
`:129 krediYeter` ve `KrediYetersiz` dalına dokunulmaz: `kredi >= 0` her zaman doğru olduğu için o dal paketliye zaten hiç render edilmez. [Ç]
*Neden bu adım zorunlu:* aksi hâlde kullanıcıya "3 kredi düşer" denip düşülmez — yalanın yönü değişir, kalitesi değişmez.
### Kabul kriterleri
```sh
# 1) Çağrılar duruyor (idempotency çapası sağlam)
grep -c "spendCredits(" src/features/rapor/rapor-actions.ts # 2
grep -c "grantCredits(" src/features/rapor/rapor-actions.ts # 2
# 2) Tutarlar koşullu; sabit tutar hiçbir harcama/iade satırında kalmadı
grep -c "hasPaket ? 0 : RAPOR_KREDI" src/features/rapor/rapor-actions.ts # 2
grep -n "amount: maliyet\|delta: maliyet" src/features/rapor/rapor-actions.ts # 4 satır
grep -c "delta: RAPOR_KREDI\|amount: RAPOR_KREDI" src/features/rapor/rapor-actions.ts # 0
grep -c "creditBalance - RAPOR_KREDI" src/features/rapor/rapor-actions.ts # 0
# 3) requestId/refId zinciri değişmedi
grep -n "refId: requestId\|refId: input.requestId" src/features/rapor/rapor-actions.ts # 4 satır
# 4) Arayüz aynı şeyi söylüyor
grep -n "raporKredi={" src/features/rapor/components/listem-icerik.tsx # hasPaket ? 0 : RAPOR_KREDI
grep -c "ucretsiz" src/features/rapor/components/tercih-degisti-modali.tsx # >=3
# 5) Paket vaadi metinleri tek kaynaktan (hardcoded 60 yok)
grep -rn "danışman sorusu" src/ # 3 sonuç, üçü de credits/paketKredi interpolasyonu
# 6) Kapsam dışı dosyalar el değmemiş
git diff --name-only HEAD~2 # yalnız 3 dosya; credits.ts / liste-uretici.tsx YOK
pnpm exec next typegen && pnpm exec tsc --noEmit && pnpm lint
```
**QA'ya devredilen davranış kriterleri (statik grep ile kanıtlanamaz, oturum ister):** paketlide liste ve revizyon bakiyeyi düşürmez · `credit_ledger`'da `delta = 0` satır yazılır · aynı `requestId` ile ikinci üretim `DUPLICATE`/`SURUYOR` döner · üretim hatasında paketliye kredi **eklenmez** · paketsizde davranış bit-bit aynı.
### Riskler ve tuzaklar
1. **`user.hasPaket` 0/1 olabilir** (drizzle/SQLite boolean); `? :` her iki temsille de doğru çalışır — `=== true` **yazma**.
2. **Revizyon dalında `maliyet` daima 0** — bu bir hata değil, paket vaadinin ta kendisi (`MAX_REVIZYON = 2` hak sayacı sınırı korur, `:274-280`).
3. **Delta-0 ledger satırı ve iyzico mutabakatı:** `credit_ledger` bir muhasebe defteri değil, kredi hareket kaydıdır; iyzico tarafı `orders` + `grantCredits(reason: "purchase")` üzerinden yürüyor (`krediKaydiVarMi`, `credits.ts:202-212`). Delta-0 satırının mutabakata etkisi **olmaması gerekir** ama bu benim çıkarımım [Ç] — **`guvenlik-uyum` merge öncesi açıkça cevaplasın** (aşağıda soru metni).
4. `kredi-hatirlatma.ts:36` paketliyi de tarayabilir (sohbet kredisi 3'ün altına inerse); e-posta metni "listenin tamamını açmak istersen" diyor (`eposta.ts:188`) — paketliye yanlış. **G3'ten önce de var olan bir hata**, kapsam dışı, backlog #B3.
### Commit planı
1. `fix(kredi): paketliden liste ve revizyon kredisi düşmez` — `rapor-actions.ts`
2. `fix(listem): fark modalı paketliye kredi bedeli göstermez` — `listem-icerik.tsx`, `tercih-degisti-modali.tsx`
`[Bilal]` commit'i **yok** — paket içeriği kararı CEO'da verildi (24-ceo-kapanis §B).
### Okunacak Next dokümanı
- `node_modules/next/dist/docs/01-app/01-getting-started/07-mutating-data.md` — Server Action sözleşmesi (dönüş tipi, `"use server"`).
- `node_modules/next/dist/docs/01-app/02-guides/data-security.md` — server/client sınırında veri ve `server-only`.
---
# G2 + G5 · `odeme/kasa-ve-metin`
**Amaç:** Ödeme yüzeyinde iki delik kapanır — hatanın ölçüsü yok (G2) ve satın alma niyetiyle gelen kişiye "sonra al" deniyor (G5).
**Dal:** `odeme/kasa-ve-metin` · **Taban:** `9bd448b` · **Öncelik: 3 (vakit yetmezse ilk kesilecek paket budur).** · **Merge öncesi `guvenlik-uyum` denetimi (para yüzeyi).**
CEO'nun birleştirme gerekçesi: ikisi de aynı yüzeyin küçük düzeltmesi, ayrı dal maliyeti faydasından büyük (24-ceo-kapanis §D-9).
### Dokunulacak dosyalar
| Dosya | Satır | Ne |
|---|---|---|
| `src/lib/analitik.ts` | 36 civarı (`OlayAdi`) | `odeme_hata` adı |
| `src/features/odeme/odeme-actions.ts` | 16, 35, 45, 48, 87, 91, 102, 108 | Hata dönüşlerine `kod` alanı |
| `src/features/odeme/components/satin-al-form.tsx` | 48-55 | İki hata dalında `olay("odeme_hata", { kod })` |
| `src/features/odeme/components/paket-satinal.tsx` | 140-144 | "Paketi sonra alırsın." kalkar |
| `src/features/kullanici/components/giris-karti.tsx` | 28-31, 52-66 | `/paket` callback'i için üçüncü metin dalı |
### Kapsam dışı
- **Birincil CTA metni ve hedefi** (`paket-satinal.tsx:131-139`) — 21 Eylül kararı, R4 ile kalıcı kapalı. Buton metnine, rengine, hedefine **dokunulmaz**.
- Fiyatın girişsiz dala eklenmesi (ürün Ö2a) — R4.
- `odeme_tamamlandi` sunucu olayı, yeni sayfa görüntüleme enstrümanı, `/odeme` ve `/odeme/sonuc` yüzeyleri, iyzico entegrasyonu, `GirisBaslik`'in typewriter mesajları, paket adı (A2).
### Adım adım — G2 (ölçüm)
**1) Olay adı** — `analitik.ts` `OlayAdi` birliğine `| "odeme_hata"` (`odeme_baslatildi`'nın hemen altına) + 2 satır yorum: ne zaman atıldığı ve `kod` özelliğinin allowlist'li olduğu.
**2) Hata kodu sunucudan gelsin** — `odeme-actions.ts`
`OdemeBaslatDurum`'un hata dalı kod taşısın:
```ts
export type OdemeHataKodu = "URUN" | "ZATEN_PAKETLI" | "TOPUP_PAKETSIZ" | "SUNUCU";
export type OdemeBaslatDurum = { error: string; kod: OdemeHataKodu } | { yonlendir: string };
```
Eşleme: `:35` → `"URUN"` · `:45` → `"ZATEN_PAKETLI"` · `:48` → `"TOPUP_PAKETSIZ"` · `:87`, `:91`(initialize başarısız), `:102`(imza geçersiz) → `"SUNUCU"`. Kullanıcıya giden `error` metinleri ve `console.error` satırları **aynen kalır**; iyzico'nun `errorCode`/`errorMessage` değerleri **Rybbit'e gönderilmez** (yalnız log'da kalır — kardinalite ve sızıntı).
*Kapsam düzeltmesi:* CEO G2'yi yalnız `satin-al-form.tsx` + `analitik.ts` olarak tanımlamıştı; sunucu bugün yalnız serbest metin `error` döndürüyor (`odeme-actions.ts:16`) [D], dolayısıyla istemci "anahtar eksik" ile "paketin zaten aktif"i ayıramaz — G2'nin tek amacı olan teşhis oluşmaz. 6 satırlık `kod` alanı bunu çözer. **Vakit darsa asgari sürüm:** sunucuya dokunmadan yalnız iki kod (`"BAGLANTI"`, `"SUNUCU"`); kabul kriteri yine sağlanır, teşhis gücü düşer.
**3) İstemci olayı** — `satin-al-form.tsx:48-55`
- `catch` dalı (`:48`): `olay("odeme_hata", { kod: "BAGLANTI" })`, sonra mevcut `toast.error(BAGLANTI_HATASI)`.
- `"error" in sonuc` dalı (`:52`): `olay("odeme_hata", { kod: sonuc.kod, urun })`, sonra mevcut `toast.error(sonuc.error)`.
`olay("odeme_baslatildi", { urun })` (`:43`) ve `disabled`/`gidiyor` mantığı **değişmez**. Toast metinleri değişmez. Kullanıcı girdisi/serbest metin olay özelliği olarak **gönderilmez**.
### Adım adım — G5 (metin)
**4) "Paketi sonra alırsın." kalkar** — `paket-satinal.tsx:140-144`
Kalan cümle: "Girişte {DENEME_KREDISI} deneme kredisi tanımlanır; liste {RAPOR_KREDI} kredi, ilk {ACIK_SATIR} satır açık." (21 Eyl kararıyla korunan ilk iki cümle). Üç import'un üçü de kullanımda kalır → lint temiz. Birincil butona dokunulmaz.
**5) `/giris`'te üçüncü metin dalı** — `giris-karti.tsx:28-31, 52-66`
```ts
// Ödeme niyeti: /paket callback'i ile gelen kişiye üçüncü kez ücretsiz
// deneme anlatılmaz; girişin tek işi ödemeyi sürdürmek.
const odeme = callbackURL.startsWith("/paket");
```
`funnel` ifadesi **değişmez** (`/sonuc` ve `/listem?uret=1` dalları birebir korunur — `/paket` zaten ikisiyle de eşleşmiyor [D]). Paragraf üç dallı olur: `odeme ? (…) : funnel ? (…) : (…)`.
Önerilen `odeme` metni: **"Girişten sonra ödemeye kaldığın yerden devam edersin — paketin hesabına tanımlanır."** Deneme kredisi vaadi bu dalda **birincil cümle olmaz**; istenirse ikincil küçük satır olarak kalabilir.
`<GirisBaslik funnel={funnel} />` **değişmez**: `/paket` dalında `funnel === false` olduğu için başlık sade "Giriş yap" olur — typewriter mesajları liste dilinde (`giris-baslik.tsx:6-13`), ödeme niyetine uymaz. [D]
### Kabul kriterleri
```sh
grep -n "odeme_hata" src/lib/analitik.ts # 1 (OlayAdi)
grep -n "odeme_hata" src/features/odeme/components/satin-al-form.tsx # 2
grep -n "odeme_baslatildi" src/features/odeme/components/satin-al-form.tsx # 1 (davranış korunmuş)
grep -n "kod:" src/features/odeme/odeme-actions.ts # 6 hata dönüşü
grep -rn "errorMessage\|errorCode" src/features/odeme/components/ # 0 (iyzico detayı istemcide yok)
grep -rc "Paketi sonra alırsın" src/ # 0
grep -n "Giriş yap — ilk" src/features/odeme/components/paket-satinal.tsx # 1 (CTA metni değişmemiş)
grep -n 'startsWith("/paket")' src/features/kullanici/components/giris-karti.tsx # 1
grep -n 'startsWith("/sonuc")\|startsWith("/listem?uret=1")' src/features/kullanici/components/giris-karti.tsx # değişmemiş
pnpm exec next typegen && pnpm exec tsc --noEmit && pnpm lint
```
### Riskler ve tuzaklar
1. **`OdemeBaslatDurum` tipi değişince tüm dönüş yolları güncellenmeli** — `tsc` bunu yakalar; `"error" in sonuc` daraltması aynen çalışır.
2. **`kod` değerleri allowlist'li sabit dizgiler olmalı** — kullanıcı girdisinden türeyen hiçbir şey Rybbit'e gitmez (PROTOKOL 9 + `uretim-basladi-olayi.tsx:18-24`'teki yerleşik kural).
3. `satin-al-form.tsx` `"use client"`; `olay()` zaten import edili (`:8`), yeni bağımlılık yok.
4. **Yayın koşulu (CEO):** G2, Bilal'in gerçek test ödemesiyle **aynı yayında** çıkar — tek başına canlıya alınırsa ölçüm boş kalır.
5. A2 (paket adı) bu dosyaya sonra girecek — paket adına **dokunma** (çakışma kararı #2).
### Commit planı
1. `feat(olcum): odeme_hata olayı — ödeme hunisine payda` — `analitik.ts`, `odeme-actions.ts`, `satin-al-form.tsx`
2. `content(paket): "Paketi sonra alırsın." kalkar` — `paket-satinal.tsx`
3. `content(giris): /paket callback'i ödemeye devam metni alır` — `giris-karti.tsx`
### Okunacak Next dokümanı
- `node_modules/next/dist/docs/01-app/01-getting-started/07-mutating-data.md` — Server Action dönüş tipleri.
- `node_modules/next/dist/docs/01-app/02-guides/analytics.md` — istemci olay enstrümanı.
---
## Merge sırası ve bağımlılıklar
Dosya kümeleri ayrık → **teknik bağımlılık yok**, sıra yalnız öncelik. Önerilen: **G1 → G3 → G2+G5 → (A2, en son)**.
Tam `pnpm build`i **hiçbir dal koşmaz**; entegrasyon build'i Bilal'in sabah komut sırasında (24-ceo-kapanis §C3), push edilecek commit'in kendisi üzerinde APFS klonlu worktree ile alınır.
## Uzman yönlendirmesi
| Kim | Ne | Ne zaman |
|---|---|---|
| **`guvenlik-uyum`** | **G3 merge öncesi zorunlu:** "`credit_ledger`'a `delta = 0` satır yazmak iyzico mutabakatını ya da iade/uyuşmazlık takibini bozar mı? `refund` satırının delta'sı 0 olduğunda `rapor-actions.ts:171-184`'teki 'önceki deneme başarısızdı' çıkarımı hâlâ geçerli mi?" | Bu gece (24-ceo-kapanis §D-11) |
| **`guvenlik-uyum`** | G2: `kod` allowlist'inin kişisel veri/kart verisi taşımadığı, iyzico hata detayının istemciye inmediği | G2 merge öncesi |
| **`tasarimci`** | G1 adım 9'un `[Bilal]` cümlesi + `first:mt-0` sonrası ilk ekran hiyerarşisi; G5'in üçüncü `/giris` metni | Dallar yazıldıktan sonra, tek raporda |
| **`qa-muhendisi`** | G3'ün davranış kriterleri (oturum + `data/app.db` okuması ister); G1'in giriş→üretim zinciri | Dal başına tek rapor satırı; düşen kriterde commit alınmaz |
| **`analitik`** | `tadimlik_goruntulendi` seri kırılması notu; `odeme_hata` için pano/huni | G1 ve G2 merge sonrası |
| **`yapay-zeka-muhendisi`** | Gerek yok — prompt, havuz kuralları ve LLM çıktısı üç pakette de değişmiyor (T12 tetiği açılmıyor) | — |
| **`veri-muhendisi`** | Gerek yok — `yokatlas.db` ve veri hattı dışarıda | — |
## İtirazlar
1. **G2'nin kapsamı eksikti (düzeltildi).** Sunucu bugün yalnız serbest metin `error` döndürüyor; istemci hatayı sınıflandıramaz, dolayısıyla "kasa neden çalışmıyor" sorusu ölçülemez. `odeme-actions.ts`'e `kod` alanı eklenmeden G2 yalnız "bir hata oldu" sayar. Düzeltilmiş kapsam yukarıda; asgari sürüm de yazıldı.
2. **G3'ün kapsamı eksikti (düzeltildi).** CEO iki harcama + iki iade dalını saydı; `rapor-actions.ts:243` ve `:363`'teki bakiye dönüşü ile `tercih-degisti-modali.tsx`'in kredi metni dışarıda kalmıştı. Onlar kapatılmazsa paketli kullanıcı "3 kredi düştü" görür, düşmez — G3'ün amacı olan "söz = kod" sağlanmaz. Kapsam 1 dosya + 1 türetilmiş bayrak kadar büyütüldü.
3. **CEO'nun `giris-karti.tsx` yolu yanlıştı** (`src/features/auth/…` → doğrusu `src/features/kullanici/components/giris-karti.tsx`), `rapor-actions.ts` satır numaraları `84fc88e` referanslıydı ve `9bd448b`'de kaydı. Tüm atıflar bu şartnamede güncellendi.
4. **G1'de CEO'nun yazmadığı iki zorunlu adım var:** `?kaynak=` allowlist'i (yoksa ölçüm `bilinmiyor`a düşer) ve `sonucHref` import'unun kaldırılması (yoksa lint kırmızı). İkisi de tek satır.
5. **İtiraz etmediğim yer:** `spendCredits`'in kaldırılmaması kısıtı doğru ve koddan doğrulandı (`uniqueIndex("ledger_reason_ref")`, `schema.ts:133`). Daha "sade" görünen "paketliyse hiç çağırma" yolu idempotency çapasını **ve** başarısız-deneme işaretini birlikte kaybettirirdi.
## Backlog önerileri (BACKLOG.md'ye dokunulmadı)
- **B1** — `tadimlik_goruntulendi` seri kırılması notu `analitik.ts`'e taşınsın (G1 ile G2 aynı dosyada çakışmasın diye bu gece `tadimlik-satiri.tsx`'te kalıyor). XS.
- **B2** — `liste-uretici.tsx:263-285`'teki `hasPaket` "kredi yetersiz" dalı G3 sonrası liste üretimi için ölü kod; Bilal'in dosyası olduğu için bu gece dokunulmadı. XS.
- **B3** — Kredi-bitti hatırlatma e-postası paketliyi de tarayabiliyor (`kredi-hatirlatma.ts:29-38`) ve metni "listenin tamamını açmak istersen" diyor (`eposta.ts:188`) — paketliye yanlış. Sorgu `hasPaket = false` koşulu ya da metin dalı istiyor. S.
- **B4** — `ACIK_SATIR` `rapor-maske.ts`'te `server-only` modülde yaşıyor; istemci metinleri bu sayıyı tek kaynaktan okuyamıyor (bugün `liste-uretici.tsx:274` "3 kredi"yi elle yazıyor). Sabitlerin client-safe bir modüle ayrılması. S.
- **B5** — `/sonuc` tadımlığının `useGorunumOlayi` eşiği ilk ekrana taşındıktan sonra gözden geçirilsin; "görüntülendi" artık neredeyse her ziyaretle eşit olacağı için huni değeri düşecek — anlamlı ölçüt "tadımlık CTA tıklaması / tadımlık görüntülenmesi" oranıdır. XS.
## Bilal'den istenen (2 madde)
1. **G1'in son commit'i (`[Bilal] content(tadimlik): CTA'nın yeni vaadi`) onayı** — tadımlık butonunun yeni cümlesi senin diline mi uysun, yoksa kendi cümleni mi yazacaksın? Commit tek başına düşürülebilir hazırlanıyor. ~2 dk.
2. **Üç dalın tek build'de doğrulanması** — sabahki C3 komut bloğunu çalıştırırken `funnel/sonuc-ilk-ekran`, `odeme/paket-vaadi`, `odeme/kasa-ve-metin` dallarını entegrasyon worktree'sinde birleştirip `pnpm build`i o commit üzerinde al; ajanlar disk yüzünden build koşmadı. ~10 dk.