diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md
index 46298de..07a825a 100644
--- a/docs/quality/uygulama-durumu.md
+++ b/docs/quality/uygulama-durumu.md
@@ -1010,3 +1010,9 @@ Taslak türü seçicisine erişilebilir ad eklendi. Üretim derlemesi geçti. Ö
- İzole QA tarayıcısında altı kontrol geçti: 22 Eylül'deki 10:30 Akdeniz önce, 11:00 Kayseri sonra; Ekim girişinde 31 gün ve 1 Ekim başlığı; sonraki ay Kasım; önceki ay Ekim; bugüne dönüş; Akdeniz detayında aynı tarih/saat. Kanıt qa-calendar-navigation.json.
- 228 otomatik test ve derleme geçti. Test sunucusunun çıktısında önceki firma alanı değişikliğinde yinelenmiş aria-invalid niteliği görüldü; tek niteliğe indirildi. Son derleme bu uyarı olmadan geçti; büyük paket uyarısı sürüyor.
- QA sekmesi ve yalnız bu test için açılan sunucu kapatıldı (exit 0). Mobil genişlikler, tarihi bilinmeyen kaydın tarayıcı senaryosu ve Google/Outlook aktarımı bu kabul kapsamında değil; açık kalır. Canlıya yayın yapılmadı.
+
+### Favori kurumlarda kayıt onayı ve sürüm koruması — 6 Eylül 2026
+- Favori kurumların iyimser state/effect kaydı kaldırıldı. Yıldız seçimi ancak sürümlü sunucu yazımı başarılı olunca değişir; hata mevcut listeyi korur ve ortak kayıt hata alanında gösterilir. Bekleme sırasında favori düğmeleri kilitlenir, aria-busy/aria-pressed durumları yayınlanır.
+- favori_kurumlar API yazımında okunan içerik sürümü zorunlu; eski sürüm 409 alır. Liste olmayan, boş veya yinelenen kurum girdileri reddedilir. Boş listeye dönmek desteklenir.
+- 229 otomatik test ve derleme geçti. HTTP testi sürüm zorunluluğu, art arda başarılı kayıt, eski sekme silme isteğinin reddi, hatalı liste ve son favoriyi kaldırmayı doğruladı.
+- Yeni yıldızın tarayıcı bekleme/hata ve iki sekme kabulü açık. Bu değişiklik bildirim aboneliğini otomatik değiştirmez; abonelik kendi kaydedilmiş kurum kapsamını kullanır. Diğer genel anahtarlardan rakip izleme ve hatırlatma ayarlarının eşzamanlı kayıt davranışı ayrıca incelenmeli. Canlıya yayın yapılmadı.
diff --git a/docs/quality/yedek-geri-yukleme.md b/docs/quality/yedek-geri-yukleme.md
index 206f0da..e088210 100644
--- a/docs/quality/yedek-geri-yukleme.md
+++ b/docs/quality/yedek-geri-yukleme.md
@@ -77,3 +77,5 @@ Kuyruktaki ilanın artık kaynakta bulunmaması, teklif zamanının değişmesi,
`GET /api/me/data` artık `veri` ile aynı okumaya ait `revisions` döndürür. `firma_profil` ve `saved_filters` PUT/POST istekleri `revision` göndermelidir (yeni kayıt için null). Eksik sürüm 428, değişmiş sürüm 409 üretir; otomatik üzerine yazma veya otomatik tekrar yoktur. Frontend ve backend birlikte sürümlenmelidir. Eski açık sekme 428 alırsa kaydedilmemiş girdiler korunup sayfa yenilenmelidir. Veritabanı şeması değişmez; eski sunucuya dönmek bu iki anahtardaki çakışma korumasını kaldırır. Yayın kabulünde iki sekmenin aynı kaydı değiştirmesi ve reddedilen sekmenin girdilerinin korunması sınanmalıdır.
Ekip kaydı (`ekip`) da aynı içerik sürümü sözleşmesine dahil edildi. Yeni ekip arayüzü sunucu onayını bekler. Eski istemciden sürümsüz ekip yazımı 428 ile reddedilir; frontend/backend birlikte güncellenmelidir. Ekip alanlarının geçersiz veya kimliklerinin yinelenmiş olduğu eski kayıtlar önce düzeltilmelidir; sessiz veri temizliği yapılmaz.
+
+Favori kurumlar (`favori_kurumlar`) da sürümlü kullanıcı verisi yazımı ister. Eski istemcinin sürümsüz yazımı 428 döner; frontend/backend birlikte sürümlenmelidir. Bekleyen değişiklikler sunucudan onay gelmeden favori listesinde gösterilmez.
diff --git a/server/index.js b/server/index.js
index 7ce5f79..9941314 100644
--- a/server/index.js
+++ b/server/index.js
@@ -1207,7 +1207,9 @@ const server = http.createServer(async (req, res) => {
validateTeam(body.deger);
kotaKontrol(user.email, 'ekip', 0, body.deger.length, { isAdmin: user.isAdmin });
}
- const protectedKey=['firma_profil','saved_filters','ekip'].includes(k);
+ if(k==='favori_kurumlar' && (!Array.isArray(body.deger) || body.deger.some(name=>typeof name!=='string'||!name.trim()||name.length>1000) || new Set(body.deger.map(name=>name.trim())).size!==body.deger.length))
+ return json(res,400,{success:false,error:'Favori kurum listesi geçersiz veya yinelenen kurum içeriyor.'});
+ const protectedKey=['firma_profil','saved_filters','ekip','favori_kurumlar'].includes(k);
if(protectedKey && (!Object.hasOwn(body,'revision') || (body.revision!==null && (typeof body.revision!=='string' || !/^[a-f0-9]{64}$/.test(body.revision)))))
return json(res,428,{success:false,kod:'VERSION_REQUIRED',error:'Güncel kayıt sürümü gerekli. Sayfayı yenileyip tekrar deneyin.'});
const revision=store.setData(user.email, k, body.deger, protectedKey ? body.revision : undefined);
diff --git a/src/components/TenderCard.jsx b/src/components/TenderCard.jsx
index 4014d61..6148fb8 100644
--- a/src/components/TenderCard.jsx
+++ b/src/components/TenderCard.jsx
@@ -3,7 +3,7 @@ import { useApp } from '../context/AppContext';
import { Star, MapPin, Clock, Tag, Package, Building2, AlertTriangle, Sparkles, CalendarDays, Megaphone } from 'lucide-react';
export const TenderCard = ({ tender }) => {
- const { toggleTrackTender, setActiveTenderDetail, favoriteKurumlar, toggleFavoriteKurum, kalemEslesen, searchQuery } = useApp();
+ const { toggleTrackTender, setActiveTenderDetail, favoriteKurumlar, favoriteSaving, toggleFavoriteKurum, kalemEslesen, searchQuery } = useApp();
// Aramada eşleşen kalemler öne alınır — firma kendi ürününü hemen görsün.
const eslesenKalemler = (kalemEslesen && tender.ikn && kalemEslesen.get(tender.ikn)) || null;
const isFavKurum = favoriteKurumlar?.includes((tender.kurum || '').trim());
@@ -80,6 +80,8 @@ export const TenderCard = ({ tender }) => {
{tender.kurum}