# 06 — CTO planı — 2026-09-21 gece vardiyası (Tur 3) Hazırlayan: CTO ajanı · Girdi: `05-ceo-kararlari.md` §3 + `01`/`02`/`03`/`04` · Kod **değiştirilmedi**; yalnızca bu dosya yazıldı. Tüm dosya:satır atıfları main HEAD `56925e1` üzerinde okunarak doğrulandı. (Tek yan etki: `p4-eposta` worktree'sinde doğrulama için `next typegen` + `tsc` + `lint` koşuldu; yalnızca gitignore'lu `.next/` ve `next-env.d.ts` oluştu, `git status` temiz.) ## 1. Özet (5 madde) 1. **Dört paket de uygulanabilir; dosya kümeleri ayrık.** CEO'nun listesindeki üç gizli çakışma çözüldü: (a) P3'ün "tek sabit"i `seo.tsx`'e (P1'in) giremez → yeni dosya; (b) P2'nin `paket_goruntulendi` olayı `/paket` sayfasına (P3'ün) dokunmadan yeni `src/app/paket/layout.tsx` ile bağlanır; (c) P2 `src/app/layout.tsx`'e dokunmaz, erken olay kaybı `analitik.ts` içinde kuyrukla çözülür. 2. **P2 kök nedeni doğrulandı ve bir tane daha bulundu.** Hook `useRef` + `[ad, anahtarEki]` deps'li effect kullanıyor; eleman ilk commit'te yoksa (banner) ya da sökülüp yeniden takılıyorsa (tadımlık) gözlem hiç kurulmuyor → callback ref. Ek bulgu: Rybbit script'i `afterInteractive`; `olay()` script yokken sessizce düşürüyor → mount anında atılacak yeni olaylar (`sonuc_/paket_goruntulendi`) sert yüklemede kaybolur → küçük kuyruk şart. 3. **P3'te CEO'nun kapsamı eksik (CTO itirazı #1).** "İhtimal dili" yalnız `riskNotuUret`'te değil: tadımlık havuzunun `riskNotu`'su **LLM üretimi ve DB'de kayıtlı**, prompt'u açıkça "yüksek/orta/düşük ihtimal dili kullan" diyor (`scripts/tadimlik-uret.ts:70-71`); danışman sohbetinin sistem prompt'u da aynı (`src/app/api/soru/route.ts:48`). Girişsiz ziyaretçinin /sonuc'ta gördüğü tek yapay zekâ satırı eski dille kalırdı. Çözüm: risk cümlesi tek saf fonksiyona (`riskNotuMetni`) alınır, tadımlıkta **okuma anında** deterministik üretilir — prod'da LLM yeniden koşusu gerekmez. 4. **P4'te soğuma kaynağı değiştirildi (CTO itirazı #2).** better-auth 1.6.23'te `verification.identifier` = **token** (e-posta değil); e-posta yalnız `value` JSON'unda ve satır `sendMagicLink` çağrılmadan **önce** yazılıyor → tablo tabanlı soğuma kendi satırına takılır ve kayan pencere üretir. Yerine süreç içi `Map` (tek konteyner; şema/DB yazımı yok). Ayrıca prod guard **modül yüklenirken değil gönderim anında** atmalı; aksi hâlde `next build` (NODE_ENV=production, env'siz) kırılır. 5. **Doğrulama tuzağı:** taze worktree'de `pnpm exec tsc --noEmit` **baseline'da kırık** (`PageProps` global tipi `.next/types`'tan geliyor). Önce `pnpm exec next typegen` koşulmalı — denendi: typegen → tsc 0 hata, `pnpm lint` temiz. Tam `pnpm build`'i yalnız P1 koşar (HTML kabul kriteri); dört paralel build ~8 GB RAM ister, diğerleri tsc+lint ile kapanır, build'i Tur 4'te orkestratör sırayla alır. ### Dosya sahipliği (ayrık; listede olmayan dosyaya dokunulmaz) | Paket | Dosyalar (**Y** = yeni) | |---|---| | **P1** `gece/p1-rehber-altyapi` | `src/features/rehber/components/el-cizimi-sema.tsx` · `src/features/rehber/components/rehber-makale.tsx` · `src/lib/rehber.ts` · `src/lib/tarih.ts` **Y** · `src/app/sitemap.ts` · `src/lib/seo.tsx` (yalnız `articleJsonLd`) | | **P2** `gece/p2-olcum` | `src/components/use-gorunum-olayi.ts` · `src/lib/analitik.ts` · `src/features/sihirbaz/components/{hero-form,cta-sira-form,sihirbaz-cagri-karti}.tsx` · `src/features/liste/components/tercih-profili-kapisi.tsx` · `src/features/kullanici/components/giris-form.tsx` · `src/features/rapor/components/{liste-uretici,listem-govde}.tsx` · `src/app/paket/layout.tsx` **Y** · `src/features/odeme/components/paket-gorunum-olayi.tsx` **Y** · `src/components/rybbit-identify.tsx` · `src/features/kullanici/components/user-nav.tsx` · (salt-okunur doğrulama, değişiklik beklenmiyor: `kaydet-banner.tsx`, `tadimlik-satiri.tsx`) | | **P3** `gece/p3-vaat-dili` | `src/app/page.tsx` · `src/app/paket/page.tsx` · `src/features/odeme/components/paket-satinal.tsx` · `src/lib/ai/rapor.ts` (yalnız `riskNotuUret`) · `src/app/layout.tsx` (yalnız `description`) · `src/lib/veri-kapsami.ts` **Y** · **CTO eki:** `src/lib/risk.ts` · `src/lib/tadimlik-havuzu.ts` · `scripts/tadimlik-uret.ts` · `src/app/api/soru/route.ts` (yalnız 48. satır) · `src/components/arama-funnel-karti.tsx` (yalnız 38. satır) · `src/features/katalog/components/veri-kaynagi.tsx` (yalnız `export`) | | **P4** `gece/p4-eposta` | `src/lib/eposta-gonder.ts` · `src/lib/eposta.ts` · `src/lib/auth.ts` · `src/lib/analitik-sunucu.ts` · isteğe bağlı ayrı commit: `.gitea/workflows/ci.yaml` | Çakışma kararları: `seo.tsx` → P1 (P3 oraya sabit eklemez). `src/app/paket/page.tsx` + `paket-satinal.tsx` → P3 (P2 yeni layout dosyası kullanır). `src/app/layout.tsx` → P3 (P2 script'e dokunmaz). `tadimlik-satiri.tsx` → P2, `tadimlik-havuzu.ts` → P3. `analitik.ts` → P2, `analitik-sunucu.ts` → P4. `giris-form.tsx` → P2, `auth.ts` → P4. ### Tüm yazılımcılar için ortak kurallar - Yalnız kendi worktree + dalında lokal commit; **push/merge yok**. `git add -A` / `git add .` **yasak** — yalnız sahip olunan dosyalar yol vererek eklenir (build `data/*.db-shm` gibi izlenen dosyaları kirletebilir; `data/app.db`, `docker-compose.yml`, `docs/geo/geo-raporu.md` asla). - Doğrulama sırası (worktree kökünde): `pnpm exec next typegen && pnpm exec tsc --noEmit && pnpm lint`. Dev sunucusu/tarayıcı yok (AGENTS.md). - Next'e özgü API'den önce `node_modules/next/dist/docs/` (P1: `01-app/03-api-reference/03-file-conventions/01-metadata/sitemap.md`, `04-functions/generate-metadata.md`; P2: `03-file-conventions/layout.md`). Doğrulananlar: `openGraph.modifiedTime` tipte var (`opengraph-types.d.ts:28`); sitemap `lastModified: string | Date`; `next typegen` (`06-cli/next.md:175`). - Commit mesajı sonu: `Co-Authored-By: Claude Fable 5.1 `. Rapor: `07-yazilimci-raporlari.md`'ye kendi başlığı. --- ## 2. P1 · Rehber altyapısı **Amaç:** Şema metni JS'siz botlara görünsün; mermaid hatasında sonsuz iskelet bitsin; `guncelleme` tarihi uçtan uca aksın; frontmatter tırnağı ve tablo taşması düzelsin. **Kök neden (kanıt):** - `el-cizimi-sema.tsx:85-97`: `aria-label` yalnız `svg` state'i dolunca render ediliyor; SSR çıktısı = `aria-hidden` iskelet + figcaption. Bileşen client ama SSR edilir → içine konan `
` sunucu HTML'ine girer. - `el-cizimi-sema.tsx:55-74`: `import("mermaid").then(async …)` zincirinde `catch` yok; `mermaid.render` atarsa (`getBBox is not a function`, 2 oturum — tarayıcıya özgü, kaynağı saptanamadı) `svg` hiç dolmaz, iskelet kalır, promise yakalanmaz. Mermaid ayrıca `#d` geçici div'ini `body`'de bırakabilir (`mermaid.core.mjs:1248`). - `rehber.ts:71-74`: `.replace(/^["']|["']$/g, "")` iki ucu bağımsız siliyor. Etkilenen yalnız 2 dosya (`merkezi-yerlestirme…md:3`, `taban-siralamalar…md:3`); 24 dosyanın hiçbirinde tam tırnaklı değer yok. - `rehber.ts:20-26` `RehberOzet`'te `guncelleme` yok; `sitemap.ts:22` `/rehber` girdisinde `lastModified` yok, `:47` `y.tarih`; `seo.tsx:149-169` `dateModified` yok; `rehber-makale.tsx:32` yalnız `publishedTime`, gövdede tarih yok. - `rehber-makale.tsx:79`: `[&_table]:w-full`, sarmalayıcı yok. **Değişiklikler:** 1. `src/lib/tarih.ts` **(yeni, saf, `server-only` yok):** `export function trTarih(iso: string): string` — `/^(\d{4})-(\d{2})-(\d{2})$/` ile ayrıştır, sabit `AYLAR = ["Ocak",…,"Aralık"]` dizisinden `"21 Eylül 2026"` üret; biçim uymazsa `throw`. `Date`/`toLocale*`/saat dilimi **kullanma**. 2. `src/lib/rehber.ts`: - Tırnak: değer uzunluğu ≥ 2, ilk ve son karakter **aynı** tırnak (`"` ya da `'`) **ve** aradaki metin o tırnağı içermiyorsa soy; aksi hâlde olduğu gibi bırak. - `RehberOzet`'e `guncelleme?: string`. `metaDogrula`: `tarih` ve (varsa) `guncelleme` `YYYY-MM-DD` değilse ya da `guncelleme < tarih` ise build'i kıran `Error` (mevcut hata mesajı kalıbıyla). `oku()` ve `ozetOku()` (cache'ten kırpma dalı dahil, `:169-170`) alanı taşır. Sıralama `tarih`'e göre **kalır**. `RehberAramaKaydi` değişmez. - Tablolar: `parcala()` içindeki iki `marked.parse` çıktısını tek yardımcıdan geçir: `` → `
`, `
` → `` (marked 18 ``'ı özniteliksiz basar; global `marked.use` **kullanma**). `:12` yorumundaki "10 makale"yi düzelt. 3. `src/lib/seo.tsx` — yalnız `articleJsonLd`: `dateModified?: string` parametresi; çıktıda `dateModified: opts.dateModified ?? opts.datePublished`. 4. `src/features/rehber/components/rehber-makale.tsx`: metadata'da `openGraph: { type: "article", publishedTime: yazi.tarih, modifiedTime: yazi.guncelleme ?? yazi.tarih }`; `articleJsonLd`'ye `dateModified`; `aciklama` paragrafının altına `

