# Re-Review — Plan: Platform Omurgası v2.2 (EGT-21) — 25.07.2026

> **Review type:** plan-review (re-review, N1…N4 kapanış doğrulaması + etkileşim analizi)
> **Reviewed:** `git diff d0219a2..21647ed` (index, chunk2, chunk3) — diğer chunk'lar değişmedi, önceki okumalar geçerli
> **Upstream (kilitli):** spec v1.7 APPROVED · AD-01…AD-28
> **Previous review:** `review-omurga-plan-2026-07-25-r3.md` (v2.1 REJECTED, N1…N4)
> **Stage locks in effect:** ⛔ sahip kararı: otomatik silme/anonimleştirme yok (uygulanmış)

---

## N1…N4 kapanış doğrulaması

| Bulgu | Doğrulama | Durum |
|---|---|---|
| N1 — politika yokluğu default-deny, trigger ateşlenmiyor | Deney 10 tuzağı canlıda yeniden üretti (politikasız UPDATE sessizce 0 satır); `FOR UPDATE/DELETE USING (true)` eklendi ve trigger'ın `RAISE` ettiği doğrulandı; "`USING (true)` izolasyonu zayıflatmaz — trigger koşulsuz reddeder" gerekçesi doğru; kriter 2 netleştirildi. `audit_activity`'nin trigger'sız/politikasız kalması (retention chunk-drop, kullanıcı DELETE'inin default-deny ile ölmesi) tutarlı. | ✅ KAPANDI |
| N2 — ofis NULL-audit yazımı bekçiye ve `write_tenant_id()` değerlendirmesine takılıyor | (a) Dar bekçi muafiyeti dört kuralla sınırlanmış: yalnız üç audit tablosu × yalnız INSERT × yalnız `AuditWriter`/`AccessLogger` yolu × bağlamsızda NULL zorunlu; SELECT her zaman bağlam ister. (b) **Deney 11 bu turun en değerli kaydı:** r3'te önerdiğim "iki ayrı permissive politika" çözümünün çalışmadığını canlıda gösterdi — PostgreSQL her iki `WITH CHECK`'i de değerlendiriyor, fırlatan fonksiyon NULL satırı da düşürüyor. Önerim yanlıştı; seçilen çözüm (`app.write_tenant_id_or_null()` fırlatmayan varyant + `IS NOT DISTINCT FROM`) doğru araç ve deney 12'nin dört senaryosu (bağlamsız-NULL ✓, tek-tenant ✓, çok-tenant ✗, çapraz ✗) davranışı tam kapatıyor. Fırlatmayan varyantın **yalnız audit politikalarıyla** sınırlanması ve normal tablolarda fırlatan varyantın korunması ("sessiz NULL yerine anlaşılır hata") doğru ayrım. | ✅ KAPANDI |
| N3 — `customers` INSERT'i imkânsızdı | `customers_insert WITH CHECK (true)` + yazılı gerekçe: INSERT mevcut hiçbir tenant'ın verisini açığa çıkarmaz, risk yetkisiz *oluşturma*dır ve `customer.create` izni (uygulama katmanı) onu taşır — RLS'in koruduğu şey INSERT'te söz konusu değil. Gerekçe sağlam; DELETE politikasızlığının AD-23 ile hizası korunmuş ve kayda geçmiş. | ✅ KAPANDI |
| N4 — geniş kapsamlı okumada erişim logu yazılamıyordu | chunk3 §3.5: `AccessLogger` log satırını `TenantContext::forWrite($recordTenantId, ...)` iç bağlamında yazar (F5 semantiği — daraltma serbest, `finally` ile geri yükleme); ofis-alanı kaydında NULL + §3.6 muafiyeti. Kriter 2c. | ✅ KAPANDI |

## Sorulan etkileşim analizi

`write_tenant_id_or_null()`'ın audit-yalnız kullanımı ve `customers` INSERT istisnası **izolasyon varsayımlarını kırmıyor**:

- Fırlatmayan varyantın dört senaryosu deney 12'yle kapalı; çok-tenant kapsam + NULL satır kombinasyonu da geçer ve **meşrudur** (geniş ofis akışı içinde ofis-alanı olayının audit'i) — N-1'in "NULL yalnız ofis olaylarında" kuralını `AuditWriter` taşıyor, DB katmanı tenant'lı satırlar için kapalı.
- `audit_writes_select`'teki fırlatan `app.tenant_ids()` deney 11'in dersinden etkilenmez: SELECT muafiyet dışıdır, bağlamsız SELECT zaten bekçide ölür; DB'ye ulaşsa bile fırlatma **fail-closed** yönündedir (veri sızmaz, gürültülü hata).
- `customers` istisnası kırmızı çizgi 1'i etkilemez (INSERT veri göstermez); müşteri rolünün DB katmanında INSERT yapabilmesi kalan tek genişlik ve gerekçesi plana yazılmış.

