Files
kolaytercih/docs/gece-vardiyasi/2026-09-21/08-kod-denetimi.md
bilalgursen 0ab4dbe88c
All checks were successful
Deploy / deploy (push) Successful in 7m18s
Enhance Traefik configuration for www.kolaytercih.com
Added a new router and middleware for handling www to apex domain redirection, ensuring that Let's Encrypt certificates are obtained for both domains. Updated the docker-compose.yml file to include necessary rules and middleware for proper redirection and TLS handling.

Also, updated the CEO agent description to remove redundant details and clarified the QA engineer's responsibilities regarding testing critical flows and error reporting. Adjusted the CTO agent's section for consistency in expert guidance and clarified the designer's tools to include 'Skill' for better design resource management.
2026-09-21 20:37:12 +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.