Yayın: ` + (yalnız `guncelleme` varsa) ` · Güncelleme:

` içinde: `
Şemayı metin olarak oku

{ariaLabel}

`. Gizli metin (`sr-only`, `display:none`) **yok**. **Commit bölünmesi:** (1) `fix(rehber): frontmatter tırnak + tablo sarmalayıcı` · (2) `feat(rehber): guncelleme alanı → sitemap/JSON-LD/OG/görünür tarih` · (3) `fix(rehber): şema metni SSR'da + mermaid hata toparlama`. Hepsi [KARAR]. **Kabul kriterleri / doğrulama:** typegen+tsc+lint; ardından **yalnız bu pakette** `pnpm build`, sonra (ana repodaki mevcut build'de baseline ölçüldü: ilk iki grep **0**, description `Üst sıraya…` diye tırnaksız başlıyor — yani kriterler ayırt edici): - `grep -c "Şemayı metin olarak oku" .next/server/app/rehber/tercih-listesi-nasil-yapilir.html` ≥ 1 ve aynı dosyada `grep -c "Tercih listesi dilim şeması"` ≥ 1; `.next/server/app/meraklisina.html`'de `grep -c "Sihirbaz akış şeması"` ≥ 1. - `grep -o 'name="description" content="[^"]\{0,30\}' .next/server/app/rehber/merkezi-yerlestirme-nasil-calisir.html` → `"Üst sıraya` ile başlar (aynısı `taban-siralamalar-nasil-degisir`). - Aynı HTML'de `rehber-tablo` sınıfı ve `Yayın:` + `