Files
kolaytercih/docs/gece-vardiyasi/2026-09-22/26-veri-kalitesi.md
bilalgursen 2b350d0c5d content(rehber): gece vardiyasının 14 yeni yazısı + 2 düzeltme, ekip raporları ve kurallar
14 yeni rehber yazısı (hepsi editör denetiminden geçti, 46 yazıda 829 iç link
tarandı, 0 kırık hedef; 180+ DB rakamı yazıların kendi filtreleriyle yeniden
koşuldu):

  taban-siralamalari-alti-yilda-nasil-degisti · kac-net-ile-hangi-bolum ·
  siralama-bandlari-hangi-kapilar-acilir · ayni-bolum-farkli-universite-siralama-farki ·
  hangi-bolumlerin-kontenjani-azaldi · yeni-acilan-bolumler-nasil-degerlendirilir ·
  bilgisayar-mi-yazilim-muhendisligi-mi · kktc-universiteleri-okunur-mu ·
  yapay-zekaya-tercih-sordum-guvenilir-mi · ek-madde-1-puanim-yetiyor-mu ·
  universitede-ilk-hafta-ders-kaydi-muafiyet-kayit-dondurma · yks-2027-takvimi ·
  rehber-ogretmenler-icin-veri-kaynaklari · bolumumu-sevmedim-hangi-kapilar-var

İki canlı yazı düzeltildi:
- veliler-icin-tercih-rehberi: veli diliyle kapanış; altındaki "sıralamanı gir"
  kutusu artık veliye ölü uç değil
- bos-kontenjanlar-ne-anlatiyor: "birkaç yüz kişilik fark" aslında ~9 bin
  (ÖSYM 265.356 ↔ bizim 256.532); payda açıklaması tersti — ek kontenjan
  paydaya girseydi oran düşerdi. guncelleme alanı eklendi (sitemap lastmod).

Kurallar (AGENTS.md):
- Yayın sonrası arama motoru bildirimi: URL denetimi yalnız canlıda 200 dönen
  adrese; kota sayacı; mülk/hesap notu; sitemap grep kuralı
