Files
kolaytercih/docs/gece-vardiyasi/2026-09-21/08-kod-denetimi.md
bilalgursen d10eff31b3 docs: gece vardiyası 2026-09-21 raporları
Çok ajanlı gece vardiyasının çıktıları: analitik (Rybbit) bulguları, SEO/GEO
konu planı, marka stratejisi, canlı mail testi, CEO kararları (2 tur), CTO
şartnamesi, yazar/yazılımcı raporları, bağımsız kod denetimi ve sabah özeti
(yazılı + sesli). Kod değişiklikleri gece/* dallarında.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 13:08:24 +03:00

15 KiB
Raw Blame History

08 — Kod denetimi (CTO / bağımsız denetçi) — 2026-09-21

Kapsam: gece/p1-rehber-altyapi, gece/p2-olcum, gece/p3-vaat-dili, gece/p4-eposta (hepsi 56925e1 tabanlı). Kod değiştirilmedi, commit atılmadı. Yöntem: git diff 56925e1..<dal> + worktree'den tam dosya okuma + kütüphane kaynağı (better-auth 1.6.23 magic-link / with-hooks, resend 6.18) + scratchpad'de tsx betikleri (P1 parser'ı 24 ve 27 dosyalık iki içerik kümesinde; P4 guard matrisi; docker compose config ile env dosyası ayrıştırma) + sqlite3 -readonly (data/yokatlas.db, data/app.db).

Şeffaflık notu: entegrasyon worktree'sinde yanlışlıkla iki salt-okunur git komutu (git log, git status --short) çalıştırdım; dosya değişmedi, build'e etkisi beklenmez.

1. Hükümler

Dal Hüküm Gerekçe
P1 gece/p1-rehber-altyapi MERGE EDİLEBİLİR Parser 24 mevcut + 3 yeni yazıda (7 guncelleme alanı dahil) hatasız koştu; yalnız hedeflenen 2 açıklama değişti; hydration/catch/sitemap mantığı temiz.
P2 gece/p2-olcum MERGE EDİLEBİLİR Callback ref + React 19 temizliği, kuyruk zamanlayıcısı ve PII kuralları doğru; yalnız düşük önemli ölçüm sapması riskleri var. Son commit (identify) Bilal kararı.
P3 gece/p3-vaat-dili DÜZELTMEYLE İlk iki commit temiz; 52520ac doğrulanmamış bir rakamı "Armut, 2026" diye kaynak göstererek yayına sokuyor — doğrulanmadan alınmamalı. Ayrıca tadımlık gerekce/trendOzeti DB metinleri eski dille kalıyor (paket amacı eksik kapanıyor).
P4 gece/p4-eposta MERGE EDİLEBİLİR (koşullu) Guard mevcut prod env'inde (anahtar + EMAIL_FROM dolu) girişi kırmıyor; EMAIL_FROM fiilen zaten zorunluydu. Koşul: lokal docker testi + P2'nin e1bb482 commit'iyle birlikte merge (yoksa 30. sn'de "Yeniden gönder" sessizce yutulur).

Kritik bulgu: yok. Yüksek bulgu: 1 (P3 52520ac).

2. Bulgular (en ciddiden)

# Dal + commit Dosya:satır Sorun Somut hata senaryosu Önem Önerilen düzeltme Kesinlik
1 P3 52520ac [Bilal] src/app/page.tsx:478-480 (+ :92,168,474, src/app/paket/page.tsx:52) Doğrulanmamış fiyat aralığı gerçek bir şirkete atfediliyor: "Danışman fiyat aralığı: Armut, 2026." Yazılımcı raporu rakamın açılıp teyit edilmediğini kendisi söylüyor. Merge → ana sayfada üçüncü tarafa atıflı, kaynağı olmayan rakam yayında; Armut sayfası farklı aralık gösteriyorsa yanıltıcı karşılaştırmalı reklam / yanlış atıf. yüksek Bu commit'i Bilal kaynağı açıp teyit edene kadar alma. 9d450c5 ondan bağımsız: git merge-tree --merge-base=52520ac cae2aaa 9d450c5 temiz → git merge cae2aaa + git cherry-pick 9d450c5. Teyit edilirse kaynak satırına URL/erişim tarihi ekle. DOĞRULANDI (kod + rapor)
2 P3 cae2aaa src/lib/tadimlik-havuzu.ts:84-93 Yalnız riskNotu deterministik yapıldı; aynı satırın gerekce ve trendOzeti alanları hâlâ DB'deki LLM metni. Yorumdaki "olasılık dili sızamaz" iddiası yanlış. Lokal data/app.db tadimlik_havuzu: 514 satırın 13'ünde gerekce "yerleşme olasılığı yüksektir / yüksek ihtimal" içeriyor, 126'sında trend_ozeti "Son 4 yılda…" diyor. Girişsiz ziyaretçi /sonuc'ta "yerleşme olasılığı yüksektir" + sayfanın başka yerinde "son 5 yıl" görür. orta Ya pnpm tadimlik --force (yeni prompt'la) prod'da koşulmalı (Bilal; bellek notundaki prod koşusu zaten bekliyor), ya da okuma anında gerekce/trendOzeti için `/olasılı ihtimal/ieşleşen satırlar atlanmalı /trendOzetidetrendOzetiUret` benzeri deterministik üretilmeli. Yorum satırı düzeltilmeli.
3 P4 2af4a9f × P2 e1bb482 src/lib/auth.ts:66-77 × giris-form.tsx:41-44 Paketler arası bağımlılık: sunucu 60 sn soğumada isteği sessizce 200 ile yutuyor. P2'nin 60 sn geri sayımı yoksa istemci 30. sn'de yeniden göndermeye izin veriyor. P4 tek başına merge → kullanıcı 30. sn'de "Yeniden gönder" → "gönderildi" ekranı, mail çıkmaz, kullanıcı gelmeyecek maili bekler. (İkisi birlikteyken: istemci sayacı yanıt geldikten sonra, sunucu damgası istek başında başlar → istemci 60 sn ≥ sunucu 60 sn, tutarlı.) orta (yalnız ayrı merge edilirse) P2'yi P4'ten önce ya da aynı anda merge et. Kalan kenar durum (sayfa yenileme / ikinci cihazdan 60 sn içinde tekrar istek → sessiz başarı) kabul edilebilir: ilk mail zaten gitti ve 15 dk geçerli. DOĞRULANDI
4 P4 5992cf3 src/lib/eposta-gonder.ts:47-55 Prod guard'ın 500 döndürdüğü koşullar (betikle denendi): (a) RESEND_API_KEY yok ya da boş string ve EPOSTA_KONSOL_FALLBACK≠1; (b) anahtar var ama EMAIL_FROM yok/boş. Gitea'da RESEND_API_KEY ya da EMAIL_FROM secret'ı silinir/yeniden adlandırılırsa CI satırı EMAIL_FROM= (boş) yazar → her magic-link isteği 500, e-postayla giriş kapalı (Google etkilenmez). Bugünkü prod'da kırılma beklenmez: 04-mail-testi.md canlı mailin giris@mail.kolaytercih.com'dan geldiğini gösteriyor → iki secret da dolu. Eski kodda da boş EMAIL_FROM ??'yi geçip Resend'e from:"" gidiyordu → zaten hata; yani EMAIL_FROM yeni zorunlu hale gelmedi, yalnız hata görünür oldu. CI her iki satırı da yazıyor (ci.yaml:27-28), Dockerfile env gömmüyor, runtime env_file: .env.production. düşük (bilgi) Merge öncesi Gitea'da RESEND_API_KEY ve EMAIL_FROM secret'larının dolu olduğunu gözle teyit et (değerine bakmadan, varlığına). Lokal docker testi şartname §5'teki gibi. DOĞRULANDI
5 P2 cf19903 src/features/sihirbaz/components/cta-sira-form.tsx:80,168 useTercihProfili useSyncExternalStore + sunucu snapshot'ı null: hydration'da profilli ziyaretçi için de önce profilsiz <section ref> bağlanır, sonra senkron yeniden render ref'i düşürür. IO ilk bildirimi ile React'in passive-effect görevi arasında sıra garantisi yok. Kayıtlı profilli ziyaretçi, CTA kartı ilk ekranda görünen bir sayfayı sert yüklerse cta_form_goruntulendi yanlışlıkla 1 kez atılabilir → huni giriş adımı şişer. Kart çoğu sayfada fold altında olduğundan nadir. düşük IO callback'inde göndermeden önce el.isConnected + profil yokluğunu kontrol et ya da ref'i profil === null && hydrated koşuluna bağla. Merge'i engellemez; Rybbit'te cta_form_goruntulendi / sira_girildi{cta} oranı izlenmeli. OLASI (yazılımcı da çalıştırarak doğrulamadı)
6 P2 fa43392 (kapsam dışı kalan kardeş sorun) src/lib/analitik.ts:170-181, rybbit-identify.tsx Kuyruk yalnız olay() için; kullaniciTanit() script yokken hâlâ sessiz no-op. OAuth / magic-link dönüşü (sert yükleme): identify effect'i script'ten önce koşar → o sayfa oturumunda kullanıcı eşlenmez; kuyruktan boşalan sonuc_goruntulendi{girisli:evet} anonim profile yazılır, P4'ün sunucu giris_basarili{user_id} olayıyla aynı profilde birleşmez. Regresyon değil (öncesinde de böyleydi) ama yeni huninin son adımını zayıflatır. düşük-orta Backlog: identify'ı da aynı kuyruğa al (tek son-değer slotu yeter). OLASI
7 P4 17046c8 src/lib/auth.ts:161-181, analitik-sunucu.ts:34-52 Doğruluk tarafı temiz: void + sunucuOlayi içi try/catch + env yoksa erken return → giriş bozulamaz, unhandled rejection yok; ctx.path magic-link'te /magic-link/verify, Google'da /callback/:id (kaynakta doğrulandı); hook queueAfterTransactionHook içinde koşuyor. Ama olay sunucunun IP/UA'sıyla (user-agent: node) gidiyor. Rybbit bot filtresi bu UA'yı düşürürse ya da olayı ayrı bir "sunucu oturumu"na yazarsa huni adımı tarayıcı oturumuyla bağlanmaz. odeme_tamamlandi de aynı yoldan; CTO zaten o 3 kaydın kaynağından şüpheli. düşük Secret'lar eklendikten sonra Bilal bir test girişiyle Rybbit'te giris_basarili'nin düştüğünü ve kullanıcı profiline bağlandığını doğrulasın; bağlanmıyorsa /api/track gövdesine istekten user_agent (ve gerekiyorsa ip_address) geçirilmesi ayrı iş. OLASI
8 P4 2af4a9f src/lib/auth.ts:41-43 Normalizasyon trim().toLowerCase(); Gmail nokta/+etiket eşlemesi yok. (Boşluklu adres zaten z.email()'de reddediliyor, trim fiilen no-op.) kurban+1@gmail.com, kurban+2@… ile aynı kutuya soğuma aşılır; yine de IP limiti (5/60 sn) sınırlar. Bellek: her çağrıda budama → Map ≤ son 60 sn'deki adres sayısı, sızıntı yok. Yeniden başlatmada sıfırlanır, çoklu kopyada paylaşılmaz (tek konteyner — kabul). Google OAuth yolu sendMagicLink'e hiç girmiyor → etkilenmez. düşük Şimdilik kabul; istenirse gmail/googlemail için + sonrası ve noktalar atılabilir. DOĞRULANDI
9 P1 1a75f42 src/features/rehber/components/el-cizimi-sema.tsx:109-125 <figcaption> artık <figure>'ün ortasında (iskelet/svg → figcaption → <details>); HTML içerik modeline göre figcaption ilk ya da son çocuk olmalı. Tarayıcılar tolere eder, hydration'ı etkilemez; bazı doğrulayıcı/erişilebilirlik araçları uyarı verir. düşük <details>'i figcaption'dan önce koy ya da figcaption'ın içine al. DOĞRULANDI
10 P3 16c4508 src/lib/veri-kapsami.ts:6 VERI_YIL_SAYISI = 5 kolon kapsamıyla tutarlı (sira2021…sira2025). Ancak 23.610 programın yalnız 9.909'unda beş yılın tamamı dolu (sira2021 dolu: 11.450; sira2025 dolu: 17.994). "Her tercih için … trend analizi (son 5 yıl)" — 2025 tabanı olan programların ~%45'inde fiilen 5'ten az yıl var. Eski "4 yıl" da aynı ölçüde yaklaşık bir ifadeydi; regresyon değil. düşük İstenirse metin "son 5 yıla kadar" yapılabilir; Bilal kararı. DOĞRULANDI (sqlite3 -readonly)
11 P1 d9e125c src/lib/rehber.ts:145 TARIH_BICIMI yalnız biçim denetliyor: 2026-02-31 geçer (ay 13 ise trTarih build'i kırar, gün için kontrol yok). Yazar 2026-09-31 yazarsa sitemap/JSON-LD'ye geçersiz tarih çıkar. Bugünkü 27 dosyada yok. düşük Date.UTC ile gün-ay geçerlilik kontrolü eklenebilir. DOĞRULANDI

Özellikle bakılıp temiz bulunanlar

  • P1 parser: P1'in rehber.ts'i hem kendi 24 dosyasında hem gece/rehber-yazilari'nın 27 dosyasında (7 guncelleme) hatasız; tüm tarih/guncelleme geçerli, guncelleme ≥ tarih. Tırnak değişikliği yalnız hedeflenen iki aciklama'yı etkiliyor (başka tırnak uçlu frontmatter değeri yok). Tablo sarmalayıcı: 8 tabloda aç/kapa sayıları eşit; içerikte ham <table HTML'i yok (dengesiz </div> riski yok). <details open={hata || undefined}> sunucu ve ilk istemci render'ında aynı → hydration uyuşmazlığı yok. .catch hem import() hem render hatasını yakalıyor, iptal korumalı, geçici #d<id> temizleniyor. Sitemap lastmod = guncelleme ?? tarih, /rehber = en büyük ISO string; yazı yoksa undefined (alan yazılmaz). sayfaMetadata openGraph'ı yayarak birleştiriyor → modifiedTime çıkar.
  • P2 hook/kuyruk: callback ref, React 19 temizlik fonksiyonu döndürüyor; erken dönüşlerde undefined → React sökmede ref(null) çağırır, !el ile karşılanıyor. StrictMode çift mount: gözlemci disconnect + yeniden kurulur; olayBirKez sessionStorage ile tekilleşir. setInterval her iki çıkışta (kuyruguBosalt, deneme sınırı) temizleniyor; en çok 20 olay × 10 sn. paket/layout.tsx sunucu bileşeni, dinamik API yok; useSearchParams kullanılmıyor → /paket statikliği etkilenmez; /paket altında başka rota yok (olay yalnız /paket'te atılır). PII: olay özellikleri yalnız kaynak, kod, status, yontem, girisli, sira_kovasi; ham sıra/e-posta yok. SihirbazBolumu yalnız /sonuc'ta kullanılıyor → sonuc_goruntulendi adı doğru.
  • P3 risk: riskNotuMetni doğrudan riskHesapla çıktısını tüketiyor → eşikler rozetle birebir (1,4× / ≥ sıra). Tadımlık yolu riskHesapla(efektifSira, sira) ?? dilimdenRisk(dilim) — tadimlik-satiri.tsx:107 rozetiyle aynı ifade; efektifSira eksik/null → dilimden türetilir, null metne düşmez. program kolonu rozet tarafında zaten aynı şekilde okunuyor (yeni null riski yok). rapor.ts'te yalnız riskNotuUret gövdesi + import değişti; LLM şeması/prompt'u (5/13/6) aynı. tadimlik-uret.ts ve api/soru değişiklikleri yalnız prompt metni.
  • P4 env dosyası: yeni yorum satırları (apostrof ve → içeren) docker compose config ile denendi → doğru ayrıştırılıyor; boş secret'lar "" geliyor, kod || ile varsayılana düşüyor. replyTo alan adı Resend 6.18 tipinde var. Modül seviyesinde throw yok → next build env'siz kırılmaz. kredi-hatirlatma.ts yeni throw'u mevcut try/catch ile yutuyor.
  • Paketler arası sembol çakışması yok: giris_hata (P2, istemci OlayAdi) ile giris_basarili (P4, SunucuOlayAdi) ayrı tipler; trTarih (P1) ile KATALOG_TARIH_METNI (P3) bağımsız; P2 paket/layout.tsx + P3 paket/page.tsx ayrık dosyalar.
  • AGENTS.md: yeni badge/CTA/modal yok; "Yayın:" satırı düz metin; hero kutusu mevcut kutu; sihirbaz davranışı değişmedi → SEMA güncellemesi gerekmiyor; /meraklisina'da çıkan <details> metni tek kaynak ariaLabel'dan geliyor.

3. Bilal için önerilen merge sırası

  1. gece/rehber-yazilari + gece/p1-rehber-altyapi birlikte (P1 olmadan guncelleme alanları görünmez; P1'in doğrulaması yeni frontmatter'larla sınandı — temiz).
  2. gece/p2-olcum — ilk üç commit (fa43392, cf19903, e1bb482). c055e82 (identify'dan e-posta/isim kaldırma) Bilal kararı; reddedilirse git merge gece/p2-olcum~1. Not: Rybbit'te önceden yazılmış e-posta/isim trait'leri kod değişikliğiyle silinmez.
  3. gece/p3-vaat-dili — git merge cae2aaa (ilk iki commit) + istenirse git cherry-pick 9d450c5 (YÖK logosu→metin; çakışmasız doğrulandı). 52520ac'yi Armut rakamı teyit edilmeden alma (bulgu #1). Deploy sonrası prod'da tadımlık havuzu yeni prompt'la yeniden üretilmeli (bulgu #2).
  4. gece/p4-eposta — en son, P2 merge edildikten sonra. Önce lokal docker testi (şartname §5: EPOSTA_KONSOL_FALLBACK=1 ile link konsolda, 60 sn içinde ikinci istek link üretmez; bayraksız/anahtarsız → formda hata). Gitea'da RESEND_API_KEY + EMAIL_FROM secret'larının dolu olduğunu teyit et. ea3ba5f (ci.yaml) + RYBBIT_* secret'ları eklenmeden giris_basarili prod'da no-op kalır (zararsız).
  5. Push öncesi AGENTS.md kuralı: birleşik HEAD temiz worktree'de pnpm build.