# Review — Omurga Chunk 3 (Audit) Implementation — 2026-07-26 — r2

> **Review type:** impl re-review (heyet 134 bulgu kapanışlarının bağımsız doğrulanması + değişen alanların taze taraması)
> **Previous review:** `review-omurga-chunk3-impl-2026-07-26.md` (❌ REJECTED, 13 bulgu)
> **Upstream document(s) read:** `docs/plans/omurga-plan_chunk3.md` (v2.4.4) · `docs/plans/omurga-plan.md` (index, 2.4.4 girdisi) · `docs/plans/omurga-plan_chunk2.md` §2.5, §2.6, **§2.6.1 (yeni W sözleşmesi)** · `docs/specs/omurga-spec.md` §7.1 (revize) · `docs/arch/mimari-kararlar.md` **AD-06 (yeniden yazıldı, 26.07.2026)** · `docs/plans/omurga-plan_chunk6.md` §6.9-7d · `docs/plans/omurga-plan_chunk9.md` S-12 · `docs/issues.md`
> **Stage locks in effect:** Spec v1.9.1 + Plan v2.4.4 locked *(ancak bkz. F-8: spec içeriği değişti, sürüm damgası değişmedi)*
> **Reviewed artifact:** branch `feature/EGT-32-audit`, HEAD `f19c924`; çalışma ağacı temiz (yalnız `docs/claude/heyet.md` değişik — KamuSM referans temizliği, incelendi, sorun yok)
> **Bağımsız doğrulama:** `composer test` bu review sırasında koşuldu → **91 + 137 = 228 test yeşil**; `phpstan` **0 hata**; `pint --test` temiz. ⚠ **TeamCity'de `f19c924` için koşum YOK** (son görünen build'ler önceki commit'lere ait) — merge öncesi full CI zorunluluğu (CLAUDE.md kalite adım 3) açık kalem.

---

## Bölüm 1 — Heyet 134 bulgularının kapanış doğrulaması

Kanıtların tamamı bu turda koddan/dokümandan yeniden okundu; hiçbiri implementer beyanından alınmadı.

