> Bu sayfa, `create-devoplus-one-app`'in her uygulamaya yazdığı `AGENTS.md`'nin örnek bir uygulama (`ornek-uygulama`, bütün servisler açık) için üretilmiş halidir. Uygulama deposundaki kendi `AGENTS.md`'niz esastır.

# AGENTS.md: Örnek Uygulama (Devoplus One uygulaması)

Bu dosya, bu uygulamayı geliştiren AI aracına (Claude Code, Codex vb.) yöneliktir. Kurallar bağlayıcıdır; bir
kuralın gerekçesi SDK belgesinde ya da platform karar defterindedir (aşağıda "Kaynaklar").

Kullanıcıya soru sorarken ortamınız seçenekli soru kartı gösterebiliyorsa (örneğin Claude Code'da `AskUserQuestion`)
soruları kartla ve iş dilinde 2-4 şıkla sorun; önerdiğiniz şıkkı ilk sıraya koyup "(önerilen)" yazın. Gösteremiyorsa
metinle sorun ve şıkları numaralı yazın. Teknik soru sorulmaz.

## Platform sözleşmesi

- Uygulama **Devoplus One** üzerinde, **Devoplus Portal içinde iframe ile** çalışır.
  Kimlik, kiracı ve backend erişimi `@devoplus/one-sdk`'dan gelir; **kendi oturum/çerez/token mekanizması YAZILMAZ.**
- `X-One-*` başlıklarını platform (dispatch) yazar; uygulama bunları `readContext(request.headers)` ile **okur**, asla
  üretmez. İstemciden gelen `X-One-*` başlıkları platformda silinir.
- `ctx.user.sub` devoplus-auth kimlik uzayıdır (`UserAccount._id`); Organization Schema `User._id` DEĞİLDİR.
  İkisi de 24-hex olduğu için karıştırılırsa sessizce hiç eşleşmez.
- Dış dünyaya yalnız `fetch` ile ve yalnız platformun izin verdiği adreslere çıkılır (CA backend `env.API_BASE`);
  başka adrese istek **403** döner. Veritabanı sürücüsü, TCP soketi, üçüncü taraf API anahtarı kullanılmaz.
- **Sır koda/git'e yazılmaz.** Backend kimlik bilgisi platformun çıkış katmanındadır; uygulama hiçbir sır taşımaz.
  `one-ci validate|plan|deploy` git'e girmiş/girecek dosyaları ve son dağıtımdan bu yana eklenen satırları tarar (sır
  taraması kapısı, D-53): API anahtarı, belirteç, özel anahtar, parolalı bağlantı adresi ya da depoya girmiş
  `.dev.vars`/`.env` dağıtımı durdurur. Yerel değerler `.dev.vars`'tadır ve `.gitignore`'dadır. Test sabiti gibi
  gerçek olmayan bir bulgu `.one-secrets-ignore`'a parmak izi + **gerekçe** ile eklenir; sır bir kez commit'lendiyse
  dosyadan silmek YETMEZ: geçmişte kalır, sağlayıcısında iptal edilip yenilenir.
- **Kod güvenlik taraması (D-65):** birleştirme isteğinde ve dağıtımdan önce Semgrep koşar; yüksek önemli bulgu
  (`ERROR`, `CRITICAL`, `HIGH`) hattı durdurur. Kullanıcı girdisini `eval`/`new Function`'a ya da `innerHTML`'e veren kod
  yazılmaz. Gerçek olmayan bir bulgu `.one-sast-ignore`'a parmak izi + **gerekçe** ile eklenir; kaynaktaki `nosemgrep`
  yorumu sayılmaz.
- **Hat (CI) platformundur:** bu depoya `.gitlab-ci.yml` ya da başka bir CI dosyası YAZILMAZ (platform projesindeki hat
  koşar, deponunki okunmaz). Testler `pnpm test` ile MR'da koşar; `pnpm-lock.yaml` commit edilir. Kurulum betiği
  (`postinstall`, `.pnpmfile.cjs`) EKLENMEZ; hattın dağıtım işi bağımlılık kurmaz, kurulum yalnız sırsız işte yapılır.

## Veri ve servisler

- **Veritabanı** `env.DB` (SDK tipi `OneDatabase`): bu kurulumun (uygulama × portal) KENDİ veritabanıdır; tabloya `PortalId` eklenmez, portal
  süzgeci yazılmaz; kişiye özel satır `sub` ile bağlanır. Şema `migrations/NNNN_*.sql` ile ilerler: var olan dosya
  DEĞİŞTİRİLMEZ, yeni dosya en büyük numaradan SONRA eklenir. `DROP TABLE`, `ALTER TABLE ... DROP/RENAME` ve WHERE'siz
  `DELETE` yazılmaz: önceki sürüm geri alındığında çalışmaz; yeni tablo/sütun eklenir, eskisi sonraki sürümde
  kaldırılır. `one-ci deploy` bu üç kuralı denetler (süreklilik kapısı) ve ihlalde dağıtımı durdurur.
- **Kayıt tutan ekranlar için önce veri modeli:** tabloyu, tipleri ve liste/kayıt/güncelleme/silme uçlarını elle yazmak
  yerine varlıkları `one.data.json`'da tanımlayın, `one data generate` çalıştırın (yeni `migrations/NNNN_one_data.sql` +
  `src/data/types.ts` + `migrations/.one-data-snapshot.json`; üçü de depoya girer, anlık görüntü elle düzenlenmez) ve
  uçları SDK `createCrudHandler({ model: dataModel, entity, records: 'shared' | 'own', canWrite? })` ile bağlayın.
  Model yalnız büyür: alan silme, ad/tür değişikliği, zorunlu yapma ve seçim değeri çıkarma üretilmez, komut durur;
  seçim alanına yeni değer eklemek serbesttir (migration gerekmez). Arama Türkçe katlanmıştır: her metin alanının
  `<alan>_fold` arama kolonu vardır; bir metin alanını elle yazdığınız SQL ile güncellerseniz bu kolonu da
  `foldSearchText(değer)` ile yazın ya da `NULL` yapın. Excel/CSV/yazdırma uçları hazırdır
  (`/api/<varlık>/export.xlsx`, `export.csv`, `report.html`); dosya arayüzde `one.download` ile Portal'a verilir, PDF
  tarayıcının "PDF olarak kaydet"iyle alınır. Belgeler: SDK başvurusu "Veri modeli ve kayıt işlemleri" + "Rapor ve dışa aktarma".