- Yıkıcı git komutları yasak: reset/checkout --/restore/clean/stash
- Tarayıcı açma: tasarımcı ve UX gerekli gördüğünde açabilir (yetenek için
  .claude/agents/tasarimci.md'ye tarayıcı araçları eklendi)

docs/gece-vardiyasi/2026-09-22/ — 34 rapor: CEO kararları ve kapanış turu,
marka/ödeme analizi, sıfır ödeme teşhisi, SEO konu planı, veri kalitesi,
editör denetimi, CTO şartnamesi, yazılımcı ve güvenlik raporları, SABAH-OZETI.

docs/ekip/YAYIN-KUYRUGU.md — Search Console bildirim kuyruğu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 23:25:34 +03:00

33 KiB
Raw Blame History

26 — Veri mühendisi: programs.il temizliği (A4/T34) + vakıf burs etiketi (A5)

Tarih: 22 Eylül 2026, gece vardiyası · Rol: veri-muhendisi · Mod: yalnız teşhis. Yazılan tek dosya: bu rapor. data/ altına, veritabanına, scripts/ altına hiç yazılmadı; tüm bağlantılar sqlite3 -readonly data/yokatlas.db. Kopya alınmadı (disk), pnpm install/build koşulmadı.

Kanıt işaretleri: [D] bu gece koşulan sorgu · [K] koddan çıkarım (dosya:satır) · [doğrulanmalı] birincil kaynakla teyit şart.


1. Beş maddelik özet

  1. Kök neden tek cümle: il, fakulte ve isim alanları yalnız INSERT yolunda yazılıyor, UPDATE yolunda hiç tazelenmiyor. scripts/refresh.ts:665-690 — mevcut satıra yalnız sira/puan/kontenjan/yerlesen yazılır. Bu yüzden 2021'de üçüncü parti bir CSV'den (scripts/ingest.ts:4 → yokatlas-dataset-2025) gelen konum ve ad değerleri kalıcı: API doğrusunu söylese bile DB'ye giremiyor. il kirliliği bir "YÖK Atlas hatası" değil, bizim tazeleme hattımızın kör noktası.
  2. A5'in cevabı: kırılım YAPILABİLİR, veri elimizde. Burs/dil etiketi YÖK Atlas API'sinde ayrı bir kolon değil, birimAdi alanının içinde geliyor ("Tıp (İngilizce) (Burslu)") — scripts/refresh.ts:37-39 bunu açıkça yazıyor. Kanıt: 2026'da yeni açılan vakıf lisans satırlarının %93,7'si (326/348) etiketli, eskiden beri var olan satırların yalnız %14,2'si (628/4.427) etiketli [D]. Fark, --isim-esitle bayrağının varsayılan KAPALI olmasından geliyor. Tek koşuyla geri alınabilir. → T11 "kırılım var" tarafına düşer.
  3. A4'ün rakamları: 21.493 satırın 20.092'si tam ve temiz 81 Türkiye ili, 1.291'i KKTC/yurt dışı, 110'u boş [D]. Türkiye tarafında eksik il yok, uydurma il yok — yani /il/[slug] (T7) yalnız bir filtreye ihtiyaç duyuyor, şema değişikliğine değil.
  4. Yanlış unitur 100 satır (Kıbrıs Amerikan 42 + Uluslararası Balkan 34 + Uluslararası Saraybosna 24) [D] — bunlar sihirbazın "Devlet" adımında devlet programı gibi görünüyor. Ters yönde daha büyük bir sızıntı var ve şimdiye dek raporlanmamış: sihirbazın "Vakıf (burslu olursa)" kovası unitur <> 'DEVLET' ile kurulduğu için (src/lib/db.ts:77-83) içine 1.256 KKTC/yurt dışı satırı giriyor — vakıf havuzunun %13,4'ü.
  5. Kapsam dışı ama yayını etkileyen bulgu: kontenjan<yıl> kolonu yalnız genel kontenjanı tutuyor (kontenjan2025 = gk2025, 19.857/19.857 satır), yerlesen<yıl> ise tüm kota türlerinin toplamı (gkY+sgyY+dprmY+obkY, scripts/refresh.ts:212-217). Bu yüzden devlet lisans doluluğu %102,1 çıkıyor [D] — matematiksel olarak imkânsız. Bu gece yayımlanan bilgisayar-mi-yazilim-muhendisligi-mi yazısındaki "devlet YM %102,1" rakamı bu sapmadır. KKTC ve vakıf rakamları etkilenmiyor (KKTC'de aşan satır 0; devlet lisansta 5.618) — yani kktc-universiteleri-okunur-mu yazısı güvende.

2. Veri kalitesi tablosu

# Kontrol Sorgu (hepsi sqlite3 -readonly data/yokatlas.db) Sonuç Önem
K1 Farklı il değeri sayısı SELECT COUNT(DISTINCT il) FROM programs 91 (81 TR ili + 6 KKTC + 4 yurt dışı) bilgi
K2 Boş il SELECT COUNT(*) FROM programs WHERE il IS NULL 110 satır / 9 kurum; hepsi son_kilavuz_yili=2026 P1
K3 TR dışı il satırı WHERE il IN ('LEFKOŞA','GİRNE','GAZİMAĞUSA','LEFKE','GÜZELYURT','KIBRIS','SARAYBOSNA - BOSNA - HERSEK','BİŞKEK-KIRGIZİSTAN','TÜRKİSTAN-KAZAKİSTAN','BAKÜ-AZERBAYCAN') 1.291 P1
K4 TR ili tam mı aynı listenin NOT IN'i + COUNT(DISTINCT il) 81/81 — eksik il yok, fazla il yok ✓ temiz
K5 Yanlış unitur='DEVLET' WHERE unitur='DEVLET' AND (universite LIKE 'KIBRIS AMERİKAN%' OR LIKE '%BALKAN%' OR LIKE '%SARAYBOSNA%') 100 satır P1
K6 "Vakıf" kovasının saflığı WHERE unitur IS NOT NULL AND unitur<>'DEVLET' GROUP BY unitur VAKIF 7.974 · KKTC 1.145 · VAKIF MYO 121 · YURTDISI KAMU 99 · YURTDISI VAKIF 12 P1
K7 unitur boş WHERE unitur IS NULL 0 ✓ (unitur-onar.ts işini yapmış) ✓
K8 Aynı kurumun iki universite yazımı bazAd çakışma sorgusu (§3.4) 95 kurum / 1.449 satır — bildirilen "10 KKTC üniversitesi" değil, katalogun tamamı P2
K9 Aynı kurumda iki farklı unitur K8 sorgusunun unitur kırılımı 4 kurum: Balkan, Saraybosna, Manas, Yesevi (DEVLET↔YURTDISI KAMU / KAMU↔VAKIF) P1
K10 Ayırt edilemeyen program adı (2026 aktif) GROUP BY universite,isim,tur,onlisans HAVING COUNT(*)>1 4.206 grup / 10.164 satır (Vakıf 6.079 · Devlet 3.209 · KKTC 985) P1
K11 Vakıf lisansta burs etiketi §4 sorgusu 3.821/4.775 etiketsiz (KKTC 747/849) — bildirilen rakam doğrulandı P1
K12 Etiketin satır yaşıyla ilişkisi §4 sorgusu 2026'da yeni: 326/348 etiketli; eskiden var olan: 628/4.427 kanıt
K13 yerlesen > kontenjan WHERE yerlesen2026 > kontenjan2026 11.964/21.493 satır; devlet lisansta 5.618, KKTC lisansta 0 P1
K14 kontenjan = genel kontenjan mı kontenjan2025 <> gk2025 0 satır → evet, yalnız genel kontenjan kanıt
K15 netler yetim satırı LEFT JOIN programs ... WHERE p.id IS NULL 8.215 satır (silinmiş/kimliği devredilmiş kodlara ait) P2
K16 netler yıl kapsamı SELECT MAX(yil) FROM netler 2025 — T36 doğrulandı, 2026 net verisi yok P1
K17 Aykırı sıralama WHERE sira2026<=0 0 ✓ ✓
K18 fakulte boş (2026 aktif) WHERE fakulte IS NULL AND kontenjan2026 IS NOT NULL 0 ✓ ✓

3. Görev 1 — programs.il (A4 / T34)

3.1 Bildirilen bulguların tek tek doğrulanması

Bildirilen Durum Kanıt
91 farklı il değeri DOĞRU K1
il boş 110 satır / 9 üniversite DOĞRU K2; kurum listesi §3.2
Uluslararası Balkan Ü. 26 satır il='SARAYBOSNA - BOSNA - HERSEK', kurum Üsküp'te DOĞRU (26 satır) — ayrıca aynı kurumun 8 satırı il boş, 14 satırı ayrı adla YURTDISI KAMU §3.2
Kıbrıs Amerikan Ü. 42 satır unitur='DEVLET' DOĞRU (36 + 6, iki ad hattı) K5
src/lib/db.ts:79 devlet filtresi bunları devlet sayıyor DOĞRU uniturFiltreSql, src/lib/db.ts:77-83 [K]
10 KKTC üniversitesi iki ayrı universite adıyla duruyor DOĞRU ama eksik tarif — aynı desen 95 kurumda var (1.449 satır). KKTC'ye özgü değil, API'nin 2026 ad biçiminden geliyor K8
ODTÜ KKK programları ana kampüsle aynı universite ve il altında YARI ÇÜRÜTÜLDÜ. 8 KKK satırı il='KIBRIS' ve fakulte='ODTÜ Kuzey Kıbrıs Kampusu (KKTC-Güzelyurt)' taşıyor — yani ayrılabilir. Ortak olan yalnız universite. Ama ODTÜ Yazılım Mühendisliği'nin 2 satırı gerçekten il='ANKARA', fakulte='Mühendislik Fakültesi' ile duruyor → yazarın asıl iddiası doğru, sebebi farklı §3.3

3.2 Boş il: 110 satır, 9 kurum, hepsi 2026 kılavuzundan

SELECT universite, unitur, COUNT(*) FROM programs WHERE il IS NULL GROUP BY 1,2 ORDER BY 3 DESC;
Kurum unitur Satır il nereden türetilebilir
TİRAN NEW YORK ÜNİVERSİTESİ (TİRAN-ARNAVUTLUK) YURTDISI KAMU 36 ad parantezi
İÇİŞLERİ BAKANLIĞI VE MİLLİ SAVUNMA BAKANLIĞI ADINA SAĞLIK BİLİMLERİ ÜNİVERSİTESİNDE EĞİTİM ALACAKLAR DEVLET 15 türetilemez (sahte kurum adı; bakanlık kontenjanı)
ULUSLARARASI BALKAN ÜNİVERSİTESİ (ÜSKÜP-MAKEDONYA) YURTDISI KAMU 14 ad parantezi
ULUSLARARASI SARAYBOSNA ÜNİVERSİTESİ (SARAYBOSNA-BOSNA-HERSEK) YURTDISI KAMU 14 ad parantezi
ULUSLARARASI SARAYBOSNA ÜNİVERSİTESİ DEVLET 9 kardeş satırdan
ULUSLARARASI BALKAN ÜNİVERSİTESİ DEVLET 8 kardeş satırdan (Üsküp, Saraybosna değil)
KIRGIZİSTAN-TÜRKİYE MANAS ÜNİVERSİTESİ (BİŞKEK-KIRGIZİSTAN) YURTDISI VAKIF 8 ad parantezi
HOCA AHMET YESEVİ … (TÜRKİSTAN-KAZAKİSTAN) YURTDISI VAKIF 4 ad parantezi
KIBRIS AMERİKAN ÜNİVERSİTESİ DEVLET 2 kardeş satırdan (LEFKOŞA)

95 satır ad/kardeş satırdan doldurulabilir; 15 satır (MSB/İçişleri) doldurulamaz — ÖSYM bunları tek bir yerleşkeye bağlamıyor. Tahminle doldurmuyoruz; eksik, eksik kalır ve veri-kalite'de beyaz listeye alınır.

3.3 Kök neden — hangi betik hangi alanı nereden yazıyor

Alan Yazan Kaynak UPDATE yolunda tazeleniyor mu
il ingest.ts (2021 CSV) → sonra yalnız refresh.ts INSERT (r.ilAdi, satır 679) üçüncü parti CSV, sonra API HAYIR
unitur ingest.ts CSV → refresh.ts INSERT uniturSec() (518-524) → unitur-onar.ts yalnız NULL'lar CSV, sonra API (koşullu) HAYIR
fakulte ingest.ts CSV → refresh.ts INSERT fakulteSec() CSV, sonra API HAYIR
isim ingest.ts CSV → refresh.ts INSERT r.birimAdi; UPDATE yalnız --isim-esitle ile (643-648) CSV, sonra API bayrak kapalıyken HAYIR
sira/puan/kontenjan/yerlesen refresh.ts UPDATE (474-480) API evet

Üç mekanizma üst üste biniyor:

(a) UPDATE körlüğü. refresh.ts:665-680 — if (exists.get(id)) update.run(...) yalnız dört yıl kolonunu yazar. 2021 CSV'sinden gelen il/fakulte/isim beş yıldır hiç tazelenmedi. Kıbrıs Amerikan'ın 36 satırı il='LEFKOŞA', Balkan'ın 26 satırı il='SARAYBOSNA - BOSNA - HERSEK' — ikisi de 2021 verisi taşıyor (kontenjan2021 IS NOT NULL), yani CSV kökenli [D].

(b) uniturSec yanlışı kalıcılaştırıyor. refresh.ts:519-525:

const uniturSec = (universite: string, api: string | null): string | null => {
  for (const k of [universite, bazAd(universite)]) {
    const set = uniturlar.get(k);
    if (set?.size === 1) return [...set][0];   // ← DB'deki değer API'yi EZİYOR
  }
  return api;
};

Niyet doğru (sözlük tutarlılığı, uniturOnar'ın çelişki koruması patlamasın diye), sonuç ters: API "KKTC" dese bile DB'de tek bir DEVLET varsa DEVLET yazılır. Kıbrıs Amerikan'ın 2026'da eklenen 6 yeni satırı bu yüzden de DEVLET oldu.

(c) İki ayrı ad hattı = iki ayrı kaynak. API 2026'da üniversite adını AD (İL) biçiminde veriyor (ORTA DOĞU TEKNİK ÜNİVERSİTESİ (ANKARA), DOĞU AKDENİZ ÜNİVERSİTESİ (KKTC-GAZİMAĞUSA)). CSV ise sade ad + ayrı il veriyordu. Böylece 95 kurumda iki paralel ad hattı oluştu:

-- K8
WITH b AS (SELECT universite, unitur, il,
  TRIM(CASE WHEN universite LIKE '%)' THEN substr(universite,1,instr(universite,' (')-1)
            ELSE universite END) baz, COUNT(*) n
  FROM programs GROUP BY 1,2,3)
SELECT baz, universite, unitur, COALESCE(il,'(NULL)'), n FROM b
WHERE baz IN (SELECT baz FROM b GROUP BY baz HAVING COUNT(DISTINCT universite)>1);
-- 95 kurum / 1.449 satır

Yıl profili: AD (TR ili) eki 1.224 satır/80 kurum · AD (KKTC-şehir) 149/10 · AD (şehir-ülke) 76/5 [D].

ODTÜ örneği — sorunun tam resmi. universite='ORTA DOĞU TEKNİK ÜNİVERSİTESİ (ANKARA)' altında 11 satır var: 3'ü il='ANKARA' (KKTC uyruklu kontenjanı, doğru), 8'i il='KIBRIS' + fakulte='ODTÜ Kuzey Kıbrıs Kampusu (KKTC-Güzelyurt)' (doğru). Ama universite='ORTA DOĞU TEKNİK ÜNİVERSİTESİ' altındaki 72 eski CSV satırının içinde Yazılım Mühendisliği de var:

SELECT universite, il, fakulte, isim, kontenjan2026, sira2026
FROM programs WHERE isim LIKE 'Yazılım Müh%' AND universite LIKE 'ORTA DOĞU%';
-- ORTA DOĞU TEKNİK ÜNİVERSİTESİ | ANKARA | Mühendislik Fakültesi | Yazılım Mühendisliği | 45 | 299241
-- ORTA DOĞU TEKNİK ÜNİVERSİTESİ | ANKARA | Mühendislik Fakültesi | Yazılım Mühendisliği |  5 | 293024

ODTÜ Ankara'da Yazılım Mühendisliği yoktur; program Kuzey Kıbrıs Kampusu'ndadır (ncc.metu.edu.tr/tr/sng, yazar 13 teyit etmiş) [doğrulanmalı: 2026 ÖSYM kılavuzu]. 45/5 kontenjan ikilisi de KKK'nin klasik Burslu / %50 İndirimli desenidir — ama iki satırın adı birebir aynı, çünkü A5'teki etiket kaybı tam burada vuruyor. Yani üç hata aynı satırda: yanlış il, yanlış fakulte, kayıp burs etiketi. Aynı desen ODTÜ'de 16 ad grubunda daha var (Bilgisayar Müh. ×3 → 105/65/65, Elektrik-Elektronik ×3, İşletme ×6 …) [D].

3.4 Etki haritası — hangi yüzey ne yanlış gösteriyor

Yüzey Dosya Ne oluyor Etkilenen
Sihirbaz "Devlet" adımı src/lib/db.ts:78 unitur = 'DEVLET' KKTC ve Balkan/Saraybosna programları devlet üniversitesi gibi havuza giriyor 100 satır
Sihirbaz "Vakıf (burslu olursa)" adımı src/lib/db.ts:79-80 unitur <> 'DEVLET' KKTC (1.145) + yurt dışı (111) satırı vakıf havuzuna giriyor; etiket kaybı yüzünden öğrenci hangisinin burslu olduğunu da göremiyor 1.256 satır / havuzun %13,4'ü
Sihirbaz il adımı (facet) src/lib/db.ts:325-327 — kodda not düşülmüş: "81 il + KKTC/yurtdışı" DAÜ'nün 276 satırı GAZİMAĞUSA ve KIBRIS çiplerine bölünmüş; aynı bölünme 9 KKTC kurumunda var 1.080 satır
/universiteler hub'ı src/lib/katalog.ts:270-337 MAX(unitur) + alfabetik ilk ad hattı kazanıyor → Uluslararası Balkan Ü. "Devlet / Saraybosna" görünüyor (doğrusu: yurt dışı kamu / Üsküp) 3 kurum kartı
/universite/<slug> src/lib/katalog.ts:402 stats.devletMi Kıbrıs Amerikan Ü. sayfasında "devlet üniversitesi" rozeti 1 sayfa
il faceti / il listesi aynı il boş 110 satır hiçbir il filtresine düşmüyor — sessizce kayboluyor 110 satır
/il/[slug] planı (T7, #18 yazısı) henüz yok Bugünkü SELECT DISTINCT il 91 değer döner → /il/baku-azerbaycan, /il/kibris gibi sayfalar üretilir önkoşul
Bu gecenin yazıları content/rehber/bilgisayar-mi-yazilim-muhendisligi-mi.md ODTÜ head-to-head tablodan çıkarılmış ve neden çıkarıldığı yazıda yazılmış → doğru davranış, düzeltme gerekmiyor. Ama aynı yazının devlet doluluk rakamları §5'teki kota asimetrisinden sapmalı 1 yazı, 2 rakam
content/rehber/kktc-universiteleri-okunur-mu.md KKTC evreni unitur='KKTC' OR universite LIKE 'KIBRIS AMERİKAN%' ile elle kurulmuş → yazı doğru; düzeltme sonrası sorgu sadeleşir, rakam değişmez —

3.5 Önerilen şema — yeni kolon yok

Soru "il yanında ulke/bolge mi, yoksa ayrı kampus alanı mı" şeklinde soruldu. Cevabım: ikisi de değil. Her iki bilgi de zaten DB'de:

  • Ülke/statü ayrımı unitur'de yaşıyor: DEVLET · VAKIF · VAKIF MYO · KKTC · YURTDISI KAMU · YURTDISI VAKIF. Bir ulke kolonu bu sözlüğün kopyasını çıkarır ve iki alanın çelişme ihtimalini yaratır.
  • Kampüs bilgisi fakulte'de yaşıyor: ODTÜ Kuzey Kıbrıs Kampusu (KKTC-Güzelyurt), ASBÜ-Kuzey Kıbrıs Yerleşkesi (KKTC-Lefkoşa), İTÜ-KKTC EĞİTİM ARAŞTIRMA YERLEŞKESİ (GAZİMAĞUSA) [D].
A) ulke/bolge kolonu B) il normalize + kampus kolonu C) Kolon yok: il'i normalize et, unitur'ü düzelt
Göç yokatlas-goc.ts + ingest.ts + refresh.ts INSERT + veri-kalite.ts aynısı yok
Dolduran kaynak API'de böyle bir alan yok — ad parantezinden türetmek zorundayız aynı sorun ad parantezi (aynı iş, kolonsuz)
Yıllık refresh maliyeti her yıl yeni kolonun da senkronu aynı + kampus eşlemesi il senkronu (zaten yapılmalı)
Okuyan taraf değişikliği katalog.ts, db.ts, facet, tipler aynısı + tipler tek satır filtre: AND unitur IN ('DEVLET','VAKIF','VAKIF MYO')
T7 (/il/[slug]) çözülür mü evet evet evet
Çift kaynak riski unitur ile çelişebilir fakulte ile çelişebilir yok

Öneri C. İki UPDATE + bir satır kod. Kurucunun "over-engineering yapma" talimatına uyan en küçük çözüm.

C'nin tek gerçek açığı, şema değil hat açığı: il/fakulte/isim UPDATE yolunda tazelenmediği sürece aynı kir her yıl birikir. Bunun kolonla ilgisi yok — çözümü refresh.ts'e --kimlik-onar/--isim-esitle ile aynı güvenlik desenini taşıyan bir --yer-esitle bayrağı: API ile il/fakulte farkı olan satırları önce listeler, gözle onaylanınca yazar. Ayrı bir CTO şartnamesine değer; bu gece yazılmaz.

3.6 Düzeltme betiği taslağı — KOŞULMADI, yalnız yazıldı

Ön koşullar: dev sunucu kapalı (WAL tek yazar), önce yedek, sonra pnpm veri-kalite --yil 2026.

# 0) Yedek — geri alma yolu budur. WAL'i önce kapat.
sqlite3 data/yokatlas.db "PRAGMA wal_checkpoint(TRUNCATE);"
cp data/yokatlas.db data/yokatlas.db.yedek-2026-09-22
BEGIN;

-- ── 1) unitur: yanlış DEVLET etiketleri (K5 = 100 satır) ────────────────
-- Kıbrıs Amerikan Ü. YÖK'ün KKTC listesinde (yok.gov.tr/tr/university?type=6)
UPDATE programs SET unitur='KKTC'
 WHERE unitur='DEVLET' AND universite LIKE 'KIBRIS AMERİKAN%';            -- 42