| 134 bulgusu | Durum | Bağımsız kanıt |
|---|---|---|
| **F-1** Merkle revizyonu AD-06 ile çelişiyordu | ✓ kapalı (upstream değişti) | AD-06 yeniden yazıldı (Sets, 26.07.2026): zincir + dış tanık artık **karar**, KamuSM/ESHS bilinçli terk, delil sınıfı değişimi (takdiri delil) ve hukuk teyidi açık kalem olarak yazılı; spec §7.1 hizalı; plan §3.8 sapma bloğuyla revize. Karar impl'de değil upstream'de verilmiş — 134'ün istediği süreç buydu |
| **F-2** Çalışma ağacı derlenmiyordu | ✓ kapalı | KamuSM sınıflarına sarkan referans kalmadı (grep: `app/ config/ tests/ database/` temiz); `SealAuditBatch`/`PublishAuditRoots`/`VerifyAuditChain` schedule'da (`AuditServiceProvider`, `AuditWiringTest` "schedules sealing, publishing…"); 228 test bu review'da koştu |
| **F-3** §3.8 sıkıştırma sapması planda karşılıksızdı | ✓ kapalı | Plan §3.8: "⚠ Sıkıştırma yok — columnstore RLS'li tabloda kullanılamıyor… vazgeçildi" artık plan metninde |
| **F-4** §3.9 CA sapması planda karşılıksızdı | ✓ kapalı (ama bkz. yeni F-7: eski CA maddesi bölümde unutulmuş) | Plan §3.9 ⚠ bloğu: rollup + W sözleşmesi ataması |
| **F-5a** dinamik SQL (G-3) | ✓ kapalı | `..._000300_create_audit_merkle_function.php`: `EXECUTE format` yok, iki sabit dal (`IF p_table = 'audit_writes' … ELSIF … ELSE RAISE`); G-3'e "allowlist + %I istisna değildir" ibaresi eklendi |
| **F-5b** G-2 dönüş sözleşmesi | ✓ kapalı (sözleşme düzeyinde; niteleme eksiği için bkz. yeni F-6) | chunk2 §2.6 G-2 yeniden yazıldı: "kapsam dışı satır içeriği dönmez" + tek satırlık sabit sütunlu `RETURNS TABLE` = bileşik skaler + tek taramadan gelme şartı (gerekçesiyle) |
| **F-5c** yazan SECURITY DEFINER'ın yanlış G-1 beyanı | ✓ kapalı | chunk2 **§2.6.1 W-1…W-6** eklendi; `roll_up_usage_daily` COMMENT'i "chunk2 2.6.1 W contract (a scope independent WRITE, not a G class read)" — canlı migration metninde doğrulandı; SECURITY DEFINER'sız alternatiflerin reddi gerekçeli (`..._000200:134-148`) |
| **F-6** `field_class` varchar(64) taşması | ✓ kapalı | `audit_access.context` jsonb kolonu (`..._000100:128-134`); feed filtreyi context'e taşıyor (`BackofficeAuditFeed.php:121-131`); `AccessLogger` ayrıca `mb_substr(...,0,64)` ile savunmada |
| **F-7** `LogUsage` rota sınırı yok | ✓ kapalı | `config('audit.usage_routes')` desen listesi (`ofis.* panel.* katilimci.* backoffice.*`); `shouldRecord` adsız rotayı ve desen dışını reddediyor (`LogUsage.php:49-69`) |
| **F-8** `masked_fields`'te e-posta yok | ✓ kapalı | `config/audit.php` masked_fields: `'email'` eklendi; `Masker::email()` artık ulaşılabilir |
| **F-9** kriter 7'nin driver-yolu kanıtı yok | ✓ kapalı | `AuditWriteGuaranteeTest` "masks sensitive values on the package driver path too": `AuditProbeModel` + `national_id` kolonu, model create → `audit_writes` string araması |
| **F-10** 17c kasıtlı ihlal probu yok, tarama üç tabloya kilitliydi | ✓ kapalı | `AuditSchemaTest`: tarama **tüm şema politikalarına** genişledi (`tablesCarryingTheCrossTenantFlag()`); "catches the flag being carried by another table" probu bayrağı fixture tabloya taşıyıp yakalanışını gösteriyor |
| **F-11** `OfficeAuditFeed` bağlamı kendisi açmıyordu | ✓ kapalı | Sınıf `TenantContext::run(Closure)` ile kendi sınırını açıyor; `TenantContext::run(Closure)` iç içe çağrıda mevcut (daraltılmış) kapsamı koruyacak şekilde düzeltildi (genişletme exception'ı yerine kompozisyon); yeni test "opens its own tenant boundary" iki durumu da (daraltılmış Action içi + bağlamsız) sınıyor. *(Semantik değişikliğin chunk2 §2.5 metnine işlenmemesi → yeni F-9)* |
| **F-12** bekçi `app.` atlaması yalnız `pg_tables`'a karşı sınanıyordu | ✓ kapalı | `AuditSchemaTest` "keeps the app schema free of every kind of relation": `pg_class.relkind IN ('r','p','v','m','f')` — VIEW/matview/foreign table dahil; view-sahibi-RLS gerekçesi test yorumunda doğru anlatılmış |
| **F-13** kriter 11b sınanamıyordu | ✓ kapalı | 11b düşürüldü (çift kayıt tespiti: chunk6 §6.9-7d zaten aynı kriterdi); 7d "tek yer" işaretli; plan 2.4.4 girdisi + emsal P-4 |

**13/13 kapalı.** Kapanışların niteliği de yerinde: hiçbiri testi gevşeterek değil, kodu/sözleşmeyi düzelterek kapatılmış.

## Bölüm 2 — Değişen alanların taze matrisi (AD-06 zinciri + yeni kriterler)

| Upstream madde | Durum | Kanıt |
|---|---|---|
| AD-06: append-only trigger + mühür dondurma | ✓ | `deny_mutation` (önceki tur) + `app.deny_seal_rewrite()` (`..._000100:344-380`); `AuditChainTest` üç dondurma testi |
| AD-06: "Batch'teki tek bir satır değişse kök değişir" | ❌ | **Yaprak yalnız `(id, created_at, tenant_id)` kapsıyor** (`app.audit_leaf_line`); içerik kolonları zarfın dışında → **F-1** |
| AD-06: hash zinciri, tahrifat yerel kalamaz | ✓ (zincir aritmetiği) / ❌ (zamanlanmış tespit) | `SealAuditBatch::link`, `previous_chain_hash` bağı testli; ancak `VerifyAuditChain` kökleri satırlarla **karşılaştırmıyor** → **F-2** |
| AD-06: dış tanık, hesapla → yayımla → kaydet | ✓ | `SealAuditBatch::handle` sırası; `AuditWitnessTest` "writes no seal at all when the witness cannot be reached" + "recovers when publishing succeeded but storing did not" (determinizm/idempotency dahil) |
| AD-06: "periyodik doğrulama işi yerel zinciri yayımlananla karşılaştırır" | ⚠ | `compareWithWitness` iki yönlü yazılmış ✓ ama (a) tanık kopyası fetch'siz stale/yazılabilir workspace'ten okunuyor → **F-3**; (b) bu yolun sıfır test kapsamı var → **F-4** |
| AD-06: kural adlandırma `sha256-utc-iso-v1` | ✓ yazım / ⚠ tüketim | Mühürde taşınıyor, COMMENT'lerde, testte; ancak `hash_algorithm`'ı **okuyan hiçbir kod yok** — "verifiers read this field first" iddiası karşılıksız (F-2'nin parçası) |
| AD-06: WORM arşiv | ✓ | `ArchiveAuditBatch` yeni şemayla hizalı (`merkle_root`/`chain_hash`/`witnessed_at` yazıyor, silinmiş kolon referansı kalmadı) |
| §3.8 oturumdan bağımsız yaprak | ✓ | `to_char(... AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US')`; üç TZ/DateStyle kombinasyonunda aynı kök testi. Kuralın geri kalanı da oturumdan bağımsız doğrulandı: `bigint→text` ve `::text` cast'leri locale'siz, `digest/encode` deterministik, `array_agg ORDER BY id` benzersiz anahtar üzerinde, yaprak/ebeveyn girdileri yapısal olarak ayrışık (ebeveyn girdisi saf 128-hex, yaprak `|`/`T`/`:` içerir → domain karışması yok), tek yaprak kopyalanmıyor |
| Kriter 21 (zincir dışarıdan yeniden hesaplanabilir + malleability) | ✓ | "computes the chain hash in a way an outsider can reproduce", "chains published seals in order", "handles an odd number of leaves…" |
| Kriter 22 (tahrifat görünür) | ⚠ | İkinci yarı ✓ (mühür düzenleme → `VerifyAuditChain` raporluyor). İlk yarı **yalnız kapsanan kolonda** (`tenant_id`) ve **testin kendi elle recompute'uyla** gösteriliyor — `VerifyAuditChain` aynı tahrifatı **yakalamaz** (F-2), içerik kolonu tahrifatını kök dahi yakalamaz (F-1) |
| Kriter 23 (yalnız hash ve sayı çıkar) | ✓ | `CapturingWitness` alan listesi birebir + `AuditWitnessTest` "publishes hashes and counts, and nothing else" (olay adı/aktör sızmıyor) |
| Kriter 24 (tanıksız dürüstlük) | ✓ | "leaves a seal unwitnessed when no witness is configured" |
| Kriter 25 (yayım önce; idempotent telafi) | ✓ | İki test (`writes no seal at all…`, `recovers when publishing succeeded…`) |
| Kriter 26 (mühür dondurma, üç test) | ✓ | DELETE red + zincir alanı UPDATE red + witness/archive yazılabilir |
| Kriter 27 (doğrulama tanıkla karşılaştırıyor, iki yön) | ⚠ | İlk bacak ✓; **tanık bacaklarının testi yok** (F-4) |
| Kriter 5 (mühürleme engellenince iş+audit duruyor) | ⚠ | Eski test silindi; yeni test yalnız "mühür oluşmadı"yı assert ediyor, audit/iş kaydının kaldığını assert etmiyor. Mimari gereği doğru (mühür yazım yolunun dışında) ama kriterin istediği kanıt inceldi |
| Kriter 14 (yeni metin: tanık adaptörü + null impl + issues kalemi) | ✓ | `AuditWitness`/`NullAuditWitness`/`GitAuditWitness`; `docs/issues.md` tanık kalemi (repo şartları canlı doğrulanmış: public, GH013, deletion ruleset) |
| Önceki turda ✓ olan ve bu turda değişmeyen satırlar (§3.1–§3.7, kriter 1–18 kalanları) | ✓ | r1 matrisi geçerli; değişen dosyalar (`AccessLogger`, `LogUsage`, `OfficeAuditFeed`, `TenantContext`) yukarıda yeniden dorulandı; 228 test + kasıtlı ihlal probları yeşil |

**Zorunlu taramalar:** Red-line — ihlal yok; yeni SECURITY DEFINER yüzeyi büyümedi (aynı iki fonksiyon, artık doğru sözleşme beyanlarıyla); `SET LOCAL` disiplini korunuyor; AI attribution beş commit'te de yok; elle DDL yok (tanık repo'su migration kapsamı dışında, DB değil). Wire — yeni sembollerin tamamı bağlı (schedule/config/env); `GitAuditWitness` `AUDIT_WITNESS` env üzerinden bağlanıyor (`.env.example` yazılı) ✓; **`OfficeAuditFeed`/`BackofficeAuditFeed` hâlâ 0 production caller** (aşağıda, açık soru 3). Dil — kod tamamen İngilizce; `.env.example` ve config yorumları uygun; commit mesajları hook formatında.

## ✅ What works

- **13 bulgunun kapanma biçimi örnek nitelikte.** Özellikle F-10 (tarama şema-geneline alınıp kasıtlı ihlal probu eklendi), F-12 (relkind genişletmesi doğru gerekçeyle) ve F-11 (kompozisyon problemi kökünden, `TenantContext::run(Closure)` semantiğiyle çözüldü ve iki yönlü testlendi).
- **Yaprak kuralının oturum bağımsızlığı gerçek bir keşif ve doğru kapatılmış.** TZ/DateStyle kusuru canlı doğrulanmış, kural tek adrese (`app.audit_leaf_line`) alınmış, üç-kombinasyon testi eklenmiş, kural adı mühre yazılmış. Bu turun kendi bulduğu bulgu, raporlanandan da önemli çıktı (aşağıda F-1/F-2 — aynı bölgede daha derin iki eksik).
- **Yayım sırası (hesapla → yayımla → kaydet) doğru takas** (soru 4'ün cevabı): determinizm argümanı sağlam, iki yönlü kriz senaryosu (tanık yok → mühür yok; yayım var → kayıt yok → yakınsama) gerçek testlerle gösterilmiş. Tanık kesintisi audit yazımını etkilemiyor, yalnız delil gecikiyor — AD-06'da bilinçli bedel olarak yazılı. Tek operasyonel pürüz: iki `SealAuditBatch` aynı workspace klonunu paylaşıyor; eşzamanlı koşumda git kilidi geçici hata üretebilir — güvenli yönde düşer (mühür oluşmaz, sonraki tur telafi eder).
- **Upstream süreç bu kez doğru işletildi:** AD-06 kararı Sets'e sorulup AD/spec/plan üçlüsüne gerekçe, delil sınıfı uyarısı ve hukuk teyidi kalemiyle işlendi; G/W ayrımı "ihlali meşrulaştırmak için standart genişletilmez" ilkesiyle çözüldü — W sözleşmesi (soru 1) makul dar: W-2'nin "hiçbir gerekçeyle" dili + W-6'nın S-9 ayrı-sınıf envanteri + tek örnek beyanı kapıyı küçük tutuyor.
- Bağımsız doğrulama: 228 test, PHPStan 0, Pint temiz — üçü de bu review'da koşuldu.

## ⚠️ Findings & Risks

**F-1 — Merkle yaprağı audit satırının İÇERİĞİNİ kapsamıyor; "tek satır değişse kök değişir" iddiası yalnız üç metadata kolonu için doğru.** `app.audit_leaf_line(id, created_at, tenant_id)` — `event`, `user_id`, `user_type`, `old_values`, `new_values`, `actor_type`, `ip_address`, `url`, `tags` (ve `audit_access`'te `field_class`/`context`) zarfın dışında. *Failure scenario:* DB owner yetkili biri trigger'ı kapatıp bir onay kaydının `new_values`/`user_id`'sini değiştirir ("X, 1M'lik teklifi onayladı" → başka içerik), trigger'ı açar: kök, zincir, tanık ve `VerifyAuditChain` **hepsi temiz** — çünkü değişen hiçbir kolon hash'lenmiyor. AD-06'nın Merkle satırı ("Batch'teki tek bir satır değişse kök değişir") ve kriter 22'nin amacı bu haliyle karşılanmıyor; kriter 22 testi yeşil çünkü **kapsanan** kolonu (`tenant_id`) düzenliyor. Delil iddiasının merkezindeki boşluk — TZ kusuru ile aynı sınıf, daha derin.

**F-2 — `VerifyAuditChain` kökleri satırlarla hiç karşılaştırmıyor; zamanlanmış doğrulama satır tahrifatını (kapsanan kolonlarda bile) yakalayamıyor.** `verify()` `audit_batch_merkle_root`'u çağırıyor ama sonucu yalnız `row_count < seal->row_count` için kullanıyor (`VerifyAuditChain.php:69-88`); `merkle_root` karşılaştırması yok ve `chain_hash` kontrolü **saklanan** kökle yapılıyor. Kök karşılaştırması yapısal olarak da mümkün değil: fonksiyon `to_id` üst sınırı almıyor, tarihsel batch'in kökü yeniden hesaplanamıyor. Plan §3.8 "Kökleri satırlardan yeniden hesaplar… karşılaştırır" diyor, sınıfın kendi docblock'u da — kod yapmıyor. *Failure scenario:* kriter 22 testinin birebir senaryosu (trigger kapat, `tenant_id` değiştir) üretimde koşsa günlük doğrulama **sıfır bulgu** üretir: satır sayısı aynı, zincir alanları tutarlı, tanık karşılaştırması chain_hash bazlı ve mühürler değişmedi. Ek olarak: `hash_algorithm` alanını okuyan tüketici yok — "verifiers read this field first" (SealAuditBatch yorumu) karşılıksız; kural v2'ye geçtiğinde eski halkaları hangi kuralla doğrulayacağını bilen kod bulunmuyor.

**F-3 — `GitAuditWitness::published()` mevcut klonu fetch etmiyor; doğrulama, uygulamanın yazabildiği stale kopyaya karşı yapılıyor.** Metod yalnız `.git` yokken clone alıyor (`GitAuditWitness.php:73-80`); varken `storage/app/audit-anchor` içeriğini olduğu gibi okuyor. Docblock tam tersini söylüyor ("from the remote rather than from our copy… a local clone nobody refreshed would prove only that we agree with ourselves"). *Failure scenario:* satırları ve mühürleri yeniden yazabilen bir saldırgan workspace dosyalarını da düzenler → `compareWithWitness` "tanıkla" mutabık kalır; public repo farklıdır ama onu okuyan otomatik hiçbir şey yoktur. Tanık karşılaştırmasının varlık sebebi olan bağımsızlık, okuma anında kayboluyor.

**F-4 — Kriter 27'nin tanık bacaklarının test kapsamı sıfır.** `compareWithWitness` hiçbir testte dolu `published()` verisiyle koşmuyor: `AuditChainTest`'te witness `Null`/`Capturing` (ikisi de `[]` döndürüyor), `AuditWitnessTest`'te `VerifyAuditChain` hiç çağrılmıyor. "a published seal is missing locally" ve "a local seal is missing from the witness" yolları — kriter 27'nin açıkça test istediği iki yön — hiç çalıştırılmamış kod. (Mevcut "catches a seal whose own hash was edited" testi bulguyu `verify()`'ın zincir-içi kontrolüyle yakalıyor, tanıkla değil.)

**F-5 — `witnessed_at` sözleşmeye aykırı olarak bizim saatimiz.** `WitnessReceipt` kontratı "recordedAt is when the WITNESS says it received it, never when we say we sent it" der; `GitAuditWitness` her iki dönüş noktasında `Date::now()` yazar ve şema yorumu "Filled when an external witness has recorded…" der. Bilirkişiye "tanığın kayıt zamanı" olarak sunulabilecek bir kolonda uygulama saati duruyor; gerçek tanık zamanı yalnız forge'un push kaydında. Ayrıca `alreadyPublished` yolunda `reference` olarak güncel HEAD dönüyor — satırı içerir ama yayımlayan commit değildir.

**F-6 — G-2'nin yeni "kriptografik digest" kategorisi niteliksiz: düşük-entropili içerik üzerinde doğrulama oracle'ı olur** (soru 2'nin cevabı). Eski metnin reddedeceği, yeni metnin geçireceği tehlikeli örnek: tek bir satırın (veya tek alanın) içeriği üzerinde digest döndüren bir fonksiyon — `sha256(satır)` "geri döndürülemez özet"tir ama TCKN gibi ~10⁹'luk uzayda taranabilir ve tahmin-doğrulama kapısı açar. Bugünkü iki fonksiyon güvenli (yaprak yalnız metadata; toplu kök tek satıra indirgenemez); açık olan kod değil **sözleşme** — "özet, tek kayıt/tek alan içeriğine değil küme üzerine olmalı" tarzı bir sınır yazılmadıkça G-2 bu fonksiyonu reddedemez.

**F-7 — Plan §3.9 kendi içinde çelişiyor:** yeni ⚠ maddesi "continuous aggregate kullanılamıyor" derken hemen altındaki eski madde "TimescaleDB **continuous aggregate**: `analytics_usage_daily`…" olarak duruyor — r1 F-4 kapanışında eski satır silinmemiş.

**F-8 — Spec içeriği değişti, sürüm damgası değişmedi.** §7.1'in delil maddesi yeniden yazıldı (nitelikli damga → mühür/tanık/takdiri delil) ama başlık hâlâ *1.9.1* ve revizyon geçmişinde girdi yok. Planın kendi R4-1 kuralı ("sürüm damgası ile revizyon girdisi tek adımda atılır") spec'e uygulanmamış; "spec v1.9.1" pini artık iki farklı içeriği işaret ediyor (heyet 133 öncesi ve sonrası).

**F-9 — İki senkron kaybı:** (a) `TenantContext::run(Closure)`'ın yeni semantiği (aktif kapsam varsa onu korur) chunk2 §2.5 tablosuna işlenmedi — tablo hâlâ "ortam kapsamıyla çalışır" diyor; kanun tek yerde değil. (b) `docs/issues.md` tanık kalemi "VerifyAuditChain yalnız yerel zinciri kontrol ediyor" derken kod tanık karşılaştırması içeriyor — kalem kodun gerisinde (gerçi F-3/F-4 varken cümle fiilen doğru sayılır; yine de kalem güncel duruma göre yazılmalı).

**F-10 — Kriter 5'in kanıtı inceldi.** Eski "keeps the business record when only the stamping fails" testi silindi; yerine gelen test yalnız mühür yokluğunu assert ediyor, audit/iş kaydının ayakta kaldığını etmiyor. Mimari gereği doğru davranış, ama kriterin istediği gösterim artık dolaylı.

**Operasyonel not (bulgu değil):** `VerifyAuditChain` her mühür için `from_id`'den itibaren **tüm sonraki satırları** tarıyor — mühür sayısı × satır sayısı büyümesi; birkaç yıllık veride günlük işin maliyeti kuadratik. `epoch_date` kenar durumu: gün dönümünde publish-başarılı/store-başarısız kalan halka ertesi gün farklı `epoch_date` ile kaydedilir; chain_hash karşılaştırması etkilenmez, yalnız tanık kopyasıyla yerel kayıt arasında bu alan farklı kalabilir.

## Sorulan altı noktanın özeti

1. **W sözleşmesi** doğru sınırlandı; tek yumuşak nokta "türev tablo"nun tanımsız olması — bugün tek örnek ve S-9 ayrı-sınıf envanteri kapıyı tutuyor, ama W'ya ikinci aday geldiğinde "hangi tablo türevdir" sorusunun cevabı sözleşmede yok.
2. **G-2 gerçekten daraltıyor** (satır içeriği yasağı ilk kez yazılı) — ama digest kategorisi niteliksiz: F-6'daki oracle örneği eski metinde reddedilir, yenisinde geçer.
3. **Yaprak kuralı** oturum bağımlılığı açısından tam (to_char/UTC, cast'ler, sıralama, domain ayrışması — hepsi doğrulandı); eksik olan kuralın **tüketimi**: `hash_algorithm`'ı okuyan kod yok (F-2) ve asıl boşluk yaprağın kapsamı (F-1).
4. **Yayım sırası doğru takas** — determinizm + idempotency testli; tanık kesintisi audit'i durdurmaz, mühürü geciktirir; bu bilinçli ve yazılı.
5. **Mühür dondurma yüzeyi:** zincir yazılabilir alanlara dayanmadığı için bu yüzeyden zincir tahrif edilemez; ancak sahte `witnessed_at`/`witness_reference` yazmak (normal UPDATE, trigger izin verir) mührü "tanıklı" gösterir — bunu yakalayacak tek mekanizma compareWithWitness'in "local seal missing from the witness" bacağı, o da bugün F-3 (stale okuma) ve F-4 (testsiz) nedeniyle güvenilir değil. Yüzey, F-3/F-4 kapanınca kabul edilebilir.
6. **Orphan kabulü** aşağıda açık soru olarak Sets'e bırakıldı.

## ❓ Open Questions

1. F-1 için yön kararı: yaprak kuralı içerik kolonlarını kapsayacak şekilde **v2**'ye mi alınacak (mevcut halkalar `sha256-utc-iso-v1` olarak kalır — versiyonlama tam da bunun için kuruldu), yoksa "zincir yalnız satır varlığını/yerleşimini korur" sınırı AD-06'ya açıkça mı yazılacak? İkisi de meşru; şu anki durum ikisi de değil (iddia geniş, kapsam dar).
2. `f19c924` için TeamCity full run — merge öncesi zorunlu adım; lokal koşum (bu review dahil) onu ikame etmez.
3. **Orphan kabulü (açık talep):** `OfficeAuditFeed` ve `BackofficeAuditFeed` tüketicileri chunk6/chunk8'e kadar production path'siz. Plan bunu bilinçli sıralıyor ("omurga iş ekranı üretmez"); CLAUDE.md onay kuralı 4 ile çatışmanın tutanağa bağlanması Sets'in kararı. Reviewer görüşü: plan kararı olduğu için bulgu sayılmadı; kabul kaydı düşülmesi yeterli.
4. Tanık deposu README'sindeki doğrulama prosedürü F-1/F-2 kararından etkilenir — kural değişirse README ve `sha256-utc-iso-v1` birlikte sürümlenmeli (issues.md'deki "bağımsız doğrulama aracı" kalemiyle aynı pakette).

## Verdict: ❌ REJECTED

Heyet 134'ün 13 bulgusunun 13'ü de gerçekten ve doğru biçimde kapatılmış — kapanış işçiliği bu projenin en iyi turlarından. Red, eski bulguların değil **yeni mühür mimarisinin doğrulama katmanının** ürünü: zincirin koruduğu küme iddiadan dar (F-1), zamanlanmış doğrulama tahrifatı yakalayamıyor (F-2), tanık karşılaştırması stale ve testsiz (F-3, F-4). Bu dördü aynı bölgede birleşiyor: **mühür üretimi sağlam, mührün bekçiliği henüz değil.** Kalanlar (F-5…F-10) sözleşme nitelemesi ve doküman senkronu düzeyinde. F-1 yön kararı (kapsamı genişlet ya da iddiayı daralt) Sets'indir; hangisi seçilirse F-2/F-3/F-4 onun etrafında kapanmalı.