- **Dosya deposu** `env.FILES` (SDK `OneFiles`): kurulum başına depo. Anahtar/boyut kuralları SDK'dadır; kullanıcıdan gelen ad
  `sanitizeFileName` ile tek parçaya indirilir; yükleme gövdesi `readBoundedBody` ile alınır (`request.arrayBuffer()`
  YAZILMAZ, çünkü sınır gövde belleğe alındıktan sonra uygulanırdı). Liste imleci istemciye olduğu gibi döner.
  Anahtar `uploads/<sub>/<uuid>-<ad>` biçimindedir: her kullanıcı yalnız KENDİ ön ekini listeler/indirir/siler
  (`uploadPrefixFor`), uuid aynı adla ikinci yüklemenin öncekini ezmesini önler. Ortak ön ek ya da yalnız dosya adı
  YAZILMAZ: başkasının dosyasını ezer ve anahtarı bilen herkese sildirir.
- **Önbellek** `env.CACHE` (SDK `OneCache`): kurulum başına, yalnız **önbellek**. TTL zorunludur (60 sn ile 30 gün arası),
  değer JSON ≤ 1 MiB. Önbellek nihai tutarlıdır; sayaç, kilit ya da kalıcı kayıt için KULLANILMAZ; kalıcı veri veritabanındadır.
- **Backend** (`BackendClient`): CA `api/v1/*` uçları kullanıcının uygulama belirteciyle çağrılır; backend
  "kullanıcı yetkisi ∩ uygulama scope'u ∩ portal" kuralını kendisi uygular. Uç adı/alanı **uydurulmaz**: belgeden
  ya da kardeş depodan doğrulanır, doğrulanamıyorsa geliştiriciye sorulur.
- **E-posta/SMS/mobil bildirim** yalnız SDK `sendMail`/`sendSms`/`sendPush` ile; alıcı `sub` listesidir (ya da org
  kimliği `toOrgUserIds`), serbest adres/telefon/cihaz kimliği YAZILMAZ ve başka bir bildirim servisi kullanılmaz.
  Manifest `scopes` içinde `mail.execute`/`sms.execute`/`push.execute` olmalı. Bu üç scope ve `dexa.write`, kullanıcı
  uygulamaya o portalda AYRICA ve süreli (30 gün) onay vermeden belirtece GİRMEZ (D-59): uygulama yine açılır, portal
  üstte "İzinleri incele" düğmesini gösterir. Bu işleri yapan rotayı `requireScope` ile koruyun; arayüzde
  `awaitsUserConsent(ctx, scope)` `true` ise düğmeyi kapatıp "portaldaki İzinleri incele düğmesiyle izin verin" yazın.
  Mobil bildirim yalnız başlık (≤ 100) +
  metin (≤ 300) ve isteğe bağlı `path` (dokununca açılacak uygulama içi sayfa) taşır; her ortamda ve portalda açık
  değildir (kapalı kanalda `ONE-APP-E-029`; SMS için `ONE-APP-E-110`); kodunuz bu
  hatada da çalışmayı sürdürmeli, e-postayı ya da asıl işlemi düşürmemelidir (`notify` kanalları bağımsız gönderir).
