@appaflytech/zeyrek-ai-mcp 0.0.43 → 0.0.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/knowledge/backendConstraints.js +86 -2
  2. package/dist/knowledge/componentCatalog.js +63 -26
  3. package/dist/knowledge/componentEvents.js +6 -0
  4. package/dist/knowledge/connectDataBind.js +43 -6
  5. package/dist/knowledge/connectDataDefinition.js +74 -3
  6. package/dist/knowledge/connectDataRules.js +14 -0
  7. package/dist/knowledge/mobileComponentCatalog.js +66 -7
  8. package/dist/knowledge/mobileStore.js +61 -2
  9. package/dist/knowledge/platform/mobile.js +1 -1
  10. package/dist/knowledge/platform/mobileRules.js +2 -1
  11. package/dist/knowledge/platform/mobileTabs.js +31 -2
  12. package/dist/knowledge/platform/web.js +1 -1
  13. package/dist/knowledge/rules.js +195 -4
  14. package/dist/knowledge/textHooks.js +434 -0
  15. package/dist/platform/capture/capture.js +1 -1
  16. package/dist/platform/capture/readiness.js +4 -0
  17. package/dist/platform/errors.js +22 -2
  18. package/dist/prompts/mobilePublish.js +21 -0
  19. package/dist/resources/examples/flow-biometric-confirm.json +199 -0
  20. package/dist/resources/examples/microfunction-node-dependencies.json +19 -0
  21. package/dist/resources/guides/access.md +3 -3
  22. package/dist/resources/guides/component.md +20 -4
  23. package/dist/resources/guides/deployment.md +98 -0
  24. package/dist/resources/guides/entity.md +1 -1
  25. package/dist/resources/guides/expressions.md +5 -1
  26. package/dist/resources/guides/external-component.md +6 -4
  27. package/dist/resources/guides/merge.md +81 -4
  28. package/dist/resources/guides/microfunction.md +104 -9
  29. package/dist/resources/guides/microservice.md +34 -5
  30. package/dist/resources/guides/mobile-connect-data.md +70 -0
  31. package/dist/resources/guides/mobile-flow.md +64 -0
  32. package/dist/resources/guides/mobile-publish.md +161 -16
  33. package/dist/resources/guides/mobile-steps.md +17 -0
  34. package/dist/resources/guides/mobile.md +41 -23
  35. package/dist/resources/guides/page.md +11 -5
  36. package/dist/resources/guides/translation.md +95 -0
  37. package/dist/resources/knowledge.js +3 -1
  38. package/dist/resources/prompts/mobile-publish-prepare.md +89 -0
  39. package/dist/resources/schema-fingerprint.json +4 -4
  40. package/dist/resources/schemas/ApplyMicrofunctionArchiveChangesCommand.json +76 -0
  41. package/dist/resources/schemas/ConnectDataDescribeDto.json +44 -0
  42. package/dist/resources/schemas/ConnectDataResultDto.json +10 -0
  43. package/dist/resources/schemas/CreateMicrofunctionCommand.json +8 -0
  44. package/dist/resources/schemas/InvokeMicrofunctionCommand.json +0 -4
  45. package/dist/resources/schemas/MobileStorePreflightScope.json +13 -0
  46. package/dist/resources/schemas/StartMobileBuildCommand.json +35 -1
  47. package/dist/resources/schemas/UpdateMicrofunctionCommand.json +8 -0
  48. package/dist/resources/schemas/UpsertMobileStoreSettingsCommand.json +75 -0
  49. package/dist/server.js +2 -0
  50. package/dist/tools/common.js +6 -0
  51. package/dist/tools/components.js +19 -6
  52. package/dist/tools/connectData.js +9 -4
  53. package/dist/tools/deployments.js +1 -1
  54. package/dist/tools/designSchemaPatch.js +16 -4
  55. package/dist/tools/environments.js +3 -3
  56. package/dist/tools/files.js +1 -1
  57. package/dist/tools/flows.js +29 -3
  58. package/dist/tools/layouts.js +32 -12
  59. package/dist/tools/merges.js +8 -8
  60. package/dist/tools/microfunctions.js +53 -3
  61. package/dist/tools/mobileStore.js +83 -21
  62. package/dist/tools/pages.js +59 -24
  63. package/dist/tools/platformInfo.js +4 -2
  64. package/dist/tools/textHookGuard.js +116 -0
  65. package/package.json +2 -2