Ancak iki **dar** tutarsızlık kaldı — ikisi de bu turun düzeltmelerinin kendi beyanlarıyla ilgili:

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

**M1 (minör) — `LogActivity`'nin yazım yolu N2 muafiyetinin bileşen listesinin dışında kaldı.** §2.5 muafiyeti "yalnız `AuditWriter` / `AccessLogger` üzerinden yapılan yazımlarda tanınır" der; oysa `audit_activity`'yi yazan bileşen §3.5'e göre **`LogActivity` middleware'idir** ve muafiyet listesinde yok. Ayrıca middleware'in aktivite INSERT'ini hangi bağlamda çalıştırdığı tanımsız: müşteri isteğinde `TenantContext::run` transaction'ı Action/Query'dedir, middleware o sınırın dışındadır; ofis isteğinde bağlam hiç yoktur. Bu haliyle aktivite kaydı ya bekçiye takılır ya da gevşek garanti gereği **her istekte sessizce düşer** — log işlevsizleşir ve fark edilmez. Düzeltme dar: muafiyet listesine `LogActivity` (veya "aktivite yazımı `AccessLogger` üzerinden yapılır" kuralı) eklenmeli ve middleware'in INSERT bağlamı (müşteri isteğinde ortam kapsamlı `run`, ofis isteğinde NULL + muafiyet) bir cümleyle yazılmalı.

**M2 (minör) — chunk2 kriter 6'nın genel iddiası, bu turun bilinçli istisnalarıyla çelişiyor.** Kriter 6: "Her tenant-kapsamlı tabloda … **komut bazlı dört politika (SELECT liste, INSERT/UPDATE/DELETE tek tenant)** … (şema sorgusuyla kanıt)." Oysa artık: `customers` → INSERT `WITH CHECK (true)`, DELETE politikası **yok**; `audit_writes`/`audit_access` → INSERT `or_null` varyantı, UPDATE/DELETE `USING (true)`; `audit_activity` → UPDATE/DELETE politikası yok. Kriter 6'nın şema kanıtı bu tablolarda **başarısız olur** — test yazıldığında kriter kendi şemasıyla çelişir. Düzeltme: kriter 6'ya istisna listesi eklenmeli ("standart desen istisnaları: `customers` §2.8, audit tabloları §3.6 — her istisna kendi kriterleriyle sınanır").

## ✅ What works

- **Deney disiplini bu turda zirve yaptı:** dört bulgunun dördü de canlıda hem tuzak hem düzeltme yönünde doğrulandı (deney 10-12). Özellikle deney 11, review'ın kendi önerisini test edip çürüterek daha iyi bir çözüme ulaştı — "başkasının önerisi kanıt değildir" ilkesinin plana içselleşmiş hali.
- `write_tenant_id_or_null()`'ın kapsam sınırı (yalnız audit) ve `IS NOT DISTINCT FROM` kullanımı NULL-güvenli eşitliğin doğru aracı.
- N2 muafiyetinin dört kurallı dar tanımı, "istisna tabloya değil işleme aittir" desenini bekçi katmanına da taşıyor — tutarlı mimari dil.
- `customers` politika bloğunun gerekçeli SQL'i ve DELETE politikasızlığının AD-23'e bağlanması, kanun standardında "neden böyle" sorusunu kapatıyor.
- Revizyon geçmişi 2.2 tablosu diff ile birebir örtüşüyor.

## ❓ Open Questions

Yok — M1/M2'nin çözüm yönü açık, karar gerektirmiyor.

## Verdict: ❌ REJECTED

N1…N4'ün dördü de gerçek içerikle ve canlı deney kanıtıyla kapandı; sorulan etkileşim analizi de temiz — fırlatmayan varyant ve `customers` istisnası izolasyon varsayımlarını kırmıyor. Kalan iki bulgu dar ve mekanik: `LogActivity`'nin muafiyet listesi/bağlam tanımı dışında kalması (M1) ve kriter 6'nın artık kendi bilinçli istisnalarıyla çelişmesi (M2). İkisi de birkaç satırlık düzeltme; scope içinde bulgu varken orta yol olmadığından verdict REJECTED'dır. Bu iki madde kapatıldığında planın implementasyona hazır olmadığını gösteren hiçbir şey kalmıyor.
