# Review — EGT-50 r3: Heyet 131 Bulgularının Kapanışı — 2026-07-26

> **Review type:** re-review (r2'nin R2-1…R2-10 bulguları + 3 açık sorusunun kapanış doğrulaması; spec v1.9.1 · plan v2.4.1 · chunk9 S-1…S-15)
> **Previous review:** `docs/reviews/review-egt50-ofis-backoffice-2026-07-26-r2.md` (REJECTED) · ilk tur: `…-2026-07-26.md`
> **Upstream document(s) read:** r2 bulgu listesi (birincil ölçüt) · `CLAUDE.md` kırmızı çizgiler · AD-09/AD-21/AD-23
> **Other cross-referenced documents:** `git diff 2c309ce..b11fd48` (tam revizyon diff'i) · `omurga-spec.md` (Y-4, §7.5, başlık, revizyon geçmişi) · `omurga-plan.md` · chunk2 (§2.5 muafiyet tablosu, §2.5.1 — satır doğrulamalı) · chunk3 (§3.6, §3.6.1 B-1…B-8, kriterler 15b–20) · chunk8 (§8.5, kriter 9b) · chunk9 (§9.5 S-1…S-15, kriter 7) · `config/audit.php` · `docs/claude/heyet.md` · `docs/issues.md`
> **Stage locks in effect:** F-2/F-3 Sets kararları ve L-1a/L-1b kapalı — yeniden tartışılmadı. Chunk 1/2 implementasyonları APPROVED. `backoffice_users` ve chunk3 implementasyonu kapsam dışı.

---

## Bulgu Kapanış Matrisi

| r2 bulgusu | Durum | Kanıt |
|---|---|---|
| **R2-1** (majör) — Y-4 mutlak kalmıştı | ✓ kapandı | `omurga-spec.md:196` — istisna adlandırılmış, kalıcılığı ve tehdit varsayımı referanslı, "her iki kimlik alanı" açık, "başka istisna yoktur" kapanışı var; AD-23 kural 1 ile aynı dil |
| **R2-2** (majör) — bayrak geri alınmıyordu | ✓ desen kapandı; iç içe semantiği eksik | **B-5**: `AuditReadScope` + `finally` içinde `'off'`; kriter **17b-2** transaction-içi mirası ve exception yolunu sınıyor. Kalan: reentrancy/iç içe davranış (→ **R3-1**) |
| **R2-3** (majör) — feed'in uygulama zemini yoktu | ✓ zemin kuruldu; chunk2'ye yansıtılmadı | **B-6/B-7/B-8** + kriterler 17e/17f/17g — bileşen-muafiyeti deseni doğru. Ancak chunk2 §2.5'in kesin muafiyet tablosu güncellenmedi (→ **R3-2**) |
| **R2-4** (orta) — S-12 taranamazdı | ✓ biçim doğru; allowlist eksik + kriter 19 eski | Allowlist mekanik; ama `config/audit.php` listede yok ve içinde `audit_writes`/`audit_access` geçiyor; kriter 19 hâlâ dar tarifte (→ **R3-3**) |
| **R2-5** (orta) — S-13 taranamazdı | ✓ ilke doğru; yol listesi yarım | Namespace ölçütü doğru fikir; "Inertia sayfa/controller katmanı"nın dosya yolu karşılığı yazılmadı (→ **R3-4**) |
| **R2-6** (minör) — S-4 bayrağı görmüyordu | ✓ kapandı | chunk2 §2.5.1 ve S-4 `SET app.*` ailesine genelleştirildi; `set_config('app.…', …, false)` kapsandı; sonradan eklenen değişkenler kendiliğinden kapsamda |
| **R2-7** (minör) — index tutarsızlıkları | ✓ kapandı | Pin v1.9 + "REJECTED oldu; v1.9 cevaptır ve **onay beklemektedir**" — provenance artık doğru. (Yeni, aynı desende küçük pürüz: sürüm başlıkları → **R3-6**) |
| **R2-8** (minör) — heyet.md L-1a'yı bilmiyordu | ✓ kapandı | `heyet.md` L-1a/L-1b + "bulgu açmadan önce bak" + "istisnaların genişletilmesi bulgudur" — tam istenen biçim |
| **R2-9** (minör) — yanlış kriter atfı | ✓ kapandı | chunk3 §3.6: "bu sınırı sınayan **kriter 15b**'dir (kriter 16 yalnız parametre imzasını kapsar)" |
| **R2-10** (minör) — 9b'nin taraması evsizdi | ✓ ev kuruldu; tarif kendi kuralını deliyor | **S-15** + chunk8 9b bağlandı + kriter 7 "on beş". "Benzeri alan adları" ifadesi mekanik değil (→ **R3-5**) |
| **Açık soru 1** — bayrak neden `audit_usage`'ta | ✓ kapandı | B-3: kapsamdan çıkarıldı, gerekçesi yazıldı (L-3 + agregat yolu), 17c `audit_usage` reddini de sınıyor, SQL yorumu eklendi. Doğru karar — yüzey iki tabloya indi. (Görünür kıldığı komşu boşluk → **R3-7**) |
| **Açık soru 2** — `CASE` alternatifi | ✓ kapandı | §3.6.1'e değerlendirme + red gerekçesi; *"deney 11'in dersi CASE ile aşılmaz, yalnız yer değiştirir"* tespiti doğru — hata satır-bağımlı olurdu, deterministik değil |
| **Açık soru 3** — feed'in erişim logu bağlamı | ✓ kapandı | B-8: NULL tenant, `AccessLogger` muafiyeti, bayrak kapandıktan **sonra**; kriter 17g sınıyor |

---

## ✅ What works

- **B-5'in çekirdeği doğru ve 17b-2 tam gereken kriter.** Transaction-içi miras — r2'nin tek gerçek güvenlik boşluğu — `finally` içinde kapanan iş birimiyle çözüldü ve kriter hem ikinci-sorgu senaryosunu hem exception yolunu adıyla sınıyor. *"Bu kriter olmadan yeni desenin tek gerçek güvenlik boşluğu açık kalır"* cümlesinin plana girmesi, kriterin ileride "gereksiz" diye budanmasını da önler.
- **B-6'nın muafiyet deseni doğru sınıfta:** tabloya değil bileşene, iki tabloya, yalnız SELECT — chunk2'nin `AuditWriter` emsalinin birebir devamı; 17e'nin **iki yönlü** testi (içeriden geçer / dışarıdan takılır) muafiyetin genişlemediğinin doğru kanıtı. B-7'nin `OfficeAuditFeed`'i yolun tamamen dışında tutması ve B-8'in log yazımını bayrak-sonrasına alması kurguyu temiz kapatıyor.
- **B-3'ün daraltılması** ("çapraz-tenant ham usage okumasının kullanım senaryosu yoktur; yüzey ne kadar darsa o kadar iyidir") sorulmadan verilecek en iyi cevaptı; 17c'nin `audit_usage` reddini de sınaması tutarlı.
- **§9.5'in yeni meta-kuralı** — *"tarif 'şu dizge şu dosyaların dışında geçmesin' biçimine indirgenemiyorsa listeye girmez"* — üç turun tarama tartışmasını ilkeye bağlıyor; S-12'nin "listeyi büyütmek plan değişikliğidir" cümlesi chunk2 kriter 6 emsaliyle aynı disiplin.
- **`CASE` notu ve red gerekçesi** teknik olarak doğru ve ileriye dönük koruma amacı açıkça yazılmış.
- **Y-4 ve AD-23 artık aynı dili konuşuyor;** istisna iki katmanda da adlandırılmış, tekliği iki yerde de beyan edilmiş. Müşteri tarafı yetki devri (U-4/U-5) genel kurala tabi kalıyor — Sets'in 5. sorusunun cevabı: evet, doğru kapsanıyor; firma yetkilisi kendi sahip olduğu panel yetkisi çerçevesinde çalışır, istisna ona uzanmaz.
- **heyet.md güncellemesi** gelecekteki yanlış bulguyu iki yönde de önlüyor (istisnayı bulgu sanma / istisnanın genişletilmesini kaçırma).

## ⚠️ Findings & Risks (in-scope for this review type)

**R3-1 (orta) — B-5'in iç içe (reentrancy) semantiği tanımsız; sabit `'off'` geri yüklemesi iç içe kullanımda dış scope'u sessizce bayraksızlaştırır.** `AuditReadScope` çıkışta koşulsuz `'off'` yazıyor. İç içe iki scope açılırsa iç olanın `finally`'si dış scope'un kalan sorgularını bayraksız bırakır — hata fırlamaz, backoffice feed'i **sessizce eksik** (yalnız NULL-tenant) sonuç döndürür; bu, projenin en sevmediği hata sınıfıdır (sessiz yanlış sonuç). chunk2 §2.5 aynı problemi tenant bağlamı için **"önceki değeri sakla + `finally`'de geri yükle"** ile çözmüştü; B-5 bu desenin yalnız yarısını aldı. Bugün tek çağıran `BackofficeAuditFeed` (B-2) olduğu için senaryo uzak, ama plan "kesin semantik" disiplinine sahip: ya chunk2 gibi sakla-geri-yükle, ya açık reentrancy yasağı + guard exception — ikisinden biri yazılmalı. (Sets'in 1. sorusunun ikinci yarısının cevabı: evet, `TenantContext`'in yaptığı gibi önceki değeri geri yüklemek — veya iç içeyi açıkça yasaklamak — gerekir.) Küçük ek: `app.audit_read_all()` fonksiyonunun yalnız tam `'on'` değerinde true döndüğü (NULL / `''` / `'off'` üçünün de false olduğu — deney 6'nın boş-string dersi) fonksiyon sözleşmesi olarak yazılmalı; bugün yalnız "bağlamsızsa false" yazıyor, 17b/17b-2 davranışı dolaylı sınıyor.

**R3-2 (orta) — chunk2 §2.5'in kesin muafiyet tablosu B-6 ile çelişiyor; iki belgede iki farklı beyan.** chunk2 `omurga-plan_chunk2.md:211-214` hâlâ: muafiyet *"yalnız INSERT"*, bileşenler *"`AuditWriter`, `AccessLogger`, `UsageLogger`"*, ve **"Bu tablolara SELECT her zaman `TenantContext` gerektirir"** (satır 213, istisnasız). B-6 ise `AuditReadScope` içinden iki tabloya SELECT muafiyeti tanımlıyor. Bekçiyi chunk2'ye göre yazan implementer feed'i kırar; B-6'ya göre yazan chunk2'nin kesin listesini deler. Bu, F-7/R2-4'ün üç turdur kovalanan deseninin ta kendisi: **kesin liste güncellenmeden ona istisna tanıyan uzak belge**. chunk2'nin muafiyet tablosuna `AuditReadScope` satırı (yalnız SELECT, yalnız `audit_writes`/`audit_access`, chunk3 §3.6.1 B-6 referanslı) işlenmeli. (Sets'in 2. sorusunun cevabı: desen ve 17e kanıtı yeterli; **kanunun kendisi** henüz tek yerde değil.)

**R3-3 (orta) — S-12 allowlist'i `config/` içermiyor ve kriter 19 eski dar tarifte kaldı.** (a) Mevcut `config/audit.php` `audit_writes`/`audit_access` dizgelerini içeriyor (`'tables'` haritası, satır 19-24) ve §3.4'e göre owen-it `implementation` bağlaması da (`AuditWrite`) oraya yazılacak — S-12'nin allowlist'inde `config/` yok; tarama **ilk koşumda kendi meşru config'ini kırar**. (b) chunk3 **kriter 19** (`omurga-plan_chunk3.md:278`) hâlâ *"Audit tabloları `App\Queries\Audit\**` dışından sorgulanmıyor"* diyor — S-12'nin yeni allowlist'i (Support, Models, Jobs, migrations, tests) ile doğrudan çelişiyor; kriter yazıldığı haliyle `StampAuditBatch`'i ihlalci sayar. Kriter, S-12'nin diline çekilmeli ("adlandırılmış allowlist dışından").

**R3-4 (minör) — S-13'ün "Inertia sayfa/controller katmanı" tarifi dosya yolu olarak kesin değil.** `app/Filament/**` açık; Inertia tarafı için yol yazılmamış (`app/Http/Controllers/**`, `resources/js/**` gibi). §9.5'in kendi meta-kuralı "dizge + dosya listesi" istiyor; S-13'ün yarısı hâlâ ad değil kavram ("controller katmanı"). Sets'in 3. sorusunun S-13 bacağı: **henüz değil** — iki yol adı eklenince mekanik.

**R3-5 (minör) — S-15 "benzeri alan adları" ifadesiyle §9.5'in kendi meta-kuralını deliyor.** `login_count`, `last_login_at`, `session_duration`, `online_seconds` — dört dizge mekanik ✓; "**benzeri**" mekanik değil. Tarama fiilen dört dizgeyi tarayacaksa "benzeri" ölü kelimedir ve yanlış güven verir; benzerlerini yakalamak kod incelemesinin/heyetin işiyse bu açıkça yazılmalı (S-15 sonlu listeyi tarar; listeye ekleme plan değişikliğidir).

**R3-6 (minör) — Sürüm başlıkları revizyon geçmişiyle çelişiyor (R2-7'nin deseni, yeni olay).** Talep ve revizyon geçmişleri **v1.9.1 / v2.4.1** derken belge başlıkları hâlâ *"Sürüm 1.9"* (`omurga-spec.md:3`) ve *"Sürüm 2.4"* (`omurga-plan.md:3`). Tek belgede iki sürüm beyanı; pin doğrulaması yapan bir sonraki okuyucu için belirsizlik.

**R3-7 (orta; bu revizyonun açtığı değil, B-3'ün görünür kıldığı) — `analytics_usage_daily`'nin okunma mekanizması hiçbir belgeye bağlanmamış.** B-3 doğru olarak "backoffice usage'ı yalnız agregattan okur" diyor; ama o agregatın **çapraz-tenant okunma yolu** tanımsız: `analytics_usage_daily` chunk2 §2.8'in **kesin** bağlamsız-tablo listesinde yok (listede olmayan her tablo tenant-kapsamlıdır ve §2.4 desenini uygular — arch testi bunu zorlar); TimescaleDB continuous aggregate'i RLS politikası taşımaz; bekçi karşısındaki durumu ve G-1 ("kapsam dışı okuma yalnız dar `SECURITY DEFINER` fonksiyonla") ile ilişkisi yazılmamış. chunk8 §8.6 `UsageSummary`'nin ekranını tanımlıyor ama okuma mekanizmasını değil. Sets'in 4. sorusunun cevabı: B-3 **yeni boşluk açmadı** (ham okuma L-3 gereği zaten ekrana taşınamazdı), ama backoffice analitiğinin tek meşru yolu artık yalnız bu agregat olduğundan, mekanizmasının tanımsızlığı chunk8 implementasyonundan önce kapatılmak zorunda — muhtemel adres: §2.8 listesine gerekçeli kayıt veya G-1 deseninde dar fonksiyon + S-9 envanteri; karar plan turunundur, bu review yalnız boşluğu raporlar.

## ❓ Open Questions (to be answered before next stage)

1. **`AuditReadScope`'un iç içe davranışı hangisi olacak** — sakla-geri-yükle mi, reentrancy yasağı mı? (R3-1'in karar sorusu; ikisi de kabul edilebilir, biri seçilip yazılmalı.)
2. **R3-7'nin adresi:** `analytics_usage_daily` §2.8'e gerekçeli istisna olarak mı girer, yoksa G-1 deseninde dar bir okuma fonksiyonu mu tanımlanır? (chunk8 implementasyonundan önce; Spec 2/chunk8 turunda karara bağlanabilir.)

## ⚠️ Upstream Findings

Yok. R3-7 önceden var olan bir plan boşluğudur; onaylı chunk2 implementasyonuyla çelişki değil, chunk8'in henüz yazılmamış implementasyonunun önünde eksik tanımdır.

## Previous Findings Status

| # | Durum |
|---|---|
| R2-1 | ✅ Kapandı |
| R2-2 | ✅ Kapandı (kalan dar nokta: iç içe semantiği → R3-1) |
| R2-3 | ⚠ Kısmen: zemin kuruldu; chunk2 muafiyet tablosu güncellenmedi (R3-2) |
| R2-4 | ⚠ Kısmen: allowlist biçimi doğru; `config/` eksik + kriter 19 eski (R3-3) |
| R2-5 | ⚠ Kısmen: ilke doğru; Inertia yol listesi eksik (R3-4) |
| R2-6 | ✅ Kapandı |
| R2-7 | ✅ Kapandı (aynı desende yeni küçük olay: R3-6) |
| R2-8 | ✅ Kapandı |
| R2-9 | ✅ Kapandı |
| R2-10 | ⚠ Kısmen: ev kuruldu; "benzeri" ifadesi mekanik değil (R3-5) |

## Verdict: ❌ REJECTED

Üç majörün üçü de özünde kapandı — B-5/B-6/B-7/B-8 kurgusu ve 17b-2/17e kriterleri doğru; Y-4 istisnası iki katmanda tutarlı. Kalanlar bir öncekinden de dar: **R3-2** ve **R3-3** "plan kanundur" rejiminde implementer'ı iki farklı kanunla baş başa bırakan metin çelişkileri (chunk2 muafiyet tablosu ↔ B-6; kriter 19 ↔ S-12; S-12 ↔ mevcut `config/audit.php`), **R3-1** tek cümlelik bir semantik kararı, **R3-4/5/6** kelime düzeltmesi. **R3-7** tek gerçek tasarım boşluğu ve chunk8'e kadar ertelenebilir — ama bir yere yazılmadan kapanmış sayılmaz (`docs/issues.md` kalemi yeterli olur). Dördüncü turun diff'i muhtemelen yirmi satırı geçmez; bulguların hiçbiri kurulan güvenlik desenini yeniden açmıyor.