-- Balkan + Saraybosna: aynı kurumun diğer ad hattı zaten YURTDISI KAMU
UPDATE programs SET unitur='YURTDISI KAMU'
 WHERE unitur='DEVLET'
   AND (universite LIKE 'ULUSLARARASI BALKAN%'
     OR universite LIKE 'ULUSLARARASI SARAYBOSNA%');                      -- 58

-- ── 2) il: KKTC'de 'KIBRIS' toplayıcısını şehre indir (179 satır) ───────
-- Şehir zaten ad ya da fakülte parantezinde yazılı; tahmin YOK.
UPDATE programs SET il = CASE
    WHEN universite LIKE '%(KKTC-LEFKOŞA)%'    THEN 'LEFKOŞA'
    WHEN universite LIKE '%(KKTC-GİRNE)%'      THEN 'GİRNE'
    WHEN universite LIKE '%(KKTC-GAZİMAĞUSA)%' THEN 'GAZİMAĞUSA'
    WHEN universite LIKE '%(KKTC-LEFKE)%'      THEN 'LEFKE'
    WHEN fakulte LIKE '%(KKTC-Lefkoşa)%'       THEN 'LEFKOŞA'
    WHEN fakulte LIKE '%(KKTC-Güzelyurt)%'     THEN 'GÜZELYURT'
    WHEN fakulte LIKE '%(GAZİMAĞUSA)%'         THEN 'GAZİMAĞUSA'
  END
 WHERE il='KIBRIS'
   AND (universite LIKE '%(KKTC-%' OR fakulte LIKE '%(KKTC-%' OR fakulte LIKE '%(GAZİMAĞUSA)%');
