- docs/ekip-raporlari/2026-10-07, 2026-10-09, 2026-10-10 raporları ve ekran görüntüleri (ses/ altındaki mp3 dosyaları depoya alınmadı, 38 MB) - BACKLOG #38: rehber yazılarına "Dinle" düğmesi adayı - scripts/seslendir.py (Türkçe TTS) ve .gitignore'a .venv-tts/ - scripts/eval/sonuc: 8–10 Ekim revizyon eval çıktıları Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
46 KiB
CTO şartnamesi — Bilal'in 10 maddelik geri bildirimi (7 Eki 2026)
Taban: dal feat/arc-secim-sayi, HEAD 4d30dd1. Tüm dosya:satır atıfları bu commit üzerinde doğrulandı. Çalışma ağacında yalnız data/app.db kirli (Bilal'in); dokunulmaz.
Özet (5 madde)
- Beş paket, dosya kümeleri ayrık: A (sohbet sunucusu: rehber linki doğrulama + revizyon sınırının kaldırılması), B (revizyon "ilk N" garantisi), C (program varyant etiketi: burs/dil/öğretim türü), D1 (sohbet UI: shimmer, logo varyantı, soru geçişi), D2 (program ayrıntı çekmecesi: "Yıllara göre" kaldırma, açılış bug'ı, logo→içerik koreografisi). Madde 7 için iş yok.
- Üç kök neden koddan doğrulandı: (1) rehber linki için kod tarafında hiçbir doğrulama yok — denetim yalnız vaat/kayıt/revizyon beyanına bakıyor (
src/lib/ai/cikti-denetimi.ts), prompt "adres uydurma" diyor ama model uyduruyor; (2-3)programs.isimsütunu burs/dil varyantını taşımıyor (Başkent Tıp ×6 satır: Burslu/%50/Ücretli × Türkçe/İngilizce; ayırt edici alanlarprogram_ozellik.burs_orani / ogrenim_dili / ogrenim_turu), hiçbir liste bu alanı göstermiyor; (4) "ilk N" kuralı modelin seçimine bırakılmış —secimiOnaryalnız eksik satırı tamamlarken +4 öncelik veriyor, model 24 satırı İstanbul'suz doldurursailkTercihleriOneAlöne alacak satır bulamıyor; dev mock yolu (mockRapor) kuralı hiç uygulamıyor. - İtiraz (madde 8-9): "Drawer'daki info" liste çekmecesinde değil;
liste-cekmecesi.tsx'te bilgi paneli yok. Info düğmesi ve "Yıllara göre" bölümüsrc/features/katalog/components/program-ayrinti.tsx(ProgramAyrintiButonu; rapor-listesi.tsx:364 ve program-tablosu.tsx:443'ten açılıyor). Açılış bug'ının kök nedeni çift durak (0,6 → 0,92) + veri geldikten sonra boyun değişmesi (alt-sayfa.tsx ResizeObserver yeniden hedefliyor, kısık durakta gövde kaydırmıyor). - Test altyapısı: vitest/jest yok; mekanik testler
scripts/eval/*.tsiçinde,pnpm tsx --conditions=react-server <betik>ile koşuyor ve çıkış kodu 1 ile düşüyor (akis-denetim.tsdeseni: sahte OpenAI ucu + geçici app.db kopyası). Yeni testler bu desene uyar:baglanti-denetim.ts,revizyon-denetim.ts,varyant-denetim.ts. - Merge sırası: paketler bağımsız; tek bilinçli çakışma A↔D1 (
sohbet-client.tsx'inrevizyonHakkiprop'u) — A prop'u 0/1 bayrak olarak besler, D1 dokunmaz, entegrasyonda orkestratör 3 satırlık yeniden adlandırma yapar. Build'i yalnız orkestratör alır; paketlertypegen + tsc + lintile kapanır.
Dosya sahipliği
| Paket | Madde | Dosyalar (Y = yeni) |
|---|---|---|
| A sohbet sunucusu | 1, 6 | src/lib/ai/baglanti-denetimi.ts (Y), src/lib/ai/sohbet-akisi.ts, src/app/api/soru/route.ts, src/lib/ai/danisman-prompt.ts, src/lib/ai/araclar.ts, src/lib/ai/sohbet-meta.ts, src/lib/credits.ts, src/features/rapor/liste-revizyon.ts, src/features/rapor/components/listem-icerik.tsx, src/features/rapor/components/listem-govde.tsx, src/features/rapor/components/sohbet-senaryo-verisi.ts, src/app/page.tsx, src/features/odeme/components/paket-satinal.tsx, scripts/eval/akis-denetim.ts, scripts/eval/baglanti-denetim.ts (Y) |
| B revizyon "ilk N" | 4 | src/lib/revizyon-kurali.ts, src/lib/ai/rapor.ts, scripts/eval/revizyon-denetim.ts (Y) |
| C program varyantı | 2, 3 | src/types/yokatlas.ts, src/lib/db.ts, src/lib/program-varyant.ts (Y), src/lib/rapor-havuzu.ts, src/components/liste-satiri.ts, src/features/liste/liste-queries.ts, src/features/liste/liste-dogrula.ts, src/features/liste/hooks/use-tercih-profili.ts, src/features/liste/components/program-ekle-butonu.tsx, src/features/liste/components/program-tablosu.tsx, src/features/liste/components/liste-cekmecesi.tsx, src/features/rapor/components/rapor-listesi.tsx, src/features/rapor/rapor-queries.ts, src/features/katalog/components/universite-program-tablosu.tsx, src/features/katalog/components/bolum-program-listesi.tsx, scripts/eval/varyant-denetim.ts (Y) |
| D1 sohbet UI | 5, 10 | src/features/rapor/components/sohbet-client.tsx, src/features/rapor/components/sohbet-anketi.tsx, src/lib/adim-gecisi.ts (Y), src/features/sihirbaz/components/sihirbaz-adimlar.tsx, src/components/ui/loader.tsx (yalnız gerekirse, bkz. D1-5) |
| D2 program ayrıntı çekmecesi | 8, 9 | src/features/katalog/components/program-ayrinti.tsx, src/features/katalog/components/akademik-ayrinti.tsx, src/components/ui/acilis-sahnesi.tsx (Y), docs/tasarim/DESIGN.md (yalnız §4.19) |
| — | 7 | iş yok |
Dokunulmayanlar (bilerek): src/lib/ai/cikti-denetimi.ts (link denetimi ayrı saf modülde; vaat/kayıt denetimleriyle karışmasın), src/features/rapor/rapor-actions.ts (revisionCount sayaç olarak kalır), src/lib/appdb/schema.ts (göç yok), src/features/rapor/components/arac-pilleri.tsx, src/lib/ai/cikti-denetimi.ts, src/components/animasyonlu-mark.tsx, src/components/ui/alt-sayfa.tsx.
Çakışma kararları
sohbet-client.tsx— A ve D1. Sahibi D1. A,listem-govde.tsx:450'derevizyonHakki={kalanHak}yerinerevizyonHakki={hasPaket ? 1 : 0}geçer ve yanına// 0/1 bayrak; prop adı entegrasyonda revizyonAcik oluryorumu koyar. D1 prop'a dokunmaz. Entegrasyonda orkestratörrevizyonHakki?: number→revizyonAcik?: booleanyeniden adlandırmasını (sohbet-client.tsx:352,385,944,1075 + listem-govde.tsx:450) tek commit'te yapar.src/lib/ai/rapor.ts— B ve C. Sahibi B. C,programBilgisi(rapor.ts:∼140) fonksiyonuna dokunmaz; varyant etiketi kayıtlı rapora yazılmaz, okuma anındarapor-queries.ts'te zenginleştirilir (eski kayıtlara da işler — bkz. C-4).src/lib/ai/danisman-prompt.ts,araclar.ts,route.ts— A tek sahip. Madde 1'in prompt tarafı ve madde 6'nın prompt/araç/route değişiklikleri aynı pakette olduğu için çakışma yok.docs/tasarim/DESIGN.md— D2 tek sahip (yalnız §4.19). D1'in §4.20'ye eklemesi gereken bir not varsa raporuna yazar, tasarımcı/orkestratör entegrasyonda işler.rapor-havuzu.ts— C. B bu dosyaya dokunmaz (B'nin işirapor.ts+revizyon-kurali.ts).
Tüm paketler için ortak kurallar
- Yalnız verilen worktree + dalda lokal commit; push/merge yok.
git add -A/git add .yasak; dosyalar yol vererek eklenir. Yıkıcı git komutları (reset --hard,checkout --,restore,clean,stash) yasak (AGENTS.md). - Worktree'de
node_modulessymlink değil klon (cp -Rc node_modules <worktree>/node_modules); Turbopack symlink'te patlıyor. - Doğrulama sırası, her commit'ten önce:
pnpm exec next typegen && pnpm exec tsc --noEmit && pnpm lint. (package.json'datypegenscript'i yok;pnpm exec next typegenkullanılır. Taze worktree'de typegen koşulmadantscbaseline'da kırıktır —PagePropstipi.next/types'tan gelir.)git statustemiz kalmalı; typegen yalnız gitignore'lu çıktı üretir. pnpm buildkoşulmaz (paralel build RAM'i tüketir); tam build'i entegrasyonda orkestratör alır.next buildenv'siz koşar: modül yüklenirken env zorunlu kılan kod yazılmaz; guard çağrı anında atar.- Dev sunucusu / tarayıcı yok. Next'e özgü bir API'ye dokunmadan önce
node_modules/next/dist/docs/içindeki sayfa okunur; A için01-app/03-api-reference/03-file-conventions/route.md(Route Handler) ve01-app/01-getting-started/15-route-handlers.md. - AGENTS.md / DESIGN.md: rozet =
SectionEyebrow; CTA'da yıldız yok,ArrowRightsonda; kapatma çarpısıDialogContentsınıf seti;dark:yazılmaz, yüzeybg-card;pnpm tasarim:denetimsıfır hata (lint koşuyor). Sihirbaz/liste mimarisi değişmiyor →/meraklisinaşeması bu turda değişmez (B'nin "ilk N" garantisi havuz kuralını değil seçim onarımını değiştirir; şema zaten "aday havuzu dışına çıkılmaz" diyor). - Mekanik testler
scripts/eval/altında,pnpm tsx --conditions=react-server <betik>ile koşar, beklenti tutmazsaprocess.exit(1); desenscripts/eval/akis-denetim.ts. Testlerdata/app.db'yi doğrudan kullanmaz — geçici kopya (copyFileSync+APP_DB_URL=file:…+node scripts/db-goc.mjs). - Her paket raporu: değişen dosyalar, komut çıktıları (typegen/tsc/lint + paket testi), "koddan çıkarım" işaretli kalanlar, backlog önerisi.
Paket A — Sohbet sunucusu: rehber linki doğrulama (madde 1) + revizyon sınırı kaldırma (madde 6)
A-0 Kök neden (doğrulandı)
- Madde 1.
danisman-prompt.ts:247-251modele "adres araç sonucundan gelir, adres uydurma" diyor;rehber_ara/rehber_oku(araclar.ts:304, 378, 1288-1314, 1476-1492) gerçek slug'ları döndürüyor. Amasohbet-akisi.ts:207-247çıktı denetimi yalnız vaat dili, kayıt beyanı, revizyon beyanı ve şema sızıntısına bakıyor;/rehber/<slug>için hiçbir kontrol yok. Model araç çağırmadan ezberden[Yatay geçiş](/rehber/yatay-gecis-rehberi)yazabiliyor; istemci (sohbet-baglanti.tsx:58,78) her/rehber/[a-z0-9-]+adresini "Rehber yazısı" hapı yapıyor → 404'e giden hap. Gerçek slug kaynağıcontent/rehber/*.md(50 dosya) →getAllRehberSlugs()/getRehberAramaKayitlari()(src/lib/rehber.ts:224-264,server-only). - Madde 6.
MAX_REVIZYON = 2(credits.ts:18); kapıliste-revizyon.ts:82-88; promptdanisman-prompt.ts:75-82; araçaraclar.ts:472("en fazla 2 hak"),araclar.ts:1764,1770; routeroute.ts:25,199,210; UIlistem-icerik.tsx:12,180,listem-govde.tsx:78,102,219,335,450,sohbet-client.tsx:352,385,944,1075; pazarlamapage.tsx:31,247,paket-satinal.tsx:37; demosohbet-senaryo-verisi.ts:78,90; tipsohbet-meta.ts:50(kalanHak). DB sütunureports.revision_count(schema.ts:165,drizzle/0000_baslangic.sql:62) — göç gerekmez: sütun sayaç olarak kalır (kaç kez kuruldu, istatistik), yalnız kapı olmaktan çıkar.
A-1 Rehber linki doğrulama — tasarım
Akış text-delta parçalarıyla istemciye anında gidiyor (sohbet-akisi.ts:189-194); cevap bitince eklenen "düzeltme notu" yanlış hapı ekrandan geri alamaz. Bu yüzden düzeltme akış sırasında yapılır: küçük bir tampon, bir bağlantı adayı başladığında metni tutar, bağlantı kapanınca doğrulayıp bırakır. Bağlantılar kısadır (≤ ~120 karakter), gecikme hissedilmez.
Yeni saf modül src/lib/ai/baglanti-denetimi.ts (DB/ağ/Next yok; server-only import etmez — sohbet-akisi.ts gibi betiklerden koşar):
export type RehberKaydi = { slug: string; baslik: string };
export type BaglantiDuzeltme = { slug: string; sonuc: "yakin" | "duz"; yeniSlug?: string };
/** Metindeki /rehber/<slug> adreslerini (markdown bağlantısı, ters tırnak ve çıplak adres) gerçek kayıtlarla eşler. */
export function rehberBaglantilariniDuzelt(metin: string, rehberler: readonly RehberKaydi[]): { metin: string; duzeltmeler: BaglantiDuzeltme[] };
/** Akış tamponu: delta verilir, güvenle bırakılabilen metin döner; bitir() kalanı boşaltır. */
export class BaglantiTamponu {
constructor(rehberler: readonly RehberKaydi[]);
besle(delta: string): string;
bitir(): string;
readonly duzeltmeler: BaglantiDuzeltme[];
}
Kurallar:
- Eşleme: slug
content/rehberkaydıysa dokunma. Değilse en yakın gerçek slug: slug'lar-ile parçalanır, Dice/Jaccard benzerliği hesaplanır; eşik ≥ 0,5 ve tek aday açık ara önde (ikinciden ≥ 0,15 fazla) ise[başlık](/rehber/yeni-slug)olarak yeniden yazılır (başlık kayıttan). Aksi hâlde markdown bağlantısı düz metne iner ([Yatay geçiş](/rehber/x)→Yatay geçiş), çıplak/rehber/xadresi silinir (öncesindeki "bkz." gibi bağlaç kalır; aşırı mühendislik yok). - Tampon: metinde
[ya da/rehber/görülünce o noktadan itibaren tutulur; markdown bağlantısı)ile, çıplak adres slug dışı bir karakterle (boşluk, nokta, satır sonu,)) kapanınca doğrulanıp bırakılır.[sonrası 160 karakter içinde](gelmezse tutulan metin olduğu gibi bırakılır (sıradan köşeli parantez).bitir()kalanı doğrulayıp boşaltır. /universite/…,/bolum/…bu turda doğrulanmaz (backlog).
sohbet-akisi.ts değişikliği:
danismanAkisiseçeneklerinerehberler?: readonly RehberKaydi[]eklenir (verilmezse tampon pasif: betikler/evaller eski davranışı korur).- Okuma döngüsünde (
:181-195):text-start→ yeniBaglantiTamponu;text-delta→cikti = tampon.besle(parca.delta);metin += cikti;ciktiboş değilsewriter.write({ ...parca, delta: cikti });text-end→kalan = tampon.bitir(); kalan varsa öncetext-deltaolarak yazılır, sonratext-end. Diğer parçalar olduğu gibi. CiktiDenetimi'nebaglantiDuzeltme: numbereklenir;console.info("[soru] rehber bağlantısı düzeltildi:", n)loglanır (slug'lar loglanabilir, kişisel veri değil).- Kayıt (
onTamam(metin)) düzeltilmiş metni alır — ekran ve DB aynı.
route.ts: import { getRehberAramaKayitlari } from "@/lib/rehber" ve danismanAkisi({ ..., rehberler: getRehberAramaKayitlari() }). (Prod'da cache'li, rehber.ts:52-60.)
danisman-prompt.ts BAĞLANTI maddesi (:247-251): bir cümle eklenir: "Rehber adresi yalnız bu cevapta rehber_ara / rehber_oku sonucunda gördüğün sayfa alanından gelir; görmediğin bir yazıya bağlantı verme, önce rehber_ara çağır." (Kod koruması asıl savunma; prompt yalnız araç çağrısını teşvik eder.)
A-2 Revizyon sınırının kaldırılması
credits.ts:MAX_REVIZYONsilinir (yorumuyla).liste-revizyon.ts::82-88HAK kapısı silinir;RevizyonSonucu.kodbirliğinden"HAK"çıkar;ozetCikarimzasındanrevisionCountve dönenkalanHakçıkar (:46-57);:40,43,126,160metinlerindeki "revizyon hakkın kullanılmadı" → "kredin/paketin etkilenmedi" (dürüst: paketlide bedel 0).revisionCount: +1güncellemesi kalır (:155).sohbet-meta.ts:50:kalanHaktipten çıkar (eski kayıtlardaki fazla alan çalışma zamanında zararsız).araclar.ts::472açıklamasında "(paketli adayda, en fazla 2 hak)" → "(paketli adayda; sınır yok)";:1764HAK dalı silinir;:1770"kalan revizyon hakkını belirt" ibaresi silinir;AracBaglami.kalanRevizyon(:103-104) silinir.danisman-prompt.ts:75-82revizyonBolumu(r: { paketli: boolean }, params):kalanHakdalları silinir; paketli durum metni: "Revizyon sınırsız; her kurulum 20-60 sn sürdüğü için isteği yine tek seferde doğru kur." 3. maddedeki "kalan hakkı belirt" silinir.sistemPromptuseçeneğirevizyon?: { paketli: boolean }olur.route.ts:MAX_REVIZYONimport'u ve:199,210hesapları silinir;revizyon: { paketli };aracBaglami.kalanRevizyonkalkar.listem-icerik.tsx:12,180:MAX_REVIZYONimport'u vekalanHakprop'u kalkar.listem-govde.tsx:kalanHakprop'u (:78,102) silinir;:219"(kalan hak: N)" ifadesi silinir;:335koşuluhasPaketolur;:450→revizyonHakki={hasPaket ? 1 : 0}+ yorum (çakışma kararı 1).sohbet-senaryo-verisi.ts:78kalanHak: 1ve:90"Kalan revizyon hakkın: 1." silinir.page.tsx:31,247:MAX_REVIZYONimport'u kalkar; madde metni "Sınırsız liste revizyonu (danışmanla sohbetten)".paket-satinal.tsx:37: aynı metin.paket/page.tsx:13vepage.tsx:185zaten "liste revizyonları" diyor, dokunulmaz.sohbet-akisi.ts:134-137"bu cevapta ikinci revizyon çalıştırılmaz" korunur — bu bir hak değil, cevap başına döngü koruması.
A-3 Kabul kriterleri
grep -rn "MAX_REVIZYON\|kalanHak\|kalanRevizyon" src scripts→ 0 satır.grep -rn "revizyon hakk" src→ 0 satır (eval raporları ve docs hariç).pnpm tsx --conditions=react-server scripts/eval/baglanti-denetim.ts→… senaryo geçti, çıkış 0. Senaryolar (en az): (a) gerçek slug'lı markdown bağlantısı aynen kalır; (b)/rehber/yatay-gecis-rehberi→/rehber/yatay-gecis-cap-yandal(yakın eşleşme, başlık kayıttan); (c)/rehber/tamamen-uydurma-seyler→ düz metin; (d) ters tırnak içi ve çıplak adres de düzeltilir; (e) tampon: aynı bağlantı 3-4 deltaya bölünerek beslenincebesleçıktılarının birleşimi +bitir()tek seferlik sonuçla birebir aynı; (f)[ile başlayıp bağlantı olmayan metin ("[2024] verisi") kayıpsız geçer.pnpm tsx --conditions=react-server scripts/eval/akis-denetim.ts→ mevcut 7 senaryo + yeni 2 senaryo geçer:rehber-uydurma(model[OBP](/rehber/obp-nedir) ve [Yatay geçiş](/rehber/yatay-gecis-rehberi)yazar;rehberlerseçeneği gerçekcontent/rehberlistesinden verilir; beklentibaglantiDuzeltme === 2, kaydedilen metinde/rehber/obp-nedir-nasil-hesaplanirve/rehber/yatay-gecis-cap-yandalvar,/rehber/obp-nedir)yok) verehber-gercek(gerçek slug,baglantiDuzeltme === 0).BeklenentipibaglantiDuzeltmeile genişler; eski senaryolara0yazılır.typegen + tsc + linttemiz.
A-4 Commit planı
feat(sohbet): rehber bağlantıları akışta gerçek slug'larla doğrulanıyor— baglanti-denetimi.ts, sohbet-akisi.ts, route.ts, danisman-prompt.ts (BAĞLANTI cümlesi), baglanti-denetim.ts, akis-denetim.ts.feat(revizyon): revizyon sayısı sınırı kaldırıldı — tek koşul paket + sohbet kredisi— kalan dosyalar.[Bilal]ayrı commit yok; pazarlama metni değişikliği Bilal'in açık isteği.
A-5 Uzman ve denetim
yapay-zeka-muhendisi: prompt cümlesini ve yakın-eşleşme eşiklerini (0,5 / 0,15) gözden geçirir;pnpm eval:sohbet'e "rehberde olmayan konu" senaryosu ekler (backlog).guvenlik-uyum: merge öncesi A-2 (para/paket vaadi değişiyor;/paketmetni ve koşullar sayfası tutarlı mı). Link düzeltmede log yalnız slug içerir, kişisel veri yok.
Paket B — Revizyon "ilk N" garantisi (madde 4)
B-0 Kök neden (doğrulandı)
Boru hattı var ama belirleyici değil:
efektifParams(revizyon-kurali.ts:45-83) İstanbul'u havuz süzgecine ekliyor ✔;havuzOlusturonceliklileri kırpmada öne alıyor (rapor-havuzu.ts:343-347) ✔; havuz JSON'unda"oncelikli": trueişaretleniyor (rapor.ts:424-442) ✔; prompt "EN AZ N tane seç" diyor (rapor.ts:566-571) — ricadır.secimiOnar(rapor.ts:207-270) modelin seçtiği geçerli satırları olduğu gibi alır, yalnız eksik kalan yeriuyum(+4 öncelikli) sırasıyla doldurur. Model 24 geçerli satırı öncelikli satırsız doldurursa hiçbir şey tamamlanmaz;ilkTercihleriOneAl(rapor.ts:866) öne alacak satır bulamaz → "ilk 5 İstanbul" istendi, liste değişmedi gibi görünür. (YETERLI_MODEL_SECIMI= 12 olduğundan ikinci deneme de tetiklenmez.)- Dev mock yolu (
rapor.ts:572-575→mockRapor:368-422)ilkTercihleriOneAl/oncelikliyi hiç uygulamaz — lokalde anahtarsız denemede kural hiç görünmez. - Model kuralı hiç çıkarmıyorsa (liste_revize'yi
iller.ekleile çağırıpilkTercihlervermiyorsa) bu kod tarafı değil;pnpm eval:sohbetkonusu (yapay-zeka-muhendisi, B-5).
B-1 Değişiklik
revizyon-kurali.ts — yeni saf fonksiyon:
/**
* Dilim seçimi kurulduktan sonra "ilk N" ölçütüne uyan satır sayısı N'den
* azsa, havuzdaki uyan ama seçilmemiş programlar, aynı dilimdeki en düşük
* uyumlu uymayan satırların yerine konur (dilim adetleri değişmez). Havuzda
* yeterli uyan yoksa eldeki kadarı; takas edilen satır kaynak "tamamlandi"
* olur (gerekçe koddan şablonlanır). Dönen `eklenen` = takas sayısı.
*/
export function ilkTercihleriGarantile<S extends { programId: string; kaynak: string }>(
secim: Record<Dilim, S[]>,
havuz: readonly AdayProgram[],
k: RevizyonKurali | undefined,
uyum: (p: AdayProgram) => number,
yeniSatir: (p: AdayProgram) => S,
): { secim: Record<Dilim, S[]>; eklenen: number };
Takas kuralı: öncelikli adaylar uyum azalan sırayla; her aday kendi diliminde, uymayan satırlar arasından uyumu en düşük olanı çıkarır (model gerekçesi olan satırlar önce korunur: kaynak === "model" olanlar en son çıkar). Dilim adetleri sabit kalır; dilim hedefi 0 olan dilimde takas yapılmaz. Havuz öncelikli-dilim dağılımı N'e yetmiyorsa eldeki kadar.
rapor.ts:
secimiOnarsonrası,nihaibelirlendikten sonra (:703civarı, metin denetiminden önce)ilkTercihleriGarantile(nihai.secim, havuz, kural, uyum, (p) => ({ programId: p.id, gerekce: null, kaynak: "tamamlandi" }))uygulanır;onarim.tamamlanan += eklenen;taniyeilkNEklenenalanı eklenir (RaporTani tipi genişler).mockRapor(:368-422):onceliklisüzgecini alır,dilimdenseçiminioncelikliolanları önce alacak şekilde sıralar ve sonundailkTercihleriOneAluygular (mock prod sırasını taklit eder — kayıtlı yorum zaten "prod ile aynı hedef hesabı" diyor).- Kullanıcı mesajındaki "EN AZ N tane seç" cümlesi kalır (modelin gerekçe yazması için), ama sonuç koda bağlıdır.
B-2 Test — scripts/eval/revizyon-denetim.ts (Y)
akis-denetim.ts deseni: sahte OpenAI ucu; AI_BASE_URL/AI_API_KEY sahte; TYPESAFE_API_KEY silinir; data/yokatlas.db gerçek (salt okunur), app.db gerekmez. Sahte model chat/completions isteğinden response_format.json_schema adetlerini (hayal/dengeli/guvenli minItems) ve kullanıcı mesajındaki ADAY HAVUZU … : {…} JSON'unu okur, her dizi için oncelikli olmayan id'lerden tam adet seçer (gerekçe "test"), genelDegerlendirme/uyarilar sabit. Senaryolar:
ilk5-istanbul: params{ sira: 100000, tur: "say", iller: ["Ankara","İzmir"], kategoriler: [], revizyon: { ilkTercihler: { adet: 5, iller: ["İstanbul"] }, not: "ilk 5 İstanbul" } }→raporUretAyrintili(params, "ilk 5 İstanbul", undefined); beklenti:rapor.tercihler[0..4]her birininprogramlar[id].il === "İSTANBUL"(veri büyük harf;olcuteUyarzatentr-TRküçültüyor),tercihler.length === 24,tani.ilkNEklenen === 5, dilim adetlerikapsam.dagilimile aynı.ilk3-vakif:ilkTercihler: { adet: 3, universiteTipi: "vakif" }→ ilk 3 satıruniturDevletMi(unitur) === false.havuz-yetersiz:ilkTercihler: { adet: 10, iller: ["Ardahan"] }→ hata fırlatmaz, ilk satırlar eldeki Ardahan programları (0 da olabilir), sonuç 24 satır;tani.ilkNEklenen ≤ 10.mock-yolu:AI_API_KEYboş +NODE_ENV !== "production"ilemockAktif()→ senaryo 1 params; ilk 5 İstanbul.
Çıkış 0/1. Sahte ucun "kasten öncelikliyi atlaması" test koduna yorumla yazılır (gerçek model davranışını ölçmez).
B-3 Kabul kriterleri
pnpm tsx --conditions=react-server scripts/eval/revizyon-denetim.ts→ 4/4, çıkış 0.pnpm tsx --conditions=react-server scripts/eval/akis-denetim.tshâlâ geçer (B dokunmaz, regresyon kontrolü).typegen + tsc + linttemiz.eval:robotçağrı imzası değişmez (raporUretAyrintilidört parametre).
B-4 Commit planı
fix(revizyon): "ilk N" ölçütü seçimde kodla garanti ediliyor; mock yolu da kuralı uyguluyor— revizyon-kurali.ts, rapor.ts.test(revizyon): mekanik ilk-N denetimi (sahte model önceliği atlar)— revizyon-denetim.ts.
B-5 Uzman
yapay-zeka-muhendisi:pnpm eval:sohbet'e "ilk 5'i İstanbul'a göre güncelle" / "ilk 5'i X şehrine göre güncelle" cümleleriyle araç argümanı denetimi ekler (ilkTercihler.adet === 5 && iller == ["İstanbul"]); modeliller.ekleile yetiniyorsadanisman-prompt.ts:85-89örnekleri genişletilir (bu A'nın dosyası — bulgu rapora, değişiklik sonraki tura).
Paket C — Program varyant etiketi (madde 2, 3)
C-0 Kök neden (doğrulandı, veriden)
data/yokatlas.db:(universite, isim)ikilisi 4.225 grupta tekrar ediyor (SELECT universite, isim FROM programs GROUP BY 1,2 HAVING COUNT(*)>1). Örnek BAŞKENT ÜNİVERSİTESİ / Tıp: 6 satır (id 200610657, 200610666, 200611488, 200690357, 200690358, 200690359),isimhepsinde "Tıp"; farkprogram_ozellik'te:burs_orani∈ {Ücretli, %50 İndirimli, Burslu} ×ogrenim_dili∈ {Türkçe, İngilizce}. İÖ/UOLP'de de aynı (Marmara Hukuk:107210332"Hukuk" ve107200444"Hukuk (UOLP-…)(%25 İndirimli)" — UOLP parantezi isimde kalmış, burs/dil kalmamış).program_ozellikkapsamı tam: 21.493 programın 0'ı eksik.- Hiçbir liste varyantı göstermiyor:
rapor-listesi.tsx:242-262,program-tablosu.tsx:352-367,liste-cekmecesi.tsx:438-455,universite-program-tablosu.tsx,bolum-program-listesi.tsx(grep:burs|ozellik→ 0). Danışman/liste modelinin havuz JSON'u da varyantı görmüyor (rapor-havuzu.ts:427-452). - Birebir aynı kayıt (aynı id) tekrarı yok:
secimiOnartekrarları atıyor (rapor.ts:233-236). "Tekerrür" algısı yalnız etiket eksikliğinden.
C-1 Tasarım — tek kaynak Program.varyant
src/lib/program-varyant.ts (Y, saf):
export type VaryantGirdisi = { bursOrani: string | null; ogrenimDili: string | null; ogrenimTuru: string | null };
/** Satır altı etiketi: "Burslu · İngilizce", "%50 İndirimli", "İkinci Öğretim", "Ücretli · Uzaktan"; Türkçe + Örgün + burs bilgisi yoksa null. */
export function varyantEtiketi(g: VaryantGirdisi): string | null;
Sıra: burs (varsa, olduğu gibi: "Burslu" / "%50 İndirimli" / "Ücretli"), dil (yalnız "Türkçe" değilse), öğretim türü (yalnız "Örgün Öğretim" değilse; metin DB'deki hâliyle: "İkinci Öğretim", "Açıköğretim", "Uzaktan Öğretim"). Ayraç " · ".
src/types/yokatlas.ts: Program'a varyant: string | null (zorunlu alan; ProgramListeSatiri Omit olduğu için otomatik taşır; ManuelTercih ayrı tip).
src/lib/db.ts: SELECT_COLS (:40-42) üç ilişkili alt sorgu kazanır: (SELECT burs_orani FROM program_ozellik o WHERE o.id = programs.id) AS bursOrani, aynı şekilde ogrenimDili, ogrenimTuru. Tüm SELECT ${SELECT_COLS} FROM programs kullanımları takma adsız (db.ts:224,322, katalog.ts:199,519, araclar.ts:945,957) → programs.id çözümlenir. ProgramHamSatiri üç alanı ekler; programSatiri (:56) varyant: varyantEtiketi(row) üretir. programs_arsiv sorguları (ARSIV_SELECT) dokunulmaz (arşivde ozellik yok; kapanan programlar için varyant null kalır). Tablo yokluğu: ozellikTablosuVarMi koruması alt sorgu için gerekmez — data/yokatlas.db depoda ve Docker seed'inde (Dockerfile:28,75) tabloyla geliyor; yazılımcı bunu commit mesajına yazar.
Model ve danışman: rapor-havuzu.ts:427 havuzuKompaktJson satırına ...(p.varyant ? { varyant: p.varyant } : {}) eklenir (model aynı programın burslu/ücretli varyantlarını ayırt eder; gerekçede "burslu" yazabilir — metindekiSayilar(p.isim, …) sayı denetimi "%50" için rapor.ts:779 listesine p.varyant eklenmesi gerekir; bu B'nin dosyası → C raporuna not, entegrasyonda orkestratör tek satır ekler: ...metindekiSayilar(p.isim, p.universite, p.varyant ?? "")).
UI (satır altı): liste-satiri.ts'e SATIR_VARYANT = "shrink-0 rounded-full bg-slate-100 px-2.5 py-0.5 text-xs font-semibold text-slate-700" eklenmez — SATIR_CIP zaten bu (§4.15 tür çipi). Varyant SATIR_META satırında TurCipi'nin hemen ardından SATIR_CIP sınıfıyla bir çip olarak gösterilir (rapor-listesi.tsx:264 sonrası, program-tablosu.tsx aynı yer, universite-program-tablosu.tsx, bolum-program-listesi.tsx). İnteraktif değil, SectionEyebrow kuralı dışında (§4.15 çip). liste-cekmecesi.tsx:445-455 ikinci satırda üniversite adından sonra · {tercih.varyant} (text-slate-500). Null ise hiçbir şey çizilmez.
Manuel liste: liste-dogrula.ts:21-33 ManuelTercih.varyant?: string | null (opsiyonel: eski localStorage kayıtları geçer; dogrula string ise alır); use-tercih-profili.ts programEkleIste kopyalarken varyant taşır; program-ekle-butonu.tsx:22-31 ProgramOzet pick'ine "varyant" eklenir; liste-queries.ts:15-29 programOzetle varyant döner.
Kayıtlı rapor (/listem): rapor-queries.ts kullanicininRaporu (:35) sonucu dönmeden önce programOzellikleriGetir(Object.keys(result.programlar)) ile result.programlar[id].varyant = varyantEtiketi(...) yazılır (24 id, tek sorgu; eski kayıtlar da kazanır). RaporSonuc.programlar[id] tipine varyant?: string | null eklenir (src/features/rapor/types/rapor.ts — C'ye ait, B dokunmaz). rapor-listesi.tsx p.varyant okur. listeOzetiCikar (rapor.ts, B) bu turda varyantı modele yazmaz — backlog.
Tekilleştirme kuralı ([KARAR]): birebir aynı kayıt tekrarı yok; varyantlar ayrı ÖSYM kodu ve ayrı taban sırasıdır, listede ikisi de meşru. Ek kural eklenmez. Model varyantı görünce zaten "Başkent Tıp burslu + Başkent Tıp %50" seçimini gerekçelendirebilir. Üçten fazla varyantın aynı listeye girmesi sorun olursa secimiOnar'a dilim içi üst sınır (B'nin dosyası) sonraki tur.
C-2 Test — scripts/eval/varyant-denetim.ts (Y)
pnpm tsx scripts/eval/varyant-denetim.ts (react-server koşulu gerekmez; db.ts ve program-varyant.ts saf/sqlite):
varyantEtiketibirim:{Burslu, İngilizce, Örgün}→ "Burslu · İngilizce";{null, Türkçe, Örgün}→ null;{Ücretli, Türkçe, İkinci Öğretim}→ "Ücretli · İkinci Öğretim";{%50 İndirimli, null, null}→ "%50 İndirimli".programGetir("200610657")…("200690359")6 Başkent Tıp kaydınınvaryantdeğerleri birbirinden farklı ve hiçbiri null.dilimAra(100000, "say", "dengeli", { limit: 50 })sonucunda her satırdavaryantalanı tanımlı (string ya da null) — SELECT_COLS'un bütün yolu kapsadığının kanıtı.- Devlet/Türkçe/örgün bir program (ör.
107210332Marmara Hukuk) →varyant === null.
C-3 Kabul kriterleri
- Test 4/4, çıkış 0;
typegen + tsc + linttemiz;pnpm tasarim:denetimsıfır hata (yeni çipSATIR_CIPsınıf dizisiyle birebir). grep -n "varyant" src/features/rapor/components/rapor-listesi.tsx src/features/liste/components/program-tablosu.tsx src/features/liste/components/liste-cekmecesi.tsxher dosyada ≥1.sqlite3 data/yokatlas.dbdosyası değişmez (git statustemiz; test salt okur).
C-4 Commit planı
feat(veri): Program.varyant — burs/dil/öğretim türü etiketi tek kaynaktan (program_ozellik)— types, db.ts, program-varyant.ts, rapor-havuzu.ts, varyant-denetim.ts.feat(liste): varyant çipi — sonuç tablosu, kendi listem çekmecesi, katalog tabloları— liste-queries, liste-dogrula, use-tercih-profili, program-ekle-butonu, program-tablosu, liste-cekmecesi, universite-program-tablosu, bolum-program-listesi.feat(listem): kayıtlı raporun satırlarına varyant (okuma anında zenginleştirme)— rapor-queries.ts, types/rapor.ts, rapor-listesi.tsx.
C-5 Uzman
veri-muhendisi:scripts/veri-kalite.ts'e "aynı (universite, isim, fakulte) grubunda varyant etiketi tekrar eden satır" denetimi (bu turda değil; backlog). UOLP/ÇAP parantezlerininisimde kalmasının (bolumBazAdi) varyantla çakışıp çakışmadığını gözden geçirir.tasarimci: çipin satırdaki yeri ve 375 px'te sarma (DESIGN §4.15 "ad sarar, çipler altına iner").
Paket D1 — Sohbet UI: shimmer, logo varyantı, soru geçişi (madde 5, 10)
D1-0 Kök neden (doğrulandı)
- Shimmer yok:
soru_soraracıHAPSIZ_ARACLAR'da (arac-pilleri.tsx:48) → hap çizilmez; form yalnızoutput-availableolunca gelir (sohbet-client.tsx:112-119). Sorular üretilirken (tool-soru_sorparçasıinput-streaming/input-available) metin boş olduğundanCevapIskeleti(gri üç çizgi,:316-324) görünür;TextShimmerLoader(loader.tsx:322-350) var ama kullanılmıyor. - Logo: site header
AnimasyonluMark sadeceHover= varsayılan varyantrota(site-header.tsx:26,animasyonlu-mark.tsx:108,172). Sohbet avatarı araç çalışırkenzincir(sohbet-client.tsx:305-307). Bilal'in istediği: header'dakiyle aynı →rota. - Soru geçişi:
SohbetAnketi(sohbet-anketi.tsx:85-142) shadcnQuestionnaireilkeli;Itemaktif değilkenhidden+inertalır (node_modules@shadcn/react/dist/questionnaire/index.js,Je()→itemProps.hidden: !_), geçiş animasyonu yok. Sihirbaz (sihirbaz-adimlar.tsx:252-282)TransitionPanel(src/components/core/transition-panel.tsx) + yön (yon),x: ±100%,opacity,height(useMeasure), yaystiffness 300 / damping 30, hareketi azalt'ta çapraz solma.
D1-1 Değişiklik
Madde 5 (sohbet-client.tsx):
DanismanAvatar:varyant={canli && aracta ? "rota" : "nabiz"}; yorum güncellenir ("header logosuyla aynı rota koreografisi").- Mesaj gövdesinde (
:1004-1017):const soruHazirlaniyor = m.parts.some(p => p.type === "tool-soru_sor" && "state" in p && p.state !== "output-available" && p.state !== "output-error").metinboş vesoruHazirlaniyoriseCevapIskeletiyerine<TextShimmerLoader text="Soruları hazırlıyorum…" size="sm" className="px-0.5" />(role="status"sarmalı,aria-live="polite"). Metin varsa shimmer satırı metnin altında, formun yerinde durur; form gelince kalkar. TextShimmerLoadergradyanı--muted-foreground/--foregroundtoken'ı kullanıyor (loader.tsx:340).pnpm tasarim:denetimya da görsel dil bunu reddederse yalnız o zamanloader.tsx'te gradyanvar(--color-slate-400)→var(--color-slate-900)→var(--color-slate-400)olur (karanlık mod token'dan döner;dark:yok).
Madde 10:
src/lib/adim-gecisi.ts(Y, saf, "use client" yok): sihirbazdaki varyant ve geçiş sabitleri buraya taşınır:export function adimGecisiVaryantlari(opts: { hareketiAzalt: boolean; yukseklik: number | "auto" }): Variants; // enter/center/exit, custom = yön export function adimGecisi(hareketiAzalt: boolean): Transition; // x/height spring 300/30, opacity 0.2; azaltılmışta 0.12 solmasihirbaz-adimlar.tsx:254-282bu iki fonksiyonu kullanır; davranış birebir (diff yalnız taşınma).sohbet-anketi.tsx:Questionnairekontrollü olur:item={aktif}+onItemChange={(ad) => { setYon(indeks(ad) > indeks(aktif) ? 1 : -1); setAktif(ad); }}(ilkelitem/onItemChangeprop'larını destekliyor,index.d.ts:23-27). HerQuestionnaireItemiçeriği (Title/Description/Choices/Error)motion.div'e sarılır:variants={adimGecisiVaryantlari(...)},custom={yon},initial={false},animate={s.ad === aktif ? "center" : "enter"},transition={adimGecisi(hareketiAzalt)}; sarmal kaprelative overflow-hidden. Yükseklik: aktif içerikuseMeasureile ölçülür, sihirbazdaki gibiheightvaryanta geçer (ilk ölçümde"auto").- Çıkış animasyonu sınırı: ilkel pasif
fieldset'i anındahiddenyaptığı için çıkış kayması görünmez. Yazılımcı önceQuestionnaireItem'ahidden={false}veinert={false}geçip görünürlüğüdata-activeile kendisi yönetmeyi dener — bunun işe yaramasıb(mergeProps) fonksiyonunda kullanıcı prop'unun kazanmasına bağlı:node_modules/.pnpm/@shadcn+react@0.3.1_*/node_modules/@shadcn/react/dist/chunk-HBS6WEDP.jsiçindekibokunur. Kazanıyorsa pasif item'larTransitionPanelmantığıyla (exit→position:absolute) tam çift yönlü geçiş alır; kazanmıyorsa yalnız giriş animasyonu kalır ve rapora "koddan çıkarım: çıkış görünmüyor" yazılır. Her iki durumda form gönderimi (FormData,:70-78) bozulmamalı: pasif item'ların input'ları DOM'da kalır. - Ortak bileşen kararı ([KARAR]):
TransitionPaneltaşınmaz; sihirbaz paneli çocukları değiştirirken anket ilkelin kendi item'larını yönetiyor. Ortak olan sabitler ve varyant üreticisi (adim-gecisi.ts); bu "birebir aynı geçiş"i sağlar, bileşeni zorlamaz.
D1-2 Kabul kriterleri
grep -n '"zincir"' src/features/rapor/components/sohbet-client.tsx→ 0;grep -n "TextShimmerLoader" src/features/rapor/components/sohbet-client.tsx≥ 1.grep -n "stiffness: 300" src/features/sihirbaz/components/sihirbaz-adimlar.tsx src/features/rapor/components/sohbet-anketi.tsx→ 0 (sabit tek kaynakta:src/lib/adim-gecisi.ts'te 1).sohbet-anketi.tsxitem=veonItemChange=içerir;sihirbaz-adimlar.tsxadimGecisiVaryantlariimport eder.typegen + tsc + linttemiz;pnpm tasarim:denetimsıfır hata.- Rapor: çıkış animasyonu elde edildi mi (mergeProps bulgusu, dosya:satır).
D1-3 Commit planı
feat(sohbet): soru formu hazırlanırken shimmer; araçta avatar header logosuyla aynı rota koreografisi.refactor(hareket): sihirbaz adım geçişi sabitleri lib/adim-gecisi.ts'e; soru formu aynı geçişle.
D1-4 Uzman
tasarimci: shimmer metni ("Soruları hazırlıyorum…") ve DESIGN §4.20/§4.33'e eklenecek iki cümle (D1 raporuna yazılır; DESIGN.md D2'nin). Dev sunucusu açabilir (AGENTS.md istisnası) — 375/768'de soru geçişinde yükseklik sıçraması veoverflow-hidden'ın odak halkasını kırpması kontrol.
Paket D2 — Program ayrıntı çekmecesi (madde 8, 9)
D2-0 Kök neden (doğrulandı) ve itiraz
- Bilgi paneli
program-ayrinti.tsx(ProgramAyrintiButonu, Info ikonu:140-170);liste-cekmecesi.tsx'te böyle bir panel yok. "Yıllara göre" =YillaraGore(:284-336),AyrintiIcerik'te:221. - Açılış bug'ı:
AltSayfaIcerik duraklar={[0.6, 0.92]} ilkDurak={0}(:171-175). (a) İlk durak 0,6'da sayfa "kısık"tır →AltSayfaGovdeoverflow-hidden touch-none(alt-sayfa.tsx:631), kaydırılamaz; kullanıcı dokunup kaydırmaya çalışır, "takılmış" görür. (b) Panel iskeletle (6×h-10 ≈ 280 px) açılır, veri gelince (yerel sqlite, ~100 ms; genelde açılış yayı 0,4 s bitmeden) içerik ~3-4 ekran olur →ResizeObserver(alt-sayfa.tsx:303-327)boyu günceller,ofset(d) = boy − durakPx(d)sıçrar; yay sürüyorsagit(d)yeniden hedefler, bitmişsey.jump→ gözle görülür zıplama. "Çok uzun diye" doğru sezgi; asıl neden açılırken boyun değişmesi ve çift durak. - Madde 9'daki "logo sonra içerik":
akademik-ayrinti.tsx:120-270AcilisSahnesi— üç renkli kare logo olarak belirir, yerine büyür, içerikdata-sonrabloklarıyla bulanıktan netleşir. Bilal aynısını program ayrıntısında istiyor. Sahne[data-kare]kartlara ihtiyaç duyuyor; program çekmecesi kartsız (§4.19) → sahneye "kartsız" mod gerekir.
D2-1 Değişiklik
src/components/ui/acilis-sahnesi.tsx (Y): AcilisSahnesi akademik-ayrinti.tsx'ten buraya taşınır (sabitler LOGO, KARE_*, ICERIK_BASI, GUCLU_CIKIS, YERLESME dâhil) ve hazir?: boolean (varsayılan true; false iken sahne oynamaz, true olunca bir kez oynar — veri bekleyen çekmece için) ve kartsız mod kazanır: [data-kare] yoksa ve [data-logo] varsa, data-logo kabındaki üç kare (data-kare yerine data-logo-kare="sol|sag-ust|sag-alt", mutlak konumlu, LOGO renk/konum) BASLANGIC + slot·SLOT_ADIMI gecikmeyle belirir (GUCLU_CIKIS), KARE_SURE·0.52'ye kadar durur, sonra scale 0.6 + opacity 0 ile söner (toplam ≈ 1 s), [data-sonra] blokları netlestir ile ICERIK_BASI'ndan itibaren 0.08 s arayla gelir. Hareketi azalt: yalnız 0,25 s solma (mevcut dal). akademik-ayrinti.tsx yeni modülü import eder; davranışı değişmez.
program-ayrinti.tsx:
YillaraGorebileşeni ve:221çağrısı silinir (APIgecmisalanı kalır; backlog).AyrintiIcerikiçindeki bölümlerdata-sonraalır (Kunyeul, Kontenjan türleri bloğu, "Son yerleşenin netleri" başlık+not+tablo tek sarmalda, Kosullardetails, Istihdam, UniversiteOzeti, kaynak paragrafı). Başlığın altında<div data-logo className="relative mx-auto h-16 w-16" aria-hidden>(üçdata-logo-karespan) — yalnızdurum === "hazir"iken render;AcilisSahnesi hazir={durum === "hazir"}sarmalı:218civarı.- Çekmece:
duraklarveilkDurakprop'ları kaldırılır (varsayılan tek durak 0,92;AkademikAyrintiile aynı). Yükleme iskeletimin-h-[70dvh]sarmalda (açılışta boy sabit; veri gelince ResizeObserver sayfayı yalnız büyütür, konum sıçramaz —alt-sayfa.tsx:11-12"içerik daha kısaysa içeriğin boyunda kalır" kuralı korunur, iskelet kısa değildir). - Veri geldiğinde içerik sahne bitene kadar görünmez (sahne
opacity:0ile başlatıyor) — "logo sonra içerik" hissi buradan.
docs/tasarim/DESIGN.md §4.19: "sayfa ekranın %60'ında açılır … tam boya çıkar" cümlesi (program-ayrinti.tsx:171-173 yorumu ve §4.10:372 atfı) → tek durak; "Yıllara göre" maddesi listeden çıkar; açılış koreografisi "AcilisSahnesi kartsız mod (logo → içerik), src/components/ui/acilis-sahnesi.tsx" olarak bir cümleyle eklenir. §4.10:372'deki "(§4.19)" atfı güncellenir.
D2-2 Kabul kriterleri
grep -n "YillaraGore\|Yıllara göre" src/features/katalog/components/program-ayrinti.tsx→ 0.grep -n "duraklar=\|ilkDurak=" src/features/katalog/components/program-ayrinti.tsx→ 0.grep -n "function AcilisSahnesi" src/features/katalog/components/akademik-ayrinti.tsx→ 0;src/components/ui/acilis-sahnesi.tsxexport eder;akademik-ayrinti.tsximport eder.grep -n "data-sonra" src/features/katalog/components/program-ayrinti.tsx≥ 5;data-logo≥ 1.typegen + tsc + linttemiz;pnpm tasarim:denetimsıfır hata (kapatma çarpısı sınıf seti değişmez).- Tarayıcı doğrulaması yazılımcıda yok;
tasarimciD2-4.
D2-3 Commit planı
refactor(ui): AcilisSahnesi ortak bileşen (components/ui/acilis-sahnesi.tsx), kartsız logo→içerik modu.fix(program-ayrinti): tek durak + sabit iskelet boyu (açılış sıçraması), "Yıllara göre" kaldırıldı, logo→içerik açılışı; DESIGN §4.19.
D2-4 Uzman
tasarimci(merge öncesi, dev sunucusu açabilir): 375 ve 768 px'te çekmece açılışında sıçrama yok mu; sahne süresi (≈1 s) uzun mu;prefers-reduced-motion. Gerekçe ve ölçümleri raporuna yazar, sunucuyu kapatır.
Madde 7 — Rybbit tracking API: iş yok
src/lib/analitik-sunucu.ts:15-17,33-52 RYBBIT_HOST/SITE_ID/API_KEY varsa POST /api/track atıyor; bayrak/TODO yok. İstemci tarafı (analitik.ts) window.rybbit script'i, tracking API ile ilgisiz. Env adları .env.local'da mevcut (değer okunmadı), CI .gitea/workflows/ci.yaml:41-43 build'e geçiriyor, prod docker-compose.yml .env.production dosyasından alıyor. API sunucuda yeni açıldıysa odeme_tamamlandi ve giris_basarili (:19) olayları bugünden itibaren düşmeye başlamalı — doğrulama analitik ajanının işi (backlog).
İtirazlar
- Madde 8-9 dosya adı. "Liste çekmecesi" değil
program-ayrinti.tsx;liste-cekmecesi.tsx'e yalnız C (varyant satırı) dokunur. Orkestratörün "sohbet-client.tsx ve liste-cekmecesi.tsx'e çakışmadan bölün" kısıtı bu sayede kendiliğinden sağlanıyor. - Madde 8 çözümü "Yıllara göre"yi kaldırmakla bitmez. Bug tablo uzunluğundan değil çift durak + açılışta boy değişiminden; tablo kalksa da panel ~3 ekran. D2 üçünü birlikte yapar.
- Madde 1 için "cikti-denetimi.ts'e ekle" yetmez. Oradaki denetimler cevap bittikten sonra çalışır ve ekrandaki hapı geri alamaz; akış içi tampon gerekir (A-1).
cikti-denetimi.tsdokunulmaz. - Madde 2-3 için "rapor-havuzu/rapor.ts'te tekilleştir" yanlış yön. Kayıtlar birebir aynı değil; sorun etikettir ve veri katmanında çözülür (
Program.varyant), böylece /sonuc, katalog, kendi listem ve rapor aynı anda düzelir. - Madde 6'da DB göçü gerekmez.
revision_countsayaç olarak kalır; kaldırmak iki göç ve geri dönüş maliyeti getirir, hiçbir kazanç yok. - Madde 4 "test iste" — modelin kuralı çıkarıp çıkarmadığı mekanik testle ölçülemez; B'nin testi kod yolunu kilitler, model tarafı
eval:sohbet'e (yapay-zeka-muhendisi) gider.
Merge sırası ve bağımlılıklar
A, B, C, D1, D2 birbirinden bağımsız; önerilen sıra B → C → A → D2 → D1 (küçükten büyüğe, UI en sona). Entegrasyonda orkestratör: (1) sohbet-client.tsx prop yeniden adlandırması (çakışma kararı 1); (2) rapor.ts:779 metindekiSayilar(... p.varyant) tek satırı (C-1 notu); (3) pnpm build; (4) scripts/eval/{akis,baglanti,revizyon,varyant}-denetim.ts dördü; (5) pnpm eval:robot --profil … isteğe bağlı (anahtar ister, Bilal).
Backlog önerileri (kapsam dışı)
/universite/…ve/bolum/…sohbet bağlantılarının da gerçek slug'larla doğrulanması (katalog haritalarıkatalog.ts); A'nın tamponu genişletilebilir.listeOzetiCikarveprogram_araçıktısınavaryant(danışman "burslu mu?" sorusuna listeden cevap versin).veri-kalite.ts: aynı (üniversite, isim, fakülte) grubunda varyant etiketi tekrarlayan satır uyarısı; UOLP/ÇAP parantezleri.api/program-ayrintigecmisalanı artık kullanılmıyor →programGecmisiGetirsorgusu kaldırılabilir (küçük tasarruf).eval:sohbet'e "rehberde olmayan konu" ve "ilk 5'i X'e göre güncelle" senaryoları (yapay-zeka-muhendisi).analitik: tracking API açıldıktan sonraodeme_tamamlandi/giris_basariliolaylarının Rybbit'te göründüğünün teyidi; önceki dönem için huni boşluğu notu.sohbet-client.tsx1.105 satır; soru formu/revizyon parçası ayrı dosyaya (D1 sonrası).- Paketsiz ama kredili adayın
liste_revizekullanıp kullanamayacağı — bugün paket şart (liste-revizyon.ts:71-77); Bilal'in "kredisi olduğu sürece" sözü bunu kapsıyorsa ürün kararı.
Bilal'den istenen
- Madde 6: paketsiz-kredili adayda revizyon açılsın mı, yoksa paket şart kalsın mı? (tek cümle, 1 dk) — şartname "paket şart kalır" varsayıyor.
- Prod
.env.productioniçindeRYBBIT_HOST / RYBBIT_SITE_ID / RYBBIT_API_KEYtanımlı mı (yalnız varlık; madde 7'nin sunucu olayları buna bağlı) — 1 dk. - D2 sahnesi için tercih: logo kareleri sönerek mi, yoksa başlığın yanına küçülerek mi kaybolsun? (varsayılan: sönerek) — 1 dk.