@@ -17,7 +17,7 @@ verir (gateway kendi çağrısında 200 gönderir; platform varsayılanı 10'dur
17
17
  |---|---|
18
18
  | Birkaç satır dönüşüm, alan eşleme, koşul | `code` step / ifade (aynı süreç, en ucuz) |
19
19
  | İfadelerde çıplak adla çağrılacak yardımcı | global function (`global_function_create`, JS) |
20
- | Kütüphane gerektiren iş (kriptografi, PDF, görüntü, parse) | microfunction (zip kaynak) |
20
+ | Kütüphane gerektiren iş (kriptografi, PDF, görüntü, parse) | microfunction + bağımlılık (§Bağımlılıklar) |
21
21
  | Python gereken iş | microfunction — platformun ifade motoru YALNIZ JavaScript'tir |
22
22
  | Uzun süren/CPU yiyen iş | microfunction (kendi bütçesi, API sürecini bloke etmez) |
23
23
 
@@ -42,12 +42,13 @@ def handler(args, context):
42
42
  return {"total": args["a"] + args["b"], "environment": context["environment"]}
43
43
  ```
44
44
 
45
- - `args` — adımın `rule.expression`'ından üretilen argüman nesnesi. Fonksiyonun
46
- `parameterDtoId`'si yoksa `{}` gelir.
45
+ - `args` — adımın `rule.expression`'ından üretilen argüman. Şekli `parameterBehavior`'dır:
46
+ `object` ⇒ nesne, `array` ⇒ dizi, `primitive` ⇒ tek değer. Fonksiyonun `parameterDtoId`'si
47
+ yoksa `{}` gelir.
47
48
  - `context` — **salt okunur anlık görüntü**. İçine yazmak hiçbir şeyi değiştirmez; akışa dönen
48
49
  tek kanal fonksiyonun DÖNÜŞ DEĞERİdir.
49
50
 
50
- `context` şekli (ölçüldü, `MicrofunctionStep.BuildContext`):
51
+ `context` şekli (ölçüldü, `MicrofunctionStep.EvaluateAsync`'in kurduğu sözlük):
51
52
 
52
53
  ```jsonc
53
54
  {
@@ -96,6 +97,16 @@ draft ──publish──▶ publishing ──▶ published ──update──
96
97
  4. `microfunction_delete` runtime'daki fonksiyonu da kaldırır; bir workflow adımı kaydı
97
98
  kullanıyorsa 400 ile reddedilir.
98
99
 
100
+ ## Test çağrısı ve kayıtlı argüman
101
+
102
+ - `microfunction_invoke`'un `args`'ı **herhangi bir JSON değeridir** — liste modelli fonksiyona
103
+ dizi, tek değer modelliye sayı/metin gönderin; workflow adımının gönderdiği şekille aynıdır.
104
+ - `testArguments` Studio'nun Test call sekmesinin kayıtlı argüman metnidir (≤ 64 KB). Olduğu gibi
105
+ saklanır, JSON geçerliliği denetlenmez, koşuma girmez; deployment ve template onu taşır.
106
+ - `microfunction_update` **tam gövdedir**: `testArguments` verilmezse kayıtlı metin silinir —
107
+ güncellemeden önce `microfunction_get`'ten alın. Kaydetmek `revision`'ı artırır (yayındaki
108
+ kayıt `hasUnpublishedChanges: true` olur).
109
+
99
110
  ## Workflow adımı (WF-MF-001)
100
111
 
101
112
  Adım tipi `microfunction`; `workflow`, `automation` ve `middleware` akışlarının hepsinde
@@ -177,13 +188,97 @@ kullanıcısı verin, `roleArn` fonksiyonun koşacağı execution rolüdür ve *
177
188
  deseni tutmayan hesap gateway'de durur (MFR-001, block). Kaydettikten sonra
178
189
  `microfunction_runtime_test_connection` ile doğrulayın.
179
190
 
191
+ ## Bağımlılıklar: dış paketler (MF-001, MF-007)
192
+
193
+ Paketlerin tek gerçeği dilin manifest dosyasıdır: Python `requirements.txt`, Node `package.json`.
194
+
195
+ | Kaynak | Nerede bildirilir |
196
+ |---|---|
197
+ | `inline` | `dependencies` alanı — **satır başına bir paket**; platform yayında manifest dosyasını bu metinden yazar |
198
+ | `archive` | Zip'teki `requirements.txt` / `package.json` (`microfunction_files_apply` ile düzenlenir); `dependencies` alanı yok sayılır |
199
+
200
+ Satır biçimi (inline; boş ve `#` satırları yorumdur, toplam ≤ 16 KB):
201
+
202
+ - **python:** requirements.txt satırı olduğu gibi — `pillow==11.1.0`, `requests>=2.32`,
203
+ `python-dateutil==2.9.0.post0`. pip seçeneği (`-r`, `--index-url`, `-e`) **yasak**.
204
+ - **node:** `ad@aralık` — `sharp@^0.33.0`, `@scope/ad@1.2`; aralıksız satır (`dayjs`) `latest` olur.
205
+ Ad küçük harftir.
206
+
207
+ Kural dışı satır istek gitmeden durur (MF-001, block; platform 422
208
+ `MicrofunctionDependenciesInvalid`).
209
+
210
+ **Yayın.** Etkili satır varsa Built-In yayın paketleri Fission builder'da kurar (`pip install -r
211
+ requirements.txt` / `npm install`); kayıt build süresince `publishing` kalır — ölçülen süreler 4–30 sn,
212
+ tavan 300 sn. Kurulum düşerse kayıt `publish-failed` olur ve `publishError` build günlüğünün son
213
+ 4 KB'ını taşır (`No matching distribution found for …` gibi) — bağımlılığı düzeltip yeniden yayınlayın.
214
+ Etkili satır yoksa yayın build'siz ve bugünkü hızındadır.
215
+
216
+ **Sembolik bağ.** Built-In paketi sembolik bağ taşıyamaz. Platform `.bin` dizinlerindeki bağları
217
+ (`node_modules/.bin/*`: npm'in kurduğu ya da zip'te gelen) kendisi atar; bu yüzden çalışma anında
218
+ paketlerin komut satırı araçları yoktur — kütüphaneyi `require` / `import` ile kullanın. Zip'te başka
219
+ bir bağ varsa yayın `publish-failed` olur ve mesaj bağın yolunu verir: bağı gösterdiği dosyayla
220
+ değiştirin. `file:` ile bağlanan yerel paket de npm kurulumunda bağ olur; zip'e `.npmrc` koyup
221
+ `install-links=true` yazın (ölçüldü 2026-09-30). Eski `.npmrc` `bin-links=false` geçici çözümü artık
222
+ gerekmez; bin komutu çağıran kurulum betiklerini (`bcrypt` gibi yerel paketler) kırdığı için önerilmez.
223
+
224
+ **Eski archive kayıtları:** Faz 1'de paketleri zip'e koyup `requirements.txt` / `package.json`'da da
225
+ bildiren kayıt, bir sonraki yayında build'e girer (manifest artık okunur). Bu kurulum ağa çıkar ve
226
+ birkaç saniye–dakika sürer; zip'teki kurulu paketler yerinde kalır.
227
+
228
+ **Built-In Python 3.13, Node 22'dir** (AWS Lambda'daki `python3.13` / `nodejs22.x` ile aynı). C
229
+ uzantılı paketler (Pillow gibi) hazır wheel'den kurulur — ölçüldü 2026-09-29: `pillow==11.1.0` ile
230
+ bir PNG'yi açıp boyutlandıran inline fonksiyon yayınlandı ve çağrıldı. 2026-09-29'a kadar Built-In
231
+ Python 3.7'ydi; o sürümde kurulmuş paketler yeniden yayında 3.13 için yeniden kurulur.
232
+
233
+ **AWS Lambda'da kurulmaz** (MF-007, warn): paket olduğu gibi gider. Lambda'da paketler kurulmuş
234
+ hâlde zip'in içinde gelmelidir (archive kaynağı; Python'da paket klasörleri, Node'da `node_modules`);
235
+ aksi hâlde yayının probe adımı import hatasıyla `publish-failed` olur.
236
+
237
+ Örnek: `ngapps://examples/microfunction-node-dependencies`.
238
+
239
+ ## Zip kaynağı: arşiv dosyaları (MF-006)
240
+
241
+ `sourceKind: 'archive'` kaydın dosyaları platformdaki zip'in İÇİNDE yaşar; tek gerçek kaynak o
242
+ zip'tir (yayın, deployment ve şablon onu okur). Dört tool:
243
+
244
+ | Tool | Ne yapar |
245
+ |---|---|
246
+ | `microfunction_archive_scaffold` | Arşivi olmayan kayda dilin iskeletini yazar ve kaydı zip kaynağına çevirir: node → `main.js` + `package.json` (`dependencies: {}`), python → `main.py` + `requirements.txt` (yalnız yorum); entrypoint `main.handler`. Boş manifest build gerektirmez. Arşiv varsa 409 |
247
+ | `microfunction_files_list` | `{ hash, entries: [{ path, size, isDirectory }] }` — yalnız açık girdisi olan klasörler listelenir, ara klasörler yollardan çıkar |
248
+ | `microfunction_file_get` | `{ path, size, isBinary, content }` — 1 MB üstü ya da UTF-8 olmayan dosyada `isBinary: true`, içerik yok |
249
+ | `microfunction_files_apply` | `{ expectedHash, operations[] }` işlemlerini SIRAYLA uygular, yeni zip üretir, eski zip'i siler |
250
+
251
+ İşlemler: `write-file` (oluştur ya da değiştir; ikili dosyada `content` base64 +
252
+ `encoding: 'base64'`, MCP'den ≤ 8 MB; satırlara bölünmüş base64 da olur, platform boşluğu yok
253
+ sayar) · `create-directory` · `delete` (klasörse altındaki her
254
+ girdiyle) · `rename` (`newPath`; klasörse altındakilerle birlikte). Yol `'/'` ayraçlıdır, kökle
255
+ başlamaz, `/` ile bitmez, boş/`.`/`..` segmenti ve ters bölü içermez — klasör de sondaki `/`
256
+ olmadan yazılır (`lib`). Kural dışı gövde istek gitmeden durur (MF-006, block).
257
+
258
+ **Yalnız zip kaynaklı kayıtta:** satır içi kayıtta `microfunction_files_apply` 400 döner
259
+ (`sourceKind`); önce `microfunction_archive_scaffold` (arşiv yoksa) ya da `microfunction_update`
260
+ ile `sourceKind: 'archive'`. Aynı anda iki yazan olursa biri kazanır, öteki 409 alır.
261
+
262
+ **`expectedHash` zorunlu sıra:** önce `microfunction_files_list`, dönen `hash`'i `apply`'a verin.
263
+ Kayıt o arada değiştiyse 409 (`MicrofunctionArchiveConflictException`) döner ve hiçbir şey
264
+ yazılmaz — listeyi yeniden okuyup işlemleri ona göre kurun. Arşivi olmayan kayıtta hash verilmez.
265
+
266
+ **Entrypoint yayında denetlenir.** Değişiklik entrypoint modülünü silse ya da taşısa bile yazılır
267
+ (MF-006 warn; sonraki bir işlem modülü ya da klasörünü aynı yola geri getiriyorsa uyarı yok), ama `microfunction_publish` modül zip'te yoksa 400 döner. Modül yolu entrypoint'in
268
+ son `.`'sından öncesi + dilin uzantısıdır: `handlers/sum.handler` → `handlers/sum.js`
269
+ (Python'da `.py`). **Node'da giriş modülü `index.js` olamaz**: o ad platform shim'inindir; yayın
270
+ bunu da 400 ile reddeder.
271
+
272
+ Paketler zip'teki `requirements.txt` / `package.json`'a yazılır ve Built-In yayında kurulur
273
+ (§Bağımlılıklar). Yayındaki kaydı değiştirmek `hasUnpublishedChanges` üretir; yeni kod
274
+ `microfunction_publish` ile canlıya alınır.
275
+
180
276
  ## Sınırlar (v1)
181
277
 
182
- - **Bağımlılık build'i yok:** inline kod yalnız standart kütüphanedir; bağımlılık zip içinde gelir.
183
- - **MCP'den zip yükleme ve indirme yok** (`sourceKind: 'archive'` kaydı açılabilir, dosya
184
- Studio'dan yüklenir) — Faz 2. Yüklenmiş zip platform ucunda okunabilir durumdadır
185
- (`GET /{proje}/microfunctions/{id}/archive`, `application/zip`) ve Studio inspector'ında bir
186
- indirme düğmesi taşır.
278
+ - **Bağımlılık build'i yalnız Built-In'de:** AWS Lambda'da paketler zip'te gelmelidir (MF-007).
279
+ - **Tek parça zip yükleme ve indirme MCP'de yok:** zip'in içeriği dosya düzeyinde okunur ve yazılır
280
+ (§Zip kaynağı). Zip'in tamamı platform ucunda okunabilir durumdadır
281
+ (`GET /{proje}/microfunctions/{id}/archive`, `application/zip`) ve Studio'dan indirilir.
187
282
  - `timeoutSeconds` ve `memoryMb` komutta non-nullable int'tir: **göndermezseniz 0 gider ve platform
188
283
  422 döner.** Gateway değer uydurmaz, eksikliği çağrıdan önce söyler (MF-001, block; ürün
189
284
  varsayılanları 30 / 256). Aynı kural provider ↔ `runtimeId` eşleşmesini, inline kaynakta `code`
@@ -344,13 +344,42 @@ kart doğru sütuna geçer, başlığı ve sorumlusu silinir.
344
344
  > ifadeden gelir ve `undefined` dönen ifade de `null` yazar —
345
345
  > `platform_guide('workflow')` §"Table update adımı".
346
346
 
347
+ ### Güncelle/sil filtresi: tek satır + sahiplik koşulu (MS-019, block)
348
+
349
+ `update` · `upsert` · `delete` ucu **tek kök** filtre ister ve hedef her zaman **tek satırdır**:
350
+
351
+ | Filtre ağacı | Sonuç |
352
+ |---|---|
353
+ | Tek filtre: benzersiz alan + `eq` | Geçer |
354
+ | Tek kök `group`; çocukların hepsi tekil ve `conditionType: 'and'` (ilki dahil); biri benzersiz alan + `eq` | Geçer |
355
+ | Benzersiz alan `neq`/`in`/`contains`… · çocuklardan biri `or` · iç içe grup · kökte iki filtre | Gateway MS-019 ile durdurur; platform `FilterFieldMustUniqueEqException` / `InvalidFilterCountException` |
356
+ | Koşullarda benzersiz alan yok | Platform `FilterFieldMustUniqueException` (gateway alanın benzersizliğini bilmez) |
357
+
358
+ **Basit erişim modelinde sahiplik koşulunu uç kendisi koyar.** Kimliği bilen herkes `id eq`
359
+ filtreli bir uçla başkasının satırını değiştirebilir; bunu kapatan grup:
360
+
361
+ ```jsonc
362
+ "filters": [{
363
+ "filterType": "group", "conditionType": "and",
364
+ "children": [
365
+ { "filterType": "single", "conditionType": "and", "fieldId": "<id alanı>", "alias": "<entity slug>", "operator": "eq", "source": "query-string" },
366
+ { "filterType": "single", "conditionType": "and", "fieldId": "<sahip alanı>", "alias": "<entity slug>", "operator": "eq", "source": "expression", "value": "user.id" }
367
+ ]
368
+ }]
369
+ ```
370
+
371
+ `user.id` için uca auth middleware'i bağlanır (MS-011). Sahip koşulu tutmazsa işlem **0 satır**
372
+ döner, hata vermez. Karmaşık erişim modelinde platform sahip koşulunu satır kapsamından kendisi
373
+ AND'ler (`platform_guide('access')`); orada bu grup gereksizdir ama zararsızdır. AND/OR filtrenin
374
+ kendinde değil **bir öncekine bağlantısındadır**: `or` taşıyan çocuk, ilk çocuk bile olsa hedefi
375
+ genişletir. Kural yalnız kayıtta uygulanır; 2026-10-15'ten önce kaydedilmiş uyumsuz uç yeniden
376
+ kaydedilince reddedilir. Workflow table adımı bu kuralı uygulamaz.
377
+
347
378
  ### Toplu güncelleme FİLTREYLE kurulmaz
348
379
 
349
- "Bekleyen bütün başvuruları onayla" bir table-source `update` ucuyla YAZILAMAZ. `update`
350
- operasyonu tam olarak **bir tekil alan filtresi** ister: birden çok filtre
351
- `InvalidFilterCountException`, tekil olmayan alanla filtre `FilterFieldMustUniqueException`
352
- döndürür (ölçüldü 2026-09-20, S2-22). Yani hedef kümeyi bir KOŞUL belirleyemez, yalnız bir ANAHTAR
353
- belirler. İki doğru yol var:
380
+ "Bekleyen bütün başvuruları onayla" bir table-source `update` ucuyla YAZILAMAZ: hedef kümeyi bir
381
+ KOŞUL belirleyemez, yalnız bir ANAHTAR belirler (yukarıdaki §"Güncelle/sil filtresi"; ilk ölçüm
382
+ 2026-09-20, S2-22). İki doğru yol var:
354
383
 
355
384
  | Ne istiyorsun | Yol |
356
385
  |---|---|
@@ -0,0 +1,70 @@
1
+ # Mobil veri bağlama rehberi
2
+
3
+ Mobil uygulamada (`type:'mobile'`) bileşeni veriye bağlamak web'deki connect-data sözleşmesinin aynısıdır
4
+ (`platform_guide('page')` Kural 1c-3: `connect_data_describe` → `connect_data_bind` → `connect_data_get`).
5
+ Bu rehber yalnız telefonun farkını anlatır: tanıma eklenen telefon alanları, mobil projede yazılan varsayılanlar,
6
+ gruplu liste ve kurallar. Ekranın kendisi `platform_guide('mobile')`'dadır.
7
+
8
+ ## Telefonda
9
+
10
+ Telefon kararları tanımda durur, bileşen prop'unda değil; web aynı tanımı okur ve bu alanları yok sayar.
11
+
12
+ - **Seçim alanı:** alana dokunmak **alt sayfa** açar (hızlı liste, son seçilenler, arama); alanın yanındaki ↗
13
+ **tam ekran** seçimi açar (arama, filtre ve sıralama çipleri). Varsayılan yüzeyi `lookup.mobileMode` seçer:
14
+ `sheet` (varsayılan) | `fullscreen`. Satır `lookup.mobileRow { title, subtitle?, trailing? }`: başlık, alt satır
15
+ ve sağda mono değer.
16
+ - **Liste ve tablo:** **sonsuz kaydırma** varsayılandır (`behavior.mobile.infiniteScroll`); sayfalar birleşir,
17
+ düşen sayfa "Yeniden dene" gösterir. Kapalıysa listenin sonunda "Daha fazla yükle".
18
+ - **Mobil projede `connect_data_bind` varsayılanları yazar:** kök lookup'a `mobileMode: 'sheet'` ve
19
+ `mobileRow.title` ← label alanı; liste/seçim bağlantısına `behavior.mobile: { infiniteScroll: true }`.
20
+ Yeniden bağlamada kayıtlı telefon ayarı korunur; `null` kaldırır.
21
+ - Studio sihirbazındaki "Telefonda" kutusu aynı alanları yazar; web'de çekmece Uygula bunları silmez.
22
+
23
+ ## Seçim bileşenleri
24
+
25
+ `dropdown` ve `multi-select` kök lookup'ı (entity kaynağı) telefonda böyle açar. `behavior.mobile` seçim
26
+ bileşeninde: `search` (alt sayfada arama kutusu), `infiniteScroll`, `pullToRefresh`, `recentSelections`.
27
+ `mobileRow` parçaları lookup `fields` içinden seçilir (dışındaysa CD-004); `fields: []` (türetilen lookup)
28
+ yalnız başlığın dolu olmasını ister. Tek seçim alt sayfada dokununca kapanır; çoklu seçim onay kutusu +
29
+ "Seçililer (n)" + Temizle/Uygula ile yazar, `change` yalnız Uygula'da gider; `max-length` doğrulaması dolunca
30
+ öbür satırlar pasifleşir.
31
+
32
+ `autocomplete` lookup taşımaz (seçim listesi gerekiyorsa `dropdown`): bağlı `options` `value` + `label` ister,
33
+ `caption`/`icon` isteğe bağlıdır. Bağlantı arama alanı seçmişse (`behavior.searchable`) yazılan metin
34
+ `search { searchText }` ile düğüme yazılır ve bağlantı yeniden okunur — sunucu arar, akış gerekmez.
35
+
36
+ ## Tablo ve liste
37
+
38
+ - **`datatable`:** `behavior.mobile` → `filterChips` (web `filterable` kolonlarından çip şeridi), `swipeDelete`
39
+ (onaylı kaydırarak sil), `infiniteScroll`, `pullToRefresh`. Kart görünümü `behavior.mobileCard
40
+ { title, fields?, trailing? }` seçili yollardan (dışındaysa CD-004); yoksa ilk metin alan başlıktır.
41
+ Satıra dokunmak düzenleme alt sayfasını açar (bağlantının `edit` ayarıyla).
42
+ - **`repeater`:** bağlı satırlar sayfa sayfa birleşir; öğe şablonundaki metne alan bağlamak
43
+ `page_schema_patch` ile `set_binding { prop, expression: '{{ item.<alan> }}' }` (Studio'nun "Şablonda" seçimi
44
+ aynı op'u yazar).
45
+
46
+ ## Gruplu liste
47
+
48
+ `grouped-list` `rows`u bağlanabilir; bağlarken `grouping` **zorunludur** (kök ADR 0019):
49
+
50
+ ```json
51
+ { "grouping": { "by": "dueDate", "sectionOrder": "asc", "dateBucket": "day", "otherSectionTitle": "Tarihsiz", "stickyHeaders": true, "showCounts": true } }
52
+ ```
53
+
54
+ - `by` seçili ve sıralanabilir bir alan (`asis` hariç); `sectionOrder` `asc` · `desc` · `asis`.
55
+ - `dateBucket` (`day` · `week` · `month`) yalnız `date`/`datetime` alanda (describe `dateBuckets`). Sunucu
56
+ sırayı gruplama alanıyla başlatır (azalanda boşlar sonda); `date` alanında kovayı satıra `__section` yazar,
57
+ `datetime` cihazın yerel gününe kovalanır.
58
+ - Bağlıyken bölümleme tanımdan okunur; `groupBy`/`sectionOrder`/`otherSectionTitle`/`stickySectionHeaders`/
59
+ `showCounts` prop'ları yok sayılır. Bağsız (`rows` ifadeyle) gruplu liste: `platform_guide('mobile')`
60
+ §Güne göre görev listesi.
61
+
62
+ ## Kurallar
63
+
64
+ | Kod | Ne zaman | Ne yapılır |
65
+ |---|---|---|
66
+ | CD-004 | `mobileRow` parçası lookup alanı değil; `mobileCard` parçası seçili yol değil | alanı seçime ekleyin ya da parçayı değiştirin |
67
+ | CD-019 | gruplu liste `grouping`siz; tarih dışı alanda `dateBucket` | `grouping { by, sectionOrder }` verin |
68
+ | CD-020 | `grouping` gruplama adımı bildirmeyen bileşende | `grouping`i kaldırın |
69
+ | PAGE-035 | `mobileMode` `sheet`/`fullscreen` dışında | iki değerden birini yazın |
70
+ | CD-013 | `grouping` entity dışı kaynakta | entity kaynağı kullanın |
@@ -0,0 +1,64 @@
1
+ # Mobil akış adımları rehberi
2
+
3
+ Mobil uygulamada (`type:'mobile'`) akış motoru web'deki motorun aynısıdır (`platform_guide('page')`, `flow_create`).
4
+ Bu rehber yalnız barındırıcıya — cihaza — dokunan adımların telefondaki sözleşmesini ve tariflerini anlatır.
5
+ Ekranın kendisi, sayfa olayları (`mount` · `focus` · `destroy`) ve uzak push `platform_guide('mobile')`'dadır.
6
+
7
+ ## Adımlar
8
+
9
+ Motorun kendi adımları (`setter`, `condition`, `microservice`, `code`, `function`, `flow-trigger`,
10
+ `validation`) iki platformda aynıdır. Barındırıcıya dokunan adımlar:
11
+
12
+ | Adım | Mobilde |
13
+ |---|---|
14
+ | `redirect` | yığına iter (göreli yol) ya da dışarıda açar (tam adres) |
15
+ | `navigate` | eski mobil şemaların adı — `redirect`'in takma adıdır; gateway `redirect`'e çevirir |
16
+ | `alert` | tipli uyarı (`success`/`error`/`info`/`warning`) toast'tur ve beklemez; düz uyarı onay bekler. `message`/`description` metin şablonudur (`"{{ t('kayit.tamam') }}"`; `platform_guide('page')` §alert). Eylem düğmeli bildirim ("Silindi · Geri al") `alert`le kurulmaz: sayfaya bir `toast` bileşeni koyun (`actions: [{ text, flow }]`, gerekirse `keepOpen`), akış setter'la `isOpen: true` yazar; basış eylemin kendi akışını koşar (`actionClick { text, index }`) |
17
+ | `confirm` | `yes` / `no` dalları; `message`/`title`/`confirmText`/`cancelText` metin şablonudur (`{{ }}`, çeviri `t('anahtar')`) |
18
+ | `download` | adres cihazın açma sayfasında açılır; ikili gövde (JSON değil) önbelleğe dosya olarak yazılıp paylaşım sayfasında (`expo-sharing`) açılır, Studio mobil önizlemesi dosyayı indirir (`MOB-005`, bilgi) |
19
+ | `storage` | `args.name` `state.page.<key>` / `state.app.<key>` ise o state'i yazar (`platform_guide('mobile')` §Kalıcı state), değilse cihaz deposuna |
20
+ | `setcookie` | güvenli cihaz deposuna (SecureStore) yazar; değer sonraki her istekte `X-Flow-Cookie` başlığıyla gider ve middleware/workflow onu web'deki gibi `cookie.<ad>` olarak okur — `Cookie` başlığıyla gitmez, çünkü iOS onu yerel çerez kavanozuyla virgülle birleştirip bozuyordu (`MOB-005`, bilgi); aynı adlı sunucu çerezi varsa o kazanır. Studio paleti mobilde de sunar |
21
+ | `share` | RN paylaşım sayfası (metin + bağlantı; Android'de bağlantı metne eklenir, `title` iOS'ta e-posta konusu, Android'de seçici başlığı); akış `always` ile sürer — iOS'ta sayfa kapanınca, Android'de seçici açılır açılmaz (RN kapanışı bildirmez). Argümanlar `platform_guide('page')` §`share` adımı |
22
+ | `haptic` | dokunsal geri bildirim (`expo-haptics`, izin istemez). `args.value` stil, **düz metin**: `success` · `warning` · `error` · `light` · `medium` · `heavy` · `selection`; boş → `medium`. Değer yazmaz, hep `always`. Web'de titreşim varsa kısa titreşim, yoksa hiçbir şey (paletten yalnız mobilde sunulur) |
23
+ | `location` | cihaz konumu (`expo-location`; ilk kullanımda izin sorulur; derleme konumu taşımıyorsa sorulmaz, `error` dalı `status: 'unavailable'`). `args.value` doğruluk, düz metin: `low` · `balanced` · `high` (boş → `balanced`); `args.isRaw: true` → adres çözülmez. Sonuç `Steps.<adım>.data = { latitude, longitude, accuracy, address }`, `address = { country, region, subregion, city, district, street, postalCode }` ya da `null` (Türkiye'de il `region`, ilçe `subregion`, yoksa `city`). İzin reddi `Steps.<adım>.error = { status: 'denied', message }`, okunamayan konum `unavailable`: adımın `error` dalı bağlıysa o koşar (`always` koşmaz), yoksa akışın hata akışı, o da yoksa `always` (`data: null`). Web'de tarayıcı konumu, adres hep `null` |
24
+ | `notification-permission` | bildirim izni ister (sorulmuşsa yeniden sormaz, kalıcı red yeniden sorulmaz). Sonuç `Steps.<adım>.data.status`: `granted` · `denied` · `undetermined`; red hata değildir, hep `always` |
25
+ | `notification-schedule` | yerel bildirim planlar (`expo-notifications`; uzak push değil). `args.name` kimlik ve `args.title` başlık **metin şablonudur** — yalnız `{{ }}` içi çözülür (`'ilac-{{ item.id }}-0800'` kayıt başına ayrı kimlik, `"{{ t('ilac.vakti') }}"` çeviri; `{{ }}` dışındaki ifade çözülmez, FLOW-022); aynı kimlik eskisinin yerini alır. `args.message` gövde (**ifade**), `args.value` zaman (**ifade**): sonucu `'HH:mm'` → her gün, `{ weekday: 1–7, time: 'HH:mm' }` → her hafta (1 = Pazartesi), ISO tarih-saat → bir kez. `args.path` dokununca açılacak sayfanın slug'ı, düz metin — şablon çözmez (+ `args.query` ifade), boşsa uygulama açılır. İzin yoksa `error.status: 'denied'`, geçmiş ya da okunamayan zaman `invalid`: `error` dalı → hata akışı → `always`. Web'de desteklenmez (`unsupported`) — sayfa kapalıyken tarayıcı gösteremez |
26
+ | `notification-cancel` | `args.name` kimlikli planlı bildirimi siler (planlamadaki gibi metin şablonu: `'ilac-{{ item.id }}-0800'`), `'*'` hepsini; hep `always` |
27
+ | `permission` | bir cihaz iznine bakar ya da ister. `args.value` izin türü, **düz metin — ifade olamaz** (mağaza derlemesinin taşıyacağı izinler bu değerden, akış koşmadan hesaplanır; `FLOW-038`); `args.name` kip: `check` yalnız okur (sistem penceresi açılmaz), `request` verilmemiş ve hâlâ sorulabilen izni ister (boş → `request`). Sonuç `Steps.<adım>.data = { status, canAskAgain }`: `granted` · `denied` · `undetermined` · `unavailable` (derleme bu izni taşımıyor ya da web önizlemesi); `canAskAgain: false` → yalnız Ayarlar'dan değişir. `tracking` (iOS App Tracking Transparency) sonucu ayrıca `advertisingId` taşır: izin `granted` iken IDFA (iOS) / AAID (Android), değilse `null`; oynatıcı `tracking`'i hiç taşımaz (`unavailable`). Red hata değildir, hep `always`. Türler: `mobile_store_get { detail: ['permissions'] }` (`support: supported` satırlarının `key`'i) |
28
+ | `open-settings` | uygulamanın sistem ayar sayfasını açar; `Steps.<adım>.data.opened`. Argüman almaz |
29
+ | `back` | gezinme yığınında bir geri (iOS geri kaydırmasıyla aynı sonuç; kök ADR 0013). Argüman almaz (yazılan okunmaz), sonuç yazmaz, hata üretmez, hep `always`. Yığının ilk ekranında hiçbir şey yapmaz; Studio önizlemesinde runtime'ın kendi gezinmesi yoksa yine hiçbir şey, web uygulamasında tarayıcının geri'sidir. Formu kaydedip önceki ekrana dönmek için `redirect` yerine bunu kullanın: `redirect` yığına yeni ekran iter, geri kaydırması kaydedilen forma döner. 2026-10-08 öncesi runtime ve backend `back`'i tanımaz |
30
+ | `ad-show` | tam ekran reklam (kök ADR 0012). `args.value` mağaza ayarındaki yerleşimin **adı**, düz metin (`FLOW-039`); biçimi `interstitial` · `rewarded` · `rewardedInterstitial` olmalı. Reklam yüklenip gösterilir, kullanıcı kapatınca `Steps.<adım>.data = { shown: true, reward }` ile sürer — `reward` ödüllü reklamı sonuna kadar izleyende `{ type, amount }`, değilse `null` (ödül ayrı dal değil, `condition` ile okunur). 10 sn'de reklam yoksa, SDK hata verirse, derleme reklam taşımıyorsa ya da rıza yoksa `error.status: 'unavailable'`; tanımsız ya da banner yerleşimi `invalid`: `error` dalı → hata akışı → `always`. Oynatıcı ve web önizlemesi her zaman `unavailable` — reklamsız dalı orada sınayın. Paletten yalnız mobilde |
31
+
32
+ ## Bildirim ve izin
33
+
34
+ Bildirim adımları paletten yalnız mobil uygulamada sunulur. Tarif — S04 "her gün hatırlat": önce
35
+ `notification-permission` (S03 düğmesi), sonra `timepicker`'ın değeriyle `notification-schedule`
36
+ (`value: "<saat>.value"`, değer `HH:mm`) ve `error` dalında kullanıcıya mesaj. Çevrimdışı bandı adım değil
37
+ ifadedir: `isHidden: {{ Device.isOnline }}` (`platform_guide('expressions')`).
38
+
39
+ **İzin tarifi** (izin, ihtiyaç anında — düğmeye basılınca — istenir; sayfa açılışında değil): `permission`
40
+ `check` → `condition`, **bu sırayla**: `status === 'granted'` ise işe devam; `'unavailable'` ise özelliği sunma
41
+ (Ayarlar'da açılacak izin yoktur — `canAskAgain` burada da `false` gelir); `canAskAgain` ise `permission` `request`
42
+ ve sonucuna göre devam; değilse nedenini söyleyip `open-settings`. `location` adımı, `map.showUser` ve dosya alanının
43
+ kamera/galeri kaynağı izni kendileri de ister — `permission` adımı *ne zaman* sorulacağını seçmek ve sonucu
44
+ okumak içindir. `unavailable` bir hata değil, derlemenin o izni taşımadığının haberidir
45
+ (`platform_guide('mobile-publish')` §İzinler).
46
+
47
+ ## Reklam
48
+
49
+ **Reklam tarifi** (kök ADR 0012): (1) `mobile_store_update { settings: { ads: { enabled: true, iosAppId, androidAppId,
50
+ testMode: true, units: [{ name, format, ios, android }] } } }` — reklam, App ID'si olan platformun derlemesine `tracking`'i de sokar (ATT metni
51
+ gider, kip `off` olamaz); (2) banner için sayfaya `adBanner` `unit: '<banner yerleşimi>'`; (3) geçiş/ödüllü reklam için
52
+ düğme akışına `ad-show` `value: '<yerleşim>'`, `error` dalında reklamsız devam; (4) ödül `condition`:
53
+ `Steps.<adım>.data?.reward` doluysa ödülü ver. Açılış reklamı adım değil ayardır (`ads.appOpenUnit`). Sıra cihazda:
54
+ rıza formu → açılış izinleri (ATT) → SDK; iOS'ta izleme izni yoksa reklam kişiselleştirilmeden istenir. Yerleşim adının
55
+ ayarda tanımlı olduğu sayfa/akış yazımında denetlenemez — `mobile_store_get` `settings.ads.units` ve `adsWarnings`
56
+ (`MOB-026`); yayın ayrıntısı `platform_guide('mobile-publish')` §Reklamlar.
57
+
58
+ ## Konum ve harita
59
+
60
+ `style`, `accuracy`, `reverseGeocode` gibi anahtarlar sözleşmede yoktur ve platform onları sessizce düşürür
61
+ (`FLOW-029`). "Haritada aç" için adım yoktur: `map`'in `press` olayı düğüme `mapsUrl` yazar, akış
62
+ `redirect` `isRaw: true`, `path: '<harita>.mapsUrl'` ile açar. Tarif — L05 "mevcut konumumu kullan":
63
+ düğmenin `click`'i → `location` (`always` → setter `il.value = Steps.konum.data.address?.region`, `error` →
64
+ setter hata metni). Uzak push (sunucudan gelen bildirim): `platform_guide('mobile')` §Uzak push.
@@ -5,23 +5,96 @@ hata kodları. Uygulamanın kendisi (ekranlar, akışlar, uzak push gönderimi)
5
5
 
6
6
  ## Genel
7
7
 
8
- Mağaza derlemesi bir Mac runner'da koşar; uygulama **production ortamını** okur (yükleme için açılış sayfası
9
- production'a dağıtılmış olmalı; yalın `build` onsuz da derlenir, uygulama "yayımlanmamış" ekranını gösterir). Mağaza bilgisi ortam değil uygulama düzeyindedir (Core DB), dağıtımla kopyalanmaz.
8
+ Mağaza derlemesi bir Mac runner'da koşar; uygulama işin **ortamını** okur — boşsa production (yükleme için
9
+ açılış sayfası o ortamda olmalı, production'da dağıtılmış olmalı; yalın `build` onsuz da derlenir, uygulama
10
+ "yayımlanmamış" ekranını gösterir). Mağazaya yalnız production işinin derlemesi gider (§Test derlemesi). Mağaza
11
+ bilgisi ortam değil uygulama düzeyindedir (Core DB), dağıtımla kopyalanmaz.
12
+
13
+ ## Yolculuk
14
+
15
+ Studio › Yayınla sekiz adımdır; 1–5 ortak (3'ün test derlemesi platform başına), 6–8 iOS ve Android için ayrı
16
+ sekmede yürür (biri de tek başına yayınlanabilir):
17
+
18
+ 1. **Production'dan çıkar** — uygulama production ortamını okur; hangi mağazaya gidileceği
19
+ (`settings.releaseTargets`) burada seçilir.
20
+ 2. **Telefonunda dene** — Zeyrek Mobile ile (`platform_guide('mobile')` §Cihazda dene).
21
+ 3. **Test sürümü** — üç bölüm: kimlik ve sürüm · Apple hesabı · Google Play hesabı; ardından isteğe bağlı test
22
+ derlemesi (TestFlight / Play iç test, istenen ortamdan — §Test derlemesi) ve test kullanıcıları.
23
+ 4. **Yayına hazırla** — yedi bölüm: mağaza metinleri · görseller · yaş derecelendirmesi · gizlilik · beyanlar ·
24
+ sürüm, fiyat, yayın · konsolda kalanlar. Ajanın `mobile_store_update` / `mobile_asset_*` yolu; 3 ve 4'ün iş
25
+ tarifi MCP prompt'u `mobile_publish_prepare`'dir.
26
+ 5. **Canlıya al** — iki tür güncelleme (aşağıda).
27
+ 6. **Son kontrol** — mağaza ön kontrolü (`scope: 'store'`); aynı düğme derlemeyi başlatır (`mobile_build_start`:
28
+ iOS `testflight`, Android `internal`; Android'de paket türü).
29
+ 7. **Derle** — işin ilerlemesi ve sonucu (`mobile_build_status`).
30
+ 8. **Mağazada yayınla** — incelemeye gönder, yayınla, kanala taşı: insan onayıyla Studio'da.
31
+
32
+ **Doldurmadan ilerlenmez.** Studio sonraki adımı kilitler: 3'te kimlik ya da bu sürümün gittiği mağazanın hesap
33
+ bölümü eksikken 4'e (test derlemesi kapı değildir), 4'te eksik bölüm varken 5'e (konsol bölümü elle yapılır,
34
+ kilide girmez), 5'te production'a gitmemiş değişiklik varken (`detail: ['changes']` → `live` dolu) 6'ya, 6'da
35
+ bu platformun mağaza işi yokken 7'ye, 7'de başarıyla biten iş yokken 8'e geçilmez. 3'teki test işi 6–8'i açmaz.
36
+ Ajan kullanıcıyı "sonraki adıma geç" diye yönlendirmeden önce o adımın koşulunu karşılar.
37
+
38
+ ## Test derlemesi
39
+
40
+ Yayınla 3 · Test sürümü, mağaza hazırlığından ve canlıya almadan **önce** TestFlight'a / Play iç test kanalına bir
41
+ derleme gönderir. İsteğe bağlıdır; her başlatma derleme numarası ayırır ve kimliği kilitler.
42
+
43
+ - **Ortam:** `mobile_build_start { platform, target, environment, dryRun: true }` — `environment` ortamın adı ya da
44
+ slug'ı: `development`, `production` ya da `Ready` durumdaki bir dal; boş = production. Yalnız `build`,
45
+ `testflight`, `internal` alır; yükseltme hedefinde gateway istek gitmeden reddeder. Etkin olmayan
46
+ ortam ya da hazır olmayan dal backend'de 422 `environment`. Yanıt ve `mobile_build_status` işin `environment`'ını
47
+ taşır.
48
+ - **Ön kontrol `scope: 'test'`** (environment verilince kendiliğinden; `'store'` ile birlikte reddedilir, yükseltme
49
+ hedefinde verilmez). Yalnız test yüklemesinin istediğini sorar:
50
+
51
+ | Satır | `scope: 'test'` |
52
+ |---|---|
53
+ | P-01 | yalnız kimlik: `displayName`, `bundleIdentifier`, `androidPackage`, `scheme`, `version`, sayaçlar (metin ve Play sürüm notu sınırı yok) |
54
+ | P-03, P-04, P-05, P-07, P-08, P-09 | aynı (hesap ve imza, konsolda uygulama kaydı, https, etkin iş, runner) |
55
+ | P-06 | seçilen ortamda: production dışında `ok environment_ready` / `fail environment_page_missing` |
56
+ | P-11 | aynı kurallar, `fail` → `warn` (push eksikliği testi durdurmaz) |
57
+ | P-12, P-14 | seçilen ortamın kullanımı ve akışları |
58
+ | P-13 | `manual ads_test_mode` yok (test kipi testte beklenir) |
59
+ | P-02, P-10, P-15…P-21 | sorulmaz — metin, görsel, beyan ve App Review kuralları mağazaya giden işte |
60
+
61
+ - **Yayın kaynağı yalnız production işidir.** Production'dan alınan ve testte beğenilen derleme sonradan yeniden
62
+ derlenmeden incelemeye (`app-store-review`) ya da kanala (`play-promote`) gider. Development ya da dal derlemesi
63
+ **asla** mağazaya yükseltilemez ve daha yeni olsa da production işini gölgelemez. Dal merge'ten sonra silinir; o
64
+ dalın test derlemesi açılamaz olur.
65
+ - Development ya da dal derlemesi test edene o ortamın verisini açar; ortamın erişim kipi `off` ise korumalı alanlar
66
+ uygulanmaz — ortamı seçerken kullanıcıya söyleyin.
67
+ - **Play taslak uygulama:** Play'de hiç yayınlanmamış uygulamada API yalnız `draft` sürüm açabilir; ilk iç test
68
+ yayını Play Console'dan elle başlatılır (`play.releaseStatus` ayarı korunur).
69
+ - TestFlight **dış** test grubu (Test bilgisi, Beta App Review) bu yolun dışındadır; test kullanıcıları iç test
70
+ ekibine Studio'dan davet edilir.
71
+
72
+ ## İki tür güncelleme
73
+
74
+ `mobile_store_get { detail: ['changes'] }` son production dağıtımından beri değişenleri iki kovada döner:
75
+ `live` (sayfa, akış, uç — production'a dağıtılınca telefonlara dakikalar içinde gider, mağaza gerekmez;
76
+ dağıtım insan onaylıdır) ve `store` (izin, kimlik, görsel, kabuğun taşıdığı ayar — yeni mağaza sürümü ve
77
+ 1–3 gün inceleme ister → adım 6). Kova boşsa o tür güncelleme gerekmez.
10
78
 
11
79
  ## Kim ne yapar
12
80
 
13
81
  **Ajanın yaptığı:** `mobile_store_get` (ayar, kilitli alanlar, görsel sayısı, beyanlar; `manifest: true`;
14
- `detail: ['assets','signing','runners','audit']` id'li görseller, maskeli kimlik bilgisi/hesap, runner, denetim) ·
82
+ `detail: ['assets','signing','runners','audit','status','changes']` id'li görseller, maskeli kimlik bilgisi/hesap,
83
+ runner, denetim, mağaza durumu, iki kova) ·
15
84
  `mobile_store_update` (`settings` yaması · `declarations` · `registerAppleBundleId`) · `mobile_asset_create` /
16
- `_update` (sıra) / `_delete` · `mobile_build_start` (`dryRun`, `runnerScope`) · `mobile_build_status` ·
17
- `mobile_build_log` · `mobile_build_cancel`.
85
+ `_update` (sıra) / `_delete` · `mobile_build_start` (`dryRun`, `environment`, `scope`, `runnerScope`, `runnerId`, `packageType`) ·
86
+ `mobile_build_status` · `mobile_build_log` · `mobile_build_cancel`.
18
87
 
19
88
  **Beyan kullanıcınındır:** `declarations` (App Privacy, Data safety, içerik derecelendirme, hedef kitle,
20
89
  uygulama erişimi) yalnız kullanıcı formu mağazada doldurduğunu onaylayınca `true` yazılır.
21
90
 
22
91
  **İnsanın Studio › Yayın › Hesaplar'da yaptığı (MCP'de araç yok):** Apple/Google hesabı, sertifika, profil,
23
92
  keystore, harita anahtarı, demo parolası, push kimlikleri — APNs anahtarı, Firebase hesabı,
24
- `google-services.json` (gizli bilgi) ve runner kaydı. `review.demoPassword` şemada reddedilir.
93
+ `google-services.json` (gizli bilgi) ve runner kaydı. `review.demoPassword` şemada reddedilir: App Review demo
94
+ hesabı (P-15) Studio'da girilir. **Test kullanıcıları** (TestFlight davetleri, Play test grubu ve katılım
95
+ bağlantısı) Studio › Yayınla'dan davet edilir — kişisel e-posta taşıdığı için MCP'de araç yok. **Mağazaya
96
+ gönderim** (adım 8: `app-store-review`, `app-store-release`, `play-promote`) geri dönüşsüz ya da insan onaylıdır;
97
+ ajan yalnız `dryRun` ile ön kontrolünü okur ve kullanıcıyı Studio'ya yönlendirir.
25
98
 
26
99
  **Ekran görüntüsü:** `page_screenshot { environment: 'production', device, format: 'jpeg', quality: 92,
27
100
  language, colorScheme: 'light', outPath }` → `mobile_asset_create { path, kind: 'screenshot', deviceClass, locale }`.
@@ -44,36 +117,90 @@ ikon/splash/öne çıkan görsel tekli yuvadır: yeni yükleme eskisinin yerine
44
117
  ## Hedefler
45
118
 
46
119
  `build` (iki platform, yalnız derler) · `testflight` (yalnız iOS) · `internal` (yalnız Android, Play
47
- iç test). TestFlight'ta runner yükledikten sonra iş `running` + `store_processing` kalır (`awaitingStore`),
120
+ iç test). Android paketi `packageType`: `aab` (varsayılan, mağaza) · `apk` (yalnız `build`; telefona doğrudan
121
+ kurulur, mağazaya **gitmez** — indirme bağlantısı Studio'da). Runner verilmezse backend seçer: kurumun kendi
122
+ Mac'i çevrimiçiyse o, değilse Zeyrek'in runner'ı; yanıtta `runnerKind` (`own` · `zeyrek`). TestFlight'ta runner yükledikten sonra iş `running` + `store_processing` kalır (`awaitingStore`),
48
123
  Apple işleyince `completed` (en çok 60 dk); bu durumda iptal 409. İptal 409'unun mesajı nedeni söyler (iş zaten
49
124
  tamamlandı / başarısız / iptal edildi, mağaza yüklemeyi işliyor).
50
- TestFlight'ta derleme yüklenip mağaza metni/ekran görüntüleri reddedilirse iş yine tamamlanır;
51
- `storeResult.listingError` (hata kodu, ör. `store_rejected`) + `listingMessage` (ne yapılacağı) gelir. Derleme
52
- TestFlight'tadır, numara harcanmıştır: metni düzeltip yeni sürüm yüklemek gerekir, işi yeniden başlatmak aynı
53
- metni yeni numarayla yeniden dener.
125
+ `testflight` ve `internal` yalnız ikiliyi yükler; mağaza metni, ekran görüntüleri ve inceleme bilgisi yükseltme
126
+ işinde gider (iOS `app-store-review`, Android `play-promote` `production` kanalına). Eski TestFlight işlerinde
127
+ görülen `storeResult.listingError` + `listingMessage` o işin metin yüklemesinin reddidir; derleme yine TestFlight'tadır.
128
+
129
+ ## Mağaza yayını
130
+
131
+ Yayın hedefleri önceki adımın derlemesini taşır (yeni derleme yapmaz): `app-store-review` son TestFlight
132
+ derlemesini incelemeye gönderir, `app-store-release` onaylanan sürümü yayınlar, `play-promote` son iç test
133
+ derlemesini `releaseTrack` kanalına (`closed` | `production`) taşır. Kaynak yalnız **production işinin** derlemesidir
134
+ (development / dal test derlemesi sayılmaz); yoksa P-22 `fail`. Yükseltme hedefi `environment` ve `scope: 'test'` almaz.
135
+ **MCP'den yalnız `dryRun`**: gerçek çağrı `release_target_studio_only` ile durur, hiçbir iş açılmaz. App Review'a ek
136
+ görsel: `mobile_asset_create { kind: 'reviewAttachment' }` (dilsiz; yalnız `app-store-review` gönderimine girer).
137
+
138
+ `mobile_store_get { detail: ['status'] }` → `status.apple` (`appStoreState`, sürüm, kademeli durum),
139
+ `status.play.tracks[]` (kanal, durum, `versionCodes`, `rolloutPercent`) ve `status.actions[]`: Studio'daki
140
+ yayın düğmeleri, her biri `irreversible` + `consequence` (ne olacağı, istek dilinde). `irreversible: true`
141
+ geri alınamaz (App Store'da yayın, Play üretim kanalı; `releaseType` `manual` değilse incelemeye göndermek de) —
142
+ kullanıcıya `consequence`'ı aynen söyleyin.
143
+
144
+ Yayın ayarı (`mobile_store_update { settings }`, MOB-022 aynası): `apple.releaseType` `manual` (varsayılan) ·
145
+ `automatic` · `scheduled` (+ ileri `scheduledAt`), `apple.phasedRelease` (7 güne yayılan otomatik
146
+ güncelleme), `apple.pricing` `free` | `tier-1…tier-87`, `apple.territories` `['all']` ya da ISO alfa-3 kodları
147
+ (gönderim yalnız EKLER; satıştan kaldırma App Store Connect'te), `play.contactPhone` (E.164),
148
+ `play.contactWebsite` (https), `play.promoVideoUrl` (YouTube), `play.rolloutPercent` 1–100 (üretim kanalı;
149
+ boş = 100).
150
+
151
+ ## Yaş derecelendirmesi
152
+
153
+ `settings.apple.advisory` Apple'ın yaş anketidir: soru anahtarı → cevap (metin). Ön kontrol (P-21) tek cevabı
154
+ yeterli sayar, ama App Store Connect beyanı **her soru cevaplı** değilse reddeder ve Studio'nun bölümü de
155
+ ancak hepsi cevaplıyken tamamdır. Bütün soruları yazın (yama `advisory`'yi anahtar anahtar birleştirir;
156
+ yazılmayan soru boş kalır).
157
+
158
+ - Sıklık soruları — `NONE` · `INFREQUENT_OR_MILD` · `FREQUENT_OR_INTENSE`:
159
+ `alcoholTobaccoOrDrugUseOrReferences`, `contests`, `gamblingSimulated`, `gunsOrOtherWeapons`,
160
+ `horrorOrFearThemes`, `matureOrSuggestiveThemes`, `medicalOrTreatmentInformation`, `profanityOrCrudeHumor`,
161
+ `sexualContentGraphicAndNudity`, `sexualContentOrNudity`, `violenceCartoonOrFantasy`, `violenceRealistic`,
162
+ `violenceRealisticProlongedGraphicOrSadistic`.
163
+ - Evet/hayır soruları — `'true'` · `'false'` (metin):
164
+ `advertising`, `ageAssurance`, `gambling`, `healthOrWellnessTopics`, `lootBox`, `messagingAndChat`,
165
+ `parentalControls`, `socialMedia`, `socialMediaAgeRestricted`, `unrestrictedWebAccess`, `userGeneratedContent`.
166
+ - `kidsAgeBand` (isteğe bağlı): boş = Çocuklar kategorisinde değil; `FIVE_AND_UNDER` · `SIX_TO_EIGHT` ·
167
+ `NINE_TO_ELEVEN`.
168
+
169
+ Cevap uygulamanın içeriğinden çıkar (sayfalar, akışlar, reklam ayarı: `settings.ads.enabled` → `advertising`);
170
+ emin olunmayan soru kullanıcıya sorulur. Google Play'in IARC anketi konsoldadır (insan).
54
171
 
55
172
  ## Ön kontrol
56
173
 
57
- `mobile_build_start { dryRun: true }` yalnız `ok` OLMAYAN satırları döner (`okCount` gerisini sayar).
174
+ `mobile_build_start { dryRun: true }` yalnız `ok` OLMAYAN satırları döner (`okCount` gerisini sayar). Tablo
175
+ `scope: 'store'` (varsayılan) içindir; `scope: 'test'` farkı §Test derlemesi'nde.
58
176
 
59
177
  | id | Kod (`fail`) | Nerede düzelir |
60
178
  |---|---|---|
61
- | P-01 | `settings_incomplete`; Android `internal`'da `release_notes_too_long` (bir dilde `releaseNotes` > 500 — Play sınırı; ayar 4000'e izin verir çünkü App Store'a da gider; ikisi birlikteyse tek satırda iki cümle) | `mobile_store_update` |
179
+ | P-01 | `settings_incomplete`; Android `internal`'da `release_notes_too_long` (bir dilde `releaseNotes` > 500 — Play sınırı; ayar 4000'e izin verir çünkü App Store'a da gider; ikisi birlikteyse tek satırda iki cümle). Mesaj alanları istek dilinde adlandırır ("başlık (English)", "gizlilik politikası adresi"), ayar yolunu değil — yol `mobile_store_get`'ten | `mobile_store_update` |
62
180
  | P-02 | `assets_missing` | `mobile_asset_create` |
63
181
  | P-03 | `secrets_missing`, `secrets_unreadable`, `signing_expired` (`warn signing_expiring`) | Studio › Yayın › Hesaplar |
64
182
  | P-04 | `apple_account_missing`, `asc_bundle_id_missing`, `asc_app_missing`, `asc_unauthorized`, `asc_forbidden`, `asc_unreachable` | `asc_bundle_id_missing`: `mobile_store_update { registerAppleBundleId: true }`. `asc_app_missing`: App Store Connect'te uygulama kaydı (API açamaz, insan açar). Diğerleri: Studio hesabı |
65
183
  | P-05 | `google_account_missing`, `play_app_missing`, `play_unauthorized`, `play_forbidden`, `play_unreachable` | Play Console'da uygulama + servis hesabı yetkisi |
66
- | P-06 | `production_missing`, `production_page_missing` (yalın `build`'de `warn`, başlatmayı engellemez) | production ortamı ve dağıtım — insan onaylı, ajan yapmaz |
184
+ | P-06 | `production_missing`, `production_page_missing` (yalın `build`'de `warn`, başlatmayı engellemez); production dışı `environment` ile `fail environment_page_missing` | production ortamı ve dağıtım — insan onaylı, ajan yapmaz; başka ortamda açılış sayfası o ortamda |
67
185
  | P-07 | `https_required` | platform ayarı (destek) |
68
186
  | P-08 | `build_active` | etkin işi bekle ya da `mobile_build_cancel` |
69
187
  | P-09 | `runner_offline` | runner Mac'te `zeyrek-mobile-runner run` |
70
188
  | P-10 | `manual declarations_pending` | kullanıcı onayıyla `mobile_store_update { declarations }` (engellemez) |
71
189
  | P-11 | push (yalnız `testflight` / `internal`): `warn push_incomplete` (kimlik eksik — derleme push'suz çıkar); `push_unreadable` (kimliğin şifresi çözülemiyor), `push_profile_stale` (profil APNs anahtarından önce üretilmiş), `push_firebase_mismatch` (`google-services.json` başka Firebase projesinin), `push_package_mismatch` (dosya yüklendikten sonra Android paket adı değişmiş), `push_shell_outdated` (kabuk paketi push taşımıyor) | Studio › Yayın › Hesaplar ve kimlik bilgileri (insan yükler); `push_profile_stale`: profili yeniden üret; `push_shell_outdated`: platform ayarı (destek). Push kapalı ya da hazırsa satır `ok`'tur ve dryRun'da görünmez — hazır olduğunu `mobile_store_get { manifest: true }` → `manifest.push` söyler |
72
- | P-12 | izinler: `warn permissions_shell_outdated` (yayındaki kabuk izin kümesini taşımıyor: sabit izinler bildirilir, kamera çalışmaz, kipler uygulanmaz — başka P-12 satırı çıkmaz); kabuk taşıyorsa `fail permission_off_in_use` (kip `off` ama production o izni yazılı olarak kullanıyor) ya da `warn launch_permissions_shell_outdated` (açılışta sorulan izin var, kabuk açılışta sormuyor; kabuk kümeyi de taşımıyorsa bu cümle ilk uyarının iletisine eklenir) | `mobile_store_update { settings: { permissionModes } }` ya da kullanımı kaldırıp dağıtım; açılış uyarısında daha yeni kabuk ya da izni akış adımından isteme |
190
+ | P-12 | izinler: `warn permissions_shell_outdated` (yayındaki kabuk izin kümesini taşımıyor: sabit izinler bildirilir, kamera çalışmaz, kipler uygulanmaz — başka P-12 satırı çıkmaz); kabuk taşıyorsa `fail permission_off_in_use` (kip `off` ama production o izni yazılı olarak kullanıyor), `warn permission_shell_outdated` (derlemenin taşıdığı izin yayındaki kabuktan yeni — katalog satırının `minShellVersion`'ı, ör. `biometric` 0.6.0; manifest o izni yazmaz, açılış uyarısı varsa iletisine eklenir) , `warn launch_permissions_shell_outdated` (açılışta sorulan izin var, kabuk açılışta sormuyor) ya da `warn app_lock_shell_outdated` (uygulama kilidi açık, kabuk 0.6.0'dan eski: mağaza sürümü kilitsiz açılır); kabuk uyarıları tek satırda birleşir — kod ilk uyarınındır, ileti hepsini söyler, kabuk kümeyi de taşımıyorsa açılış ve kilit cümleleri `permissions_shell_outdated`'ın iletisine eklenir | `mobile_store_update { settings: { permissionModes } }` ya da kullanımı kaldırıp dağıtım; açılış uyarısında daha yeni kabuk ya da izni akış adımından isteme |
73
191
  | P-13 | reklam (yalnız `ads.enabled`): `warn ads_app_id_missing` (bu platformun AdMob App ID'si yok — derleme reklamsız çıkar), `warn ads_shell_outdated` (yayındaki kabuk reklam taşımıyor), yükleme hedefinde `manual ads_test_mode` (test kipi açık: mağazada test reklamı görünür), değilse `ok ads_ready` (yerleşim sayısıyla) | `mobile_store_update { settings: { ads } }`; test kipini yayından önce `testMode: false` |
74
192
  | P-14 | kod tarama (yalnız production akışlarında `scan-code` adımı varsa): `warn scan_shell_outdated` (yayındaki kabuk tarayıcıyı taşımıyor — derleme çıkar, adım `error` dalıyla biter); taşıyorsa `ok scan_ready`. Kamerayı yalnız dosya alanı kullanıyorsa satır hiç gelmez | yeni kabuk paketi (destek); ajan düzeltemez |
193
+ | P-15 | App Review satırları P-15…P-21 yalnız iOS yükleme hedeflerinde (`testflight`, `app-store-*`); sinyal okunamazsa `manual review_signals_unreadable`. P-15 demo hesabı: giriş varsa ve demo hesabı yoksa `review_demo_missing` | Studio › Yayın › Hesaplar (demo hesabı gizli bilgidir) |
194
+ | P-16 | `manual screenshot_source_unverified` (ekran görüntüsü uygulamanın gerçek ekranı mı — 2.3.3); hiç iOS ekran görüntüsü yoksa satır yok (eksikliği P-02 söyler) | kullanıcı doğrular |
195
+ | P-17 | `app_name_missing`, `app_name_too_long` (> 30); `warn`: adda yasak terim, anahtar kelime adı tekrarlıyor | `mobile_store_update { settings: { listing } }` |
196
+ | P-18 | giriş varsa `manual third_party_sign_in_unverified` (üçüncü taraf girişte Apple ile giriş — 4.8) | kullanıcı doğrular |
197
+ | P-19 | `privacy_policy_missing` | `settings.urls.privacyPolicy` |
198
+ | P-20 | kendi kaydı olan uygulamada hesap silme yolu görülmezse `manual account_deletion_unverified` (5.1.1(v)) | ekran + kullanıcı doğrular |
199
+ | P-21 | `age_rating_missing` (yaş derecelendirmesi yanıtsız) | `settings.apple.advisory` |
200
+ | P-22 | yayın hedefinde `release_source_missing`: kaynak derleme yok (`testflight` → `app-store-review` → `app-store-release`, `internal` → `play-promote`) | önce kaynak hedefi derle |
75
201
 
76
- Satırın `message` ve `fixHint`'i backend'den, istek dilinde gelir.
202
+ `manual` satır başlatmayı engellemez ama kullanıcıya söylenir; `fail` engeller. Satırın `message` ve `fixHint`'i
203
+ backend'den, istek dilinde gelir.
77
204
 
78
205
  ## İzinler
79
206
 
@@ -126,6 +253,24 @@ yerleşim o platformda yoktur. Biçim hatası `MOB-022` ile yazılmadan durur.
126
253
  - `mobile_store_get` reklam açıkken `adsWarnings` döner (`MOB-026`): test kipi, App ID'si olan platformda birimi olmayan
127
254
  yerleşim, konsol beyanları (App Privacy, Data safety, AdMob rıza mesajı — insanın işi).
128
255
 
256
+ ## Uygulama kilidi
257
+
258
+ Mağaza sürümünün Face ID / parmak izi / cihaz parolasıyla kilitlenmesi (kök ADR 0021 madde 8–11) bir mağaza ayarıdır:
259
+ `settings.appLock = { enabled, timeoutSeconds }`, süre `0` (her dönüşte) · `60` · `300` · `900` saniye. Yama alan alan
260
+ birleşir (`{ appLock: { timeoutSeconds: 300 } }` açık/kapalıyı korur).
261
+
262
+ - Açık kilit `biometric` için bir kullanımdır (`usage` içinde `source: lock`): izin `auto` kipte derlemeye girer.
263
+ `permissionModes.biometric` `off` iken kilit açılamaz, açık kilitle kip `off` yapılamaz (422 `appLock.enabled`;
264
+ gateway birleşik kayıtta istek gitmeden durdurur, `MOB-022`).
265
+ - Bir sonraki mağaza derlemesiyle etkili olur. Kilit kabuğun ayrı bir yeteneğidir (kabuk paketi ≥ 0.6.0): görünümde
266
+ `shell.carriesAppLock: false` ise ayar saklanır ama manifest `appLock` yazmaz, uygulama kilitsiz açılır
267
+ (`permissionWarnings` `MOB-031`; ön kontrol P-12 uyarır — kabuk 0.5.x'te kod `app_lock_shell_outdated`, daha eski kabukta
268
+ cümle o kabuğun ilk P-12 uyarısına eklenir, aynı satırda `fail permission_off_in_use` varsa kilit cümlesi görünmez).
269
+ - Davranış: soğuk açılışta ve arka planda süre dolup dönüşte kilit ekranı, ardından sistem penceresi; içerik kilit
270
+ açılmadan çizilmez, oturum kapanmaz. Cihazda biyometri de parola da yoksa kilit atlanır. iOS'ta uygulama değiştirici
271
+ görüntüsü örtülür, Android'de örtülmez. **Oynatıcıda (Expo Go) ve Studio önizlemesinde kilit yoktur** — tek bir
272
+ ekranı kapı arkasına almak için `biometric-auth` adımı (platform_guide('mobile-steps')).
273
+
129
274
  ## Hata kodları
130
275
 
131
276
  `mobile_build_status` → `error.code`.
@@ -28,6 +28,7 @@ Motorun kendi adımları (`setter`, `condition`, `microservice`, `code`, `functi
28
28
  | `open-settings` | uygulamanın sistem ayar sayfasını açar; `Steps.<adım>.data.opened`. Argüman almaz |
29
29
  | `back` | gezinme yığınında bir geri (iOS geri kaydırmasıyla aynı sonuç; kök ADR 0013). Argüman almaz (yazılan okunmaz), sonuç yazmaz, hata üretmez, hep `always`. Yığının ilk ekranında hiçbir şey yapmaz; Studio önizlemesinde runtime'ın kendi gezinmesi yoksa yine hiçbir şey, web uygulamasında tarayıcının geri'sidir. Formu kaydedip önceki ekrana dönmek için `redirect` yerine bunu kullanın: `redirect` yığına yeni ekran iter, geri kaydırması kaydedilen forma döner. 2026-10-08 öncesi runtime ve backend `back`'i tanımaz |
30
30
  | `ad-show` | tam ekran reklam (kök ADR 0012). `args.value` mağaza ayarındaki yerleşimin **adı**, düz metin (`FLOW-039`); biçimi `interstitial` · `rewarded` · `rewardedInterstitial` olmalı. Reklam yüklenip gösterilir, kullanıcı kapatınca `Steps.<adım>.data = { shown: true, reward }` ile sürer — `reward` ödüllü reklamı sonuna kadar izleyende `{ type, amount }`, değilse `null` (ödül ayrı dal değil, `condition` ile okunur). 10 sn'de reklam yoksa, SDK hata verirse, derleme reklam taşımıyorsa ya da rıza yoksa `error.status: 'unavailable'`; tanımsız ya da banner yerleşimi `invalid`: `error` dalı → hata akışı → `always`. Oynatıcı ve web önizlemesi her zaman `unavailable` — reklamsız dalı orada sınayın. Paletten yalnız mobilde |
31
+ | `biometric-auth` | kullanıcıyı Face ID / parmak izi (yedekle cihaz parolası) ile doğrular (kök ADR 0021). `args.title` sistem penceresindeki gerekçe, `args.cancelText` vazgeç düğmesi — ikisi metin şablonu, boşsa uygulama dilindeki varsayılan; `args.value` yedek, düz metin: `device` (biyometri olmazsa cihaz parolası; boşsa bu) ya da `none` (`FLOW-042`; `fallback` / `reason` / `cancelLabel` anahtarı sessizce düşer). Başarı: `Steps.<adım>.data = { verified: true }` → `always`. **Vazgeçmek hatadır**: `error.status` `cancelled` · `not-enrolled` · `lockout` · `failed` · `unavailable` (donanım yok ya da derleme taşımıyor) · `unsupported` (web uygulaması, production `/view`) · `invalid`; `error` dalı → hata akışı → **o da yoksa akış bu adımda biter**, `always`'e bağlı adım koşmaz. Yerel bir kapıdır, sunucu için kanıt değildir. Paletten yalnız mobilde |
31
32
  | `scan-code` | tam ekran kod tarayıcı (kök ADR 0017): tek kod okur ve kapanır. `args.value` izinli türler, **virgüllü düz metin** (`FLOW-042`): `aztec` · `codabar` · `code39` · `code93` · `code128` · `datamatrix` · `ean8` · `ean13` · `itf14` · `pdf417` · `qr` · `upc_e`; boş → hepsi. `upc_a` yok: UPC-A etiketi `ean13` ve on üç haneyle (başında `0`) döner. `args.title` ekrandaki yönerge, metin şablonu (`{{ t('anahtar') }}`). Sonuç `Steps.<adım>.data`: `{ value, type, source }` — `source` `camera` ya da `manual` (ekrandaki "elle gir"; tür bilinmiyorsa `type: null`). `error` dalı: `status` `cancelled` (kullanıcı kapattı — **vazgeçme bir hatadır, `error` dalını bağla**) · `denied` (kamera izni yok) · `unavailable` (derleme kamerayı taşımıyor ya da tarayıcı zaten açık) · `unsupported` (web uygulaması) · `invalid`. Kamera izni kendiliğinden derlemeye girer (adım production'daysa). Önizleme (`/dev`, `page_screenshot`) kamera açmaz, "kod gir" penceresi çizer: `__scan-value`'ya yaz, `__scan-confirm` ya da `__scan-cancel` (çok türde `__scan-type-<tür>`). `page_screenshot` pencereyi yalnız `captureUnready: true` ile sürer (akış pencereyi beklerken "koşuyor" sayılır; her eylem 15 sn bekler). Tam gövde: `ngapps://examples/mobile-scan-code` |
32
33
 
33
34
  ## Bildirim tarifi
@@ -58,6 +59,22 @@ rıza formu → açılış izinleri (ATT) → SDK; iOS'ta izleme izni yoksa rekl
58
59
  ayarda tanımlı olduğu sayfa/akış yazımında denetlenemez — `mobile_store_get` `settings.ads.units` ve `adsWarnings`
59
60
  (`MOB-026`); yayın ayrıntısı `platform_guide('mobile-publish')` §Reklamlar.
60
61
 
62
+ ## Biyometri tarifi
63
+
64
+ **Biyometri tarifi** (kök ADR 0021): (1) onaylanacak düğmenin akışına `biometric-auth` `args: { title: 'Ödemeyi
65
+ onaylayın' }`, işi `always` dalına, kullanıcıya söylenecek olanı `error` dalına (`Steps.<adım>.error.status`) bağlayın —
66
+ hata yolu yoksa vazgeçen kullanıcıda akış biter; (2) "bu cihazda biyometri yok" metni `isHidden: {{ Device.biometric.available }}`
67
+ (`Device.biometric = { available, enrolled, kinds }`, `platform_guide('expressions')`). Nerede ne olur: Studio önizlemesi,
68
+ canlı kip, `/dev` ve `/branch` sormaz — "Doğrulandı say" / vazgeç penceresi açar (`Device.biometric` orada açık, kayıtlı,
69
+ yüz); production `/view` ve web uygulaması `unsupported` döner. Oynatıcıda (Expo Go) yedeği `device` olan adım çalışır,
70
+ `none` olan her zaman `unavailable`. Mağaza derlemesi biyometriyi yalnız production'da kullanılıyorsa ya da uygulama kilidi açıksa taşır (izin satırı
71
+ `biometric`, Face ID metni `mobile_store_update { settings: { permissions: { biometric } } }`, kabuk 0.6.0+). `biometric`
72
+ **sorulamayan izindir**: `permission` adımının türü olamaz, açılışta sorulamaz (422). Uygulamanın tamamını açılışta
73
+ kilitlemek adım değil mağaza ayarıdır (`settings.appLock`, `platform_guide('mobile-publish')` §Uygulama kilidi). `page_screenshot` pencereyi
74
+ sürer: adımı tetikleyen tıklamanın raporu `pageState: 'awaiting-user'` der, sonraki eylem `{ action: 'click', target:
75
+ 'preview-biometric__verify' }` (Doğrulandı say) ya da `'preview-biometric__cancel'`. Tam gövde:
76
+ `ngapps://examples/flow-biometric-confirm`.
77
+
61
78
  ## Konum ve harita
62
79
 
63
80
  `style`, `accuracy`, `reverseGeocode` gibi anahtarlar sözleşmede yoktur ve platform onları sessizce düşürür