-- beklenen: 179; sonrasında  SELECT COUNT(*) FROM programs WHERE il='KIBRIS'  = 0

-- ── 3) il: yurt dışı değerini mevcut sözleşmeye uydur (26 satır) ────────
-- Mevcut sözleşme: 'ŞEHİR-ÜLKE' (BİŞKEK-KIRGIZİSTAN, TÜRKİSTAN-KAZAKİSTAN)
UPDATE programs SET il='ÜSKÜP-KUZEY MAKEDONYA'
 WHERE universite='ULUSLARARASI BALKAN ÜNİVERSİTESİ' AND il='SARAYBOSNA - BOSNA - HERSEK';  -- 26
UPDATE programs SET il='SARAYBOSNA-BOSNA HERSEK'
 WHERE il='SARAYBOSNA - BOSNA - HERSEK';                                                     -- 15

-- ── 4) il: boş 110 satırın 95'i (kalan 15 bilerek NULL) ─────────────────
UPDATE programs SET il = CASE
    WHEN universite LIKE '%(TİRAN-ARNAVUTLUK)%'       THEN 'TİRAN-ARNAVUTLUK'
    WHEN universite LIKE '%(ÜSKÜP-MAKEDONYA)%'        THEN 'ÜSKÜP-KUZEY MAKEDONYA'
    WHEN universite LIKE '%(SARAYBOSNA-BOSNA-HERSEK)%' THEN 'SARAYBOSNA-BOSNA HERSEK'
    WHEN universite LIKE '%(BİŞKEK-KIRGIZİSTAN)%'     THEN 'BİŞKEK-KIRGIZİSTAN'
    WHEN universite LIKE '%(TÜRKİSTAN-KAZAKİSTAN)%'   THEN 'TÜRKİSTAN-KAZAKİSTAN'
    WHEN universite='ULUSLARARASI BALKAN ÜNİVERSİTESİ'     THEN 'ÜSKÜP-KUZEY MAKEDONYA'
    WHEN universite='ULUSLARARASI SARAYBOSNA ÜNİVERSİTESİ' THEN 'SARAYBOSNA-BOSNA HERSEK'
    WHEN universite='KIBRIS AMERİKAN ÜNİVERSİTESİ'         THEN 'LEFKOŞA'
  END
 WHERE il IS NULL
   AND universite <> 'İÇİŞLERİ BAKANLIĞI VE MİLLİ SAVUNMA BAKANLIĞI ADINA SAĞLIK BİLİMLERİ ÜNİVERSİTESİNDE EĞİTİM ALACAKLAR';
