docs(ekip): 22-23 Eylül vardiya raporları, BACKLOG/KARARLAR/PROTOKOL güncellemeleri
All checks were successful
Deploy / deploy (push) Successful in 17s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-09-23 11:43:11 +03:00
parent a8c75ce4a7
commit a42a1a9920
27 changed files with 3508 additions and 8 deletions

View File

@@ -0,0 +1,8 @@
# 23 Eylül vardiyası — brifing
Bilal 07:22'de uyandı ve orkestratörün 01:25'te kendi kararıyla durmasını eleştirdi: talimat "sabaha kadar çalış, kesilme olursa yeniden tetikle" idi. Durma limit yüzünden değil, orkestratörün "yapılacak iş kalmadı" yorumu yüzündeydi. Bu yorum Bilal'in talimatının yerine geçemez.
**Kural bundan sonra:** iş bittiğinde durulmaz; CEO'nun kuyruğundaki açık sorular ve backlog'daki tarihli işler sürdürülür. Durmak gerekiyorsa gerekçesi Bilal'e SORULUR, tek taraflı karar verilmez.
22-23 gecesinin çıktısı: `docs/gece-vardiyasi/2026-09-22/SABAH-OZETI.md`. Dört paket dalda hazır, Bilal'in 6 kararı bekliyor.
Bu vardiyanın işi: CEO'nun `06-ceo-kararlari.md` §6'da ajanlara bıraktığı açık sorular + backlog'daki tarihli işler. Kurallar aynı: `docs/ekip/PROTOKOL.md`.

View File