- **DEXA (ERP okuma)** yalnız SDK `queryDexa(api, key, parameters?)` ile ve yalnız ÖNCEDEN tanımlı QueryStore
  anahtarlarıyla; serbest SQL ya da ERP'ye yazma YOKTUR. Anahtarları Devoplus Console'da uygulamanın kayıt defterine
  yönetici ekler (kayıtta olmayan anahtar 403). Manifest'te `services.dexa.queryKeys` ve `scopes` içinde `dexa.read`
  birlikte beyan edilir; anahtar adı uydurulmaz. Portalda açılmış sorguları parametre ve sütunlarıyla
  `one dexa queries` (MCP'de `one_dexa_queries`) listeler; anahtar oradan ya da geliştiriciden alınır. `result.unavailable === true` ise sorgu
  ÇALIŞAMADI; boş `rows` "kayıt yok" SAYILMAZ, kullanıcıya "şu anda getirilemiyor" gösterilir; `truncated` sonucun
  5.000 satırda kesildiğini söyler. `LangCode` platformundur, uygulama GÖNDERMEZ. `UserCode`, `CompanyCode`,
  `CostCenterCode`: kurulumda yönetici "kodları uygulama belirler" onayını verdiyse uygulama bunları her kipte (kişi,
  ajan, arka plan işi) gönderebilir ve sunucu kişi ile şirket kodunu kurulumun portalına karşı doğrular; onay yoksa
  gönderilen kod reddedilir (`ONE-APP-E-026`). Gönderilmezse kişi/ajan kipinde değer oturumdaki kişiden çözülür, arka
  plan işinde sorgu reddedilir. Boş değer ERP'ye gitmez: boş ya da yalnız boşluk değer `ONE-SDK-E-140` ile hiç
  gönderilmez; kodu bilmiyorsanız parametreyi göndermeyin, uydurmayın.
- **DEXA işlemi (ERP'ye YAZMA)** yalnız SDK `runDexaCommand(api, key, parameters, { idempotencyKey })` ile ve yalnız
  kayıt defterinde izinli (`dexaCommandKeys`) işlem anahtarlarıyla; manifest'te `services.dexa.commandKeys` + `scopes`
  içinde `dexa.write`. Anahtar uydurulmaz. ⛔ Her kullanıcı eylemi TEK bir `Idempotency-Key` alır: `newIdempotencyKey()`
  ile üretin ve isteği göndermeden ÖNCE veritabanına yazın; ağ hatası / zaman aşımı / 409 `ERPCMD-APP-E-016` / 424 sonrası
  AYNI anahtarla tekrar deneyin ya da `getDexaCommandStatus` ile durumu okuyun. `outcome === 'inDoubt'` ise ERP'ye
  gidip gitmediği bilinmiyor: YENİ anahtarla ASLA yeniden göndermeyin (çift kayıt). Kullanıcıya "sonuç doğrulanamadı"
  deyin. 4xx yanıt ERP'ye hiçbir şey gitmediğini söyler.
- **Arka plan işi (zamanlama / kuyruk) ve Devoplus servisleri:** işte kullanıcı belirteci yoktur; servisler
  UYGULAMANIN kimliğiyle çağrılır (`ctx.mode === 'job'`, `new BackendClient({ baseUrl, authorization: null })`).
  Kullanılabilenler yalnız manifest `jobs.services` (ve `scopes`) ile beyan edilen `mail.execute`, `sms.execute`,
  `push.execute`, `dexa.read`, `ai.use`'dur. ⛔ **Arka plan işi servisleri kurulumda yönetici onayıyla açılır**:
  onaysız çağrı 403; DEXA'ya yazma bu yolla AÇILMAZ. "Kendime gönder" yoktur, alıcıyı açıkça verin.
- **Denetim kaydı:** kritik kullanıcı işlemi `logAudit` (console izi) ile işaretlenir; kalıcı kayıt gerekiyorsa
  `recordAudit(api, ctx, action, target?, data?)` (scope `audit.write`). `target`/`data`ya kişisel veri ya da sır
  YAZILMAZ; kayıt Devoplus Console'da görünür ve 365 gün saklanır.
- **Olay kaydı (Event Logger):** çalışma zamanı olayı `logEvent(ctx, level, message, data?)`, beklenmeyen hata
  `logError(ctx, message, error, data?)`. Satırlar platform günlüğünde saklanır (platform her kurulumda açar; örnekleme
  manifest `observability.headSamplingRate`). `data`daki parola/token/secret/authorization/cookie/API anahtarı adlı
  alanlar otomatik "[gizli]" olur ve 8 KiB üstü `data` yerine boyutu yazılır; yine de kişisel veri ya da sır
  yazmayın. `problemResponse(error, ctx)` bilinmeyen hatayı bu kayda bağlamıyla düşer.

## Scope ve yetki

- Bir yetenek `one.manifest.json` dosyasındaki `scopes` içinde beyan edilmeden kullanılamaz; kod tarafında `requireScope(ctx,
  '<scope>')` kapısı konur (yoksa 403). Scope adları devoplus-auth kataloğundandır: `<modül>.<read|write|manage|execute>`
  ve ayrıca `ai.use` (Devoplus yapay zekası).
  Bu uygulama henüz scope beyan etmiyor (yalnız kimlik + kendi veritabanı).
- Menü simgesi `one.manifest.json` dosyasındaki `menu.icon` alanıdır: PrimeIcons sınıfı (ör. `pi pi-box`, `pi pi-flag`). Listede
  olmayan değer menüde varsayılan simgeyle çizilir ve doğrulama uyarı yazar; değişiklik bir sonraki yayında menüye geçer.
- Portal menüsünde görünme izni `One.OrnekUygulama.Use` kodudur; kimin kullanacağını portal yöneticisi Yetki Yönetimi'nden
  atar; uygulama kendi içinde ikinci bir yetki modeli KURMAZ (kendi rol tablosu, e-posta listesiyle yetki vb. YAZILMAZ).
- Uygulama içinde rol gerekiyorsa (ör. yalnız onaylayanlar onaylasın) **ek izin kodu** beyan edilir:
  `one.manifest.json` dosyasına `permissions: [{ "code": "<One.OrnekUygulama.Use ile aynı önek>.<Eylem>", "name": { ... } }]`.
  Portal yöneticisi bu kodu da Yetki Yönetimi'nde kullanıcı gruplarına (şirket, departman, ünvan, pozisyon gibi düğümler)
  atar; kod tarafında `requirePermission(ctx, '<kod>')` (403) ya da `hasPermission(ctx, '<kod>')` kullanılır. Portal
  yöneticisi her izne sahip sayılır (platformla aynı kural). Platform uygulamaya yalnız KENDİ ad alanındaki izinleri
  iletir; başka bir uygulamanın ya da portalın izni okunamaz.

## Eksik izin: erişim talebi

- Uygulamanın bir portala kurulması, bir ERP (DEXA) sorgusu ya da işlemi, izninin bir kullanıcı grubuna atanması ya da
  yeni bir yetki alanı gerekiyorsa kullanıcıya teknik adım anlatılmaz: talebi AI kendisi gönderir. Devoplus One
  bağlantısında `one_request_access` aracı, yoksa komut satırında `one login` ve `one request <tür> ...`. Durum
  `one_list_my_requests` / `one_get_request` ya da `one requests` ile izlenir.
- Talep izni kendiliğinden VERMEZ. Talebin portalının şirket yöneticisi Devoplus Console'da onaylar ya da gerekçesiyle
  reddeder (yetki alanı talebini platform yöneticisi onaylar); sonuç talep sahibine e-postayla bildirilir. Kayıt dışındaki
  talepleri yalnız uygulamanın sahibi gönderebilir; portal ve gönderen oturumdan gelir.
- Gerekçe (`justification`) kullanıcının iş diliyle yazılır. ERP sorgusu istemeden önce `one_dexa_queries` (ya da
  `one dexa queries`) listesine bakılır ve anahtar listeden aynen alınır; uygun sorgu yoksa `new-query` gönderilir.
  Kurulum talebinde portal anahtarı gönderilmez, kullanıcıya da sorulmaz.
- Uygulama içi izinlerin hepsi kayıt talebinde istenir: kayıttan sonra izin eklemenin talep türü yoktur, izni uygulamanın
  ana portalının şirket yöneticisi Devoplus Console'dan ekler (ana portalı belirlenemeyen uygulamada platform yöneticisi).
- Türler, alanlar ve hata kodları: SDK başvurusunun "Erişim talebi" bölümü (`node_modules/@devoplus/one-sdk/REFERENCE.md`)
  ve `one_request_access` aracının açıklaması.

## Devoplus yapay zekası: iki yön (ZORUNLU adım)

Devoplus yapay zekası bu uygulamanın verisine ve işlemlerine erişebilmelidir: kullanıcı Devoplus'ta "son notlarımı
göster" dediğinde yanıt bu uygulamadan gelir. Bu bir ek değil, uygulamanın parçasıdır: **her yeni özellikte** aşağıdaki
üç dosya birlikte güncellenir:

1. **`one.manifest.json` dosyasındaki `agent.tools`**: yapay zekanın çağırabileceği her araç: `name` (küçük harf/rakam/`-`),
   TR/EN `title` ve `description` (ne yapar, ne döndürür, ne zaman KULLANILMAZ), `readOnly`, gerekiyorsa `permission`
   (uygulamanın kendi izin kodlarından biri) ve `input` (JSON şeması; yalnız `type`, `properties`, `required`, `enum`,
   `minimum`, `maximum`, `minLength`, `maxLength`, `items`, `description`). Şemada olmayan alan çalışma anında
   reddedilir.
2. **`src/agent.ts`**: aracın işleyicisi: `defineAgentTools({ '<ad>': { handler: async ({ input, ctx, env }) => ... } })`.
   Girdi SDK'da şemaya göre doğrulanmış gelir; işleyici kullanıcının KENDİ yetkisiyle çalışır (`ctx.user`), ekrandaki
   kurallar aynen uygulanır (ör. yalnız kendi kayıtları). `ctx.admin` = kişi bu portalın şirket yöneticisi, `ctx.actor`
   = Vekalet / Yerine Görüntüle. Araç Devoplus servislerini kişi adına çağırabilir; süreli onay gerektiren scope'lar
   (e-posta, SMS, mobil bildirim, DEXA'ya yazma) ajan aracında KULLANILAMAZ. JSON'a çevrilebilir bir değer döndürün; gereksiz kişisel veri
   döndürmeyin.
3. **`AGENT_GUIDE.md`**: yapay zekanın okuduğu kullanım rehberi. Dört başlık zorunludur: `## Uygulama ne yapar`,
   `## Veri`, `## Araçlar`, `## Örnek istekler`. Her aracın adı rehberde geçer. `one-ci validate` araç beyanı olan
   uygulamada bu dosyayı arar ve eksikse yayını DURDURUR; araç beyanı yoksa "Devoplus yapay zekası bu uygulamaya
   erişemez" uyarısı verir.

Araç tasarım kuralları:

- Önce **salt okunur** araçlar (listeleme, arama, özet). Bir şeyi değiştiren araç `readOnly: false` beyan edilir:
  Devoplus yapay zekası her çağrıdan önce kullanıcıya onay sorar ve bu araçlar Vekalet / Yerine Görüntüle oturumunda
  çalışmaz. Silme gibi geri alınamaz işler araç yapılmaz; kullanıcı ekrana yönlendirilir.
- Bir araç tek bir işi yapar ve adı bunu söyler (`list-requests`, `get-request`, `approve-request`). Girdiler küçük ve
  sınırlıdır (`enum`, `minimum`/`maximum`, `maxLength`); sayfa boyu gibi değerlerin bir tavanı olur.
- Araç, kullanıcının uygulama ekranında yapabileceğinden fazlasını yapmaz: aynı izin ve aynı süzgeç (`sub`) uygulanır.
- Teknik olmayan kullanıcıya araç adı, şema ya da JSON sorulmaz. Hangi soruların bu uygulamaya ait olduğunu keşif
  görüşmesindeki iş listesinden siz çıkarırsınız; rehberi siz yazarsınız.
- Araç çağrısının yolu `/__one/agent/<ad>`'dır ve yalnız platformun imzalı çağrısı ulaşır (tarayıcıdan gelen 404 alır);
  bu yolu ekranda kullanmayın. Katalog ve rehber `/.well-known/one-agent.json`, `/.well-known/one-agent.md`
  adreslerinden kendiliğinden sunulur (`src/index.ts` başındaki iki satır); elle yazılmaz.

**Uygulamanın Devoplus yapay zekasını kullanması:** metin özetleme, sınıflandırma, alan çıkarma, metin üretme ve sohbet
için SDK `completeAi(api, ctx, { task, input, ... })` (manifest `scopes` içinde `ai.use`). Model, kota ve portalda açık
olup olmadığı Devoplus'tadır: kapalı portalda 503, kota dolunca 429 döner; uygulama bu durumda da çalışmayı sürdürür.
⛔ Model sayı HESAPLAMAZ ve kayıt YAZMAZ: tutar/toplam/tarih farkı kodda hesaplanıp `input`'a hazır verilir; modelin
çıktısı bir öneridir, veritabanına yazmadan önce kod doğrular ve gerekirse kullanıcıya onaylatır. `input`'a sır ya da
işin gerektirmediği kişisel veri konmaz.

## Kabuk ve Portal köprüsü (`src/shell.ts`)

- Sayfa üç dosyayı KENDİ adresinden yükler: `/one-ui.css` (görsel dil), `/one-bridge.js` (Devoplus One köprüsü,
  `window.one`) ve `/shell.js` (sayfanın kendi betiği). Sunucu tarafı ilk ikisini `clientAssetResponse(pathname, request)`, üçüncüsünü
  `scriptResponse(renderShellScript(...))` ile döndürür. ⛔ Satır içi `<script>` ve başka bir adresten (`https://...`) betik
  YAZILMAZ: platform ikisini de çalıştırmaz ve `one-ci validate` yayını durdurur. Kütüphane gerekiyorsa bağımlılık olarak
  eklenir ve uygulamayla birlikte sunulur. Satır içi `<style>` serbesttir.
- Portal ile HER konuşma köprüden geçer; `postMessage` doğrudan YAZILMAZ. Köprü iletinin kaynağını (`parent` sorgusundaki
  origin + üst pencere) doğrular ve yalnız o origin'lere gönderir.
- Belirteç `one.onToken(fn)` / `one.token`; istekler `one.fetch(yol, init)` ile gider (belirteç eklenir). Tema, dil ve
  Vekalet / Yerine Görüntüle bilgisi `one.onContext(fn)` ile gelir; sayfa yeniden yüklenmeden uygulanır.
- `one.start({ navigation })` sayfa hazır olunca BİR kez çağrılır: Portal'a hazır olduğunu ve menüyü bildirir, yükseklik
  bildirimini başlatır. Menü sunucuda `navigationFromManifest(manifest)` ile `one.manifest.json`'dan üretilir.
- **Sekme izni:** bir sekme yalnız belli bir izni olanlara görünecekse manifest'te öğeye `"permission": "One.<Uygulama>.<Eylem>"`
  yazın (kod `permission.code` ya da `permissions` içinde beyan edilmiş olmalı). Köprü bu sekmeyi oturumdaki kişinin
  izinleri bildirilene kadar menüye KOYMAZ: sayfa oturum bilgisini okuyunca `one.setNavigationAccess({ permissions, admin })`
  çağırır (yönetici her sekmeyi görür). Bu yalnız görünürlüktür; sayfanın verisi sunucu ucunda `requirePermission` ile
  ayrıca korunur; adresi doğrudan açan yetkisiz kişiye "yetkiniz yok" notu gösterin.
- **Yükseklik:** köprü içerik yüksekliğini Portal'a bildirir; Portal iframe'i o boyda çizer ve sayfanın kendi taşmasını
  gizler; sayfada tek kaydırma çubuğu Portal'ınkidir. ⛔ Kök öğelerde (`html`, `body`, sayfa sarmalayıcısı) `100vh` ya da
  `height: 100%` KULLANMAYIN: içerik iframe boyuna bağlanırsa yükseklik hiç küçülmez. Kendi içinde kayan bir alan
  gerekiyorsa (uzun çıktı, tablo) ona sabit bir `max-height` verin.
- Kabuk iki dili de gömer (`STRINGS`); yeni metin **iki dile birden** eklenir (test anahtar kümelerini karşılaştırır).
  Kullanıcıya görünen metin Türkçe ve resmi "siz" dilindedir; İngilizce kavram adı bırakılmaz.
- Kullanıcı girdisi DOM'a `textContent` ile yazılır, `innerHTML` ile değil. Portal'a giden bütün metinler (bildirim,
  onay sorusu, başlık) DÜZ METİNDİR; HTML yazılmaz.
- Dile bağlı her metin `one:context` gelince yeniden çevrilmelidir: statik öğe `data-i18n` taşır, sonradan
  eklenen öğeye de `setAttribute('data-i18n', ...)` yazılır; durum satırı yalnız `showStatus(anahtar)` ile değişir
  (`textContent = t(...)` yazılmaz, çünkü dil değişiminde eski dilde kalır).

## Arayüz standartları (U1 ile U12 arası, bağlayıcı)

Her Devoplus One uygulaması Portal'ın bir parçası gibi görünür. Kurallar karar defterinde D-76'dır.

- **U1 Menü:** uygulama sol menü / yan çekmece ÇİZMEZ. Görünümler `one.manifest.json` dosyasındaki `navigation` alanına yazılır
  (`[{ "path": "/", "label": { "tr": "...", "en": "..." }, "permission"?: "One.<Uygulama>.<Eylem>" }]`, en çok 12); sekmeleri Portal iframe'in üstünde çizer ve
  Portal adresi `/One/<kısa ad>/<yol>` olur (yenileme, geri ve paylaşılan bağlantı çalışır). Portal sekmesine tıklanınca
  `one.onNavigate(fn)` çağrılır; uygulama kendi içinde görünüm değiştirdiğinde `one.setRoute(yol)` çağırır. Sunucu
  `navigation` yollarının hepsinde sayfayı döndürür. Yol `/` ile başlar; sorgu ve `#` taşımaz.
- **U2 Üst bilgi:** kullanıcı adı/fotoğrafı, çıkış, portal/dil/tema seçicisi, logo ve uygulama adını tekrarlayan başlık
  ÇİZİLMEZ; bunları Portal gösterir, bilgi `ctx`'tedir. Alt sayfanın başlığı (ör. açılan kaydın adı)
  `one.setTitle(metin | null)` ile Portal'daki sayfa yoluna yazılır (en çok 120 karakter).
- **U3 Vekalet / Yerine Görüntüle:** başkası adına açılan oturumda sunucuda `ctx.actor` doludur (`{ sub, kind }`;
  `kind` `delegation` = Vekalet, `impersonation` = Yerine Görüntüle), tarayıcıda `one.actor` (`{ kind }`). Varsayılan
  olarak bu oturumlar SALT-OKUNURDUR: platform GET/HEAD dışındaki isteği uygulamaya ulaşmadan 403 ile reddeder. Uygulama
  bu oturumlarda da yazmak zorundaysa `one.manifest.json` dosyasında `"actingSessions": "write"` beyan eder ve kim adına kimin
  yazdığını (`ctx.user.sub` + `ctx.actor.sub`) kaydeder. Bu beyan bile ERP komutu, e-posta, SMS ve mobil bildirimi bu
  oturumlarda AÇMAZ; `recordAudit` her zaman yazılır ve vekili ayrıca tutar. Uyarı şeridini Portal çizer; uygulama yazma düğmelerini
  `one.actor` doluyken kapatabilir.
- **U4 Pencere ve bildirim:** Portal iframe'i içerik boyunda çizdiği için `position: fixed` öğe ekranın dışında kalabilir.
  Bildirim `one.toast({ severity, summary, detail? })` (`success`, `info`, `warn`, `error`), onay
  `await one.confirm({ message, title?, severity?, acceptLabel?, rejectLabel? })` (sonuç `true`/`false`) ile Portal'da
  çizilir. Uygulamanın kendi büyük penceresi gerekiyorsa `one.viewport.current` (`{ top, height }`, iframe pikseli) ve
  `one.viewport.onChange(fn)` ile görünen alana yerleştirilir.
- **U5 Görsel dil:** `/one-ui.css` sınıfları kullanılır: `one-btn` (`one-btn-primary`, `one-btn-danger`, `one-btn-text`),
  `one-field` + `one-label` + `one-input` / `one-select` / `one-textarea`, `one-table` (`one-table-wrap` içinde),
  `one-card`, `one-message` (`-success`, `-info`, `-warn`, `-error`), içerik sekmeleri `one-tabs` + `one-tab`
  (`aria-selected`). Renk/yazı/köşe `--one-primary`, `--one-primary-contrast`, `--one-font-family`, `--one-radius` ve
  `--one-bg`, `--one-fg`, `--one-muted`, `--one-surface`, `--one-border` değişkenlerinden okunur; açık/koyu tema ve
  Portal'ın marka değerleri bunlara yazılır. Sabit renk YAZILMAZ.
- **Hazır bileşenler (D-83):** liste, kayıt formu, süzgeç, kişi seçimi, dosya yükleme ve grafik için SDK'nın
  `/one-components.js` bileşenleri kullanılır: `<one-table>` (sıralama, arama, sayfalama, seçim, Excel/CSV),
  `<one-form>` (doğrulama, sunucu hatası alanın altında, kaydedilmemiş değişiklik), `<one-filter>`,
  `<one-person-picker>`, `<one-file-upload>`, `<one-chart>` (portaldaki grafiklerle aynı kütüphane), `<one-stat>` (gösterge
  kartı), `<one-dialog>`. Sayı ve durum özetleri cümle değil `<one-stat>` kartıdır (`<div class="one-stat-grid">`);
  ekleme/düzenleme formu liste ekranına çizilmez, `<one-dialog>` içinde açılır. Sayfa betikleri köprüden SONRA yükler
  (`<script src="/one-charts.js"></script>` ve `<script src="/one-components.js"></script>`; `clientAssetResponse` sunar,
  yüklenmezse `one-ci validate` uyarır);
  kolon/alan tanımları `/shell.js`'te JavaScript özelliğidir, etiketler `{ tr, en }`. Uzak liste ucu
  `GET /api/<varlık>?limit=&cursor=&sort=&order=&q=&<alan>=` isteğine `{ items, nextCursor }` döndürür, dışa aktarma aynı ucu
  `?format=xlsx|csv` ile çağırır. Kişi seçici için sunucuda `peopleSearchHandler(request, env, ctx)` (`/api/one/people`,
  scope `orgschema.read`; dönen `id` org kimliğidir, `sub` değil). Elle tablo/form yazılmaz. Ayrıntı: SDK başvurusu
  "Hazır arayüz bileşenleri".
- **U6 İndirme ve dış bağlantı:** iframe'de indirme ve yeni pencere kapalıdır. Dosya `await one.download({ name, mime,
  data })` ile Portal'a verilir (en çok 25 MB); dış bağlantı `one.openExternal('https://...')` ile açılır (yalnız https,
  Portal kullanıcıya sorar). `<a download>` ve `window.open` kullanılmaz.
- **U7 Kaydedilmemiş değişiklik:** formda kaydedilmemiş değişiklik varken `one.setDirty(true)`, kaydedilince
  `one.setDirty(false)`; Portal kullanıcı ayrılmadan önce sorar.
- **U8 Dil ve biçim:** her metin TR + EN; tarih `gg.aa.yyyy`, sayı `tr-TR` biçiminde; telefon E.164 saklanır; hata
  kullanıcının dilinde gösterilir; yükleniyor / boş / hata durumları ayrı gösterilir ("boş" ile "getirilemedi" karışmaz).
- **U9 Erişilebilirlik ve boyut:** en küçük genişlik 360 px, yatay kaydırma yok, kenar boşluğu 16 px; her işlem
  klavyeyle yapılabilir, odak görünür; metin/zemin kontrastı yeterlidir.
- **U10 Kimlik:** Devoplus logosu ya da Portal menüsü taklit edilmez.
- **U11 Denetim:** `one-ci validate` sol menü, kendi üst bilgisi, kapsayıcısız `position: fixed` ve eksik TR/EN için
  UYARI verir (ilk sürümde yayını durdurmaz; sonra durduracak). Uyarıyı kalıcı bırakmayın.
- **U12 Güvenlik tabanı:** platform her yanıta içerik güvenliği politikası ekler: betik, bağlantı, yazı tipi ve görsel
  yalnız uygulamanın kendi adresinden (görselde ayrıca `data:`, `blob:`, Devoplus API ve Devoplus CDN, yani kullanıcı
  fotoğrafı ve kurumsal görseller; veri isteği yine yalnız kendi adresinize); satır içi stil açık, satır içi betik kapalı;
  başka sayfayı çerçeveleme, nesne gömme ve başka adrese form gönderme kapalı. Kamera, mikrofon ve konum varsayılan
  kapalıdır; gerekiyorsa `one.manifest.json` dosyasında `"devicePermissions": ["camera" | "microphone" | "geolocation"]` beyan
  edilir. Uygulama bu politikayı kendi başlığıyla yalnız DARALTABİLİR.

## Çalışma kuralları

- **İşe başlamadan SDK sürümünü denetleyin.** Kurulu sürümü `pnpm list @devoplus/one-sdk`, kanaldaki en yeni sürümü
  `npm view @devoplus/one-sdk dist-tags` gösterir (ön sürüm kuruluysa `next`, kararlı kuruluysa `latest` etiketine
  bakılır). `one` komutu da kurulu SDK eskiyse hata akışına "Devoplus One SDK'nın daha yeni bir sürümü var" uyarısını ve
  güncelleme komutunu yazar; komut durmaz. Bu uyarıyı görürseniz başka bir işe geçmeden SDK'yı güncelleyin
  (`pnpm add @devoplus/one-sdk@latest`; ön sürüm kuruluysa `@next`, ardından `npx one upgrade` (sürümü tam sabitler, `one.manifest.json` `sdkVersion` alanını ve `AGENTS.md`'nin platform kuralları bölümünü yazar)), `pnpm type-check && pnpm test && pnpm build` çalıştırın ve
  `package.json`, `pnpm-lock.yaml`, `one.manifest.json`, `one.platform-state.json` ve `AGENTS.md`'yi kodla birlikte yükleyin. `sdkVersion` alanını elle yazmayın:
  kurulu sürümden farklıysa yayın hattı durur. Yayın hattı (`one-ci validate`) asgari sürümden eski ya da sürümü okunamayan
  SDK için `⚠ SDK:` ile başlayan bir uyarı yazar; bugün yayını durdurmaz, platform ileride durdurabilir. Uyarıyı kalıcı
  bırakmayın.
- `pnpm type-check && pnpm test && pnpm build` yeşil olmadan commit yapılmaz. Commit'ten önce `npx one doctor` da
  çalıştırın: uygulamayı bu dosyanın sonundaki "Platform kuralları" bölümüne karşı tarar, zorunlu bulguda çıkış kodu 1'dir.
  O bölümü `npx one agents sync` yazar; elle düzenlemeyin, uygulamaya özgü notları bölümün dışına yazın.
- Hata yolu tek yerdedir: `catch (error) { return problemResponse(error) }`. `OneError` RFC 9457 gövdeye çevrilir,
  bilinmeyen hata 500 döner ve ayrıntı sızdırılmaz. `OneError.retryAfterSeconds` pozitif tam sayıysa (ör. 409
  `ERPCMD-APP-E-016`) yanıt onu `Retry-After` başlığında ve gövdedeki `retryAfterSeconds` alanında da taşır.
- Kod Devoplus API'si üzerinden uygulamanın deposuna yüklenir: komut satırında uygulamanın kök dizininde `one publish`
  (projenin tam görüntüsünü yükler ve test ortamına yayınlar; açıklama `--message` ile), dosya sistemi olmayan araçta
  Devoplus One bağlantısının `one_source_upload` aracı. Deponun kendisine erişim gerekmez; kullanıcıdan da istenmez.
  Yükleme test dalına tek commit olarak yazılır ve yükleyenin adını taşır; bağımlılıklar, yerel durum klasörleri, hat
  dosyası ve sır dosyaları yüklenmez. `one-ci validate` (manifest + giriş dosyası + README.md/AGENTS.md + araç beyanı
  varsa AGENT_GUIDE.md + `one.data.json` varsa üretilmiş dosyaların güncelliği (`one data generate --check`) + arayüz
  denetimi) ve taramalardan geçmeyen sürüm yayımlanmaz. Hat düşerse nedenini `one pipeline` (ya da `one_pipeline_status`)
  gösterir. Geliştirici makinesinden doğrudan yayın YOKTUR.
- Terfi: test'ten uat'a `one promote` (uat oturumuyla) ya da `one_publish`; kod birleştirmesini ve `v<sürüm>` etiketini
  Devoplus yapar. Canlıya terfi ve canlıda geri alma insan onaylıdır: uygulamanın sahibi Portal'daki "Devoplus One
  Uygulamalarım" ekranından, şirket yöneticisi Devoplus Console'dan yapar.
- Depoya doğrudan git ile çalışılıyorsa (yalnız Devoplus ekibi) AI değişikliği `ai/<konu>` dalında yapılır ve birleştirme
  isteğiyle girer; AI ile yazılan her commit'in iletisine ayrı satırda `AI-Assisted: <araç adı>` trailer'ı eklenir.
  `one-ci deploy` commit'leri ve geldikleri dalı sürüm defterine yazar.
- `README.md` uygulamanın amacını ve davranışını, bu dosya AI kurallarını, `AGENT_GUIDE.md` Devoplus yapay zekasının
  araçları nasıl kullanacağını anlatır; davranışı değiştiren değişiklik üçünü aynı commit'te günceller (one-ci README ya da
  AGENTS yoksa veya boşsa, araç beyanı olan uygulamada AGENT_GUIDE.md eksikse yayını durdurur).

## Bağımlılık açığı: yama

- Devoplus bağımlılıkları her gün bilinen güvenlik açıklarına karşı tarar. Açık bildirildiğinde ya da hat bağımlılık
  kapısında durduğunda bulgular ve platformun önerdiği yama `one deps patch` (ya da `one_dependency_patch`) ile alınır.
  Yama kendiliğinden uygulanmaz ve depoda birleştirme isteği açılmaz: kodun doğru kaynağı yerel kopyadır.
- Yama yerel kopyada uygulanır: komut satırında `one deps patch --apply`. Komut yalnız kökteki `package.json` ve
  `pnpm-lock.yaml` dosyalarını değiştirir, commit atmaz; bu dosyalarda kaydedilmemiş değişiklik varsa, git yoksa ya da
  yama yerel koda uymuyorsa hiçbir dosyaya dokunmaz.
- Ardından bağımlılıklar kurulur, `pnpm test` ve `one build` çalıştırılır, kod her zamanki yoldan yüklenir (`one publish`
  ya da `one_source_upload`). Yama yoksa ya da uymuyorsa paket, bulgudaki düzeltilmiş sürüme elle yükseltilir.
- Bulguları yalnız uygulamanın sahibi ya da platform yöneticisi görebilir. Kullanıcıya teknik ayrıntı değil sonuç
  söylenir: hangi açığın kapatıldığı ve test ortamına gönderildiği.

## Kaynaklar

- SDK API ve hata kodları (SDK başvurusu): `node_modules/@devoplus/one-sdk/REFERENCE.md` (bu dosyayı taşımayan eski ön
  sürümlerde `README.md`)
- Platform mimarisi: `devoplus/devoplus-one` deposundaki `docs/architecture.md`; kararlar `devoplus-root/docs/devoplus-one-karar-defteri.md`
- Örnek/test konsolu: `devoplus/devoplus-one` deposundaki `apps/hello`

<!-- devoplus-one:platform-rules:start -->
## Platform kuralları

Bu bölümü `npx one agents sync` yazar; elle düzenlemeyin, bir sonraki eşitlemede yeniden yazılır. Kural kataloğu revizyonu: 2.

Uygulamanın kurallara uyup uymadığını `npx one doctor` gösterir. Zorunlu kurala uymayan kod yayında durur; uyarı ve öneriler hat çıktısına yazılır. Yayında esas olan hattın doğrulama çıktısıdır.

- **ONE-RULE-001** (öneri): Sayı ve durum özetleri cümle olarak değil, gösterge kartıyla gösterilir.
  Düzeltme: Özet bilgileri (toplam, bekleyen, geciken gibi) kalın düz metin satırı olarak yazmayın. Her göstergeyi bir `<one-stat>` kartıyla gösterin ve kartları one-stat-grid sınıflı bir kapsayıcıya koyun. Grafik gerekiyorsa `<one-chart>` kullanın ve sayfada /one-charts.js betiğini köprüden sonra, /one-components.js'ten önce yükleyin.
- **ONE-RULE-002** (uyarı): Ekleme ve düzenleme formu liste ekranına çizilmez, pencerede açılır.
  Düzeltme: Formu (`<one-form>`) bir `<one-dialog>` içine koyun. Listedeki "Yeni" düğmesi ve satır tıklaması pencereyi açsın; kayıt başarılı olunca pencere kendiliğinden kapanır.
- **ONE-RULE-003** (uyarı): Kalıcı silme yazılı gerekçe ister.
  Düzeltme: One uygulamalarında silme yumuşaktır: kaydı silmek yerine silindi olarak işaretleyin (hazır veri katmanı createCrudHandler bunu kendisi yapar). Kalıcı silme gerçekten gerekiyorsa aynı ya da bir önceki satıra gerekçeli bir yorum yazın: one-allow-hard-delete: <en az 10 karakterlik gerekçe>.
- **ONE-RULE-004** (uyarı): Silme ve pasife alma isteği kullanıcı onayından sonra gönderilir.
  Düzeltme: Silme isteğini one.remove ile gönderin; one.remove kullanıcıya onay penceresi gösterir ve yalnız onaydan sonra isteği yapar. Kendi isteğinizi yazıyorsanız önce one.confirm sorun ve isteği yalnız kabul dalında gönderin. Onaysız istek bilinçliyse aynı ya da bir önceki satıra gerekçeli bir yorum yazın: one-allow-unconfirmed-delete: <en az 10 karakterlik gerekçe>.
- **ONE-RULE-005** (zorunlu): Veri modelinde kalıcı silme yazılı gerekçe ister.
  Düzeltme: one.data.json'da "softDelete": false yazan varlığa en az 10 karakterlik bir "hardDeleteReason" ekleyin ya da "softDelete" alanını kaldırın (varsayılan yumuşak silmedir). Ardından one data generate çalıştırın.
- **ONE-RULE-006** (uyarı): Simge olarak emoji ya da oklu karakter kullanılmaz.
  Düzeltme: Düğme, sıralama ve menü simgesi için emoji ya da ok, üçgen veya üç nokta karakteri yazmayın; bazı platformlarda renkli emoji çizilir. Hazır bileşenlerin kendi simgelerini ya da satır içi SVG simgeyi kullanın; durumu (ör. sıralama yönü) metin ya da aria nitelikleriyle de söyleyin.
- **ONE-RULE-007** (zorunlu): Manifestteki SDK sürümü kurulu SDK sürümüyle aynıdır.
  Düzeltme: one.manifest.json'daki sdkVersion alanını elle düzeltmeyin. SDK 0.3.0 ve sonrasında uygulamanın kök dizininde npx one upgrade çalıştırın; komut kurulu SDK sürümünü manifeste yazar. Ardından one.manifest.json, one.platform-state.json, AGENTS.md, package.json ve kilit dosyasını birlikte yükleyin. Kurulu SDK 0.3.0'dan eskiyse sdkVersion alanını manifestten silin; bu sürümlerde beyan zorunlu değildir.
- **ONE-RULE-008** (zorunlu; SDK 0.3.0-next.0 ve sonrasında): Manifest SDK sürümünü beyan eder.
  Düzeltme: Alanı elle yazmayın. Kurulu SDK 0.3.0'dan eskiyse bu yalnız bir uyarıdır ve şimdilik bir şey yapmanız gerekmez. SDK 0.3.0 ve sonrasında uygulamanın kök dizininde npx one upgrade çalıştırın; komut kurulu SDK sürümünü one.manifest.json'daki sdkVersion alanına yazar. Ardından one.manifest.json, one.platform-state.json, AGENTS.md, package.json ve kilit dosyasını birlikte yükleyin.
<!-- devoplus-one:platform-rules:end -->