-- beklenen: 95; kalan 15 MSB/İçişleri satırı NULL kalır (kaynak da boş veriyor)

COMMIT;

Doğrulama sorguları (düzeltmeden sonra koşulur):

SELECT COUNT(DISTINCT il) FROM programs;                      -- beklenen 88 (81 TR + 5 KKTC şehri + ... )
SELECT COUNT(*) FROM programs WHERE il IS NULL;               -- beklenen 15
SELECT COUNT(*) FROM programs WHERE il='KIBRIS';              -- beklenen 0
SELECT COUNT(*) FROM programs WHERE unitur='DEVLET'
  AND (universite LIKE 'KIBRIS AMERİKAN%' OR universite LIKE '%BALKAN%'
    OR universite LIKE '%SARAYBOSNA%');                       -- beklenen 0
SELECT COUNT(DISTINCT il) FROM programs
  WHERE unitur IN ('DEVLET','VAKIF','VAKIF MYO');             -- beklenen 81   ← T7'nin kabul kriteri
SELECT SUM(kontenjan2026), SUM(yerlesen2026) FROM programs;   -- düzeltmeden ÖNCEKİYLE AYNI olmalı

Geri alma: data/yokatlas.db.yedek-2026-09-22 dosyasını geri kopyala. Betik yalnız 4 kolonsuz alana dokunuyor, sıralama/kontenjan verisine hiç dokunmuyor — son doğrulama sorgusu bunu kanıtlar.