@@ -0,0 +1,118 @@
# 23 Eylül vardiyası — orkestratör günlüğü
Başlangıç 07:25. Gerekçe: orkestratör 01:25'te kendi kararıyla durmuştu (limit değil), Bilal bunu eleştirdi. Bkz. `00-brief.md`.
## Tur 1
| Ajan | İş | Durum |
|---|---|---|
| veri-muhendisi | `programs_arsiv` etiket düzeltmesi (BACKLOG #21) | ✔ |
| tasarimci | CEO §6 açık soruları + URAP telafisi | koşuyor |
| yapay-zeka-muhendisi | Liste ücretsiz olursa kötüye kullanım eşiği (CEO §6) | koşuyor |
### veri-muhendisi ✔ → `01-arsiv-etiketi.md`
`programs_arsiv`'de 85 satır düzeldi (61 `unitur` + 24 `il`). Arşiv↔`programs` `unitur` farkı 2→0. Puan/sıra/kontenjan kolonlarında **0** değişiklik (45 kolon id üzerinden kıyaslandı). `veri-kalite` kontrol 13/14 artık arşivi de tarıyor; kusurlu DB'de ikisi de `KALDI` verdi, düzeltmeden sonra `GEÇTİ`. İdempotent. Commit'ler `9722689` (kod), `c5fd2cb` (veri), `178c56a` (entegrasyon merge) — build temiz.
**Kullanıcıya bugünkü etkisi yok** (`src/` tarafında arşivi okuyan SQL yok); düzeltme önleyici.
**Dünkü O1 rakam hatası kayda geçti ve bağımsız doğrulandı:** hub değişimi "170 → 166" değil, **132 → 129 kurum**; entegrasyon build'inin ürettiği HTML Devlet 129 / Vakıf 78 gösteriyor.
## Tur 2 (07:40) — Bilal "devam et" dedi
Vardiya içerik üretmeye de açıldı. **Ama ilk iş duplikasyon tuzağına çarptı:**
### ⚠️ BACKLOG bayat — dört yazı zaten yayında
BACKLOG #2 (KYK burs/kredi, "en geç 6 Ekim") ve #3'ün "bu hafta" üç yazısı (boş kontenjan yorumu, en çok öğrenci alan devlet üniversiteleri, en seçici devlet üniversiteleri) hâlâ `aday` görünüyor. Gerçekte dördü de yazılmış ve **`main`'e girmiş**: commit `e21cda4` (22 Eyl 2026), `git merge-base --is-ancestor e21cda4 main` ✔.
Dosyalar: `content/rehber/kyk-burs-mu-kredi-mi.md`, `bos-kontenjanlar-ne-anlatiyor.md`, `en-cok-ogrenci-alan-devlet-universiteleri.md`, `en-secici-devlet-universiteleri.md`.
Orkestratör "devam et" emriyle doğrudan yazı işine geçseydi dördü de yeniden yazdırılacaktı. **Ders: içerik işine başlamadan önce `content/rehber` ile plan karşılaştırılır; backlog durumu tek başına yeterli kanıt değil.**
Dün gece `urun-yoneticisi` backlog'u güncelledi ama bu dört maddeyi yakalamadı — kapanışları git geçmişinden değil, gece raporlarından okuduğu için.
### Koşan işler
| Ajan | İş |
|---|---|
| seo-geo | Plan ↔ `content/rehber` envanteri: ne yazıldı, ne kaldı, sırası ne |
| tasarimci | CEO §6 açık soruları + URAP telafisi (ilk kopya 600 sn takıldı, mekanik tarama orkestratörce hazırlanıp yeniden salındı) |
| yapay-zeka-muhendisi | Liste ücretsiz olursa kötüye kullanım eşiği hazırlığı (CEO §6) |
**Bilal'in 6 kararı hâlâ cevap bekliyor** — merge ve radikal değişiklik gerektirenler onlara bağlı, bu turda yapılmıyor.
### yapay-zeka-muhendisi ✔ (09:50) → `03-ucretsiz-liste-hazirlik.md`
Bilal "liste paketsize ücretsiz olsun" derse iş hazır. Özet:
- **Kilit bugün sayıyı korumuyor:** `uretim-kilidi.ts` kullanıcı başına *aynı anda tek* üretimi engelliyor (180 sn bayat kilit devri), ama kilit ~20 sn sonra bırakılıyor — ardışık üretimi durduran tek şey bugün kredi bakiyesi.
- **Önerilen eşik: kayan 24 saatte 5 başarılı üretim** (paketsiz). Bugün tüm zamanlarda 3 üretim var → eşik **0 kullanıcıyı vuruyor**; dürüst öğrencinin yeniden kurma ihtiyacı 3-4; tavanda 10,5 kuruş/kullanıcı/gün. Sayım yüzeyi mevcut `credit_ledger` — yeni tablo/göç yok.
- **Kredi çağrısı silinmemeli**, tutar `hasPaket ? 3 : 0` olmalı: 0 deltalı defter kaydı idempotency'yi (`UNIQUE(reason, ref_id)`), "önceki deneme başarısızdı" ayrımını ve günlük sayacı birlikte taşıyor. Yan etki: `kredi-hatirlatma.ts:36` eşiği `< RAPOR_KREDI` → `< 1`.
- **Revizyon kapsam dışı** — paketsiz zaten yapamıyor; "listem yanlış oldu" çıkışı tercih değiştirip yeniden üretmekten geçiyor ve o yol bu kararla zaten ücretsizleşiyor.
- Büyüklük **S**: 13 dosya ~67 satır, göç yok, yeni ekran yok, şema senkronu gerekmiyor.
### seo-geo ✔ (09:55) → `04-icerik-envanteri.md`
İki plan mükerrerler birleştirilince **18 konu: 8 yazıldı, 9 açık, 1 düştü.**
Yazılanlar `main`'de: `902c7a1` (3 yazı, 21 Eyl) · `2602b6f` (1) · `e21cda4` (4, 22 Eyl). **BACKLOG #2 ve #3 `merge` olarak kapatılmalı; #3'teki "kalan 7 yazı" sayısı da yanlış.**
Bayatlık: `ikinci-universite-acikogretim` düştü (CEO zaten reddetmişti + AÖF penceresi Ekim'de kapanıyor) · `universitede-ilk-hafta` penceresi kaçtı (üniversiteler 21 Eylül'de açıldı) → Bilal'e tek cümlelik karar: düşsün mü, 2027 Eylül'üne mi park edilsin · `kac-siralama-ile-hangi-bolume-girebilirim.md` başlığı hâlâ "(2025 Verileriyle)" diyor, katalog 2026'ya geçti → `rehber-editoru` işi.
Kısıt: Ahrefs/Search Console bağlı değil → raporda hiç hacim/trafik rakamı yok, sıralama gerekçesi yalnız takvim.
**Sıradaki yazı: `yks-2027-takvimi`** — ÖSYM'nin Kasım ortasında beklenen duyurusundan önce indekslenmeli.
## Tur 3 (09:57)
`rehber-yazari` → `gece/0923-rehber-2027` dalında 2027 takvimi yazısı. Kritik kısıt verildi: **2027 takvimi açıklanmadı, uydurma tarih yasak**; çerçeve "henüz açıklanmadı, geçmiş desen şu, açıklanınca burası güncellenecek" olacak ve tahminler tek bölümde toplanacak ki duyuru günü tek paragraf değişsin.
### tasarimci ✔ → `02-tasarim-acik-sorular.md`
1. **Sihirbaz 3 adımda kalmalı, 2'ye inmemeli** — 2'ye indirmek il adımına ikinci bir iş yükler (3. adımdan çıkardığımız hatanın aynısı), 375px'te kaydırmalı il listesiyle tür çiplerini aynı panele sıkıştırır.
2. **`/meraklisina` şema senkron borcu SIFIR** — `grep -rni "öncelik" src/features/pazarlama/ src/app/meraklisina/` → 0 satır. Ne `SEMA` düğümü, ne `aria-label`, ne figcaption öncelikleri anlatıyor. Değişecek olan belge değil, ekran.
3. **Sessiz regresyon tuzağı (iyi yakalama):** Öncelikler kaldırılırsa `tercih-farki.ts` `oncelikler`'i kıyaslamaya devam ettiği için, **hiçbir şey değiştirmemiş eski kullanıcı** `/listem`'de kalıcı "Tercihlerin değişmiş → 3 kredi harca" uyarısı görür (eski params dolu, yeni profil boş). Dosyanın kendi 8-12. satırdaki ilkesine aykırı. Dört nokta, tek dosya. K5 onaylanırsa aynı commit'e girmeli.
4. **R7 hüküm: tek commit, 28 satır.** 48'in 26'sı gerçek metin + 2 placeholder (düzeltilir), 11 dekoratif ikon, 4 fonksiyonel ikon (ayrı kural), 1 devre dışı, 4 dev paneli. **Büyük punto: 0.** Kör `sed` olmaz — aynı dosyalarda dokunulmaması gereken 11 ikon var.
5. **URAP telafisi XS:** özet paragrafın ilk virgülüne "devlet üniversitesi"/"vakıf üniversitesi".
**Tasarımcının kendi hatasının teşhisi (alıntı):** bilginin "zaten var" olduğunu o sayfada hiç aratmadan, *farklı bir sayfa tipindeki* rozetlerden genellemeyle varsaymış; tek üniversitelik detay sayfasında rozet tekrar olacağı için hiç konmamış. Doğrulama tek grep'miş.
## Tur 4 (10:05)
`yazilimci` → `gece/0922-urap` dalına URAP telafisi (dün silinen devlet/vakıf bilgisini özet cümleye geri koyma). Orkestratör kararı: bu yeni bir tasarım değil, **dün gece bizim ürettiğimiz bir bilgi kaybının telafisi** — Bilal'in 1 numaralı sorusunun (c) seçeneği. Ayrı commit, Bilal isterse almayabilir.
`trBaslikDuzeni` yasaklandı: 6 `unitur` değerinin 4'ünde bozuk metin üretiyor; dün eklenen `UNITUR_ETIKET` haritası kullanılacak.
**Uygulanmayanlar (bilerek):** R7 — CEO bu turda backlog'a havale etti, bütçe kararı Bilal'in. `tercih-farki.ts` düzeltmesi — K5 kararına bağlı, karar gelmedi.
### yazilimci ✔ (10:10) → `06-urap-telafisi.md` · `gece/0922-urap` @ 4c756c4
Özet paragrafın ilk virgülüne tür öbeği girdi: *"Boğaziçi Üniversitesi (İstanbul), **devlet üniversitesi**; 2026 YKS yerleştirme sonuçlarına göre …"*. Yeni kutu/bileşen/rozet yok, ızgara 4 kart.
`UNITUR_ETIKET` `{ kart, cumle }` biçimine genişletildi — kart etiketi ile cümle öbeği tek kaynakta, ayrışamaz. `trBaslikDuzeni` kullanılmadı (yasaktı).
Altı değerin beşi üretilen HTML'de doğrulandı; `YURTDISI VAKIF` sayfada hiç çıkmıyor (Manas/Yesevi'nin aynı slug'ında `YURTDISI KAMU` satırları da var, `UniOzet.unitur` KAMU'yu seçiyor) → koddan çıkarım olarak işaretlendi.
Çift gösterim (URAP'sız sayfada hem "Tür" kartı hem cümle) tuhaf durmuyor, kaldırma önerilmedi.
Entegrasyon tazelendi, `pnpm build` temiz — 1821 statik sayfa. Merged dağılım 129 devlet / 75 vakıf / 16 KKTC / 6 yurt dışı kamu / 3 vakıf MYO = 229.
## Backlog'a gidecek yeni bulgular (bu vardiyadan, henüz yazılmadı)
1. `slug.ts` `trBaslikDuzeni` tireden sonraki harfi büyütmüyor: "Bakü-**a**zerbaycan", "Kırgızistan-**t**ürkiye", "Türk-**k**azak" — ~10 yurt dışı/KKTC sayfası. Eski hata, bu vardiyadan değil.
2. BACKLOG #2 ve #3 `merge` olarak kapatılmalı (yazılar `e21cda4` ile `main`'de); #3'teki "kalan 7 yazı" sayısı yanlış — doğrusu `04-icerik-envanteri.md`'de.
3. `ikinci-universite-acikogretim` konusu düştü; `universitede-ilk-hafta` penceresi kaçtı (Bilal kararı).
4. `kac-siralama-ile-hangi-bolume-girebilirim.md` başlığı hâlâ "(2025 Verileriyle)" — `rehber-editoru` işi.
5. R7 kontrast: tek commit 28 satır (tasarımcı ölçtü) — bütçe kararı Bilal'in.
6. K5 onaylanırsa `tercih-farki.ts` düzeltmesi zorunlu (aksi hâlde kalıcı yanlış uyarı).
### rehber-yazari ✔ (10:20) → `05-yazi-2027-takvimi.md` · `gece/0923-rehber-2027` @ 82dffc7
`content/rehber/yks-2027-takvimi.md` (146 satır). Başlık: "2027 YKS Ne Zaman? Başvuru ve Sınav Takvimi (Henüz Açıklanmadı)".
**Disiplin notu:** Aritmetikle çıkan ve internette dolaşan "19–20 Haziran 2027" tarihini **bilerek yazmadı** — resmî tarihmiş gibi alıntılanmasın diye. 2027 kapsam/Maarif Modeli iddiasını da doğrulayamadığı için yazmadı.
2023–2026'nın tamamı ÖSYM duyurularından tek tek teyit edildi (22 kaynak URL'si yazının sonunda). Tahminler tek bölümde toplandı ("kasım", "haziranın ikinci yarısı" bantları; gün/tarih yok) → ÖSYM duyurduğu gün tek bölüm silinip tabloya sütun eklenerek ~15 dakikada güncellenir.
İkinci tablo aslında asıl değerli kısım: aşamalar arası gün sayıları (sınav→sonuç ~1 ay, sonuç→tercih 1–2 hafta, tercih kapanışı→yerleştirme 1–2 hafta).
### Orkestratör entegrasyon doğrulaması (10:25)
`gece/0923-rehber-2027` → `gece/0922-entegrasyon` @ 8c0fb61, çakışmasız. `pnpm build` **Compiled successfully**; rehber sayfası 33→34 html, `yks-2027-takvimi.html` prerender edildi.
Üretilen HTML'de **uydurma 2027 tarihi taraması temiz** (`(1[0-9]|2[0-9]) (Haziran|Temmuz|Ağustos|Şubat|Mart) 2027` → 0 eşleşme).
`BETTER_AUTH_SECRET` uyarıları worktree'de env dosyası olmamasından — dünkü vardiyada da vardı, kodla ilgisi yok.
## ⛔ Tur 5 (10:45) — DURDURULDU: yerel `main` 16 commit geride
Bilal "push ve MR yapma, önce özet sun" dedi. Doğrulama sırasında **gecenin en büyük sistem hatası** çıktı:
**Yerel `main` 9bd448b, `origin/main` c5769e2 — arada 16 commit var.** Bütün gece vardiyası bayat bir temel üzerinde çalıştı.
Uzakta olup bizde olmayanlar arasında bu geceki işlerle doğrudan çakışanlar:
| Uzaktaki commit | Etkisi |
|---|---|
| `2b350d0` content(rehber): gece vardiyasının **14 yeni yazısı** | İçerik envanteri (8 yazıldı/9 açık) **geçersiz** — uzakta 46 yazı var, yerelde 32 |
| `4acfd27` fix(kredi): **paketliden liste ve revizyon kredisi düşmez** | "Ücretsiz liste" analizinin ekonomisi değişti (aşağıda) |
| `daa232c` fix(sonuc): boş harita ilk ekranı işgal etmez | CEO'nun dün **reddettiği K2** zaten yapılmış |
| `552d5ce` / `e912665` tadımlık ve harita sırası | Analitiğin `/sonuc` sayfa sırası önerisi zaten yapılmış |
| `2373a9a` content(tadimlik): "24 satırın tamamı" iddiası kalkar | Maskeleme tartışmasının bir parçası zaten kapanmış |
**Somut zarar:** `yks-2027-takvimi` yazısı bu sabah ikinci kez yazdırıldı — uzakta 22 Eylül tarihli 151 satırlık sürümü zaten vardı. İki yazar ajanı (OBP SSS + rehber bakımı) 32 yazılık bayat ağaçta çalışırken **durduruldu**; bakım turunun ölü link/yıl taraması 14 yazıyı hiç görmediği için sonucu geçersiz olurdu. `gece/0923-*` dalları silindi.
**Kök neden:** Vardiya `git fetch` yapmadan yerel `main`'i gerçek sanıp oradan dal açtı. Sabah backlog bayatlığını yakaladım ama teşhisi eksik koydum — "backlog bayat" dedim, asıl sorunun "çalışma ağacı bayat" olduğunu görmedim.
**Kural:** Vardiya açılışında **ilk komut `git fetch`**; dallar `origin/main`'den açılır, yerel `main`'den değil.
### İyi haber — dört kod/veri paketi ayakta
`origin/main` üzerinde deneme merge'i: `0922-risk-etiketi`, `0922-olcum`, `0922-unitur-kacagi`, `0922-urap` → **dördü de çakışmasız**. Yalnız silinen yazı dalı çakıştı.
Ayrıca dördü de hâlâ **gerekli** (origin/main'de kontrol edildi): `program-tablosu.tsx`'te `RISK_ETIKET` yok, yalnız `RISK_NOKTA` var · `rapor-listesi.tsx:259,267` `sr-only sm:not-sr-only` duruyor · kaynaksız `/paket` linki **10** · `sihirbaz-adimlar.tsx:205` hâlâ yalnız ileri geçişte atıyor · `universite-icerik.tsx:316-319` "Tür" kartı `trBaslikDuzeni` ile duruyor.
### "Ücretsiz liste" kararı şekil değiştirdi
`origin/main` `rapor-actions.ts:158`: `const maliyet = user.hasPaket ? 0 : RAPOR_KREDI;`
Yani **paket sahibi artık kredi ödemiyor; kredi yalnız paketsiz denemeciden alınıyor.** Dünkü analizin "60 kredi = 20 üretim" ekonomisi geçersiz. Soru artık "kredi indirilsin mi" değil: **kredi bugün yalnızca ürünü denemek isteyen kişiyi durduruyor, para ödemiş hiç kimseyi durdurmuyor.**

View File

@@ -0,0 +1,77 @@
# 01 · Arşiv tablosundaki yanlış `unitur` etiketi kapatıldı (BACKLOG #21)
Dal: `gece/0922-unitur-kacagi` (dünkü dalın devamı) · Kaynak: `2026-09-22/09-p2-unitur.md` §5-A, `BACKLOG.md` #21
Commit'ler: `9722689` kod · `c5fd2cb` veri
## Özet (5 madde)
1. **`programs_arsiv`'de 85 satır düzeldi**: 61 `unitur` (`DEVLET` → `YURTDISI KAMU`; 31 ULUSLARARASI BALKAN + 30 ULUSLARARASI SARAYBOSNA) ve 24 `il` (Balkan'da `SARAYBOSNA - BOSNA - HERSEK` → `ÜSKÜP-MAKEDONYA`). Arşiv ile `programs` arasında `unitur` farkı olan kurum: **2 → 0**.
2. **Puan/sıra/kontenjan/yerleşen kolonlarında değişen satır: 0.** Her iki tablonun 45 kolonu id üzerinden tek tek karşılaştırıldı; `netler` ve `veri_meta` de dokunulmadı. Fark raporu §2'de ve veri commit'inin gövdesinde.
3. **Denetim de genişledi:** `veri-kalite` kontrol 13 ve 14 artık arşivi de tarıyor. Genişletme olmadan iki tablo sessizce ayrışabilirdi — kusur duran DB'de ikisi de `KALDI` verdi (çıkış kodu 1), düzeltmeden sonra `GEÇTİ`. Son durum: **0 ZORUNLU kontrol kaldı, 3 uyarı** (üçü de dünküyle birebir aynı, hiçbiri bu işten doğmadı).
4. **Dünkü yanlış rakam kayda geçti.** `cto` denetimi (`12-kod-denetimi.md` O1) haklı: `1df86cf` gövdesindeki "hub'da Devlet listesi 170 → 166" yanlıştı; 170/166 ham `programs.universite` ad sayısıdır. Doğru hub değişimi **132 → 129 kurum**. Yeni kod commit'inin gövdesinde düzeltme yazılı; entegrasyon build'inin ürettiği HTML de 129 gösteriyor (`.next/server/app/universiteler.html`).
5. **Betik idempotent**, entegrasyon dalı tazelendi: `gece/0922-entegrasyon`'a merge edildi (çakışma yok, `scripts/veri-kalite.ts` auto-merge), `pnpm build` temiz. `tsc` + `eslint` temiz.
## 1. Ne değişti
| Dosya | Değişiklik |
|---|---|
| `scripts/unitur-onar.ts` | Aşama D (`KURUM_DUZELT`) artık `programs` **ve** `programs_arsiv` üzerinde koşuyor (tablo varsa). A/B/C aşamaları `programs`'ta kaldı — arşivde NULL `unitur` yok. Kapanış dağılımı iki tablo için basılıyor. |
| `scripts/veri-kalite.ts` | Kontrol 13 (baz ad başına tek `unitur`) ve 14 (DEVLET etiketi Türkiye ili ile destekli) kaynak ilişkisi `programs` → `programs` ∪ `programs_arsiv`. Tek yeni sabit (`KURUMLAR`), kontrol gövdeleri aynı. |
| `data/yokatlas.db` | 85 satır, ayrı commit. |
Neden arşivde vardı: kapanan programlar `programs`'tan arşive olduğu gibi taşınıyor, yanlış etiket de beraberinde gitmiş. Dün Aşama D yalnız `programs`'a uygulandığı için arşiv geride kaldı.
Kapsam dışı bırakılan (bilerek): Saraybosna Ü.'nin `il` değeri (**kurum gerçekten Saraybosna'da**), arşivdeki 28 boş `il` (eksik, eksik olarak görünmeli), ad normalizasyonu (arşivde tire-boşluklu duplikat ad yok — sorguyla doğrulandı). Kıbrıs Amerikan'ın arşivde hiç satırı yok.
## 2. Fark raporu (öncesi/sonrası)
Düzeltme öncesi DB `sqlite3 -readonly … .backup` ile scratchpad'e alındı; `ATTACH` ile **her tablonun her kolonu** id üzerinden karşılaştırıldı.
```
programs: eski 21493 → yeni 21493 | 45 kolon | kayıp/yeni id 0 | DEĞİŞEN: yok
programs_arsiv: eski 3735 → yeni 3735 | 45 kolon | kayıp/yeni id 0 | DEĞİŞEN: unitur=61, il=24
netler: eski 62975 → yeni 62975
veri_meta: eski 37 → yeni 37
her iki tabloda 34 yıl/ölçü kolonu (sıra/puan/kontenjan/yerleşen + 2025 ek
kontenjanları, 2021-2026) Σ sağlaması: BİREBİR AYNI ✓
```
`unitur` dağılımı (`programs_arsiv`): `DEVLET 1106→1045`, `YURTDISI KAMU 8→69`; toplam 3735 sabit. `programs` dağılımı hiç değişmedi.
Dosya 4 KB (bir sayfa) büyüdü — WAL checkpoint; satır sayıları sabit.
## 3. Kabul kriterleri
| # | Kriter | Sonuç |
|---|---|---|
| 1 | Arşivde üç kurumun `unitur`/`il` değerleri `programs` ile tutarlı | ✅ `programs_arsiv` ↔ `programs` `unitur` farkı olan kurum: **0** (önce 2). Balkan arşivde `YURTDISI KAMU`, ili `ÜSKÜP-MAKEDONYA` (24) / boş (7); Saraybosna `YURTDISI KAMU` |
| 2 | `pnpm veri-kalite --yil 2026 --db data/yokatlas.db` → 0 ZORUNLU | ✅ `Sonuç: 0 ZORUNLU kontrol kaldı, 3 uyarı.` |
| 3 | Puan/sıra/kontenjan kolonlarında hiçbir değişiklik yok, fark raporu commit'te | ✅ §2; rapor `c5fd2cb` gövdesinde |
| 4 | `tsc` + `lint` temiz | ✅ `npx next typegen && npx tsc --noEmit` → 0; `npx eslint scripts/unitur-onar.ts scripts/veri-kalite.ts` → 0 |
| 5 | İdempotent | ✅ İkinci koşu: `programs` 0/0, `programs_arsiv` 0/0, A/B/C 0 |
Denetimin gerçekten yakaladığının kanıtı — **kusur duran DB'de** (yalnız arşiv yanlış, `programs` dün düzeltilmişti) genişletilmiş kontroller:
```
KALDI baz ad başına tek unitur — … ULUSLARARASI BALKAN ÜNİVERSİTESİ/YURTDISI KAMU,DEVLET/81 ;
ULUSLARARASI SARAYBOSNA ÜNİVERSİTESİ/YURTDISI KAMU,DEVLET/68
KALDI DEVLET etiketi Türkiye ili ile destekli — … BALKAN/SARAYBOSNA - BOSNA - HERSEK/31 ; SARAYBOSNA/30
Sonuç: 2 ZORUNLU kontrol kaldı (çıkış kodu 1)
```
Kalan 3 uyarı (dünküyle aynı, bu işten doğmadı): `il boş` 110 satır, `kimlik ATLANDI` (`data/ham` repoda yok), `yetim netler` 2066.
## 4. Kullanıcıya yansıyan etki
**Bugün: yok.** `src/` tarafında `programs_arsiv`'i okuyan hiçbir SQL yok (tablo yalnız `src/lib/kapanan-sluglar.ts`'te bir yorumda geçiyor); sihirbaz havuzu ve katalog yalnız `programs`'tan besleniyor. Bu düzeltme **önleyici**: arşiv ileride okunduğunda (kapanan program sayfası, "bu bölüm kapandı" bilgisi) aynı sızıntı — devlet filtresine giren Bosna/Makedonya programları — geri gelmesin diye. Entegrasyon build'inin HTML'inde hub sayıları değişmedi: Devlet **129**, Vakıf **78**.
## 5. Entegrasyon
`gece/0922-entegrasyon` dalı `git merge --no-edit gece/0922-unitur-kacagi` ile tazelendi — çakışma yok (`scripts/veri-kalite.ts` auto-merge, dünkü `0e689e6` "Tür" kartı düzeltmesiyle çatışmıyor). `pnpm build` temiz: 259 üniversite sayfası + 493 sayfalama yolu prerender edildi, hata/uyarı yok. Çalışma ağacı temiz.
## Bilal'den istenen
1. **Dünkü rakam düzeltmesini gözden geçir** — `9722689` gövdesinde "170 → 166 yanlıştır, doğrusu 132 → 129" satırı duruyor; merge mesajına taşımak istersen oradan alabilirsin. **~2 dk.**
2. Karar: **§5-B (`data/yokatlas.db-shm` / `-wal` git'ten çıkarılsın mı)** hâlâ açık — dün `.gitignore`'da commit'lenmemiş değişiklikleriniz olduğu için dokunmadım, bugün de dokunmadım. **~2 dk.**
3. Yok (2. madde dışında bu iş için istenen bir şey kalmadı).

View File

@@ -0,0 +1,374 @@
# 02 — Tasarım: CEO'nun açık soruları (gece vardiyası 2026-09-23)
> Yöntem: yalnız kod + DB okuması. Tarayıcı/dev sunucusu açılmadı, kod değiştirilmedi.
> R7 taraması orkestratörden hazır geldi (48 kullanım / 19 dosya), yeniden koşuldu ve
> bağlam satırları çekildi — sınıflandırma aşağıda. Kontrast değerleri token'lardan
> **hesaplandı** (§2.4). Piksel tahminleri "koddan çıkarım" olarak işaretli.
---
## Özet — 5 madde
1. **Öncelikler kalkarsa sihirbaz 3 adımda kalmalı, 2'ye inmemeli.** Üç adım = üç soru olur;
2'ye indirmek ikinci adıma ikinci bir iş yükler (tam da 3. adımdan çıkardığımız hata) ve
`/meraklisina` şemasını, `sihirbaz_adim` serisini birden bozar. Hüküm §1.2.
2. **`/meraklisina` şemasında değiştirilecek tek karakter yok.** Ne `SEMA`, ne `aria-label`,
ne figcaption, ne 4. bölüm anlatımı öncelikleri anlatıyor — grep ile doğrulandı (§1.3).
AGENTS.md senkron borcu bu işte **sıfır**; değişmesi gereken ekran, belge değil.
3. **Kaldırma "veri kaybı" değil ama bir yerde sessiz regresyon üretir:** `tercih-farki.ts`
`oncelikler`'i kıyaslamaya devam ederse, hiçbir şey değiştirmemiş eski kullanıcı /listem'de
kalıcı "Tercihlerin değişmiş → 3 kredi harca" uyarısı görür. Tek dosya, dört nokta (§1.4).
Ayrıca dünkü raporumun eksiği: öncelikler **yalnız prompt tonuna dokunmuyor**,
`tercih-degerlendirme.ts:59` üzerinden değerlendirme satırlarına da dokunuyor.
4. **R7 hüküm: tek commit, 28 satır.** 48 kullanımın 26'sı gerçek metin, 2'si placeholder
(= 28 düzeltilecek), 11'i dekoratif ikon, 4'ü fonksiyonel ikon (ayrı kural, 1.4.11),
1'i devre dışı durum (WCAG muaf), 4'ü dev paneli (kapsam dışı). **Büyük punto: 0** —
hiçbiri 18,66 px kalın / 24 px eşiğini geçmiyor, yani 28'inin de 4,5:1 borcu var.
5. **URAP takasının telafisi tek cümlelik ve sıfır yeni eleman:** özet paragrafın ilk
virgülüne "devlet üniversitesi" / "vakıf üniversitesi" girer. Yan kazanç: bugünkü "Tür"
kartı zaten 6 `unitur` değerinin 4'ünde bozuk metin basıyor ("Kktc", "Vakıf Myo",
"Yurtdısı Kamu" — `trBaslikDuzeni` tr-TR küçültmesi, `slug.ts:56-64`).
---
## 1. "Öncelikler" kalkarsa — son adımın tasarımı
Kaynak: `sihirbaz-adimlar.tsx:432-492` (3. adım paneli), `:224-231` (adım göstergesi),
`:495-540` (alt çubuk), `sihirbaz-sabitler.ts:10-18` (`ONCELIKLER`).
### 1.1 (a) Tek ekran taslağı — 375 px
Bugün 3. adımda: h3 "Üniversite tipi & öncelikler" + alt satır + "ÜNİVERSİTE TİPİ" etiketi
+ 3-4 çip + "SENİN İÇİN ÖNEMLİ OLAN" etiketi + 7 çip (375 px'te ~4 satıra sarıyor)
+ 2 satırlık açıklama. Kalkınca:
```
375 px · DialogContent (max-h-[92dvh], p-6, dikey ortalı) · sihirbaz-modal.tsx:41
┌───────────────────────────────────────────┐
│ (✕) │ ← kanonik dialog çarpısı, size-9,
│ │ border-slate-200 (dialog.tsx varsayılanı)
│ ▬▬ ▬▬ ▬▬▬▬▬ Adım 3/3 │ ← :224-231 aynen kalır
│ │ metin text-slate-500 (bugün 400, bkz. §2)
│ │
│ Devlet mi, vakıf mı? │ ← h3, font-heading text-lg font-bold
│ Seçimin hem yapay zekâ listende hem │ ← p, text-sm text-slate-500
│ sonuç sayfasındaki ücretsiz tabloda │ (bu cümle doğru: meraklisina-icerik:116-117)
│ geçerli olur. │
│ │
│ ┌─────────────┐ ┌───────────────────────┐│ ← mevcut <Cip>, canliFacetler sayılarıyla
│ │ Devlet 412 │ │ Vakıf (burslu olursa) ││ yeni bileşen yok
│ └─────────────┘ │ 288 ││
│ └───────────────────────┘│
│ ┌──────────────┐ │
│ │ Fark etmez │ │
│ └──────────────┘ │
│ │
│ ·· min-h tabanı: boş alan (bkz. 1.1-c) ··│
│ │
│───────────────────────────────────────────│ ← border-t border-slate-100 pt-4 (mevcut)
│ ← Geri Listemi oluştur → │ ← CTA deseni doğru: metin + ArrowRight size-4
└───────────────────────────────────────────┘
```
Üç karar, gerekçesiyle:
- **"ÜNİVERSİTE TİPİ" üst etiketi (`:441-444`) silinir.** h3 artık aynı soruyu soruyor;
etiket kalırsa aynı soru ekranda iki kez yazar. (Adım 1-2'deki etiketler yerinde kalır —
oralarda h3 ile etiket farklı şeyler söylüyor.)
- **h3 soru cümlesine döner:** "Üniversite tipi & öncelikler" → **"Devlet mi, vakıf mı?"**
Adım 1 ve 2 de tek soru sorduğu için sihirbaz üç adımda üç soru olur; CEO §5-1'in (a)
şıkkının ekran karşılığı bu.
- **Modal yüksekliği — tek gerçek risk ve tek satırlık çözümü.** `TransitionPanel` panel
yüksekliğini ölçüp animasyonla geçiriyor (`:207-211`, `:246-259`); 3. adım bugün en uzun
panellerden biri, kalkınca en kısası olur (koddan çıkarım: ~%50 kısalma). Dialog dikey
ortalı olduğu için modal **hem üstten hem alttan** içeri gelir: kullanıcının "Devam"a
bastığı nokta, geçişten sonra **"Listemi oluştur"un yeni yeri**ne yakın düşer. O buton
kredi harcayan butondur; yanlış basma bedeli para. **Çözüm:** yalnız 3. adım paneline
`min-h-[Npx]` ver, N = 1. adım panelinin 375 px'teki ölçülen yüksekliği (yazılımcı
ölçer; ben ölçmedim, tarayıcı açmam yasak). Yeni bileşen yok, yeni animasyon yok,
`prefers-reduced-motion` dalı (`hareketiAzalt`, `:249`) zaten var ve korunur. Büyüklük: XS.
**Aynı commit'e sığan bedava düzeltme:** bu dosyada zaten 5 `text-slate-400` metin var
(`:228, 381, 406, 442, 473`); `:473` ve `:442` bu kesimle ölüyor, kalan üçü (`228, 381, 406`)
`text-slate-500`'e çekilsin — R7'nin en görünür üç satırı, ek dosya açmadan kapanır.
### 1.2 (b) Hüküm: 3 adım kalır, 2'ye inmez
| Kriter | 3 adım (öneri) | 2 adım |
|---|---|---|
| Ekranın tek işi | ✅ her adım tek soru | ❌ 2. adım "il **ve** tür" — 3. adımdan çıkardığımız hatayı 2'ye taşır |
| `/meraklisina` şeması | ✅ `SEMA` zaten "3. adım · Devlet / Vakıf" diyor, senkron | ❌ şema + `aria-label` + figcaption + 4. bölüm anlatımı baştan yazılır |
| Ölçüm | ✅ `sihirbaz_adim{1,2,3}` serisi kırılmaz | ❌ P4'ten hemen sonra ikinci bir seri kırılması |
| 375 px | ✅ tür çipleri kendi ekranında | ❌ il araması (`max-h-56` kaydırmalı liste, `:333`) + seçilenler + tür çipleri tek panelde; iç kaydırma ile çip satırı üst üste biner |
| Algılanan yük | 3 adım × 1 soru = sihirbazın en sade hâli | 2 adım ama biri iki soruluk — kısalma hissi yok |
**Hüküm: 3 adım.** Tek karşı argüman "adım sayısı azalsın" hissi; onu zaten öncelikleri
silerek alıyoruz (ekranda 7 çip + 2 açıklama satırı eksiliyor), adım birleştirmeye gerek yok.
### 1.3 (c) `/meraklisina` mermaid şeması — **gereken düzeltme: yok**
AGENTS.md şema senkronunu zorunlu tutuyor, o yüzden borcu tek tek doğruladım:
```
grep -rni "öncelik\|oncelik" src/features/pazarlama/ src/app/meraklisina/ → 0 satır
```
- `huni-semasi.tsx` `SEMA`: `C("3. adım · Devlet / Vakıf<br/>sayılar hem alanı hem ili
hesaba katar")` — öncelik geçmiyor, adım sayısı-sırası değişmiyor, havuz kuralı
değişmiyor, 24'lük iskelet (5/13/6) değişmiyor, gevşetme sırası değişmiyor,
havuz-dışına-çıkamama değişmiyor. **Düzenlenecek düğüm yok.**
- `aria-label` (`huni-semasi.tsx:44`): "…alan, il ve devlet/vakıf adımlarıyla…" — doğru kalıyor.
- `figcaption`: öncelik geçmiyor.
- `meraklisina-icerik.tsx:107-127` (4. bölüm): "üçüncü adımdaki devlet/vakıf sayıları da
hem alan hem il seçimini hesaba katar" — doğru kalıyor.
**Yani bu kesim, belgeyi ekrana değil, ekranı belgeye uyduruyor** — CEO §2 doğrulama #9 ile
aynı sonuç. Commit mesajına tek satır yeter:
`/meraklisina şeması değişmedi: 3. adım zaten yalnız devlet/vakıf olarak belgeliydi (huni-semasi.tsx SEMA düğümü C).`
> Tek isteğe bağlı rötuş (zorunlu değil): 3. adımın ekrandaki h3'ü "Devlet mi, vakıf mı?"
> olunca şemadaki "3. adım · Devlet / Vakıf" etiketiyle kelimesi kelimesine aynı dili
> konuşur. Şemaya dokunulmaz.
### 1.4 (d) Kaldırılan alan veride ve kayıtlı raporlarda ne olacak
Kural: **hiçbir şey silinmez, yalnız bir kıyas durdurulur.** Alan alan:
| # | Yer | Karar | Neden |
|---|---|---|---|
| 1 | `types/rapor.ts:71` `oncelikler?: string[]` | **Kalır** (zaten opsiyonel) | Kayıtlı raporlar `params` JSON'ı olarak duruyor (`rapor-actions.ts:91`); tip daraltılırsa eski kayıtlar tipten düşer. Göç yok, şema değişikliği yok. |
| 2 | `sihirbaz-sabitler.ts:10-18` `ONCELIKLER` sabiti | **Silinmez** | `sihirbazDogrula` (`:108-124`) eski localStorage profillerindeki değerleri bu listeye göre süzüyor; sabit silinirse eski profillerin seçimi sessizce düşer. Sabit kalır, yalnız ekranda render edilmez. |
| 3 | `sihirbaz-sabitler.ts:132` `oncelikNormalize` | **Kalır** | Eski "İş garantisi" adını eşleyen geriye uyum köprüsü; eski raporlar gösterilirken hâlâ gerekli. |
| 4 | **`tercih-farki.ts` — tek zorunlu değişiklik** | `"oncelikler"` kıyastan **çıkar** | Çıkmazsa: eski raporun `params.oncelikler = ["Prestijli üniversite"]`, yeni profilin `s.oncelikler = []` → `kumeFarkli` **true** → /listem başlığında kalıcı "Tercihlerin değişmiş" rozeti ve 3 kredilik güncelleme çağrısı, kullanıcı **hiçbir şey değiştirmediği hâlde**. Dosyanın kendi ilkesine aykırı (`:8-12`: "olmayan alan değişmiş sayılmaz"). Dokunulacak dört nokta: `FarkAlani` union (`:22-28`), `FARK_ETIKET` (`:30-37`), `tercihFarklari` içindeki `if (kumeFarkli(params.oncelikler?…))` (`:73-75`), `farkSatirlari` içindeki `case "oncelikler"` (`:112-114`). |
| 5 | `listem-govde.tsx:129-131` "Önceliklerin: …" | **Kalır** | Koşullu (`params.oncelikler?.length`); eski raporda dolu görünür — danışmanın neyi bildiğini dürüst gösterir. Yeni raporda hiç çıkmaz. Sıfır iş. |
| 6 | `sihirbaz-sabitler.ts:182-184` prompt satırı | **Kalır** | Aynı koşul. Eski bir rapor yeniden üretilirse kullanıcının kaydettiği tercih korunur. |
| 7 | `app/api/soru/route.ts:32,77` · `lib/ai/rapor.ts:251,279` | **Değişmez** | Zaten `?? []` ile null-safe. |
| 8 | `tercih-degerlendirme.ts:59-66` `"Kolay yerleşme"` dalı | **Kalır (ölü değil, geriye uyumlu)** | **Dünkü raporumun eksiği:** öncelikler yalnız gerekçe tonuna değil, tercih değerlendirmesindeki olumlu/çelişki satırlarına da dokunuyor ("Daha güvenli seçenek önceliğini destekliyor"). Yeni kullanıcıda hiç çalışmaz, eski profilde çalışmaya devam eder. Kayıp: güvenli dilim programlarında bir olumlu satırı — kabul edilebilir, çünkü bu satır zaten yalnız 7 çipten birini seçmiş azınlıkta çıkıyordu. |
| 9 | `sihirbaz_tamamlandi` olayındaki `oncelik_sayisi` (`sihirbaz-adimlar.tsx:524`) | **Olayda kalır, hep 0 olur** | Alanı kaldırmak Rybbit'te geriye dönük okumayı bozar. **Commit mesajına seri kırılması notu** (P4'teki kalıp): *"bu tarihten sonra `oncelik_sayisi` her zaman 0'dır."* |
**Özetle geri alınabilirlik:** karar dönerse yalnız 3. adıma çip bloğu ve `tercih-farki.ts`'e
dört nokta geri konur; veride, DB'de, kayıtlı raporlarda hiçbir şey kaybolmamış olur.
CEO §5-1'deki "ucuz ve geri alınabilir" hükmü kod tarafından doğrulanıyor.
---
## 2. R7 — `text-slate-400` sınıflandırması (48 kullanım / 19 dosya)
### 2.1 Sayılar
| Sınıf | Adet | Hüküm |
|---|---|---|
| **Gerçek metin** (WCAG 1.4.3, 4,5:1 borcu) | **26** | Düzeltilir |
| **Placeholder metni** (1.4.3 kapsamında) | **2** | Düzeltilir |
| **Dekoratif ikon / `aria-hidden` glif** | **11** | Sorun değil, dokunulmaz |
| **Fonksiyonel ikon** (WCAG 1.4.11, 3:1 borcu) | **4** | Ayrı kural, ucuz — aynı commit'e katlanabilir |
| **Devre dışı durum** (`disabled:`) | **1** | WCAG 1.4.3 muaf, dokunulmaz |
| **Dev paneli** (kullanıcı görmez) | **4** | Kapsam dışı |
| | **48** | |
**Büyük punto: 0.** Eşik 18,66 px kalın ya da 24 px normal. En büyük aday
`katalog-arama.tsx:419` placeholder'ı (`text-base sm:text-lg` = 16/18 px, kalın değil) —
eşiğin altında. Yani 28'inin de borcu 4,5:1, hiçbirine 3:1 indirimi uygulanmıyor.
### 2.2 Gerçek metin — 26 satır (düzeltilecek)
`liste-uretici.tsx:429` (üretim sırasında "sayfayı kapatma" uyarısı) ·
`sihirbaz-adimlar.tsx:228` ("Adım 1/3"), `:381`, `:406` ("Seçtiklerin:"), `:442`, `:473` ·
`universite-listesi.tsx:26` (il + program sayısı), `:65`, `:75`, `:86` (başlık sayaçları) ·
`giris-form.tsx:159` ("veya" ayracı) ·
`katalog-arama.tsx:567` ("Keşfet"), `:703` (bölüm başlığı `h3`), `:742` (sağdaki detay, yalnız `sm+`) ·
`bolum-listesi.tsx:21`, `:66` · `program-netleri.tsx:176` (" / {max}" paydası) ·
`bolum-program-listesi.tsx:235` ("· 4 yıl") ·
`liste-cekmecesi.tsx:131` ("3/24" sayacı), `:231` (kesikli "ekle" butonunun etiketi) ·
`program-tablosu.tsx:217` (dilim toplamı), `:278` ("20 / 1.340 program"), `:349` ("· 4 yıl") ·
`tercih-profili-kapisi.tsx:369` (**`<label htmlFor>` — form etiketi**) ·
`secimlerim-paneli.tsx:108` ("3/24") · `tercih-haritasi.tsx:525` ("Kilitli tercih" durum metni)
Placeholder (2): `sihirbaz-adimlar.tsx:312` · `katalog-arama.tsx:419`
### 2.3 Dokunulmayanlar
- **Dekoratif ikon (11):** `liste-uretici:268` · `rapor-listesi:258` · `cta-sira-form:233` ·
`sihirbaz-adimlar:304` · `sayfalama:136` (`aria-hidden` "…") · `katalog-arama:408`, `:600` ·
`kaydet-banner:75` · `liste-cekmecesi:253` · `tercih-profili-kapisi:376` ·
`program-liste-verileri:52` ("—" veri yok glifi). Bunları koyulaştırmak metin/ikon
hiyerarşisini bozar, kazanç sıfır.
- **Fonksiyonel ikon (4):** `sihirbaz-adimlar:319` (aramayı temizle) · `kaydet-banner:102`
(banner'ı kapat) · `liste-cekmecesi:141` (sil), `:147` (`DrawerClose`). Bunlar 1.4.3
değil **1.4.11** (3:1) kapsamında ve 2,56:1 ile onu da geçmiyorlar — ama bunlar zaten
dünkü R10'da (dokunma hedefi < 44 px) ellenecek butonlar; ikisini **aynı** commit'te
yapmak mantıklı.
- **Devre dışı (1):** `giris-form.tsx:127` `disabled:text-slate-400` — WCAG 1.4.3 devre dışı
bileşenleri açıkça muaf tutuyor. Değiştirilirse "tıklanabilir" hissi verir, zarar.
- **Dev paneli (4):** `dev-panel.tsx:185, 232, 239, 320` — kullanıcı görmez.
### 2.4 Hesap ve hedef token
Kaynak: `globals.css`/Tailwind slate ölçeği, WCAG 2.x göreli parlaklık formülü.
| Renk çifti | Oran | Sonuç |
|---|---|---|
| `slate-400` (#94A3B8) / beyaz | **2,56:1** | ❌ |
| `slate-400` / `slate-50` (#F8FAFC) | **2,45:1** | ❌ |
| `slate-500` (#64748B) / beyaz | **4,76:1** | ✅ |
| `slate-500` / `slate-50` | **4,55:1** | ✅ (payı dar) |
| `slate-500` / `slate-100` (#F1F5F9) | **4,34:1** | ❌ — bu zeminde `slate-600` (6,92:1) gerekir |
| `slate-600` (#475569) / beyaz | **7,58:1** | ✅ |
**Kural:** hedef `text-slate-500`; **yalnız `bg-slate-100` üstünde duran satırlarda**
`text-slate-600`. Yeni token icat edilmiyor, palet değişmiyor (palet değişikliği `[BİLAL]`).
### 2.5 Hüküm
**Tek commit, 28 satır** (26 metin + 2 placeholder) → `text-slate-500`. Fonksiyonel 4 ikon
isteğe bağlı olarak aynı commit'e (R10 ile birlikte daha mantıklı). **Kör `sed` değil:**
aynı dosyalarda dokunulmaması gereken 11 dekoratif ikon var, bu yüzden 28 hedefli satır
düzenlemesi. Yeni bileşen yok, yeni sabit yok, düzen değişmiyor, `tsc`/`lint` riski yok.
Büyüklük: **S–M (~45-60 dk)**, tek dosyaya değil 15 dosyaya dokunduğu için inceleme yükü
küçük ama geniş → **ayrı ve tek amaçlı dal** şart.
**Bütçe sıkışırsa düzeltilecek 9 satır** (sıralı, en yüksek zarardan):
1. `tercih-profili-kapisi.tsx:369` — **form etiketi**; funnel'ın giriş kapısındaki alanın adı.
2. `sihirbaz-adimlar.tsx:228` — "Adım 1/3"; sihirbazdaki tek yazılı ilerleme göstergesi.
3. `sihirbaz-adimlar.tsx:381` ve `:406` — adım 1-2 alan etiketleri (`:442`/`:473` K5 ile ölüyor).
4. `liste-uretici.tsx:429` — "sayfayı kapatma" uyarısı; okunmazsa kullanıcı üretimi yarıda keser, **kredi yanar**.
5. `program-tablosu.tsx:278` — "20 / 1.340 program"; /sonuc'ta "daha var mı?" sorusunun tek cevabı.
6. `liste-cekmecesi.tsx:131` + `secimlerim-paneli.tsx:108` — "3/24" sayacı; manuel listenin tek geri bildirimi.
7. `tercih-haritasi.tsx:525` — "Kilitli tercih" durum metni.
8. `universite-listesi.tsx:26` — en kalabalık hub'daki tek satır üstü bilgi (il + program sayısı).
---
## 3. URAP takasının telafisi
### 3.1 Dün neyi yanlış yaptım — tek cümle
**Bir sayfada bilginin "zaten var" olduğunu, o sayfada o bilgiyi hiç aratmadan, farklı bir
sayfa tipindeki (karışık üniversiteli `/sonuc` ve `/bolum` listelerindeki Devlet/Vakıf
rozetlerinden) genellemeyle varsaydım** — oysa tek üniversitelik detay sayfasında rozet
tekrar olacağı için hiç yok. Doğrulama tek komutmuş:
```
grep -n "unitur" src/features/katalog/components/universite-icerik.tsx
→ 316-319 (yalnız "Tür" kartı)
grep -n "unitur\|Devlet\|Vakıf" src/features/katalog/components/universite-program-tablosu.tsx
→ 0 satır
```
Kural: "X bilgisi başka yerde de var" gerekçesi, **o dosyada grep'lenmeden** kullanılamaz.
### 3.2 Yan bulgu: takas edilen kart zaten bozuktu
`programs` tablosunda 6 `unitur` değeri var: `DEVLET` (12.142), `VAKIF` (7.974),
`KKTC` (1.145), `VAKIF MYO` (121), `YURTDISI KAMU` (99), `YURTDISI VAKIF` (12).
Kart `trBaslikDuzeni(uni.unitur)` kullanıyor (`universite-icerik.tsx:318`) ve
`slug.ts:56-64` tr-TR küçültmesi yapıyor → ekranda **"Kktc", "Vakıf Myo", "Yurtdısı Kamu",
"Yurtdısı Vakıf"** yazıyor. Yani 6 değerin 4'ünde bugünkü kart zaten hatalı metin basıyor.
Telafi metni **açık bir etiket haritasıyla** yazılmalı, `trBaslikDuzeni` ile değil.
### 3.3 İki alternatif
**Alternatif A — özet paragrafın ilk cümlesine tek sözcük öbeği (ÖNERİLEN)**
`universite-icerik.tsx:262-266`
```
390 px · /universite/[slug] · ilk ekran
Ana Sayfa › Üniversiteler › Boğaziçi Üniversitesi
[logo] Boğaziçi Üniversitesi Taban
Puanları 2026
Boğaziçi Üniversitesi (İstanbul), devlet üniversitesi; ← TEK EKLENEN ÖBEK
2026 YKS yerleştirme sonuçlarına göre 312 programda (mevcut <p> içinde)
öğrenci aldı; programlar 24 fakülte/yüksekokula
dağılıyor. En iyi taban başarı sıralaması 1.240. …
[ Sıralaman Boğaziçi tabanlarının neresinde? → ] ← ustSiraSatiri (mevcut)
┌──────────────────┐┌──────────────────┐
│ PROGRAM ││ FAKÜLTE / BİRİM │
│ 312 ││ 24 │
└──────────────────┘└──────────────────┘
┌──────────────────┐┌──────────────────┐
│ URAP TÜRKİYE ││ EN İYİ SIRALAMA │ ← ızgara 4 kart, CEO §3-P3 aynen
│ 18 / 198 ││ 1.240 │
│ 2025-2026 ││ │
└──────────────────┘└──────────────────┘
```
Metin (tam): `"{uni.ad}{il ? ` (${il})` : ""}, {TUR_CUMLESI[uni.unitur]}; {SON_YIL} YKS …"`
| `unitur` | Cümledeki öbek |
|---|---|
| `DEVLET` | devlet üniversitesi |
| `VAKIF` | vakıf üniversitesi |
| `VAKIF MYO` | vakıf meslek yüksekokulu |
| `KKTC` | KKTC üniversitesi |
| `YURTDISI KAMU` | yurt dışında bir kamu üniversitesi |
| `YURTDISI VAKIF` | yurt dışında bir vakıf üniversitesi |
| `null` | öbek **hiç yazılmaz** (virgül de yazılmaz — "—" yok, boşluk yok) |
Neden A:
- **Sıfır yeni eleman, sıfır düzen değişikliği, sıfır CLS, sıfır mobil taşma riski.**
Izgara 4 kart kalır; P3'ün kabul kriteri (1) hiç zorlanmaz.
- **En çok okunan konumda:** cümlenin ilk 6 kelimesi, ad ve ilin hemen yanında. Dün
paragrafı "taranabilir değil" diye reddettiğim şey bir **sayıydı** (18/198 — okuyucunun
kafasında ölçeklemesi gereken, gözle aranan bir değer); devlet/vakıf **kategorik tek
sözcük** ve zaten cümlenin öznesine yapışık — aranmıyor, okunuyor.
- **Ekran okuyucu tarafı kazanıyor:** bugün `dt/dd` çiftinde tek başına duran "Tür"
yerine, bilgiyi bağlamıyla birlikte veren bir cümle.
- **Ücret sinyali doğru kullanıcıya gidiyor:** Boğaziçi'nde kimse "devlet mi?" diye sormaz;
asıl muhatap az bilinen vakıf üniversitesinin sayfasına Google'dan inen öğrenci ve o
sayfada ilk okuduğu satır bu cümle.
**Alternatif B — "Program" kartının değerinin altına ikinci satır**
```
┌──────────────────┐┌──────────────────┐
│ PROGRAM ││ FAKÜLTE / BİRİM │
│ 312 ││ 24 │
│ Devlet üniv. ││ │ ← eklenen satır
└──────────────────┘└──────────────────┘
```
Artısı: taranabilir, kart sayısı yine 4. Eksileri: (a) kartın başlığı "PROGRAM", altındaki
satır programla ilgili değil — `dt`/`dd` anlamı bozulur; (b) 390 px'te 2 sütunlu ızgarada
kartlar eşit yükseklik aldığı için **dört kartın hepsi** birer satır uzar, ilk ekrandan
~28 px daha gider; (c) "Vakıf meslek yüksekokulu" 190 px'lik karta sığmaz, kısaltma
uydurmak gerekir. **Reddediliyor.**
### 3.4 Öneri
**A.** Büyüklük **XS** (tek `<p>` + bir etiket haritası sabiti, ~12 satır), P3 dalının
**aynı commit'ine** girmeli — takas ile telafi ayrı commit'lerde giderse arada 198 sayfa
bilgisiz kalır. Kabul kriteri ekle: *"`/universite/[slug]` çıktısında üniversitenin türü
(devlet/vakıf/KKTC/…) tam olarak bir kez, özet paragrafın ilk cümlesinde geçer;
`unitur` NULL olan kurumda hiç geçmez ve cümlede boşluk/kopuk virgül kalmaz."*
Dumandan doğrulaması ucuz: `bogazici-universitesi.html`'de "devlet üniversitesi" = 1,
`koc-universitesi.html`'de "vakıf üniversitesi" = 1.
---
## Bilal'in gözle kontrol etmesi gerekenler (375-390 px)
1. **Sihirbaz, 2. adımdan 3. adıma geçiş** — "Devam"a bastıktan sonra modal ne kadar
kısalıyor, "Listemi oluştur" parmağın kaldığı yere geliyor mu? (§1.1, `min-h` kararı buna bağlı)
2. **Sihirbaz 3. adım bugünkü hâli** — "Senin için önemli olan" çipleri 375 px'te kaç satıra sarıyor?
3. **`/universiteler` hub'ı** — üniversite adının altındaki gri "İstanbul · 312 program"
satırı gün ışığında okunuyor mu? (R7'nin en kalabalık yüzeydeki örneği)
4. **Liste üretimi sırasında** — "Bu işlem yarım dakika kadar sürebilir, sayfayı kapatma."
cümlesi görünüyor mu, yoksa gri mi kayboluyor? (R7 · kredi yakma riski)
5. **`/universite/koc-universitesi`** — bugün "Tür" kartında ne yazıyor; sayfada başka
hiçbir yerde "Vakıf" geçmediğini gözle doğrula (§3.1'in görsel karşılığı).
## Bilal'den istenen (2 madde)
1. **CEO §5 soru 1'i cevapla** (sihirbazın son adımındaki 7 öncelik çipi kalksın mı).
"Evet" ise uygulanacak her şey yukarıda hazır: tek ekran taslağı, 3 adım hükmü,
`/meraklisina` için "şema değişmiyor" kanıtı ve `tercih-farki.ts`'teki tek zorunlu
düzeltme. **~3 dk.**
2. **R7 için bütçe seç:** tam iş (28 satır, tek dal, ~45-60 dk, geniş ama sığ inceleme) mi,
yoksa §2.5'teki **9 satırlık** kısa liste mi. **~2 dk.**

View File

@@ -0,0 +1,150 @@
# Ücretsiz liste — "evet" gelirse ne yapılacak
23 Eylül vardiyası · yapay zekâ mühendisi · **kod değiştirilmedi**, gerçek LLM çağrısı yapılmadı.
Kaynak soru: `docs/gece-vardiyasi/2026-09-22/06-ceo-kararlari.md` §5-2 ve §6. Karar Bilal'in, henüz gelmedi.
## 5 maddelik özet
1. **Bugünkü kilit kötüye kullanımı sınırlamıyor — eşzamanlılığı sınırlıyor.** `src/lib/uretim-kilidi.ts` kullanıcı başına **tek seferde tek üretim** garantisi veriyor (ikinci sekme çifte harcama yapamaz), ama aynı kullanıcı sırayla istediği kadar üretim yapabilir. Kredi kalkınca geriye hiçbir sayı sınırı kalmıyor.
2. **Öneri: paketsiz kullanıcı için son 24 saatte en fazla 5 başarılı üretim.** Bugün tüm zamanlarda 3 üretim var (`01-analitik.md`) — yani 5 eşiği bugünkü kullanıcıların **hiçbirini** vurmuyor; tavanda bile bize 10,5 kuruş/kullanıcı/gün.
3. **Yeni ekran gerekmiyor — tek satır bile gerekmiyor.** İki çağıran da bilinmeyen hata kodunu zaten mevcut hata ekranına düşürüyor (`liste-uretici.tsx:186-189`, `tercih-degisti-modali.tsx:119`) ve sunucudan gelen cümleyi aynen basıyor.
4. **Kredi defteri kalmalı, ama 0 tutarla.** Defter kaydı silinirse `requestId` idempotency'si (`UNIQUE(reason, ref_id)`), iade yolunun "önceki deneme başarısızdı" ayrımı **ve** günlük sayacın sayacağı yüzey birlikte gider. 0 deltalı satır üçünü de ayakta tutar ve göç (migration) gerektirmez.
5. **Revizyon zaten paketliye kapalı ve öyle kalmalı** (`rapor-actions.ts:256`). Paketsiz kullanıcının "listemi değiştir" ihtiyacı revizyondan değil, tercih değiştirip **yeniden üretimden** geçiyor — o yol bu kararla zaten ücretsiz oluyor.
Prompt ve model değişmiyor, havuz kuralı değişmiyor → **eval seti gerekmiyor**, bu iş bir fiyat/kota işi.
---
## 1. Kötüye kullanım eşiği
### `src/lib/uretim-kilidi.ts` bugün ne yapıyor
`report_locks` tablosunda **userId birincil anahtar** (`appdb/schema.ts:175-182`). Akış:
- `uretimKilidiAl(userId, requestId)` satırı INSERT etmeye çalışır; UNIQUE ihlali = "bu kullanıcı için taze bir üretim koşuyor" (`uretim-kilidi.ts:37-43`).
- Kilit **180 sn**den eskiyse (`BAYAT_MS`) koşullu UPDATE ile devralınır — iki eşzamanlı devralmadan yalnız biri `rowsAffected > 0` görür, yani yarış çifte harcamaya dönüşemez (`:48-58`).
- Kilit **krediden önce** alınır (`rapor-actions.ts:132`), `finally` içinde bırakılır (`:143`).
**Sağladığı:** aynı anda iki üretim yok. **Sağlamadığı:** günde kaç üretim olduğu. Kilit ~20 sn sonra bırakılır; kullanıcı ardışık olarak sınırsız üretebilir. Bugün onu durduran tek şey kredi bakiyesi (5 deneme kredisi = tek liste). Kredi kalkınca **tek koruma kalmıyor** — eklemek zorundayız.
### Öneri: **5 üretim / 24 saat** (paketsiz kullanıcı başına)
| Gerekçe | Rakam / kaynak |
|---|---|
| Gerçek kullanım bu eşiğin çok altında | Tüm zamanlarda **3** liste üretimi (`01-analitik.md`); 5 eşiği bugün **0** kullanıcıyı vurur |
| Dürüst öğrencinin bir günde ihtiyacı | 1 ilk liste + tercih değiştirip yeniden kurma (`tercih-degisti-modali` → aynı `listeOlustur` kapısı): il ekleme, alan değiştirme, devlet/vakıf değiştirme, yeni deneme sonrası sıralama güncelleme ≈ 3-4 → 5 pay bırakır |
| Tavanda bize maliyeti | 5 × 2,1 kuruş = **10,5 kuruş/kullanıcı/gün**; 1.000 hesap tavanı doldursa 105 TL/gün (`04-liste-maliyet-ve-oneriler.md` A-2) |
| Yük riski yok | Kilit üretimleri sıraya diziyor; 5 üretim ≈ 5 × 20 sn ardışık, eşzamanlı yük artmıyor |
| Geri dönüş | Tek sabit; değiştirmek dakikalar |
**Pencere:** takvim günü değil, **kayan 24 saat** (`created_at >= now - 24h`). Sebep: sunucunun saat dilimi ayarlı değil (repoda TZ ayarı yok, `grep` ile bakıldı) ve takvim-gün için Europe/Istanbul hesabı yazmak, kazandırdığı şeyden fazla kod demek.
**Sayım yüzeyi:** `credit_ledger`, `reason='report_generate'`, o kullanıcı, son 24 saat — **iadesi yazılmış denemeler sayılmaz** (`NOT EXISTS (refund, aynı ref_id)`). Böylece LLM zaman aşımına düşen bir deneme öğrencinin hakkını yakmaz. Yeni tablo, yeni kolon, göç **yok**.
**Sınır nereye konur:** kilit alındıktan **sonra**, kredi/üretim öncesi (`rapor-actions.ts:140-141` arası). Kilit zaten kullanıcıyı tek akışa indirdiği için sayım yarışsızdır; reddedilen deneme `finally` ile kilidi bırakır, hiçbir harcama yapılmaz.
**Kapsam dışı (bilerek):** çok hesap açarak sınırı aşma. Gerçek tavan orada — saldırganın her 5 liste için doğrulanmış yeni bir posta kutusu açması gerekir. Bu bir kimlik/kayıt konusu, kota konusu değil; buraya çözüm yazmıyorum.
**Paketli kullanıcıya sınır konmaz:** 60 kredi zaten en fazla 20 üretim demek.
---
## 2. Eşiğe çarpan kullanıcı ne görmeli
**Cümle:**
> Bir günde en fazla 5 liste kurabiliyorsun; bu hakkını doldurdun, birkaç saat sonra yeniden deneyebilirsin — listen ve sohbet geçmişin hesabında duruyor.
**Nereye oturuyor (yeni ekran yok, yeni durum yok):**
| Adım | Dosya:satır | Ne olur |
|---|---|---|
| Sunucu | `src/features/rapor/rapor-actions.ts:48` | `code` birliğine `"LIMIT"` eklenir; `{ ok:false, code:"LIMIT", error:"<cümle>" }` dönülür |
| /listem üretim ekranı | `src/features/rapor/components/liste-uretici.tsx:186-189` | Bilinmeyen kodları karşılayan mevcut `else` dalı: `setDurum({ ad:"hata", mesaj: sonuc.error })` |
| Çizim | `src/features/rapor/components/liste-uretici.tsx:295-300` | Mevcut hata bölümü cümleyi aynen basar ("Liste kurulamadı" başlığı altında) |
| Tercih değişti modalı | `src/features/rapor/components/tercih-degisti-modali.tsx:119` → `:246` | Aynı `else` dalı, kırmızı paragraf olarak aynı cümle |
| Ölçüm | `liste-uretici.tsx:187` | Mevcut `olay("liste_uretim_hata", { kod: sonuc.code })` → `kod:"LIMIT"` kendiliğinden düşer, yeni olay adı gerekmez |
İki not:
- O ekrandaki **"Tekrar dene"** butonu kalır ve zararsızdır: sınır kontrolü krediden ve LLM çağrısından önce koştuğu için tekrar basmak aynı cümleyi döndürür, **hiçbir şey harcamaz**.
- Başlık "Liste kurulamadı" olarak kalır. Doğru bir cümle; beğenilmezse tek kelimelik başlık düzeltmesidir, yeni ekran değil.
---
## 3. Kredi ne zaman düşmeli
**Hüküm: paketsizde tutar 0 olur, defter kaydı KALIR.** Yani `spendCredits` çağrısı silinmez; `amount` `user.hasPaket ? RAPOR_KREDI : 0` olur (aynısı iade tarafında, `rapor-actions.ts:214`).
Defter kaydını silmek üç şeyi birden bozardı:
1. **Idempotency.** `credit_ledger`'ın `UNIQUE(reason, ref_id)` indeksi (`appdb/schema.ts:133`) `requestId`'nin tek uygulama noktası. `spendCredits` DUPLICATE'i buradan üretiyor (`credits.ts:88`) ve `rapor-actions.ts:168-204` "bu deneme zaten işlendi" kararını ona dayandırıyor. Kayıt yazılmazsa ağda kaybolan bir cevabın tekrarı **ikinci bir LLM çağrısı** başlatır.
2. **İade yolu.** `:171-184` ve `:303-316`, aynı `refId` ile bir `refund` satırı arayarak "önceki denemen başarısızdı, taze dene" ile "deneme uçuşta, bekle" ayrımını yapıyor. 0 deltalı `refund` satırı bu ayrımı olduğu gibi korur (`grantCredits` 0 delta ile de satırı yazar, `credits.ts:162-169`).
3. **Günlük sayaç.** Bölüm 1'deki sınır bu satırları sayıyor. Kayıt yoksa sayacak bir şey de yok → yeni tablo + göç gerekir.
0 delta güvenli mi: `spendCredits`'in `WHERE creditBalance >= 0` koşulu paketsizde her zaman tutar, yani bakiyesi 0 olan kullanıcı da üretebilir (kararın amacı bu) ve `INSUFFICIENT` bu yoldan bir daha hiç dönmez.
**Değişiklikten sonra kredinin her yolda ne olduğu:**
| Yol | Paketsiz | Paketli |
|---|---|---|
| Başarılı üretim | 0 kredi, defterde `report_generate` delta 0 | −3, değişmedi |
| LLM hatası / zaman aşımı (`:210-219`) | 0 iade edilir (defterde `refund` delta 0); kullanıcıya mevcut iade cümlesi gösterilir | +3 iade, değişmedi |
| Platform fonksiyonu 120 sn'de öldürürse | İade kodu koşamaz — ama harcama da 0 olduğu için **kayıp yok** (bugünkü tek deliğin paketsiz tarafı kapanıyor) | Bilinen delik aynen duruyor |
| İkinci sekme / geri→ileri (`SURUYOR`) | Harcama yok, sayaç artmaz | Değişmedi |
| Günlük sınır (`LIMIT`) | Harcama yok, kilit bırakılır, LLM çağrılmaz | Uygulanmaz |
| Aynı `requestId` ile tekrar (`DUPLICATE`) | Değişmedi — kayıt durduğu için ayrım çalışır | Değişmedi |
**Yan etki, atlanmamalı:** `src/lib/kredi-hatirlatma.ts:36` "kredisi bitti" tanımını `creditBalance < RAPOR_KREDI` diye kuruyor. Liste ücretsiz olunca 2 kredisi kalan paketsiz kullanıcı **sıkışmış değil** (listesi bedava, 2 soru hakkı var) ama bu e-postayı yine de alır. Eşik `< 1` olmalı.
---
## 4. Revizyon
**Bugün paketsiz kullanıcı revizyon yapamıyor.** `rapor-actions.ts:256`:
```ts
if (!user.hasPaket) return { ok: false, code: "PAKET", error: "Paket gerekli." };
```
Kapı kredi kapısından **önce**; yani paketsiz kullanıcı revizyona hiç 3 kredi harcamıyor, doğrudan çeviriliyor.
**Tavsiyem: revizyon paketliye özel kalsın, ücretsiz liste kararı revizyonu kapsamasın.** Üç gerekçe:
1. **Karşılığı yok.** Paketsizin listesi maskeli (21 satırın gerekçesi/riski/trendi kapalı — `rapor-maske.ts`). "Daha güvenli olsun" diyerek revize ettiği listenin değişen kısmını zaten okuyamaz; ücretsiz revizyon ona bir şey vermez, bize LLM çağrısı olarak geri döner.
2. **Revizyon paketin kendi özelliği.** `credits.ts:16-17`: *"Paketle gelen geri bildirimli liste revizyonu hakkı"*, `MAX_REVIZYON = 2`. Paketin satış vaadinin parçası.
3. **Kimse tıkanmıyor.** Paketsizin "listem yanlış oldu" ihtiyacı revizyondan değil, **tercihleri değiştirip yeniden üretmekten** geçiyor (`tercih-degisti-modali` → `listeOlustur`, aynı kapı) — ve bu yol kararla birlikte ücretsiz oluyor, günde 5 hakkın içinden sayılıyor. Yani ücretsiz liste kararı revizyona dokunmadan da kullanıcının çıkışını açıyor.
Sonuç: `:256` satırına **dokunulmaz**, `report_revision` tarafı **hiç değişmez**.
---
## 5. Değişecek dosyalar (dokunulmadı)
Göç yok, yeni tablo/kolon yok, yeni bileşen yok, yeni ekran yok, prompt değişmiyor.
| # | Dosya | Ne | ~satır |
|---|---|---|---|
| 1 | `src/features/rapor/rapor-actions.ts` | `code` birliğine `LIMIT`; `tutar = hasPaket ? RAPOR_KREDI : 0` (harcama `:155`, iade `:214`, dönen bakiye `:243`); sınır kontrolü `:140` civarı | ~25 |
| 2 | `src/lib/credits.ts` | `gunlukUretimSayisi(userId)` — 24 saatlik `report_generate` sayımı, iade edilenler hariç | ~18 |
| 3 | `src/features/rapor/components/tercih-degisti-modali.tsx` | Bedel metinleri `hasPaket`'e bağlanır (`:129` `krediYeter`, `:188`, `:250`, `:275`, `:328-329`) | ~10 |
| 4 | `src/features/rapor/components/listem-icerik.tsx` | `raporKredi={RAPOR_KREDI}` → `hasPaket ? RAPOR_KREDI : 0` (`:167`) | 1 |
| 5 | `src/features/sihirbaz/components/hero-form.tsx` | CTA metninden "(3 kredi)" düşer (`:222`) | 1 |
| 6 | `src/features/sihirbaz/components/cta-sira-form.tsx` | aynı (`:334`) | 1 |
| 7 | `src/features/sihirbaz/components/sihirbaz-cagri-karti.tsx` | aynı (`:363`) | 1 |
| 8 | `src/features/liste/components/tercih-profili-kapisi.tsx` | aynı (`:463`) | 1 |
| 9 | `src/features/liste/components/program-ekle-butonu.tsx` | ipucu metni + üstündeki yorum (`:73-77`) | 3 |
| 10 | `src/features/liste/components/liste-cekmecesi.tsx` | ipucu metni (`:283`) | 1 |
| 11 | `src/features/odeme/components/paket-satinal.tsx` | girişsiz alt satır: "liste 3 kredi" → "liste ücretsiz, ilk 3 satır açık" (`:141-142`) | 2 |
| 12 | `src/lib/kredi-hatirlatma.ts` | eşik `< RAPOR_KREDI` → `< 1` (`:36`) | 1 |
| 13 | `src/features/rapor/components/liste-uretici.tsx` | kredi-yetersiz ekranının paketsiz dalı (`:275`) artık üretim yolundan erişilemez — metin sadeleşir | 2 |
**Toplam ~67 satır / 13 dosya. Kaba büyüklük: S.** Ağırlık 1-3 numaralı dosyalarda; 5-11 arası saf metin. `/meraklisina` şeması ve `/kosullar` metni kredi rakamı içermiyor (grep ile bakıldı) → senkron işi yok.
Doğrulama: `typegen + tsc + lint`; kabul kriteri olarak (a) paketsiz kullanıcı 0 bakiyeyle liste kurabiliyor, (b) 6. üretim `LIMIT` cümlesini gösteriyor ve defterde yeni satır oluşmuyor, (c) aynı `requestId` ile ikinci çağrı hâlâ `DUPLICATE` yolundan geçiyor, (d) paketli kullanıcıda −3 / +3 davranışı birebir aynı.
---
## Bilal'den istenen (2 madde)
1. **§5-2'ye evet/hayır** — evetse yukarıdaki iş S büyüklüğünde, göçsüz, tek turda biter. *(2 dk)*
2. **Günlük 5 sayısını onayla ya da değiştir** — bugün 0 kullanıcıyı vuruyor, tavanda 10,5 kuruş/kullanıcı; başka bir sayı istersen tek sabit. *(1 dk)*

View File

@@ -0,0 +1,87 @@
# 04 — İçerik envanteri: iki plandaki konular gerçekte yazıldı mı?
**Tarih:** 23 Eylül 2026 · **Rol:** seo-geo · **Durum:** ENVANTER — yeni konu önerilmedi, `content/` ve `src/` altına dokunulmadı.
**Kanıt işaretleri:** **[D]** dosya/commit/birincil kaynak okundu · **[A]** ikincil kaynak · **[Ç]** çıkarım · **[doğrulanmalı]**.
**Veri kısıtı:** Ahrefs/Similarweb/Search Console bu oturumda bağlı değil. **Bu raporda hiçbir arama hacmi ya da trafik rakamı yok**; sıralama gerekçesi yalnız takvim. Rybbit'e de bakılmadı (bu iş envanter, ölçüm değil).
---
## Özet (5 madde)
1. İki plandaki konular mükerrerler birleştirilince **18 konu**. Bunların **8'i yazılmış ve `main`'de** [D], **9'u açık**, **1'i düşmüş** (CEO reddi).
2. **BACKLOG.md #2 ve #3 bayat.** #2'deki KYK yazısı ve #3'ün "bu hafta" dediği 3 yazının dördü de `e21cda4` (22 Eyl 2026) ile `main`'e girdi [D]. İki madde `merge` olarak kapatılmalı; #3'ün "kalan 7 yazı" ifadesi de yanlış — kalan 9'a çıkmıyor, çünkü 7'nin 3'ü zaten o commit'te.
3. Takvimde yaklaşan **tek sert tarih** ÖSYM 2027 sınav takvimi (Kasım ortası beklentisi; 2026'nınki 14.11.2025'te çıkmıştı [A]). Takvim yazısı takvimden önce indekslenmiş olmalı → sıradaki yazı bu.
4. **Bir konu düştü:** `ikinci-universite-acikogretim` — CEO zaten reddetmişti (OBP istisnası teyitsiz, `05-seo-plan.md:33` [D]) ve AÖF son kayıt günleri 12/19/25 Ekim'de kapanıyor [A]; pencere de kapanıyor.
5. **Bir konu penceresini kaçırıyor:** `universitede-ilk-hafta-…` — üniversiteler 21 Eylül haftasında açıldı [A]; "ilk hafta" niyeti birkaç gün içinde biter. Ya bu sezon düşer ya da çerçevesi değişir; çerçeve değişikliği **plan dışı konu icadı olur**, kararı CEO'nun.
---
## Plan konusu → yazıldı mı?
G = `docs/gece-vardiyasi/2026-09-21/02-seo-geo-konu-plani.md` (15 konu) · P = `docs/ekip-raporlari/2026-09-21/05-seo-plan.md` §4 (8 yazı).
| # | Konu (slug) | Plan | Durum | Dosya / kanıt |
|---|---|---|---|---|
| 1 | `ek-yerlestirme-sonuclari-ve-kayit` | G1 | **Yazıldı** | `content/rehber/ek-yerlestirme-sonuclari-ve-kayit.md` · 902c7a1 (21 Eyl) [D] |
| 2 | `mezuna-kalmak-mi-kayit-olmak-mi` | G2 | **Yazıldı** | `…/mezuna-kalmak-mi-kayit-olmak-mi.md` · 902c7a1 [D] |
| 3 | `obp-kirilmasi-soru-cevap` | G3 | Açık (kısmen) | Kural `obp-nedir-nasil-hesaplanir.md` §"Kritik kural: çarpan neden yarıya düşer?" içinde var; planın istediği 8 soruluk SSS sayfası yok [D] |
| 4 | `universite-okurken-tekrar-yks` | G4 | **Yazıldı** | `…/universite-okurken-tekrar-yks.md` · 2602b6f (21 Eyl) [D] |
| 5 | `kyk-burs-mu-kredi-mi` | G5 + P5 | **Yazıldı** | `…/kyk-burs-mu-kredi-mi.md` · e21cda4 (22 Eyl) [D] — **BACKLOG #2 bunu bekliyor sanıyor** |
| 6 | `ikinci-universite-acikogretim` | G6 | **DÜŞTÜ** | CEO reddi, `05-seo-plan.md:33` [D]; AÖF pencereleri Ekim'de kapanıyor [A] |
| 7 | `hedef-siralama-nasil-belirlenir` | G7 | **Yazıldı** | `…/hedef-siralama-nasil-belirlenir.md` · 902c7a1 [D] |
| 8 | `yks-2027-takvimi` (G: `yks-ne-zaman-basvuru-ve-sinav-takvimi`) | G8 + P7 | Açık | — |
| 9 | `maarif-modeli-yks-ne-zaman-degisecek` | G9 | Açık | — |
| 10 | `ek-madde-1-puanim-yetiyor-mu` (G: `merkezi-puanla-yatay-gecis-ek-madde-1`) | G10 + P8 | Açık (kısmen) | `yatay-gecis-cap-yandal.md:31` Ek Madde-1'i bir başlıkta anlatıyor; planın istediği "puanım yetiyor mu" işlem sayfası yok [D] |
| 11 | `bilgisayar-mi-yazilim-muhendisligi-mi` | G11 | Açık | — |
| 12 | `vakif-universitesi-burslu-indirimli-ucretli-farki` (G: `…bursu-kesilir-mi`) | G12 + P6 | Açık — **veri ön koşullu** | 3.839 vakıf programında burs etiketi yok (`05-seo-plan.md` T10); çözülmeden tablo yayımlanmaz |
| 13 | `bos-kontenjanlar-ne-anlatiyor` | G13 + P1 | **Yazıldı** | `…/bos-kontenjanlar-ne-anlatiyor.md` · e21cda4 [D] |
| 14 | `dgs-ile-lisans-tamamlama` | G14 | Açık (düşük) | Planın kendi notu: ürün DGS'yi kapsamıyor |
| 15 | `yapay-zeka-ile-tercih-yapilir-mi` | G15 | Açık — konu planı maddesi değil | Planın kendi notu: iş `docs/geo/geo-raporu.md` T22'de duruyor |
| 16 | `en-cok-ogrenci-alan-devlet-universiteleri` | P2 | **Yazıldı** | `…/en-cok-ogrenci-alan-devlet-universiteleri.md` · e21cda4 [D] |
| 17 | `en-secici-devlet-universiteleri` | P3 | **Yazıldı** | `…/en-secici-devlet-universiteleri.md` · e21cda4 [D] |
| 18 | `universitede-ilk-hafta-ders-kaydi-muafiyet-kayit-dondurma` | P4 | Açık — **penceresi kapanıyor** | Üniversiteler 21 Eylül haftasında açıldı [A] |
**Sayı: 18 konu → 8 yazıldı · 9 açık · 1 düştü.** (Açık 9'un 2'si kısmen kapsanıyor: #3, #10.)
`content/rehber` altında 32 dosya var [D]; 18'lik plan listesinin dışında kalan 24 yazı Ağustos–Eylül tercih sezonundan gelen mevcut stok.
Planların "yedek havuz" başlıkları (KKTC, hukuk 2026, yeni açılan bölümler, MSÜ, veli 12. sınıf, şehir değiştirmeden üniversite) bilerek sayılmadı — onaylı öneri değil, havuz.
---
## Kalan 9 için sıralama (gerekçe: yalnız takvim)
| Sıra | Konu | Takvim gerekçesi | Ne zaman yazılmalı |
|---|---|---|---|
| 1 | `yks-2027-takvimi` | ÖSYM 2027 sınav takvimi Kasım ortasında bekleniyor (2026'nınki 14.11.2025 [A]). Sayfa takvim çıkmadan indekslenmiş olmalı; takvim günü tek cümleyle güncellenir. Tek sert tarihli iş bu | Hemen (Eylül sonu) |
| 2 | `obp-kirilmasi-soru-cevap` | Ek yerleştirme sonucu **henüz açıklanmadı** — 23 Eyl itibarıyla ÖSYM duyurularında yok [D]; açıklanınca "kayıt sildirsem OBP düzelir mi" sorusu Ekim başında tepe yapar. Sitenin en güçlü kümesine (yerleşemedim / OBP) eklemleniyor | Ekim ilk hafta |
| 3 | `ek-madde-1-puanim-yetiyor-mu` | Ek Madde-1 bahar başvuruları Ocak 2027 [A]; Aralık'ta yayında olmalı | Kasım |
| 4 | `vakif-universitesi-burslu-indirimli-ucretli-farki` | Tarihsiz; **veri ön koşulu** (burs etiketi) çözülmeden yazılamaz. Vakıf burs sorusu Ocak–Şubat tanıtım sezonuna doğru ısınır | Veri çözülünce, en erken Kasım |
| 5 | `bilgisayar-mi-yazilim-muhendisligi-mi` | Tarihsiz, sezon dışı doldurucu; karşılaştırma serisinin şablonu olduğu için bir kez yazılınca sonraki çiftler ucuzlar | Boşluk doğduğunda |
**Sıraya girmeyen 4:** `maarif-modeli-yks` (tarih yok, talep kanıtı zayıf) · `dgs-ile-lisans-tamamlama` (planın kendi notu: ürün dışı) · `yapay-zeka-ile-tercih-yapilir-mi` (GEO raporu T22'nin işi, konu planı maddesi değil) · `universitede-ilk-hafta-…` (aşağıya bak).
---
## Bayatlık uyarıları
- **[DÜŞTÜ] `ikinci-universite-acikogretim` (G6).** CEO 21 Eylül'de reddetti [D]; AÖF/AUZEF/ATA son kayıt günleri 12/19/25 Ekim'de kapanıyor [A]. Plandan çıkarılmalı, yedek havuza bile dönmemeli.
- **[PENCERE KAPANIYOR] `universitede-ilk-hafta-…` (P4).** Yazı 22–28 Eylül dalgası için tasarlanmıştı; üniversiteler 21 Eylül haftasında açıldı [A], ders kaydı/muafiyet penceresi bu hafta bitiyor. Bugün başlanan bir yazı geç kalır. Kararı CEO'ya bırakıyorum: bu sezon düşsün mü, yoksa 2027 Eylül'üne mi park edilsin.
- **[BAYAT BACKLOG] #2 ve #3.** Dördü de `main`'de; maddeler `merge` olarak kapatılmalı, yoksa aynı yazılar ikinci kez yazdırılır (bu tur az kalsın oldu).
- **[BAYAT BAŞLIK] `kac-siralama-ile-hangi-bolume-girebilirim.md`** — başlık hâlâ "(2025 Verileriyle)" [D] ama katalog 2026'ya geçti. Bu bir yazı değil, `rehber-editoru` işi; iki plan da işaret ediyordu, hâlâ açık.
- Sezonu geçmiş **mevcut** yazılar (`yks-tercih-takvimi-2026`, `tercih-sonuclari-ne-zaman-2026`, `ais-tercih-nasil-yapilir-onaylama`, `ek-yerlestirme-2026`) plan konusu değil; geçmiş zamana çevrilmeleri planların "mevcut yazılara dokunuş" satırında duruyor, kapsam dışı bırakıldı.
---
## Bilal'den istenen
1. **Karar:** `universitede-ilk-hafta-…` bu sezon düşsün mü, 2027 Eylül'üne mi park edilsin — **tek cümle**.
2. Başka bir şey yok; BACKLOG #2/#3'ün kapatılması `urun-yoneticisi` işidir, Bilal'in onayını gerektirmez.
---
## Kaynaklar
`docs/gece-vardiyasi/2026-09-21/02-seo-geo-konu-plani.md` · `docs/ekip-raporlari/2026-09-21/05-seo-plan.md` · `docs/ekip/BACKLOG.md` · `content/rehber/*.md` (32 dosya, frontmatter `baslik` alanları) · `git log --diff-filter=A -- content/rehber/<slug>.md` (902c7a1, 2602b6f, e21cda4) [D] · ÖSYM duyurular, 23 Eyl 2026 okundu — ek yerleştirme sonuç duyurusu henüz yok: https://www.osym.gov.tr/Duyurular/Index [D] · KYK/AÖF/ÖSYM-2027 tarih beklentileri iki plandaki [A] kaynaklardan devralındı, yeniden doğrulanmadı [doğrulanmalı].

View File

@@ -0,0 +1,101 @@
# 05 — Yazı: 2027 YKS takvimi
**Tarih:** 23 Eylül 2026 · **Rol:** rehber-yazari · **Dal:** `gece/0923-rehber-2027` · **Commit:** `82dffc7` (push yok)
**Dosya:** `content/rehber/yks-2027-takvimi.md` (yeni, tek dosya) · **Build:** `pnpm build` worktree'de temiz geçti (rehber yolu 32 → 33)
---
## Özet (5 madde)
1. Yazı, **2027 için tek bir tarih vermiyor.** Çerçeve: "takvim henüz açıklanmadı; son dört yılın gerçek tarihleri şöyle; açıklanınca bu sayfa güncellenecek." Başlıkta da parantez içinde "(Henüz Açıklanmadı)" yazıyor.
2. **2023-2026 tarihlerinin tamamı ÖSYM duyurularından tek tek doğrulandı** (23 Eyl 2026'da okundu); 22 kaynak URL'si yazının sonunda listeli. Doğrulayamadığım tek hücre — 2023 yılı sınav takviminin yayım günü — boş bırakıldı ve tablonun altında neden boş olduğu yazıldı.
3. **Bütün tahminler tek bölümde:** `## 2027 için tahmin — bu bölümün tamamı tahmindir`. İçinde gün adı/tarih yok, yalnız "kasım", "haziranın ikinci yarısı", "şubat başı" gibi bant ifadeleri var. ÖSYM duyurduğu gün bu bölüm silinip tabloya 2027 sütunu eklenecek.
4. İki tablo var: (a) 2023-2026 aşama tarihleri yan yana, (b) aşamalar arası gün sayıları (doğrulanmış tarihlerden aritmetik). İkincisi desenin asıl istikrarlı kısmını gösteriyor: sınav→sonuç ~1 ay, sonuç→tercih 1-2 hafta, tercih kapanışı→yerleştirme 1-2 hafta.
5. **2027'ye dair hiçbir duyuru olmadığı da doğrulandı:** ÖSYM Duyurular sayfası 23 Eyl 2026'da okundu, en yeni başlıklar 2026 dönemine ait (ek yerleştirme, ÖZYES, KPSS-DHBT).
---
## Doğrulanan tarihler ve kaynakları
Hepsi 23 Eylül 2026'da `osym.gov.tr` üzerinden okundu.
| Bilgi | Değer | Kaynak |
|---|---|---|
| 2027'ye ait duyuru var mı | **Yok** (en yeni başlıklar 2026) | https://www.osym.gov.tr/Duyurular/Index |
| 2024 takviminin yayımı | 06.11.2023 | https://www.osym.gov.tr/TR,28929/osym-2024-yili-sinav-takvimi-aciklandi-06112023.html |
| 2025 takviminin yayımı | 13.11.2024 | https://www.osym.gov.tr/TR,32816/osym-2025-yili-sinav-takvimi-aciklandi-13112024.html |
| 2026 takviminin yayımı | 14.11.2025 (erişim 09.30'dan itibaren) | https://www.osym.gov.tr/osym-2026-yili-sinav-takvimi-aciklandi |
| 2026 takvim revizyonu | 03.06.2026 — NATO Zirvesi nedeniyle MEB-AGS/ALES-2/YÖKDİL-2 kaydı; **YKS etkilenmedi** | https://www.osym.gov.tr/2026-osym-sinav-takviminde-guncelleme |
| 2023 başvuru | 8-23 Mart 2023 | https://www.osym.gov.tr/2023yks-basvurularin-alinmasi |
| 2023 sınav / sonuç | 17-18 Haziran 2023 / 20 Temmuz 2023 | https://www.osym.gov.tr/2023-yuksekogretim-kurumlari-sinavi-2023yks-sonuclari-aciklandi |
| 2023 tercih | 27 Temmuz - 8 Ağustos 2023 | https://www.osym.gov.tr/2023-yuksekogretim-kurumlari-sinavi-yks-tercihlerin-alinmasi |
| 2023 yerleştirme sonucu | 19 Ağustos 2023 | https://www.osym.gov.tr/2023yks-yerlestirme-sonuclari-aciklandi |
| 2024 başvuru | 1-26 Şubat 2024 | https://www.osym.gov.tr/TR,29145/2024-yks-basvurularin-alinmasi-01022024.html |
| 2024 sınav / sonuç | 8-9 Haziran 2024 / 16 Temmuz 2024 | https://www.osym.gov.tr/2024-yuksekogretim-kurumlari-sinavi-2024yks-sonuclari-aciklandi |
| 2024 tercih | 25 Temmuz - 2 Ağustos 2024 | https://www.osym.gov.tr/2024-yuksekogretim-kurumlari-sinavi-yks-tercihlerin-alinmasi |
| 2024 yerleştirme sonucu | 13 Ağustos 2024 | https://www.osym.gov.tr/2024yks-yerlestirme-sonuclari-aciklandi |
| 2025 başvuru | 6 Şubat - 13 Mart 2025 | https://www.osym.gov.tr/2025yks-basvuru-islemlerinin-tamamlanmasi |
| 2025 sınav / sonuç | 21-22 Haziran 2025 / 19 Temmuz 2025 | https://www.osym.gov.tr/2025yks-sinav-sonuclari-aciklandi |
| 2025 tercih | 1-13 Ağustos 2025 | https://www.osym.gov.tr/2025yks-tercihlerin-alinmasi |
| 2025 yerleştirme sonucu | 25 Ağustos 2025 | https://www.osym.gov.tr/2025yks-yerlestirme-sonuclari-aciklandi |
| 2026 başvuru | 6 Şubat - 2 Mart 2026 | https://www.osym.gov.tr/2026yks-basvurularin-alinmasi |
| 2026 sınav / sonuç | 20-21 Haziran 2026 / 21 Temmuz 2026 | https://www.osym.gov.tr/2026yks-sinav-sonuclari-aciklandi |
| 2026 tercih | 29 Temmuz - 10 Ağustos 2026 | https://www.osym.gov.tr/2026-yks-tercihlerin-alinmasi |
| 2026 yerleştirme sonucu | 18 Ağustos 2026 (kayıt 24-28 Ağustos) | https://www.osym.gov.tr/2026-yks-yerlestirme-sonuclari-aciklandi |
**SQL kullanılmadı.** Yazıda sıralama/taban rakamı yok; `data/yokatlas.db`'ye ihtiyaç duyulmadı.
---
## Tahmin diye etiketlenenler (yazıda tek bölümde)
- Takvimin kasımda yayımlanması — son üç takvimin 6/13/14 Kasım'da çıkmasından çıkarım.
- Sınavın haziranın ikinci yarısındaki bir hafta sonunda olması — son dört sınavın üçü haziranın üçüncü hafta sonu (2023, 2025, 2026), biri ikinci hafta sonu (2024).
- Başvuruların şubat başında açılması — 2024/2025/2026 deseni; 2023'ün mart başvurusu deprem sonrası olağanüstü durum diye ayrıca not edildi.
- Sonuç/tercih/yerleştirme bantları.
**Bilinçli olarak yazılmayan iddialar:**
- **Somut 2027 tarihi yok.** Aritmetikle haziranın üçüncü hafta sonu 19-20 Haziran 2027'ye denk geliyor ve internette bu tarih dolaşıyor; yazıya koymadım — alıntılanıp "resmî tarih" gibi yayılmasın diye.
- **2027 kapsam/müfredat değişikliği iddiası yok.** Maarif Modeli'nin YKS'ye hangi yıl yansıyacağına dair doğrulanmış ÖSYM/YÖK duyurusu bulamadım; SSS'de yalnızca "23 Eyl 2026 itibarıyla 2027'ye dair duyuru yok, kapsam kılavuzla kesinleşir" denildi.
- **2025 kayıt tarihleri** doğrulanamadığı için kayıt satırı tablodan çıkarıldı (2023/2024/2026 elde vardı, 2025 yoktu — eksik satır yerine satırı hiç açmadım).
- **2023 sınav takviminin yayım günü** doğrulanamadı; hücre "—" bırakıldı, gerekçesi tablonun altında yazıyor.
---
## Yapı ve iç linkler
- Frontmatter mevcut yazılarla aynı: `baslik`, `aciklama` (tırnaksız), `tarih: 2026-09-23`, `guncelleme: 2026-09-23`.
- "Kısa cevap:" ile açılıyor; H2'ler soru biçiminde; 1 mermaid şeması (`%% aria:` + `%% altyazi:`, renkler her zaman metin etiketiyle: "BEKLENEN dönem — kesin değil" / "KESİN kurallar burada"); 2 tablo; SSS; kaynaklar.
- İç linkler (hepsi `content/rehber` altında dosya olarak doğrulandı): `yks-tercih-takvimi-2026`, `merkezi-yerlestirme-nasil-calisir`, `hedef-siralama-nasil-belirlenir`, `siralama-sartlari-2026`, `taban-siralamalar-nasil-degisir`, `obp-nedir-nasil-hesaplanir`, `mezuna-kalmak-mi-kayit-olmak-mi`, `universite-okurken-tekrar-yks`.
- Katalog linkleri: `/bolumler`, `/universiteler` (route var), `/tercih-robotu` (`next.config.ts` içinde `/#hero-form`'a kalıcı redirect — mevcut yazılarla aynı kullanım).
- Vaat dili yok: robot bölümünde "Bu bir yerleşme öngörüsü değil, sadece mevcut verinin sana gösterdiği manzara" cümlesi var.
---
## ÖSYM duyurduğunda ne güncellenecek
Tek bölüm + iki yer:
1. **`## 2027 için tahmin — bu bölümün tamamı tahmindir`** — tamamen silinir, yerine gerçek tarihler yazılır.
2. **Kısa cevap paragrafı** — "henüz açıklanmadı" cümlesi gerçek tarihle değişir.
3. **Birinci tabloya 2027 sütunu** eklenir (başlangıçta yalnız takvim yayımı + sınav tarihi dolu olur).
4. Frontmatter `guncelleme` alanı o günün tarihine çekilir; SSS'deki "Bu sayfa güncellenecek mi?" maddesi sadeleşir.
Başlıktaki "(Henüz Açıklanmadı)" ibaresi de o gün kalkmalı — aksi hâlde SERP'te yanlış sinyal verir.
---
## Engeller
- `www.osym.gov.tr/Sayfa/SinavTakvimi` ve bazı `yazdir?` URL'leri zaman aşımına uğradı; aynı bilgiyi kanonik duyuru sayfalarından aldım.
- ÖSYM'nin 2026 takvim PDF'i metin katmanı konumsal olduğu için satır satır okunamadı; takvim tarihleri PDF yerine duyuru sayfalarından doğrulandı.
- Başka bir yazıyla çelişki görmedim. (Envanter raporundaki `kac-siralama-ile-hangi-bolume-girebilirim.md` başlığının "2025 Verileriyle" kalması hâlâ açık — `rehber-editoru` işi, bu yazıda dokunmadım.)
---
## Bilal'den istenen
1. **Yayın önce kontrol:** başlıktaki "(Henüz Açıklanmadı)" ibaresini SEO açısından onaylıyor musun — kalsın mı, yoksa yalnız `aciklama`'da mı dursun? (2 dk)
2. **Kasım hatırlatıcısı:** ÖSYM 2027 takvimini yayımladığı gün bu sayfanın güncellenmesi için takvimine not düş; güncelleme tek bölüm + iki cümle, 15 dakikalık iş. (1 dk)
3. Başka bir şey yok.

View File

@@ -0,0 +1,74 @@
# 06 — URAP takasının telafisi (yazılımcı)
Dal: `gece/0922-urap` · Commit: `4c756c4` · Dosya: `src/features/katalog/components/universite-icerik.tsx` (tek dosya, +30/−13)
## Özet — 5 madde
1. Dün URAP kartı "Tür" kartının yerini alırken devlet/vakıf bilgisi URAP'ta yer alan
198 üniversite sayfasından tamamen düşmüştü; telafi olarak bilgi özet paragrafın ilk
virgülüne girdi: "… (İstanbul), **devlet üniversitesi**; 2026 YKS yerleştirme
sonuçlarına göre …". Tasarımcının A alternatifi birebir uygulandı.
2. Yeni kutu/bileşen/rozet yok, ızgara 4 kart. Üretilen 229 gerçek sayfanın **229'unda**
tür öbeği var (`[slug].html` PPR kabuğu hariç), 198'inde URAP kartı + 31'inde "Tür"
kartı — toplam yine 229, hiçbir sayfada delik yok.
3. `trBaslikDuzeni` kullanılmadı. Dünkü `UNITUR_ETIKET` haritası `{ kart, cumle }`
biçimine genişletildi: kart etiketi ve cümle öbeği tek kaynakta, ikisi ayrışamaz.
4. 6 `unitur` değerinin altısı da üretilen HTML'den ya da haritadan tek tek okundu (aşağıda);
haritada olmayan/NULL değerde öbek hiç yazılmıyor, cümlede kopuk virgül kalmıyor.
5. `tsc` + `lint` + `pnpm build` temiz; `gece/0922-entegrasyon` worktree'sine merge edildi,
orada da build temiz (1821 statik sayfa).
## Cümlenin 6 `unitur` değerindeki hâli
| `unitur` | Üretilen cümle (HTML'den) | Doğrulama |
|---|---|---|
| `DEVLET` | Boğaziçi Üniversitesi (İstanbul), **devlet üniversitesi**; 2026 YKS … | HTML ✔ |
| `VAKIF` | Koç Üniversitesi (İstanbul), **vakıf üniversitesi**; 2026 YKS … | HTML ✔ |
| `VAKIF MYO` | İstanbul Şişli Meslek Yüksekokulu (İstanbul), **vakıf meslek yüksekokulu**; … | HTML ✔ |
| `KKTC` | Doğu Akdeniz Üniversitesi (Gazimağusa), **KKTC üniversitesi**; … | HTML ✔ |
| `YURTDISI KAMU` | Azerbaycan Devlet Pedagoji Üniversitesi (Bakü-azerbaycan), **yurt dışında bir kamu üniversitesi**; … | HTML ✔ |
| `YURTDISI VAKIF` | … , **yurt dışında bir vakıf üniversitesi**; … | **HTML'de üretilmedi** — koddan çıkarım |
| `NULL` / haritada yok | öbek hiç yazılmaz, virgül de eklenmez | koddan çıkarım (üretimde NULL `unitur`lu üniversite yok) |
`YURTDISI VAKIF` neden HTML'de yok: bu değeri taşıyan iki kurumun (Manas, Yesevi) aynı
slug'ında `YURTDISI KAMU` satırları da var ve `UniOzet.unitur` KAMU'yu seçiyor. Harita
girdisi yine de duruyor; veri değişirse cümle hazır.
## Kabul kriterleri
1. **✔** `bogazici-universitesi.html`'de "devlet üniversitesi", `koc-universitesi.html`'de
"vakıf üniversitesi" özet paragrafta geçiyor — `.next/server/app/universite/*.html`
üzerinde `<p class="mt-4 max-w-3xl …">` içeriği çıkarılarak doğrulandı.
2. **✔** URAP'ta olmayan sayfada (Doğu Akdeniz) hem "Tür" kartı ("KKTC") hem cümle
("KKTC üniversitesi") var. **Çift gösterim tuhaf durmuyor** — kart taranabilir kısa
değer, cümle düz metin; ikisi çelişmiyor. Kaldırma önerim yok (karar zaten bende değil).
3. **✔** Izgara 4 kart: 198 sayfa URAP kartlı, 31 sayfa "Tür" kartlı, her ikisinde de
`dt/dd` sayısı 4. "Tür" kartında hiçbir sayfada "—" yok (tarandı).
4. **✔** `pnpm exec next typegen && pnpm exec tsc --noEmit` temiz, `pnpm lint` temiz,
`pnpm build` temiz (`gece/0922-urap`), merge sonrası `gece/0922-entegrasyon`'da da temiz.
## Şartnameden sapma
Yok. Tek bilinçli seçim: iki ayrı harita yerine `UNITUR_ETIKET`i `{ kart, cumle }` yaptım
(iki haritanın zamanla ayrışma riski var). Tasarımcının tablosundaki öbekler birebir korundu.
## Merge sonrası doğrulanması gerekenler
- `gece/0922-entegrasyon` merge'i çakışmasız geçti; merged build'de dağılım 129 devlet /
75 vakıf / 16 KKTC / 6 yurt dışı kamu / 3 vakıf MYO = 229 (entegrasyon dalındaki
`unitur` veri düzeltmesi 3 sayfayı DEVLET'ten doğru değere taşımış — beklenen).
- Gözle: `/universite/koc-universitesi` ve `/universite/dogu-akdeniz-universitesi` özet
paragrafı 375 px'te ilk satırda taşmıyor mu (öbek ~20 karakter uzattı).
## Kapsam dışı bulgu (dokunmadım)
`trBaslikDuzeni` tireden sonraki harfi büyütmüyor: sayfa başlıklarında "Bakü-**a**zerbaycan",
"Kırgızistan-**t**ürkiye", "Türk-**k**azak" yazıyor (`src/lib/slug.ts`). Bu dünden önce de
vardı, benim değişikliğimle ilgisi yok; ~10 yurt dışı/KKTC sayfasını etkiliyor.
## Bilal'den istenen
1. Bu commit (`4c756c4`) dün gece kaybedilen bilginin telafisi — almazsan 198 üniversite
sayfası devlet/vakıf bilgisiz kalır. **~1 dk.**
2. Kapsam dışı bulgu (`trBaslikDuzeni` tire sonrası büyük harf) ayrı iş olarak backlog'a
girsin mi? **~1 dk.**

View File

@@ -0,0 +1,399 @@
# 09 — Tasarım: `/sonuc` haritası en üste + üniversite sayfasında tür
> Temel: `is/0923-harita-ve-tur` worktree'si, `origin/main` (c5769e2). Karşılaştırma için
> `gece/0922-urap` dalı `git show` ile okundu. Tarayıcı/dev sunucusu açılmadı, kod değiştirilmedi.
> Piksel ve CLS değerleri **token/kod okumasından hesaplandı**, ölçülmedi — hesap §1.2'de açık.
> Her iki talimat da "yapılacak" kabul edildi; aşağıdakiler *nasıl* yapılacağıdır.
---
## Özet — 5 madde
1. **Harita en üste çıkabilir ve CLS bedeli sıfıra inebilir.** Tek gereken, listenin *sayısını*
sunucunun hidrasyondan önce bilmesi. Profil zaten `kolaytercih.profil` çerezi ile sunucuya
aynalanıyor (`sihirbaz-profil.ts:51-57`); aynı desenle `kolaytercih.liste-sayi` çerezi yazılır,
`/sonuc` bunu okuyup haritaya **tam ölçülü** yer ayırır. Uygulama yükü: 3 dosya, ~25 satır.
2. **"Boşken hiç gözükmesin" şartı bozulmuyor.** Çerez yoksa ya da 0 ise `ManuelHarita` bugünkü gibi
`null` döner — ilk kez gelen ziyaretçi için DOM'da hiçbir düğüm yoktur, dolayısıyla tadımlığın
`first:mt-0` kuralı (`tadimlik-satiri.tsx:134`) da bozulmaz. **İlk ziyaretçide sıra değişmiyor —
doğrulandı** (`manuel-harita.tsx:71` `return null`).
3. **Tadımlığın kaybı kabul edilebilir ve dar.** Kayıp yalnız "girişsiz + kayıtlı listesi olan"
ziyaretçide var. 375×812'de tadımlığın rozeti, H2'si ve açıklaması harita eklendikten sonra da
katlamanın üstünde kalıyor (hesap §1.4); yalnız örnek satır kartı aşağı iniyor. Bu ziyaretçi
"bu ürün ne?" aşamasını geçmiş, ekranda önce kendi listesini görmesi doğru.
4. **Üniversite sayfasında tür, ızgaraya değil kimlik satırına gider.** Izgara 5 karta çıkamaz:
`lg:grid-cols-[1fr_1fr_1.35fr] lg:grid-rows-2` şablonunda harita 2 satır kaplıyor, kartlar için
**tam 4 hücre** var (`universite-icerik.tsx:291`, `universite-konum-haritasi.tsx:77`). Tür zaten
bir *metrik* değil *kategori*; sitenin geri kalanında (4 ayrı dosyada) başlığın altındaki meta
satırında çip olarak yaşıyor. Aynı çip H1'in altına konur, ızgara 4 sayısal kart kalır.
5. **Yan bulgu: `unitur` etiketinin 4 farklı sözlüğü var** ve ikisi bozuk metin basıyor
(`universite-icerik.tsx:318` ve `bolum-program-listesi.tsx:227` → "Kktc", "Vakıf Myo").
`UNITUR_ETIKET` (bugün `gece/0922-urap`'ta, bileşenin içinde) `src/lib/`'e taşınmalı — tıpkı
`DILIM_ETIKET`'in `src/lib/risk.ts`'te tek kaynak olması gibi (AGENTS.md).
---
# Talimat 1 — `/sonuc`'ta harita en üste
## 1.1 Bugünkü durum (doğrulandı)
| Olgu | Kanıt |
|---|---|
| Sıra: tadımlık → tablo → harita | `src/app/sonuc/page.tsx:118, 124, 131` |
| Liste boşken harita hiç çizilmiyor | `manuel-harita.tsx:71` (`if (liste.length === 0) return null`) |
| Sunucu snapshot'ı **her zaman** boş | `liste-store.ts:30, 86` (`getServerSnapshot → BOS_LISTE`) |
| Liste yalnız localStorage'ta | `liste-store.ts:27, 42-46` |
| Profil ise sunucuya **çerezle** aynalanıyor | `sihirbaz-profil.ts:51-57`, `sonuc/page.tsx:65` |
| Store her sayfada hidrasyonda okunuyor | `layout.tsx:98-102` → `ListeCekmecesiLazy`/`KaydetBannerLazy` → `useManuelListe` |
Son satır kritik: liste store'u **site genelinde**, ilk açılan sayfada yükleniyor. Yani yazılacak
çerez, kullanıcı `/sonuc`'a varmadan önce (ana sayfada) yazılmış oluyor.
## 1.2 CLS hesabı — "368 px" rakamı masaüstündü
`main` = `max-w-4xl px-4` (`sonuc/page.tsx:99`). Harita `w-full`, viewBox `1050×447`
(`harita.ts:10-13`, `tercih-haritasi.tsx:46-51`), iskelet de aynı oranı rezerve ediyor
(`tercih-haritasi-lazy.tsx:14`).
| Ekran | İçerik genişliği | Harita yüksekliği |
|---|---|---|
| 375 px (mobil, trafiğin ~%90'ı) | 343 px | 343 × 447/1050 = **146 px** |
| ≥ 928 px (masaüstü) | 864 px | 864 × 447/1050 = **368 px** |
Yani `sonuc/page.tsx:107`'deki "~368 px" **masaüstü** rakamı; mobilde 146 px (+ bugünkü `mt-16` = 64 px).
CLS = etki payı × mesafe payı:
| Senaryo | Etki payı | Mesafe payı | **CLS** |
|---|---|---|---|
| Bugün (harita altta), dönüş ziyaretçisi | ~0 (kayan içerik ~2,4 ekran aşağıda) | — | **~0** |
| Harita üstte, yer ayırmadan, 375×812 | (812−220)/812 ≈ 0,73 | 146/812 = 0,18 | **~0,13** ("iyileştirilmeli") |
| Harita üstte, yer ayırmadan, 1280×800 | ~0,75 | 368/800 = 0,46 | **~0,35** ("kötü") |
| **Harita üstte + çerezle yer ayrılmış** | 0 | 0 | **0** |
(Hesap; ölçüm değil. 220 px = bant + başlık + `py-12`, önceki raporun tahmini.)
## 1.3 İki alternatif
### Alternatif A — liste sayısı çerezi, tek yuva (ÖNERİLEN)
```
375 px · /sonuc · DÖNÜŞ ziyaretçisi (çerez: liste-sayi=7)
┌───────────────────────────────┐ ← ilk boyada sunucudan gelen HTML
│ [bant] [başlık] │
│ │
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ 146 px · HaritaIskeleti
│ ░░░ harita yer tutucu ░░░ │ (aspect-[1050/447], tam ölçü)
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ │
│ ◇ Yapay zekâdan örnek satır │ chunk inince yer tutucunun
│ Yapay zekâ listendeki her │ İÇİ dolar; hiçbir şey kaymaz
│ satırı böyle anlatır │
└───────────────────────────────┘
375 px · /sonuc · İLK KEZ gelen ziyaretçi (çerez yok)
┌───────────────────────────────┐
│ [bant] [başlık] │
│ │
│ ◇ Yapay zekâdan örnek satır │ ← harita DOM'da hiç yok,
│ Yapay zekâ listendeki her │ tadımlık first:mt-0'ı korur:
│ satırı böyle anlatır │ BUGÜNKÜ EKRANIN AYNISI
└───────────────────────────────┘
```
- **CLS:** çerez isabetliyse **0**. Çerez yoksa ama liste varsa (aşağıdaki dar kaçak) 0,13 (mobil).
- **Çerezin kaçacağı tek durum:** dağıtım sonrası ilk sayfa açılışı (kullanıcı başına **bir kez**;
ana sayfada yazıldığı için `/sonuc`'a client-side geçişte bile kapanır) ve localStorage yazma
hatası. Safari ITP kaçağı **yok sayılabilir**: ITP script'le yazılan çerezi 7 günde siler ama aynı
koşulda localStorage'ı da siler — ikisi birlikte ölür, ayrışmaz. Aynı risk zaten `kolaytercih.profil`
çerezinde de var, yeni bir sınıf risk getirmiyor.
### Alternatif B — çift yuva (çerez varsa üstte, yoksa altta)
`ManuelHarita` iki konumda da yazılır; sunucu çereze bakarak hangisinin aktif olacağını seçer.
```
çerez > 0 → [HARİTA] → tadımlık → tablo → sihirbaz
çerez yok → tadımlık → tablo → [HARİTA] → sihirbaz (bugünkü davranış)
```
- **CLS:** her durumda ~0. Kaçak senaryoda bile sıfır, çünkü harita eski (güvenli) yerine düşer.
- **Bedeli:** aynı bileşen iki DOM konumunda yaşar — AGENTS.md'nin "bir eleman iki yerde görünüyorsa
tek kaynaktan" ilkesine biçimsel olarak uymasa da okuma yükü artar; ve kullanıcı bazı ziyaretlerde
haritayı üstte, bazılarında altta görür. **Tutarsızlık, kazanılan 0,13'lük tek seferlik CLS'ten
daha pahalı.**
### Hüküm: **A**
B'nin kapattığı tek delik, kullanıcı başına bir kez yaşanan ve "iyileştirilmeli" bandındaki (kırmızı
değil) bir kayma. Bunun için haritanın sabit bir yeri olmamasını kabul etmek doğru takas değil.
## 1.4 Tadımlık ne kaybediyor — hüküm: kabul edilebilir
| Ziyaretçi | Sıra değişiyor mu? | Tadımlık nerede? |
|---|---|---|
| İlk kez gelen (liste boş) | **Hayır** — harita hiç render edilmez | Bugünkü yerinde, ~220 px |
| Girişli (tadımlık zaten yok, `tadimlik-bolumu.tsx:19`) | Evet, harita tablonun üstüne çıkar | — |
| **Girişsiz + kayıtlı listesi olan** | Evet | ~220 → **~366 px** |
Üçüncü satır için 375×812 hesabı (koddan çıkarım): tadımlığın `SectionEyebrow`'u, H2'si
("Yapay zekâ listendeki her satırı böyle anlatır") ve üç satırlık açıklaması katlamanın üstünde
kalıyor; aşağı inen şey örnek satır kartının gövdesi ve CTA'sı. 375×667'de (iPhone SE) rozet + H2
görünür, paragraf yarılanır.
**Hüküm: kabul edilebilir.** Gerekçe kaybın büyüklüğü değil, kime denk geldiği: tadımlık "ödeme
öncesi tek gerçek önizleme" argümanı **ürünü ilk kez gören** kişi içindir ve o kişide sıra hiç
değişmiyor. Zaten liste kurmuş ziyaretçi için ekranın ilk işi, kendi kurduğu listeyi göstermektir;
tadımlığı onun üstüne koymak "sana hâlâ ürünü tanıtıyorum" demek olurdu.
## 1.5 Uygulayıcıya tarif (yazılımcı)
**Dosya 1 — `src/features/liste/liste-store.ts`**
1. `PROFIL_CEREZ_ADI` deseninin eşi olarak sabit ekle: `LISTE_SAYI_CEREZ_ADI = "kolaytercih.liste-sayi"`
(sabitin yeri: bu dosya; sunucu tarafı buradan import eder — `liste-store.ts` `"use client"`
olduğu için sabiti `liste-dogrula.ts`'e koymak daha temiz, orası zaten sunucu action'larıyla
paylaşılıyor, bkz. `:23-25`).
2. `sayiCereziniYaz(n: number)` — `profilCerezineYaz` (`sihirbaz-profil.ts:51-57`) ile **birebir aynı**
öznitelikler: `path=/; max-age=31536000; samesite=lax` + https'te `secure`. Değer düz tamsayı.
3. Üç çağrı yeri (listenin değiştiği her yer):
- `kaydet()` içinde (`:42-46`) — ekle/çıkar/sırala/temizle hepsi buradan geçiyor;
- `yukle()` içinde, `liste = dogrula(...)` satırından sonra (`:53`) — dağıtım sonrası ilk
açılışta çerezi kendiliğinden doğurur;
- `"storage"` dinleyicisinde `liste` atandıktan sonra (`:64`) — başka sekmedeki değişiklik.
**Dosya 2 — `src/features/liste/components/manuel-harita.tsx`**
4. İmza: `export function ManuelHarita({ yerAyir = false }: { yerAyir?: boolean })`.
5. `:71` → `if (liste.length === 0 && !yerAyir) return null;`
Boş+`yerAyir` dalında **ayrı bir iskelet dalı yazma**: fonksiyon akmaya devam etsin, `pinler` boş
dizi olur, `TercihHaritasiLazy` monte olur ve chunk inene kadar zaten `HaritaIskeleti`'ni gösterir
(`tercih-haritasi-lazy.tsx:10-21`). Böylece (a) yer tutucu ile gerçek harita **aynı** kutudan gelir,
yükseklik ayrışamaz; (b) 197 KB'lık chunk hidrasyonu beklemeden inmeye başlar.
6. `:78` `<div className="mt-16">` → `<div>`. Harita artık `main`'in ilk çocuğu; üst boşluğu
`main`'in `py-12`'sinden alır (bu, `mt-16`'nın eklenmesinden önceki durumdu). `:73-76`'daki yorum
tersini anlatıyor, **aynı commit'te** düzeltilmeli.
**Dosya 3 — `src/app/sonuc/page.tsx`**
7. `cookies()` zaten okunuyor (`:56`): `const listeSayisi = Number.parseInt(cerezler.get(LISTE_SAYI_CEREZ_ADI)?.value ?? "", 10);`
→ `const yerAyir = Number.isFinite(listeSayisi) && listeSayisi > 0;`
8. `<ManuelHarita yerAyir={yerAyir} />` satırını `:131`'den **`:118`'in üstüne** taşı.
Yeni sıra: **harita → tadımlık → tablo → sihirbaz → dipnot.**
9. `:100-114`'teki yorum bloğu bugünkü kararın tersini belgeliyor; yerine yeni gerekçe
(çerezle yer ayırma) yazılmalı — yoksa bir sonraki oturum haritayı yine aşağı alır.
10. `tadimlik-satiri.tsx:127-133`'teki yorum ("harita artık tablonun altında, üstte hiçbir kardeş
yok") da yalan olur. `first:mt-0` **kuralı doğru çalışmaya devam eder** (harita varken tadımlık
ikinci çocuk olur ve `mt-16` alır, harita yokken ilk çocuktur ve `mt-0` alır) — düzeltilecek olan
yalnız yorumun metni.
**Kabul kriterleri**
1. Listesi olmayan ziyaretçinin `/sonuc` HTML'inde harita ile ilgili **hiçbir düğüm yok**
(`grep -c "aspect-\[1050/447\]"` = 0) ve tadımlık `<section>`'ı `main`'in ilk çocuğu.
2. `kolaytercih.liste-sayi=7` çereziyle istenen `/sonuc` HTML'inde, tadımlık `<section>`'ından
**önce** `aspect-[1050/447]` kutusu var.
3. Listeye ilk `+`'dan sonra sayfa yenilenince harita en üstte ve **yukarı kayma yok**.
4. Listeyi tamamen boşaltıp sayfayı yenileyince harita yok (çerez 0 yazılmış olmalı).
**Büyüklük: S** (3 dosya, ~25 satır + 3 yorum bloğu).
## 1.6 Aynı işte kapanması gereken iki küçük delik
| # | Yer | Sorun | Düzeltme |
|---|---|---|---|
| H1 | `tercih-haritasi-lazy.tsx:14` | `animate-pulse` yer tutucu artık **katlamanın üstünde** ve `prefers-reduced-motion` okunmuyor (WCAG 2.2.2'nin ruhu; sürekli, durdurulamaz hareket) | `motion-reduce:animate-none` ekle (tek sınıf) |
| H2 | `manuel-harita.tsx:84-86` → `yurtdisi-haritalar-lazy.tsx:8-11` | `dynamic(..., { ssr:false })` ama `loading` **yok**; chunk inince ~150 px'lik şerit yoktan belirir. Bugün katlamanın altındaydı, yarın üstünde olacak | `loading: () => <div className="mt-3 h-40" aria-hidden />` ya da eşdeğer sabit yükseklik |
**Artık kalan (kabul edilen) kayma:** listesinde KKTC/yurt dışı program olan kullanıcıda yurt dışı
şeridi + "N tercih haritada gösterilemiyor" satırı sunucuda bilinemez; hidrasyonda belirir. H2
uygulanırsa bu ~150 px'lik tek kaymaya iner (mobil CLS ~0,11). Bunu da sıfırlamak çereze ikinci bir
alan yazmayı gerektirir — **önermiyorum**, kazanç bedeli haklı çıkarmıyor.
## 1.7 Reddedilen seçenekler
- **Her zaman sabit yükseklikli yer tutucu:** Bilal'in "liste eklenmezse gözükmesin" şartını
doğrudan çiğniyor.
- **Harita üstte ama küçük/sabit, büyümeyen biçim:** sorun haritanın *boyutu* değil, `null`→görünür
geçişi. 60 px'lik bir şerit bile aynı kaymayı üretir, üstelik harita okunmaz hale gelir.
- **Girişli kullanıcıda listeyi DB'den okumak:** `/sonuc` bugün oturum açmıyor; oturum + sorgu
eklemek sayfayı yavaşlatır ve **girişsiz** çoğunluğu (funnel'ın asıl kitlesi) hiç kapsamaz.
Çerez ikisini birden çözüyor.
---
# Talimat 2 — Üniversite sayfasında "tür"
## 2.1 Izgara gerçekte 4 kart değil, 4 **hücre**
```
universite-icerik.tsx:291
grid-cols-2 ← mobil
lg:grid-cols-[1fr_1fr_1.35fr] lg:grid-rows-2 ← masaüstü
universite-konum-haritasi.tsx:77
col-span-2 lg:col-start-3 lg:row-start-1 lg:row-span-2
```
Masaüstünde harita 3. sütunu **iki satır boyunca** kaplıyor; kartlara kalan yer 2 sütun × 2 satır =
**tam 4 hücre**. 5. kart şablonu bozar: ya 3. bir satır açılır (harita yanında boşluk kalır) ya da
`grid-rows-2` elle 3'e çıkarılır ve haritanın oranı bozulur. Mobilde 5. kart 2 sütunlu ızgarada
**tek başına** bir satıra düşer (yanında boşluk) ve ilk ekrandan ~90 px daha gider.
→ **(a) "ızgara 5 karta çıksın mı?" cevabı: hayır.** Gerekçe estetik değil, şablonun kendisi.
## 2.2 Asıl mesele: tür bir metrik değil
Izgaradaki diğer üç kart sayısal ve karşılaştırılabilir (Program, Fakülte/Birim, En iyi sıralama,
URAP). "Devlet" bir ölçüm değil, kurumun **kimliği**. Sitenin geri kalanı bunu zaten böyle işliyor:
başlığın altındaki meta satırında küçük bir çip olarak, dört ayrı yerde **birebir aynı sınıf setiyle**:
```
shrink-0 rounded px-1 py-px text-[10px] font-medium leading-4
DEVLET → bg-slate-100 text-slate-600 (kontrast 6,92:1 ✅)
diğer → bg-violet-50 text-violet-600 (kontrast 5,19:1 ✅ — hesap: WCAG 2.x, #7C3AED/#F5F3FF)
```
`program-tablosu.tsx:328-336` · `rapor-listesi.tsx:225-234` · `tadimlik-satiri.tsx:160-166` ·
`bolum-program-listesi.tsx:220-229`
Üniversite **detay** sayfası, bu çipi taşımayan tek yüzey. Eksik olan şey kart değil, çip.
## 2.3 İki alternatif
### Alternatif A — çip, H1'in altındaki meta satırında (ÖNERİLEN)
```
390 px · /universite/koc-universitesi · ilk ekran
Ana Sayfa › Üniversiteler › Koç Üniversitesi
┌────┐ Koç Üniversitesi
│logo│ Taban Puanları
│ 64 │ 2026
└────┘
[Vakıf] İstanbul ← EKLENEN TEK SATIR (mt-3, ~22 px)
çip sınıfı: program-tablosu.tsx:329 ile birebir
Koç Üniversitesi (İstanbul), vakıf üniversitesi; ← gece/0922-urap'taki
2026 YKS yerleştirme sonuçlarına göre … telafi cümlesi KALIR
[ Sıralaman Koç tabanlarının neresinde? → ]
┌─ harita (col-span-2, min-h-44) ──────────┐
│ │
└──────────────────────────────────────────┘
┌───────────────┐┌───────────────┐
│ PROGRAM ││ FAKÜLTE/BİRİM │
│ 78 ││ 12 │
└───────────────┘└───────────────┘
┌───────────────┐┌───────────────┐
│ URAP TÜRKİYE ││ EN İYİ SIRALAMA│ ← 4 hücre, hepsi sayısal
│ 34 / 198 ││ 412 │
│ 2025-2026 ││ │
└───────────────┘└───────────────┘
```
- **Yeni bileşen yok**, yeni sınıf yok: çip dört yerde duran sınıf setinin beşinci kopyası (ya da —
tercihen — hepsinin çıkarılacağı tek kaynak, bkz. §2.5).
- **Türün kaç kez geçtiği:** çip 1 + cümle 1 = 2, biri taranabilir, biri okunabilir. Kart yok.
- **Mobil:** H1 bloğu (logo `size-16` + `text-3xl`) 390 px'te zaten ~180 px; çip satırı 22 px ekler,
taşma riski yok (`flex items-center gap-1.5`, çip `shrink-0`, il `truncate`).
- **Erişilebilirlik:** çip metni okunur ("Vakıf"), renk tek başına anlam taşımıyor — AGENTS.md'nin
risk dili kuralının aynı mantığı.
- **Gereken tek yan değişiklik:** URAP'ı olmayan 31 sayfada 4. hücre bugün "Tür" kartı olarak
dolduruluyor (`gece/0922-urap`, `:394-417`). Çip geldiğinde o sayfalarda tür **üç kez** yazar.
4. hücre oraya **"Toplam kontenjan"** olur (`uni.stats.toplamKontenjan`, `lib/katalog.ts:380-401`).
Veri kontrolü: `data/yokatlas.db` — toplam kontenjanı 0 olan **hiçbir üniversite yok**
(`SELECT … GROUP BY universite HAVING SUM(COALESCE(kontenjan2026,0))=0` → 0 satır), yani bu hücrede
asla "—" çıkmaz. Precedent var: `minSira` de hem paragrafta hem kartta geçiyor.
### Alternatif B — "Tür" kartı geri gelir, URAP satır olur
```
Koç Üniversitesi (İstanbul), vakıf üniversitesi; 2026 YKS …
URAP Türkiye sıralaması: 34 / 198 (2025-2026) ← paragrafın altına yeni satır
┌ PROGRAM 78 ┐┌ FAKÜLTE 12 ┐
┌ TÜR Vakıf ┐┌ EN İYİ 412 ┐
```
- Artısı: diff küçük, `gece/0922-urap`'ın kart kısmı geri alınır.
- Eksileri: (a) **sayı prozaya gömülür** — URAP taranmak için var, "34/198" cümle içinde aranır hale
gelir; tasarımın dünkü gerekçesinin tersi. (b) Izgarada kategori/metrik karışımı sürer.
(c) Tür yine `text-2xl font-bold` ile "Vakıf MYO" gibi uzun değerlerde 190 px'lik karta sığmaz.
(d) Çip eksikliği kapanmaz: detay sayfası hâlâ sitenin geri kalanından farklı konuşur.
### Hüküm: **A**
Bilal'in "tür olsun" isteği A'da daha güçlü karşılanıyor: tür artık **ızgaranın dördüncü kutusunda,
ekranın ortasında** değil, **üniversitenin adının hemen altında** — hem ilk bakışta hem her sayfa
genişliğinde. Ve site genelindeki çiple aynı dili konuşuyor.
## 2.4 Uygulayıcıya tarif (yazılımcı)
Taban: `gece/0922-urap` (orada `UNITUR_ETIKET` ve telafi cümlesi hazır). Dal bayatsa önce
`origin/main` üstüne yeniden kurulmalı.
1. `UNITUR_ETIKET`i (`universite-icerik.tsx:54-66`) **`src/lib/unitur.ts`**'e taşı ve üçüncü alan
ekle: `{ kart, cumle, cip }`. `cip` = çipte görünen kısa metin (`Devlet`, `Vakıf`, `Vakıf MYO`,
`KKTC`, `Yurt dışı (kamu)`, `Yurt dışı (vakıf)`) — `kart` ile aynı değerler, ama `kart` alanı
§2.4-3 ile ölüyorsa ikisi birleştirilebilir. Gerekçe: aynı bilginin ekrandaki etiketi tek
kaynaktan gelmeli — `DILIM_ETIKET`'in `src/lib/risk.ts`'te durması gibi (AGENTS.md).
2. `universite-icerik.tsx`, H1 bloğunun (`:253-259`) **hemen altına**, `ilkSayfa` koşulundan
**bağımsız** (sayfalama sayfalarında da üniversite kimliği aynı):
```
<p className="mt-3 flex items-center gap-1.5 text-xs text-slate-500"> …çip… {il} </p>
```
Çipin sınıfı `program-tablosu.tsx:329-333`'ten **kopyalanır**, yeni varyant üretilmez.
Renk kuralı da aynı: `DEVLET` → slate, diğer her şey → violet.
`uni.unitur` NULL ise satırın tamamı render edilmez (kopuk boşluk kalmaz).
3. Izgaradaki 4. hücre (`gece/0922-urap`, `:394-417`): `uni.urap ? URAP : Tür` koşulu
→ `uni.urap ? URAP : Toplam kontenjan`. `dt`: "Toplam kontenjan", `dd`: `sayi(uni.stats.toplamKontenjan)`.
4. `bolum-program-listesi.tsx:227` `trBaslikDuzeni(p.unitur)` → `UNITUR_ETIKET[p.unitur].cip`.
Bugün o çip "Kktc" / "Vakıf Myo" / "Yurtdısı Kamu" basıyor (`slug.ts:56-64` tr-TR küçültmesi).
**Aynı commit'e girmeli**: tek kaynak taşınırken düzelmeyen tek tüketici kalmasın.
**Kabul kriterleri**
1. `bogazici-universitesi.html` → çipte "Devlet" **1 kez**, özet cümlesinde "devlet üniversitesi"
**1 kez**, ızgarada "Tür" **0 kez**.
2. `dogu-akdeniz-universitesi.html` (URAP yok) → çipte "KKTC", 4. hücrede "Toplam kontenjan" + sayı,
hiçbir hücrede "—" yok.
3. `/bolum/...` sayfalarında KKTC programının çipinde "KKTC" yazıyor, "Kktc" değil.
4. 390 px'te H1 bloğu + çip satırı yatay taşma üretmiyor (uzun üniversite adı: "İstanbul Medeniyet
Üniversitesi").
5. `pnpm build` temiz; 229 üniversite sayfasının 229'unda çip var.
**Büyüklük: S** (2 yeni/taşınan dosya + 3 dokunuş, ~50 satır).
## 2.5 Aynı işte kapanacak tutarlılık borcu
| Yer | Sorun | Kanonik kaynak |
|---|---|---|
| `program-tablosu.tsx:329` · `rapor-listesi.tsx:227` · `tadimlik-satiri.tsx:161` · `bolum-program-listesi.tsx:222` | Aynı çip sınıf seti **4 kez elle kopyalanmış**; 5.'si ekleniyor | AGENTS.md "ortak elemanlar tek kaynaktan". **Öneri:** `src/components/unitur-cipi.tsx` — küçük, tek amaçlı sunum bileşeni. Bu, "yeni bileşen tasarlama" yasağının kapsamına girmez: yeni bir *tasarım* değil, var olan beş kopyanın tek kaynağa alınmasıdır. Ayrı ve tek amaçlı commit. |
| `app/api/katalog-ara/route.ts:49-56` | `unitur` için **beşinci** sözlük: "KKTC / Yurt dışı" (arama sonucunda böyle, sayfada "KKTC") | Aynı `UNITUR_ETIKET`. Düşük öncelik; ayrı iş. |
| `program-tablosu.tsx:175` | Root `<section className="mt-16">` — `first:mt-0` yok. Girişli + listesiz kullanıcıda (tadımlık yok, harita yok) tablo `main`'in ilk çocuğu olur, tepede 64 px ölü alan kalır | `tadimlik-satiri.tsx:134` deseni (`mt-16 first:mt-0`). XS, Talimat 1 ile aynı commit'e sığar |
---
## Bilal'in gözle kontrol etmesi gerekenler (375–390 px)
1. **`/sonuc`, listeye 1-2 program ekledikten sonra sayfayı yenile.** Harita en üstte mi, açılışta
yukarı/aşağı zıplama var mı? (Talimat 1'in tek gerçek testi.)
2. **`/sonuc`, tarayıcı verisini sildikten sonra ilk kez.** Harita hiç yok mu, tadımlık en üstte mi
ve üstünde fazladan boşluk var mı? (`first:mt-0` kontrolü.)
3. **`/sonuc`, girişsiz + listeli.** Tadımlığın başlığı ("Yapay zekâ listendeki her satırı böyle
anlatır") hâlâ katlamanın üstünde mi? Hükmüm buna dayanıyor (§1.4).
4. **Listesinde KKTC/yurt dışı programı olan bir listeyle `/sonuc`.** Türkiye haritasının altındaki
yurt dışı şeridi geç belirip içeriği itiyor mu? (§1.6-H2)
5. **`/universite/koc-universitesi` ve `/universite/dogu-akdeniz-universitesi`.** Üniversite adının
altındaki tür çipi ilk bakışta görülüyor mu, yoksa çok mu küçük kalıyor? (A alternatifinin tek
öznel riski budur; "daha büyük olsun" dersen o **yeni bir çip varyantı** demektir ve site
genelindeki dört kopyayı da etkiler → `[BİLAL]` kararı.)
## Bilal'den istenen (3 madde)
1. **Talimat 1 için A'yı onayla** (liste sayısı çerezi + harita en üstte). Yeni bir işlevsel çerez
yazılacak; kişisel veri taşımıyor, `kolaytercih.profil` ile aynı sınıf. **~2 dk.**
2. **Talimat 2 için A'yı onayla**: tür = ad altındaki çip, URAP'ı olmayan 31 sayfada 4. kart
"Toplam kontenjan" olur. **~2 dk.**
3. **§2.5'teki `UniturCipi` tek-kaynak commit'i** ayrı iş olarak backlog'a girsin mi
(5 kopya → 1 bileşen)? **~1 dk.**

View File

@@ -0,0 +1,115 @@
# 10 — `/sonuc` haritası en üstte (Talimat 1)
> Dal `is/0923-harita-ustte`, worktree `.claude/worktrees/0923-harita`, temel `origin/main` (c5769e2).
> Commit: **`ea4b30a`** — `fix(sonuc): harita en üste çıkar, liste sayısı çerezi kaymayı sıfırlar`.
> Çalışma ağacı temiz, push/merge yok. Tarayıcı/dev sunucusu açılmadı (AGENTS.md).
## Özet — 5 madde
1. `ManuelHarita` `/sonuc`'ta tablonun altından **en üste** alındı (tadımlığın üstüne). `mt-16`
kalktı; harita artık `main`'in ilk çocuğu, üst boşluğunu `main`'in `py-12`'sinden alıyor.
2. **Liste boşken harita DOM'a hiç girmiyor** — yer tutucu da yok. Bilal'in şartı korundu, ürünü
ilk kez gören ziyaretçide sayfa sırası hiç değişmiyor (tadımlık → tablo).
3. CLS için `kolaytercih.liste-sayi` çerezi eklendi: client listenin **yalnızca uzunluğunu**
aynalıyor, sunucu hidrasyondan önce haritaya tam ölçülü yer ayırıyor. Öznitelikler
`kolaytercih.profil` ile birebir aynı (`path=/; max-age=31536000; samesite=lax` + https'te `secure`).
4. Şartnamenin ötesinde tek ekleme: `yerAyir` **yalnızca hidrasyona kadar** geçerli
(yeni `useListeHidreMi()`). Gerekçe aşağıda — yoksa çekmeceden liste boşaltılınca boş Türkiye
haritası ekranda kalıyordu.
5. Tasarımcının işaretlediği iki açık kapandı: iskelete `motion-reduce:animate-none`, yurt dışı
şeridine sabit yükseklikli `loading` yer tutucu. Kararın **tersini** belgeleyen üç yorum bloğu
aynı commit'te güncellendi.
## Değişen dosyalar (7 dosya, +117 / −33)
| Dosya | Ne oldu |
|---|---|
| `src/features/liste/liste-dogrula.ts` | `LISTE_SAYI_CEREZ_ADI = "kolaytercih.liste-sayi"` sabiti (client store ile sunucunun ortak zemini) |
| `src/features/liste/liste-store.ts` | `sayiCereziniYaz()`; `kaydet()`, `yukle()` ve `"storage"` dinleyicisinde çağrı + `useListeHidreMi()` |
| `src/features/liste/components/manuel-harita.tsx` | `yerAyir` prop'u, `mt-16` kaldırıldı, boş-liste yorumu yeniden yazıldı |
| `src/features/liste/components/tercih-haritasi-lazy.tsx` | İskelete `motion-reduce:animate-none` |
| `src/features/liste/components/yurtdisi-haritalar-lazy.tsx` | `loading: () => <div className="mt-3 h-26" aria-hidden />` |
| `src/app/sonuc/page.tsx` | Çerez okuma (`haritayaYerAyir`), harita `:118`'in üstüne, yorum bloğu yeniden yazıldı |
| `src/features/rapor/components/tadimlik-satiri.tsx` | `first:mt-0` yorumu yeni sıraya göre düzeltildi |
## Şartnameden iki sapma (ikisi de bilinçli)
**1. `yerAyir` hidrasyondan sonra geçersiz (ek: `useListeHidreMi`).**
Şartname `if (liste.length === 0 && !yerAyir) return null;` diyordu. Bu hâliyle `yerAyir` sunucudan
gelen sabit bir prop olduğu için iki durumda boş harita ekranda **kalıcı** olurdu:
kullanıcı çekmeceden listeyi tamamen boşaltınca (çerez 0'a döner ama prop değişmez) ve bayat çerez
durumunda (localStorage silinmiş, çerez kalmış). İkisi de Bilal'in "liste eklenmezse gözükmesin"
şartını doğrudan çiğniyordu. Çözüm: store'un hidre olup olmadığını aynı snapshot'tan okumak —
```
if (liste.length === 0 && (hidre || !yerAyir)) return null;
```
`useListeHidreMi()` sunucu snapshot'ında `false`, hidrasyondan sonra `true` döner ve `liste` ile
**aynı** `abone`/`yukle()` çağrısından doğar; ikisi tek render'da tutarlı, hidrasyon uyuşmazlığı yok.
Ayrı iskelet dalı yazılmadı — fonksiyon akıyor, `pinler=[]` ile `TercihHaritasiLazy` monte oluyor ve
chunk hidrasyonu beklemeden inmeye başlıyor (şartnamenin istediği gibi).
**2. Yurt dışı yer tutucusu `h-40` değil `h-26`.**
Şartname "`h-40` ya da eşdeğer sabit yükseklik" diyordu. Gerçek yüksekliği koddan hesapladım:
figür `w-52` (208 px) × svg oranı 1050/447 = 88,6 px + `figcaption` `text-xs` 16 px ≈ **104,6 px**
(`yurtdisi-haritalar.tsx:60-128`). `h-26` = 104 px; `h-40` (160 px) ~55 px fazladan rezerve edip
kendisi kayma üretirdi. Kap `mt-3`'ü bileşenin kendi kökündekiyle aynı. Birden çok ülke alt satıra
kayarsa fark kalır — sunucu ülke sayısını bilemez (şartnamenin kabul ettiği artık kayma).
## Kabul kriterleri
| # | Kriter | Durum | Nasıl |
|---|---|---|---|
| 1 | Listesi olanda harita en üstte, hidrasyonda zıplama yok | ✔ **koddan çıkarım** | `sonuc/page.tsx:131` artık `TadimlikBolumu`'nun (`:134`) üstünde; `haritayaYerAyir` çerezden `cookies()` ile okunuyor, `pinler=[]` yolunda `HaritaIskeleti` aynı `aspect-[1050/447]` kutusunu veriyor. Tarayıcıda ölçülmedi. |
| 2 | Liste boşken harita DOM'da hiç yok, fazladan boşluk yok | ✔ **koddan çıkarım** | `manuel-harita.tsx` `return null` dalı korundu; JSX yorumları düğüm üretmediği için tadımlık `main`'in ilk çocuğu kalıyor → `first:mt-0` (`tadimlik-satiri.tsx:134`, dokunulmadı) geçerli. |
| 3 | İlk kez gelen ziyaretçide sıra değişmiyor | ✔ **koddan çıkarım** | Çerez yok → `Number.parseInt("")` = `NaN` → `haritayaYerAyir=false` → harita `null`. Sıra: tadımlık → tablo. |
| 4 | Çerez kişisel veri taşımıyor | ✔ **kod okuması** | Değer düz tamsayı (`${LISTE_SAYI_CEREZ_ADI}=${n}`); program kimliği, üniversite adı, sıralama yok. |
| 5 | `next typegen` + `tsc --noEmit` + `lint` temiz | ✔ **çalıştırıldı** | Üçü de hatasız geçti. |
| 5b | `pnpm build` temiz | ✘ **doğrulanamadı — diskte yer yok** | Bkz. aşağıdaki engel. Turbopack derlemesi **başarılı** ("✓ Compiled successfully in 76s"), build'in içindeki TypeScript adımı `.tsbuildinfo` yazarken `ENOSPC` ile düştü. Aynı kod üzerinde ayrı koşan `tsc --noEmit` temiz geçtiği için tip riski görmüyorum; yine de build **tekrar koşulmalı**. |
## Engel — makinede disk doldu
`/System/Volumes/Data` %100 dolu, ~1 GB boş. Bir Next build'i (229 statik üniversite sayfası)
bunu aşıyor; ikinci denemede kabuk bile çıktı dosyası açamaz hâle geldi. Kendi worktree'mdeki
`.next`'i sildim (81 MB → 0). Başkalarının worktree'lerine dokunmadım ama yer oradan çıkacak:
```
.claude/worktrees/0922-entegrasyon/.next 1,4 G
.claude/worktrees/0922-urap/.next 1,4 G
```
Bu iki klasör başka dalların build çıktısı (gitignore'lu, silinmesi veri kaybı değil) — **silme
kararı benim yetkimde değil**, orkestratör ya da Bilal onaylarsa yer açılıp build tekrar koşulabilir.
## Merge sonrası doğrulanması gerekenler
1. **`pnpm build`** — yukarıdaki engel yüzünden hiç tamamlanamadı; merge öncesi mutlaka temiz geçmeli
(AGENTS.md: push öncesi build zorunlu, üstelik push edilecek commit'in kendisi üzerinde).
2. `/listem` ve navbar rozeti hâlâ aynı store'a abone — `kaydet()` yoluna eklenen çerez yazımı her
mutasyonda koşuyor, ekstra render tetiklemiyor ama akışların bir kez elden geçmesi iyi olur.
3. Çerez, site genelinde ilk açılan sayfada (ana sayfa) doğuyor çünkü liste store'u `layout.tsx`
üzerinden her sayfada yükleniyor; `/sonuc`'a doğrudan gelen yeni ziyaretçide ilk açılışta çerez
henüz yoktur — o tek seferlik durumda harita hidrasyonda belirir (şartnamenin kabul ettiği kaçak).
4. `/meraklisina` mermaid şeması **etkilenmiyor**: sihirbaz adımları, aday havuzu, 24'lük iskelet ve
dilim etiketleri değişmedi; bu commit yalnızca `/sonuc`'un DOM sırasına dokunuyor.
5. Tasarımcının §2.5'te işaretlediği `program-tablosu.tsx:175` (`mt-16` ama `first:mt-0` yok →
girişli + listesiz kullanıcıda tepede 64 px ölü alan) **kapsamım dışındaydı**, dokunmadım.
## Güvenlik notu
Bu commit yeni bir çerez yazıyor (`kolaytercih.liste-sayi`, yalnız tamsayı, 1 yıl, `samesite=lax`).
Değer kişisel veri taşımasa da gizlilik/çerez metinleriyle örtüşmenin kontrolü için
**`guvenlik-uyum` denetimi önerilir**.
## Bilal'den istenen (3 madde)
1. **Diskte yer aç** (ya da yukarıdaki iki `.next` klasörünü silmeye izin ver) ki `pnpm build`
koşulabilsin — merge'ün tek açık şartı bu. **~2 dk.**
2. **`/sonuc`'u telefonda üç durumda gözle kontrol et**: (a) listeye 1-2 program ekleyip sayfayı
yenile — harita en üstte mi, zıplama var mı; (b) tarayıcı verisini silip ilk kez gir — harita hiç
yok mu, tadımlığın üstünde fazladan boşluk var mı; (c) çekmeceden listeyi boşalt — harita anında
kayboluyor mu. **~5 dk.**
3. **Yeni çerez onayın**: `kolaytercih.liste-sayi`, yalnız tamsayı, 1 yıl, işlevsel amaçlı —
`kolaytercih.profil` ile aynı sınıf. Gizlilik metninde çerez sayımı/listesi varsa oraya da
eklenmesi gerekir mi? **~2 dk.**