# 06 — CTO şartnamesi: P0-ek + P1–P4 Hazırlayan: CTO ajanı · 22 Eyl 2026 · Taban commit: `main @ bda4d07` Kaynak kararlar: `05-ceo-karari.md`, `docs/ekip/KARARLAR.md` (22 Eyl iki kayıt) Doğrulama koşusu (CTO): `next typegen` → `tsc --noEmit` → `pnpm lint`, baseline **yeşil** (Bilal'in kirli ağacı dâhil). ## Özet 1. `.txt` açığı gerçekten duruyordu: birincil yol (`uret()` içindeki `router.replace`) ölü sunucuda Next'in kendi kodunda tam-sayfa gezinmeye düşüyor. **P0-ek olarak 22 Eyl'de orkestratör tarafından uygulandı** (aşağıda). 2. `liste-uretici.tsx` çakışması (a) ile çözüldü: P2 o dosyaya hiç dokunmuyor, olay `listem-icerik.tsx`'e monte edilen yeni bir istemci bileşeninden atılıyor. Bedeli: `yeniden_deneme` bu turda ölçülmüyor. 3. `src/lib/analitik.ts` yalnız P2'nin. P3/P4 yeni olay adı eklemiyor → dört paketin dosya kümesi kesişimi **sıfır**, dördü paralel. 4. İki itiraz: (İ1) P3'te Ö1'in ikinci yarısı yapılmamalı — ölü kod olur ve `hedef` sözleşmesini bozar. (İ4) P4'te giriş callback'i `/listem` olmamalı — bağlam kaybı, ve `?uret=1` taşırsa rızasız 3 kredi yakar; callback **bulunulan sayfa** olmalı. 5. `/meraklisina` şeması etkilenmiyor (doğrulandı): `SEMA` adım/havuz/iskelet/gevşetme kurallarını belgeliyor, P3 yalnız kapı kapandıktan sonraki gezinme hedefini değiştiriyor. ## P0-ek — birincil yol koruması (UYGULANDI, 22 Eyl) **Bulgu (Next 16.2.10 kaynağından doğrulandı):** - `fetch-server-response.js` — cevap flight değilse ya da `!res.ok` ise `doMpaNavigation(url)` → string döner (bağlantı hiç kurulamazsa catch dalı da aynısını yapar ve konsola `Failed to fetch RSC payload for … Falling back to browser navigation.` basar). - `segment-cache/navigation.js` — `typeof result === "string"` → `completeHardNavigation` → `pushRef.mpaNavigation = true`. - `app-router.js` — `mpaNavigation` dalında `location.replace(canonicalUrl)`. Yani `router.replace` ölü sunucuda tam olarak kaçınılmak istenen tam-sayfa gezinmeyi yapıyordu; ters vekilin düz metin 502 gövdesi tarayıcıya iniyor, WebKit `listem.txt` olarak indiriyordu. Üstelik iki yol birbirini eziyordu: RSC fetch'i 6 sn'den uzun asılırsa önce supap `SunucuYokEkrani`'nı basıyor, sonra MPA dalı o ekranı üstünden siliyordu. **Uygulanan düzeltme** (`src/features/rapor/components/liste-uretici.tsx`): - `sunucuAyaktaMi()` yoklaması `setDurum({ad:"geciliyor"})` ve `router.replace` **öncesine** taşındı; sunucu ölüyse `liste_uretim_hata {kod:"SUNUCUYA_ULASILAMIYOR"}` atılıp `sunucu-yok` ekranı basılıyor, gezinme hiç başlamıyor. - Yoklama bilerek `"geciliyor"`dan önce: o durum 6 sn'lik supap zamanlayıcısını kuruyor; sonrasına konsa iki yol yarışıp aynı ekranı iki kez yazardı. - `sunucuAyaktaMi` zamanaşımı sertleştirildi: `AbortSignal.timeout` yoksa (eski Safari) `Promise.race` ile 5 sn'lik yedek zamanaşımı; kaybeden fetch'in sonradan reddetmesi `.catch` ile yutuluyor (sahipsiz reddetme olmasın). Yoklama artık mutlu yolda koştuğu için asılması her başarılı üretimi kilitlerdi. - Emniyet supabındaki mevcut yoklama **kaldı** (ikinci kalkan). **Dürüst sınır:** ön-yoklama pencereyi saniyelerden milisaniyelere indirir, **kapatmaz** — konteyner yoklamadan sonra, `/listem` render'ı sırasında ölürse yine 502 gelir. Kalıcı çözüm `Dockerfile`'daki heap tavanıdır (`NODE_OPTIONS=--max-old-space-size=768`); bu ek, "kredisi yanan kullanıcı ekranda `.txt` bulmasın" garantisidir. **Durum:** Bilal'in commit'lenmemiş işinin üstüne, çalışma ağacında uygulandı. `typegen + tsc + lint` temiz. Bilal'in kendi commit'iyle birlikte `main`'e girer. **Denetim:** `guvenlik-uyum` — "listen kaydedildi, kredin yanmaz" iddiasının doğruluğu. ## Dosya sahipliği (kesişim: sıfır) | Paket | Dal | Dosyalar (**Y** = yeni) | |---|---|---| | P1 | `icerik/sezon-celiskisi` | `src/lib/sezon.ts` **Y** · `src/components/site-top-banner.tsx` · `src/features/pazarlama/components/hero-focus-button.tsx` | | P2 | `olcum/liste-uretim-basladi` | `src/lib/analitik.ts` · `src/features/rapor/components/uretim-basladi-olayi.tsx` **Y** · `src/features/rapor/components/listem-icerik.tsx` · `src/app/listem/page.tsx` · `src/features/sihirbaz/components/sihirbaz-cagri-karti.tsx` · `src/features/odeme/components/paket-satinal.tsx` | | P3 | `funnel/arti-olu-ucu` | `src/features/liste/components/program-ekle-butonu.tsx` · `src/features/liste/components/liste-cekmecesi.tsx` | | P4 | `funnel/listem-girisi` | `src/features/kullanici/components/user-nav.tsx` · `src/features/kullanici/components/giris-linki.tsx` **Y** · `src/features/rapor/components/listem-bos-cta.tsx` | **Hiçbir paketin dokunmayacağı (Bilal'in kirli ağacı — PROTOKOL kural 3):** `Dockerfile`, `src/app/globals.css`, `src/app/layout.tsx`, `src/features/rapor/components/liste-uretici.tsx`, `src/app/api/saglik/`, `docs/`, `content/rehber/*.md`. `git add -A` / `git add .` **yasak** — yalnız kendi paketinin yollarını tek tek ekle. ## Ortak kurallar 1. Yalnız verilen worktree + dalda **lokal** commit. Push/merge/deploy yok, `main`'e commit yok. 2. Commit öncesi `git status --porcelain` ile Bilal'in kirli dosyalarının stage'e sızmadığını doğrula. 3. Doğrulama: `pnpm exec next typegen && pnpm exec tsc --noEmit && pnpm lint`. (Depoda `typecheck` script'i yok; `pnpm lint` = `eslint`.) 4. **typegen'i atlama** — `PageProps` global tipi `.next/types`'tan gelir, taze worktree'de `tsc` onsuz baseline'da bile kırıktır. 5. `pnpm build`'i **yalnız P2** koşar (paralel build'ler belleği tüketir). 6. Worktree'de `node_modules` **symlink'i Turbopack'te patlar** — klon kullan: `cp -Rc /node_modules /node_modules`. 7. Next bu sürümde farklı (16.2.10) — API'ye dokunmadan önce `node_modules/next/dist/docs/` altındaki ilgili sayfayı oku. Bu depoda `cacheComponents: true` (`next.config.ts:28`). 8. AGENTS.md bağlayıcı: rozet gerekirse `SectionEyebrow`; CTA'da `Sparkles` yok, metin **sonunda** ``; yeni modalda `DialogContent` varsayılan kapatma çarpısı. 9. Bu turda hiçbir paket `huni-semasi.tsx` / `meraklisina-icerik.tsx`'e dokunmaz. 10. Dev sunucusu yok, tarayıcı yok — UI'ı Bilal kontrol ediyor. 11. Uydurma veri yok: ÖSYM 2027 takvimi açıklanmadığı için P1'de tarih **uydurulmaz**, `null` bırakılır. ## Merge sırası ve denetim Dördü paralel geliştirilir. Merge sırası **P2 → P3 → P4 → P1** (CEO'nun "payda önce" kuralı). `guvenlik-uyum` denetimi merge koşulu: **P3** (kapı → `/sonuc?sihirbaz=1` → otomatik 3 kredi harcama yolu), **P4** (giriş callback'i / açık yönlendirme yüzeyi), **P0-ek**. `tasarimci`: P4 navbar'ın 375 px davranışı ve "Yapay Zeka Listem" ↔ "Kendi Listem" ayrımı. `qa-muhendisi`: P3/P4 dallarında + → toast → sihirbaz → nereye çıkıyor; boş `/listem` CTA'sı kapıyı açıyor mu. ## İtirazlar | # | İtiraz | Düzeltilmiş kapsam | |---|---|---| | İ1 | P3'te `use-tercih-profili.ts:346` `null` dalının değiştirilmesi ölü kod olur ve `hedef` sözleşmesini bozar | Yalnız iki çağrı yeri değişir; `use-tercih-profili.ts` kapsam dışı | | İ2 | P3 girişli kullanıcıda ek tık olmadan 3 kredi harcatıyor | Kapsam aynı, `guvenlik-uyum` denetimi merge koşulu | | İ3 | P3, + toast'ının vaat ettiği risk rozetini göstermeden kullanıcıyı taşıyor | Bu tur uygulanmıyor; backlog | | İ4 | P4'te `callback=/listem` bağlam kaybı + `?uret=1` ile rızasız kredi | Callback = **bulunulan sayfa**; yeni `giris-linki.tsx` | | İ5 | P2'de `girisli` özelliği sabit (`/listem` auth kapılı) | `girisli` yerine `paketli` + `profil` | | İ6 | P2'de `yeniden_deneme` kirli dosya olmadan ölçülemez | Bu turda çıkarıldı; `liste-uretici.tsx` açıldığında eklenir | ## Backlog önerileri 1. + kapısının ödülü yerinde teslim edilsin (İ3) — M, tasarımcı girdisi gerekir. 2. `liste_uretim_basladi`'nın gerçek çağrı yerine taşınması + `yeniden_deneme`. 3. `ListemButonu` boşken gizlenmesi kuralı yeniden değerlendirilsin (`listem-butonu.tsx:24`). 4. En küçük duman testi altyapısı (`vitest`): `sonucHref`, `sihirbazDogrula`, `sezonAsamasi`, `raporMaskele`, `siraKovasi`. 5. Kalan CTA ok/ikon tutarsızlıkları — tek temizlik paketi. 6. `secimlerim-paneli.tsx` ölü kod (grep ile doğrulanıp silinmeli). 7. Footer "YKS Tercih Robotu 2026" — vizyonla çelişiyor, yıl sabiti elle gömülü.