Birlikte gitmesi gereken kod değişikliği (tek satır): /il/[slug] ve il faceti AND unitur IN ('DEVLET','VAKIF','VAKIF MYO') ile süzülmeli; yoksa /il/uskup-kuzey-makedonya üretilir. Sihirbazın "Vakıf" kovası da unitur IN ('VAKIF','VAKIF MYO') olmalı (src/lib/db.ts:79) — bu ayrı bir ürün kararı: KKTC programları havuzdan çıkarsa vakıf havuzu 9.351 → 8.095 satıra iner. [BİLAL] değil, urun-yoneticisi/ceo kararı.

İdempotens uyarısı: betik koşulduktan sonra unitur-onar.ts'in çelişki koruması (aynı üniversitede birden fazla unitur) hâlâ geçer — Balkan/Saraybosna'nın iki ad hattı farklı universite dizgileri olduğu için ayrı gruplar sayılıyor [K].


4. Görev 2 — vakıf burs etiketi (A5)

Tek cümlelik cevap

Etiket YÖK Atlas API'sinde ayrı bir alan değil, program adının (birimAdi) içinde geliyor; veri elimizde ve kırılım yapılabilir — bugün eksik olmasının sebebi refresh.ts'in --isim-esitle bayrağının varsayılan kapalı olması, kaynağın etiketi vermemesi değil.

Kanıt zinciri

(1) Kodun kendi belgesi — scripts/refresh.ts:37-39:

--isim-esitle: kimliği AYNI olup yalnız adı farklı satırlarda (API burs/dil eki veriyor: "Tıp (İngilizce) (Burslu)") isim API'den yazılır. Varsayılan KAPALI: isim, sihirbaz kategorilerini ve bölüm sayfası başlığını besler.

Yani etiketin kaynağı biliniyor ve yazılmaması bilinçli bir seçim. Gerekçe de meşru: isim sihirbaz kategorilerini besliyor, "Tıp (Burslu)" ile "Tıp" farklı kategoriye düşebilir.

(2) API isteği burs boyutunu tanıyor — refresh.ts:181 filtre nesnesinde bursOraniId: null var; yani uç nokta burs oranına göre süzmeyi destekliyor. Ama yanıt kaydından tutulan alanlar listesinde (TUTULAN_ALANLAR, satır 124-148) burs alanı yok — ham .jsonl.gz dosyası kaydın tamamını yazıyor (refresh.ts:378), ince kayıt değil, ama data/ham/ bu makinede mevcut değil (.gitignore:48), bu yüzden API yanıtında ayrı bir bursOrani alanı olup olmadığı yerel olarak doğrulanamadı [doğrulanmalı — tek --ham koşusuyla ya da 1 isteklik size:1 sorgusuyla 2 dakikada kapanır].

(3) Asıl kanıt — satır yaşına göre etiketlilik. Yeni satırlar INSERT'te r.birimAdi'yi doğrudan yazıyor (refresh.ts:677), eski satırların adı hiç güncellenmiyor:

SELECT CASE WHEN sira2025 IS NOT NULL OR kontenjan2025 IS NOT NULL
            THEN '2025te de vardi' ELSE '2026da yeni' END durum,
       COUNT(*) n,
       SUM(isim LIKE '%(Burslu)%' OR isim LIKE '%İndirimli)%' OR isim LIKE '%(Ücretli)%') etiketli
FROM programs
WHERE kontenjan2026 IS NOT NULL AND onlisans=0 AND unitur LIKE 'VAKIF%'
GROUP BY 1;
Satır n Etiketli Oran
2026'da yeni açılan (isim API'den yazıldı) 348 326 %93,7
2025'te de var olan (isim 2021 CSV'sinden) 4.427 628 %14,2

Aynı veri, aynı kurumlar, aynı yıl — tek fark satırın INSERT mi UPDATE mi yolundan geçtiği. API etiketi veriyor; biz yazmıyoruz.

