# Review — Omurga Plan Chunk 1 İmplementasyonu — 2026-07-26

> **Review type:** impl-review
> **Previous review:** N/A (chunk1'in ilk implementation review'ı)
> **Upstream document(s) read:** `docs/plans/omurga-plan_chunk1.md` (birincil upstream, kabul kriterleri §1.11), `docs/plans/omurga-plan.md` (index, v2.3), `docs/specs/omurga-spec.md` v1.7 (özellikle §12 fonksiyonel olmayan gereksinimler, §13 sınır durumları)
> **Other cross-referenced documents:** `CLAUDE.md` (17 kırmızı çizgi, dil kuralı, onay kuralları), `docs/arch/kod-yerlesimi.md`, `docs/plans/omurga-plan_chunk2.md` (§2.2, §2.4, §2.9 — yalnız "chunk1 sonraki chunk'ların önünü kesmiş mi" sorusu için), `docs/issues.md`
> **Stage locks in effect:** Spec v1.7 + Plan v2.3 locked

**İncelenen artifact:** commit `d978d95` (ana implementasyon) + `8ec20d3`, `00bb896`, `86bd1cb`, `28f5563` (takip düzeltmeleri), `main` branch.

**Bağımsız doğrulama (bu review'da bizzat koşuldu):**
- `pest --group=omurga`: **51 test, 89 assertion, yeşil** (yerel koşum)
- `phpstan analyse` level 6: **0 hata** · `pint --test`: **temiz**
- **9 kasıtlı ihlal probu** ile arch kuralları yeniden kanıtlandı (aşağıda)
- TeamCity `Egitim_Egitim` build id **#692** (build no 6): **SUCCESS, "Tests passed: 51"**, 25.07.2026 23:46 — API'den doğrulandı (#690 ve #691 de yeşil; #689 FAILURE bellek sınırıydı, `phpunit.xml:38`'de kapatılmış)

---

## Upstream Coverage Matrix

§1.11'in 15 kabul kriteri + bölüm yükümlülükleri. Kanıtlar dosya+satır; "test geçti" tek başına kanıt sayılmadı, kritik kurallar kasıtlı ihlalle yeniden kırıldı.

| Upstream § / madde | Durum | Kanıt |
|---|---|---|
| §1.11-1 Dizin iskeleti + `app/Support/` satırı | ✓ | `app/{Domain/{Shared,Authorization,Visibility},Actions,Queries,Policies,Integrations,Data,Jobs,Listeners,Support}` mevcut (d978d95 dosya listesi); `docs/arch/kod-yerlesimi.md:26` Support satırı ("Karar vermez") |
| §1.11-2 Altı config; `config/` dışında `env(` yok | ✓ | `config/{tenancy,locales,reference_codes,audit,security,notifications}.php`; probe: `app/Support/Time/EnvProbe.php` içine `env()` yazıldı → `tests/Arch/EnvTest.php:46` kırıldı (`Support/Time/EnvProbe.php:11` raporlandı), silindi |
| §1.11-3 Üç host doğru route dosyasına; host'lar config'ten | ⚠ | Uygulama rotaları doğru: `bootstrap/app.php:21-27` domain kısıtı, `config/app.php:97-101` host'lar, `HostRoutingTest` 5 test. **Ama** `route:list`: Horizon ve Filament action rotaları `domain: null` — üç host'ta da yanıt veriyor → **F-2** |
| §1.11-4 `lang/tr` = `lang/en` anahtar kümesi | ✓ | `tests/Arch/LocaleTest.php:91-130`; probe: `lang/tr/probe.php` eklendi → test kırıldı ("lang/en is missing keys: probe.probe_only_in_tr") |
| §1.11-5 Koda gömülü kullanıcı metni testi kırıyor | ✓ | Probe 1: Türkçe yorumlu PHP dosyası → `LocaleTest.php:59` kırıldı. Probe 2: `resources/views/probe.blade.php` içine "Kayit basarili" → `LocaleTest.php:88` kırıldı |
| §1.11-6 Dil çözümleme zinciri (test) | ⚠ | Zincir doğru: `app/Http/Middleware/SetLocale.php:34-52`, `LocaleResolutionTest` 6 senaryo (tercih > tenant > varsayılan, desteklenmeyen değer atlanıyor). **Ama** zincir üç host'tan yalnız ikisinde koşuyor → **F-1** |
| §1.11-7 jsonb cast dil zinciri + fallback (test) | ✓ | `app/Support/Locale/TranslatableCast.php:27-49` (aktif dil → varsayılan → herhangi bir dolu çeviri → null); `TranslatableCastTest` 8 senaryo; desteklenmeyen dil yazımı fırlatıyor (:76-83) |
| §1.11-8 UTC saklama + kimliğe göre gösterim (test) | ✓ | `DisplayTimezoneTest:44-74` canlı pgsql'e Istanbul 21:30 yazıp `18:30:00` okuyor; `:76-97` ofis/tenant/geçersiz-tenant gösterimi; `app/Support/Time/DisplayTimezone.php:25-41` |
| §1.11-9 Arch kurallarının her biri kasıtlı ihlalle doğrulanmış | ✓ | Bu review'da 8 kural yeniden kırıldı: Domain'de `Illuminate\Support\Str` importu → `LayerTest` FAIL; `env()` → `EnvTest` FAIL; Türkçe yorum → `LocaleTest` FAIL; şablonda metin → `LocaleTest` FAIL; anahtar eksikliği → `LocaleTest` FAIL; `timestamps()` migration → `TimeTest` FAIL; testte çıplak `now()` → `TestDisciplineTest` FAIL; grup dışı test dosyası → `TestDisciplineTest` FAIL. Kalanlar aynı desenin türevleri |
| §1.11-10 `phpunit.xml` force + test rolü | ✓ | `phpunit.xml:40-61` tüm `<server>` girdileri `force="true"`; `DB_USERNAME=3eegitim_test` (:57); `TestEnvironmentTest:54-74` force'suz girdiyi yakalayan meta-test; `:36-42` rolün `rolsuper=false, rolbypassrls=false` olduğunu canlı doğruluyor |
| §1.11-11 Config cache guard | ✓ | Probe: `bootstrap/cache/config.php` oluşturuldu → `tests/TestCase.php:30` `RuntimeException` fırlattı, silindi |
| §1.11-12 Donmuş saat global; çıplak `now()` yasak | ✓ | `tests/Pest.php:132-143` global `beforeEach` (`setTestNow` + `preventStrayRequests`) / `afterEach`; `tests/FrozenClock.php:16` sabit an; probe 6 ile kural kırıldı |
| §1.11-13 `app.immutable_unaccent` + aksan duyarsız arama (test) | ✓ | `database/migrations/2026_07_25_230000_...php:31-39` (IMMUTABLE PARALLEL SAFE sarmalayıcı, sözlük pinli); `AccentInsensitiveSearchTest` — "istanbul/İSTANBUL/Istanbul" 3'ü de 3 kayıt, "cagri ogun" diakritikli kaydı buluyor, `%` karakter olarak ele alınıyor (:61-64), fonksiyonel index aynı ifadeyle kuruluyor (:66-75) |
| §1.11-14 PHPStan level 6 temiz | ✓ | Bu review'da koşuldu: 0 hata; `phpstan.neon` level 6, `app+config+database+routes+tests` kapsamda |
| §1.11-15 TeamCity yeşil | ✓ | `Egitim_Egitim` build no 6 (id #692) SUCCESS "Tests passed: 51", 25.07.2026 23:46 — TeamCity API'den bağımsız sorgulandı |
| §1.2 config içerikleri plan tablosuyla uyumlu | ✓ | tenancy (kapsam anahtarı `:13`, roller `:23-27`, bağlamsız tablolar `:36-46`), audit (maskeli alanlar, hassas alanlar, 730 gün `:54`, damga aralığı), reference_codes (EG/TK/FT `:24-28`, alfabe, uzunluk), notifications (kanallar, Discord, İYS, 90/60/45/30/15/7 `:41`), security (yöntemler, MFA listesi, oturum, deneme eşiği, dosya TTL), locales. Not: `context_free_routes` ve `financial_tables` chunk2'nin kanonik listeleri — açık soru Q-1 |
| §1.2 düzeltme notu: `timezone=UTC`, `display_timezone` | ✓ | `config/app.php:70` UTC sabit (env'e bağlanmamış — bilinçli), `:84` display_timezone; plan dosyasında düzeltme notu (chunk1 §1.2, 28f5563); `docs/issues.md` "Kapandı" kaydı |
| §1.3 host ayrımı güvenlik sınırı değil notu | ✓ | `bootstrap/app.php:15-17`, `routes/office.php:14-15`, `config/app.php` hosts bloğu yorumları |
| §1.4 çevrilmeyenler: mevzuat alanları translatable tanımına konmaz | ✓ | `config/locales.php:34-37` yalnız `notification_templates` ve `consent_texts`; yorum açıkça dışlıyor |
| §1.6 grup ataması dizinden; on grup | ✓ | `tests/Pest.php:26-48`; `TestDisciplineTest:85-101` grup listesinin AD-26 ile birebir eşleştiğini ayrıca kilitliyor |
| §1.10 composer script'leri + CI adımı | ✓ | `composer.json:63-98` (`test`, `test:omurga`, `test:rls` — parallel yok, `lint`, `analyse`); TeamCity adımı build #690+ ile kanıtlı |
| Wire taraması (orphan) | ✓ | `SetLocale` → `bootstrap/app.php:32`; `UtcTimestampGrammar` → `AppServiceProvider:37-41` (bağlantı başına, chunk2'nin ikinci bağlantısına hazır); `NullTenantSettings` → `AppServiceProvider:22` bind; `BackofficePanelProvider` → `bootstrap/providers.php:8` (route:list'te `filament.backoffice.pages.dashboard` kayıtlı). `TranslatableCast`, `SearchExpression`, `DisplayTimezone`'un production caller'ı yok — **plan bunu açıkça erteliyor** (§1.4 "içerik ilgili chunk'larda", §1.9 "hangi kolonların indeksleneceği ilgili chunk'larda", §1.5) → orphan sayılmadı |
| Kırmızı çizgi taraması (17 madde) | ⚠ | 16/17 temiz: tenant/RLS/audit henüz kurulmuyor (chunk2-3'ün işi, chunk1 yalnız yer hazırlıyor — uygun); `env()` arch'la kilitli; credential yok (`.env.testing.example` şablon, gerçek değer yok); AI attribution yok (5 commit tarandı); iş kuralı config'te ve AD-08 defaults olarak işaretli; test silme yalnız iskelet `ExampleTest`'leri (plan §1.6 yeniden yapılandırması kapsamında). **Kırmızı çizgi 6 deseni:** `pgcrypto` migration'sız → **F-3** |
| Dil taraması | ✓ | Kod tarafı İngilizce (arch-zorlamalı + gözle tarama: sınıf/metot/test adları, yorumlar, config anahtarları); UI metinleri `lang/tr` + `lang/en`; tek Türkçe-içerikli test dosyası gerekçeli `arch-allow` işaretli. Not N-3: `.env.example` yorumları Türkçe (proje konvansiyonu — commit mesajları gibi; karar Sets'in) |

---

## ✅ What works

- **Arch testleri gerçek.** Kullanıcının sorusuna doğrudan cevap: boş namespace'te (örn. bugünkü `App\Domain`) Pest arch kuralları sessizce geçer, **ama** bu review'da 8 kural kasıtlı ihlalle yeniden kırıldı — ilk sınıf eklendiği anda kural ateşleniyor ve her CI koşumunda yeniden değerlendiriliyor. Risk penceresi pratikte yok.
- **Kanıt disiplini örnek seviyede.** UTC testi gerçek PostgreSQL'e yazıp okuyor; test rolü testin kendisi tarafından `rolsuper/rolbypassrls` sorgusuyla doğrulanıyor; `phpunit.xml`'in force disiplinini denetleyen meta-test var; grup listesi AD-26'ya drift-guard'la bağlanmış.
- **Üç plan-ötesi kararın ikisi temiz kapanmış:** (1) `timezone=UTC` — plan çelişkisi sahibin kararıyla, plan dosyasında düzeltme notuyla ve issues.md kaydıyla çözülmüş; `config/app.php:70` değeri bilinçli olarak env'e bile bağlamamış. (2) `timestamptz + UtcTimestampGrammar` — plan §1.5 invariant'ını gerçekten kuran mekanizma; canlı deneyle bulunmuş, arch testiyle kalıcılaştırılmış (`TimeTest`), issues.md'de "chunk2+ migration'ları timestampTz kullanmak zorunda" uyarısıyla kayıtlı. Grammar'ın `ConnectionEstablished` üzerinden bağlantı başına takılması chunk2'nin `pgsql_financial` bağlantısını şimdiden kapsıyor.
- **Dil kuralı istisnaları kuralı zayıflatmıyor** (kullanıcının 3. sorusu): `arch-allow: turkish-content` işareti dosyayı yalnız Türkçe-harf taramasından muaf tutuyor; şablon-metin kuralı ve env kuralı muafiyeti tanımıyor (LocaleTest:62-89'da marker kontrolü yok). Tek kullanım var ve gerekçesiz kullanılamayacak kadar görünür (tek grep). Özel isim listesi tek girdili (İYS) ve kurum adı.
- **Sonraki chunk'ların önü kesilmemiş** (kullanıcının 5. sorusu): `app` şeması `IF NOT EXISTS` ile kuruluyor ve `down()` şemayı düşürmüyor (chunk2'nin fonksiyonları oraya gelecek); `tenancy.php` rol adları chunk2 §2.2 tablosuyla birebir; `TimeTest` chunk2+ migration'larını doğru yöne zorluyor. Tek dikkat noktası Q-2 (aşağıda).

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

**F-1 — Dil çözümleme zinciri üç host'tan yalnız ikisinde koşuyor.** `SetLocale` yalnız `web` middleware grubuna ekli (`bootstrap/app.php:31-33`); backoffice Filament panelinin middleware yığınında yok (`app/Providers/Filament/BackofficePanelProvider.php:46-56`) ve `route:list` çıktısı `filament.backoffice.pages.dashboard` rotasının middleware listesinde `SetLocale` içermediğini doğruluyor. Plan §1.3 üç host tanımlar, §1.4 zinciri bu chunk'ın kurduğu mekanizma olarak tanımlar; spec §12 "arayüz çok dillidir" der ve backoffice'i ayrı tutmaz. Backoffice host'unda dil her zaman `app.locale`'de kalır; kullanıcı tercihi adımı hiç çalışmaz. Bu boşluğu kapatmayı üstlenen bir downstream chunk maddesi yok (chunk5 kimlik kurar, zincir wiring'i chunk1'in teslimatıdır) ve `docs/issues.md`'de bilinçli erteleme kaydı da yok — sessiz bir wire boşluğu.

**F-2 — Host ayrımı paket kayıtlı rotaları kapsamıyor.** `route:list`: `horizon/*` (Laravel Horizon) ve `filament/exports/{export}/download`, `filament/imports/{import}/failed-rows/download` rotaları `domain: null` ile kayıtlı — üç host'ta da, müşteri/katılımcının kullandığı public host dahil, yanıt veriyor. Bugünkü maruziyet sınırlı (Horizon local dışında varsayılan-red; Filament action rotaları panel auth'u istiyor), ancak `HostRoutingTest`'in kendi savunduğu özellik — "bir host başka yüzeyin rotasına cevap vermez" (`HostRoutingTest:29-35`) — paket rotaları için tutmuyor. Plan §1.3 bu yüzeye dair sessiz; implementasyon da karar kaydı düşmemiş. Public host'ta `/horizon`'un cevap veriyor olması, chunk1'in teslim ettiği host ayrımının denetlenmemiş bir istisnasıdır.

**F-3 — `pgcrypto` migration'sız kuruluyor.** `deployment/scripts/setup-test-database.sh:85` test veritabanına `CREATE EXTENSION IF NOT EXISTS pgcrypto` uyguluyor; oysa depoda pgcrypto'yu kuran hiçbir migration ve ona referans veren hiçbir kod/plan maddesi yok (unaccent'in aksine — o hem script'te hem `2026_07_25_230000` migration'ında var, tutarlı). Sonuç: test veritabanları, temiz bir production kurulumunda var olmayacak bir extension taşıyor. İleride pgcrypto'ya dayanan bir kod/test yazılırsa testte geçer, temiz prod'da kırılır — kırmızı çizgi 6'nın (PA-225 dersi) koruduğu desenin tam kendisi. Script'in üstlendiği rol/veritabanı provizyonu issues.md'de kayıtlı ve gerekçeli; pgcrypto satırı bu kaydın da dışında.

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

- **Q-1:** `config/tenancy.php:54-58` `context_free_routes` ve `:67` `financial_tables` — bunlar chunk2'nin (§2.5.1, §2.10) kanonik listeleri. Chunk1'e çekilmiş olmaları bilinçli bir "yer hazırlama" mı? Chunk2 implementasyonunda bu listelerin plan metniyle drift kontrolünü kim/hangi test yapacak?
- **Q-2:** Testler migration'ları `3eegitim_test` rolüyle koşuyor → test tablolarının sahibi bu rol. Chunk2'nin RLS testleri (AD-26: RLS testi owner rolüyle koşulmaz) bu kurulumla nasıl bağdaşacak — `FORCE ROW LEVEL SECURITY` owner'ı da kapsadığı için yeterli mi sayılacak, yoksa migration'lar ayrı rolle mi koşulacak? (Chunk1 bulgusu değil; chunk2 impl'inden önce netleşmeli.)
- **Q-3:** `.env.example` / `.env.testing.example` yorumları Türkçe. Commit mesajları gibi "geliştirici düzyazısı" konvansiyonuna mı dahil, yoksa dil kuralının "kod içi yorum" kapsamına mı girer? (Karar Sets'in; bulgu olarak yükseltilmedi.)

## Notlar (bulgu değil)

- `app/Support/` altında beş boş, git'e girmemiş yerel dizin var (`Audit`, `Crypto`, `Files`, `Http`, `Masking`) — commit'in parçası değil, yerel artık; yanıltıcı olmasın diye not edildi.
- Plan §1.7 tablosu 10 kural sayar; implementasyon 14 test/kural içeriyor (timestamptz, grup-drift-guard vb. eklemeler) — hepsi sıkılaştırma yönünde, kabul.
- İskelet `ExampleTest`'lerinin silinmesi plan §1.6'nın test ağacı yeniden yapılandırmasının doğal sonucu olarak değerlendirildi; gerçek kapsam silinmedi (0 → 51 test).

## Verdict: ❌ REJECTED

15 kabul kriterinin 13'ü dosya+satır kanıtıyla ✓; arch kuralları bu review'da bizzat kasıtlı ihlalle yeniden kanıtlandı; TeamCity/PHPStan/Pint iddiaları bağımsız doğrulandı. Ancak matrix'te iki ⚠ satırı (kriter 3 ve 6) ve kırmızı çizgi 6 desenine giren bir sapma var: F-1 (backoffice host'unda dil zinciri koşmuyor), F-2 (paket rotaları host ayrımının dışında ve karar kaydı yok), F-3 (pgcrypto migration'sız). Üç bulgu da dar ve kapatılabilir; kapanınca re-review istenmelidir.
