Ç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>
15 KiB
15 KiB
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 hemgece/rehber-yazilari'nın 27 dosyasında (7guncelleme) hatasız; tümtarih/guncellemegeçerli,guncelleme ≥ tarih. Tırnak değişikliği yalnız hedeflenen ikiaciklama'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<tableHTML'i yok (dengesiz</div>riski yok).<details open={hata || undefined}>sunucu ve ilk istemci render'ında aynı → hydration uyuşmazlığı yok..catchhemimport()hemrenderhatasını yakalıyor,iptalkorumalı, geçici#d<id>temizleniyor. Sitemaplastmod=guncelleme ?? tarih,/rehber= en büyük ISO string; yazı yoksaundefined(alan yazılmaz).sayfaMetadataopenGraph'ı 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ökmederef(null)çağırır,!elile karşılanıyor. StrictMode çift mount: gözlemcidisconnect+ yeniden kurulur;olayBirKezsessionStorage ile tekilleşir.setIntervalher iki çıkışta (kuyruguBosalt, deneme sınırı) temizleniyor; en çok 20 olay × 10 sn.paket/layout.tsxsunucu bileşeni, dinamik API yok;useSearchParamskullanılmıyor →/paketstatikliği etkilenmez;/paketaltında başka rota yok (olay yalnız/paket'te atılır). PII: olay özellikleri yalnızkaynak,kod,status,yontem,girisli,sira_kovasi; ham sıra/e-posta yok.SihirbazBolumuyalnız/sonuc'ta kullanılıyor →sonuc_goruntulendiadı doğru. - P3 risk:
riskNotuMetnidoğrudanriskHesaplaçıktısını tüketiyor → eşikler rozetle birebir (1,4× / ≥ sıra). Tadımlık yoluriskHesapla(efektifSira, sira) ?? dilimdenRisk(dilim)—tadimlik-satiri.tsx:107rozetiyle aynı ifade;efektifSiraeksik/null → dilimden türetilir,nullmetne düşmez.programkolonu rozet tarafında zaten aynı şekilde okunuyor (yeni null riski yok).rapor.ts'te yalnızriskNotuUretgövdesi + import değişti; LLM şeması/prompt'u (5/13/6) aynı.tadimlik-uret.tsveapi/sorudeğişiklikleri yalnız prompt metni. - P4 env dosyası: yeni yorum satırları (apostrof ve
→içeren)docker compose configile denendi → doğru ayrıştırılıyor; boş secret'lar""geliyor, kod||ile varsayılana düşüyor.replyToalan adı Resend 6.18 tipinde var. Modül seviyesindethrowyok →next buildenv'siz kırılmaz.kredi-hatirlatma.tsyenithrow'u mevcuttry/catchile yutuyor. - Paketler arası sembol çakışması yok:
giris_hata(P2, istemciOlayAdi) ilegiris_basarili(P4,SunucuOlayAdi) ayrı tipler;trTarih(P1) ileKATALOG_TARIH_METNI(P3) bağımsız; P2paket/layout.tsx+ P3paket/page.tsxayrı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 →
SEMAgüncellemesi gerekmiyor;/meraklisina'da çıkan<details>metni tek kaynakariaLabel'dan geliyor.
3. Bilal için önerilen merge sırası
gece/rehber-yazilari+gece/p1-rehber-altyapibirlikte (P1 olmadanguncellemealanları görünmez; P1'in doğrulaması yeni frontmatter'larla sınandı — temiz).gece/p2-olcum— ilk üç commit (fa43392,cf19903,e1bb482).c055e82(identify'dan e-posta/isim kaldırma) Bilal kararı; reddedilirsegit merge gece/p2-olcum~1. Not: Rybbit'te önceden yazılmış e-posta/isim trait'leri kod değişikliğiyle silinmez.gece/p3-vaat-dili—git merge cae2aaa(ilk iki commit) + istenirsegit 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).gece/p4-eposta— en son, P2 merge edildikten sonra. Önce lokal docker testi (şartname §5:EPOSTA_KONSOL_FALLBACK=1ile link konsolda, 60 sn içinde ikinci istek link üretmez; bayraksız/anahtarsız → formda hata). Gitea'daRESEND_API_KEY+EMAIL_FROMsecret'larının dolu olduğunu teyit et.ea3ba5f(ci.yaml) +RYBBIT_*secret'ları eklenmedengiris_basariliprod'da no-op kalır (zararsız).- Push öncesi AGENTS.md kuralı: birleşik HEAD temiz worktree'de
pnpm build.