(4) veri_meta kaydı da aynı şeyi gösteriyor. kimlik_onar:* kayıtlarının yeni.isim alanları API'den geldiği gibi duruyor: "Ebelik (Burslu)", "Ebelik (%50 İndirimli)", "Siber Güvenlik (Burslu) (İngilizce)", "Endüstri Mühendisliği (Ücretli)", "Anestezi (%25 İndirimli)" [D]. Etiket sözlüğü tam: Burslu · %75/%50/%25 İndirimli · Ücretli.

Bugünkü kırılım (bildirilen rakamlar doğrulandı)

SELECT CASE WHEN unitur LIKE 'VAKIF%' THEN 'Vakıf' WHEN unitur='KKTC' THEN 'KKTC'
            WHEN unitur='DEVLET' THEN 'Devlet' ELSE unitur END grup,
  SUM(onlisans=0) lisans,
  SUM(onlisans=0 AND isim LIKE '%(Burslu)%') burslu,
  SUM(onlisans=0 AND isim LIKE '%İndirimli)%') indirimli,
  SUM(onlisans=0 AND isim LIKE '%(Ücretli)%') ucretli,
  SUM(onlisans=0 AND isim NOT LIKE '%(Burslu)%' AND isim NOT LIKE '%İndirimli)%'
      AND isim NOT LIKE '%(Ücretli)%') etiketsiz
FROM programs WHERE kontenjan2026 IS NOT NULL GROUP BY 1;
Grup 2026 lisans Burslu İndirimli Ücretli Etiketsiz
Devlet 6.505 9 39 13 6.444 (beklenen — devlette burs etiketi olmaz)
Vakıf 4.775 339 416 199 3.821 (%80,0)
KKTC 849 39 51 12 747 (%88,0)
Yurt dışı kamu 98 14 31 19 34

Yazarların bildirdiği 3.821/4.775 ve (873 payda ile) 768/873 rakamları doğrulandı; payda farkı (849 vs 873) yalnız KKTC evreninin unitur='KKTC' mi yoksa + Kıbrıs Amerikan mı tanımlandığına bağlı — A4 düzeltmesinden sonra iki tanım birleşiyor.

data/kaynak/ altındaki dosyalarda burs bilgisi var mı

Yok. Oradaki iki dosya kurum düzeyinde YÖK istatistiği:

  • yok-istatistik-ogrenci-sayilari-2025-2026.xls — sütunları: Okuyan Önlisans · Okuyan Lisans · Okuyan Yükseklisans · Okuyan Doktora · Genel Toplam, satırları üniversite × il × (DEVLET/VAKIF/KKTC).
  • yok-istatistik-ogretim-elemani-sayilari-2025-2026.xls — öğretim elemanı sayıları.

İkisi de program düzeyinde değil, burs/ücret alanı içermiyor (JasperReports dökümü; file + strings ile başlık satırları okundu, dosyalar değiştirilmedi). Bu dosyalar A5 için kullanılamaz; başka bir işe (kurum büyüklüğü/öğrenci-öğretim üyesi oranı içerikleri) yarayabilir.

Yazarlara verilecek net cevap

Kırılım bugün yapılamaz, ama kalıcı olarak imkânsız değil. Veri kaynakta var; DB'ye tek bir refresh koşusuyla girer. Bu koşu yapılana kadar hiçbir yazıda burslu/%50/ücretli dağılımı verilmesin — bugünkü sayımlar (%80'i etiketsiz) gerçek dağılım değil, tazelenmemiş adların sayımıdır ve yayımlanırsa yanlış olur.

Bu koşunun bedeli ve riski (Bilal/CTO için)

# ÖNCE kopyada: ham dosya yoksa API'den tek geçiş (~22 sn/21.493 kayıt, 0,5 sn/sayfa)
cp data/yokatlas.db /tmp/deneme.db
pnpm refresh --yil 2026 --db /tmp/deneme.db --isim-esitle
pnpm veri-kalite --yil 2026 --db /tmp/deneme.db --onceki data/yokatlas.db
  • Etkilenecek satır: kabaca 10.164 (K10'daki ayırt edilemeyen ad grupları); kesin sayıyı koşunun "yalnız adı (burs/dil eki) farklı N satır" satırı verir (refresh.ts:609).
  • Risk — küçümsenmemeli: isim sihirbaz kategorilerini (src/lib/kategoriler.ts) ve /bolum/<slug> başlıklarını besliyor. "Bilgisayar Mühendisliği" → "Bilgisayar Mühendisliği (Burslu)" olursa kategori eşlemesi ve bölüm slug'ı kayabilir; kaydedilmiş raporlardaki program adları da tutmayabilir. Bu yüzden doğru sıra: (1) kopyada koş, (2) kategori eşleme farkını say, (3) gerekirse okuyan tarafa "etiketi kırp" normalizasyonu ekle, (4) ancak sonra gerçek DB. Bu gece yapılmaz.
  • Daha ucuz alternatif: isim'e hiç dokunmadan ayrı bir burs kolonu türetmek (Burslu/%75/%50/%25/Ücretli/—). Ama bu §3.5'te reddettiğim "yeni kolon" yoluna girer ve kategori riskini de çözmez, sadece erteler. Önerim: --isim-esitle + okuyan tarafta etiket kırpma.

5. Kapsam dışı ama yayını ilgilendiren bulgu: doluluk oranları yukarı sapmalı

Görevde yok, ama bu gece yayımlanan rakamları etkilediği için rapora giriyor.

SELECT COUNT(*) FROM programs WHERE kontenjan2025 IS NOT NULL AND gk2025 IS NOT NULL
   AND kontenjan2025 <> gk2025;        -- 0   → kontenjan = YALNIZ genel kontenjan
SELECT COUNT(*) FROM programs WHERE yerlesen2025 IS NOT NULL AND gkY2025 IS NOT NULL
   AND yerlesen2025 <> gkY2025;        -- 7.989 → yerlesen = gkY+sgyY+dprmY+obkY

scripts/refresh.ts:212-217 yerlesen() dört kota türünü toplarken, kontenjan kolonuna yalnız r.kontenjan (genel kontenjan) yazılıyor. Pay ile payda aynı evreni saymıyor:

Grup (2026 lisans) Kontenjan Yerleşen Doluluk yerlesen>kontenjan satır
Devlet 280.131 286.150 %102,1 5.618
Vakıf 99.638 78.475 %78,8 —
KKTC 13.162 7.923 %60,2 0
  • KKTC ve vakıf rakamları güvende — KKTC'de okul birincisi/şehit-gazi/depremzade kontenjanı olmadığı için sapma sıfır; kktc-universiteleri-okunur-mu yazısının ÖSYM ile birebir uyuşması bunu bağımsız olarak doğruluyor (13.162/7.923 ÖSYM Tablo 1 ile aynı).
  • Devlet doluluk rakamları sapmalı. bilgisayar-mi-yazilim-muhendisligi-mi yazısındaki "devlet YM %102,1" tam olarak bu sapmadır; %100'ü aşan her doluluk rakamı bu hatanın imzasıdır.
  • En küçük düzeltme yazı tarafında: doluluk hesabında paydayı da tüm kota türlerini kapsayacak şekilde kurmak mümkün değil (toplam kontenjan kolonu DB'de yok) → bu sezon devlet için doluluk rakamı yayımlanmasın, ya da "genel kontenjana göre" kaydıyla ve %100 üstü değerler açıklanarak verilsin. Kalıcı düzeltme kontenjanToplam<yıl> kolonu gerektirir → yeni yılın refresh'ine bağlanmalı, bu gece değil.
  • Backlog önerisi: veri-kalite.ts'e ZORUNLU kontrol olarak yerlesen>kontenjan oranı eklensin; bugün böyle bir kontrol yok (scripts/veri-kalite.ts:145-150 yalnız unitur boş / il boş bilgi satırı basıyor, çıkış kodunu etkilemiyor).

6. Backlog önerileri (urun-yoneticisine)

Öneri Gerekçe Boy
BACKLOG #9'u genişlet: "3 yurt dışı/KKTC üniversitesi DEVLET etiketli" → gerçek kapsam 100 satır + 179 il='KIBRIS' + 110 boş il + 26 yanlış şehir; çözüm §3.6 betiği bugünkü madde kapsamı gerçeğin üçte biri S
T34 kabul kriteri: SELECT COUNT(DISTINCT il) FROM programs WHERE unitur IN ('DEVLET','VAKIF','VAKIF MYO') = 81 ve COUNT(*) WHERE il IS NULL = 15 T7/#18'in önkoşulu, komutla doğrulanabilir —
Yeni: refresh.ts --yer-esitle (il/fakulte senkronu, önce listeler sonra yazar) yoksa aynı kir 2027'de yeniden birikir M
Yeni: veri-kalite.ts'e 3 ZORUNLU kontrol — (a) aynı baz kurum adında iki unitur, (b) TR dışı il × TR unitur çelişkisi, (c) yerlesen>kontenjan üçü de bu gece elle bulundu; otomatik olmalı S
A5 sonrası: --isim-esitle koşusu + kategori eşleme farkının sayımı T11'in önkoşulu M

7. Engeller

  1. data/ham/2026-2026-09-21.jsonl.gz bu makinede yok (.gitignore:48, dizin hiç yok). API yanıtında ayrı bir bursOrani/ogrenimTuru alanı olup olmadığını yerel olarak doğrulayamadım — A5'in cevabı bundan bağımsız (etiket birimAdi içinde geliyor, §4/3 kanıtı), ama "ayrıca ayrı kolon da var mı" sorusu açık kaldı.
  2. Disk. Kural gereği DB kopyası alınmadı, betik denemesi yapılmadı; §3.6 ve §4'teki komutlar hiç koşulmadı, yalnız yazıldı.
  3. ÖSYM 2026 kılavuzu açılamadı (yazar 14 de aynı engele takılmış: PDF metin katmanı çıkarılamıyor, makinede poppler yok). "ODTÜ Yazılım Mühendisliği Ankara'da değil, Kuzey Kıbrıs'ta" iddiası bu yüzden kılavuz maddesine değil, (a) ODTÜ KKK'nin kendi sayfasına ve (b) 45/5 Burslu-%50 kontenjan desenine dayanıyor → [doğrulanmalı] işaretli kaldı.

Bilal'den istenen (3 madde)

  1. §3.6 düzeltme betiğine onay — 100 unitur + 320 il satırını düzeltir, sıralama/kontenjan verisine dokunmaz, geri alma tek cp. Onaylarsan betiği veri/il-temizligi dalında yazıp kopyada doğrularım; T7 (/il/[slug]) ve #18 yazısı bu onaya bağlı. (karar: 3 dk)
  2. Ürün kararı: sihirbazın "Vakıf (burslu olursa)" adımı KKTC ve yurt dışı programlarını kapsasın mı? Bugün kapsıyor (1.256 satır, havuzun %13,4'ü) ve öğrenciye söylenmiyor. Kapsamasın dersen vakıf havuzu 9.351 → 8.095'e iner. (karar: 2 dk)
  3. Devlet doluluk rakamı bu sezon yayımlanmasın onayı — bilgisayar-mi-yazilim-muhendisligi-mi yazısındaki "%102,1" ve "%99,8" kota asimetrisinden sapmalı (§5); yazı yayına girmeden rehber-editoru o iki rakamı çıkarsın ya da "genel kontenjana göre" kaydıyla versin. (karar: 2 dk, uygulama: 10 dk)