@appaflytech/zeyrek-ai-mcp 0.0.4 → 0.0.6

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 (101) hide show
  1. package/.env.example +8 -3
  2. package/.env.production.example +2 -0
  3. package/README.md +229 -193
  4. package/dist/config.js +20 -2
  5. package/dist/knowledge/backendConstraints.js +897 -0
  6. package/dist/knowledge/componentCatalog.js +196 -42
  7. package/dist/knowledge/coverage.js +43 -0
  8. package/dist/knowledge/flowGuards.js +30 -0
  9. package/dist/knowledge/guideSections.js +49 -0
  10. package/dist/knowledge/hydrate.js +97 -0
  11. package/dist/knowledge/languageCodes.js +36 -0
  12. package/dist/knowledge/languages.js +105 -0
  13. package/dist/knowledge/layoutSummary.js +10 -0
  14. package/dist/knowledge/localization.js +51 -0
  15. package/dist/knowledge/normalize.js +449 -16
  16. package/dist/knowledge/outlines.js +136 -12
  17. package/dist/knowledge/propTypes.js +80 -0
  18. package/dist/knowledge/rules.js +618 -43
  19. package/dist/knowledge/schemaGuard.js +199 -0
  20. package/dist/knowledge/styleResolve.js +63 -4
  21. package/dist/observability/trace.js +27 -3
  22. package/dist/platform/errors.js +46 -4
  23. package/dist/platform/http.js +33 -4
  24. package/dist/platform/patch.js +27 -0
  25. package/dist/platform/refs.js +104 -0
  26. package/dist/platform/responses.js +9 -2
  27. package/dist/platform/screenshot.js +527 -16
  28. package/dist/platform/secrets.js +40 -0
  29. package/dist/platform/tokenVault.js +89 -0
  30. package/dist/platform/writeDiff.js +281 -0
  31. package/dist/resources/examples/layout-app-shell.json +1 -1
  32. package/dist/resources/examples/microservice-table-list.json +2 -4
  33. package/dist/resources/examples/page-dynamic-form.json +2 -2
  34. package/dist/resources/examples/page-flex-sections.json +99 -0
  35. package/dist/resources/examples/page-sign-in.json +3 -2
  36. package/dist/resources/guides/component.md +69 -8
  37. package/dist/resources/guides/entity.md +404 -16
  38. package/dist/resources/guides/expressions.md +44 -2
  39. package/dist/resources/guides/external-component.md +9 -0
  40. package/dist/resources/guides/getting-started.md +77 -7
  41. package/dist/resources/guides/layout.md +53 -10
  42. package/dist/resources/guides/microservice.md +226 -13
  43. package/dist/resources/guides/middleware.md +89 -1
  44. package/dist/resources/guides/page.md +383 -46
  45. package/dist/resources/guides/translation.md +126 -8
  46. package/dist/resources/guides/view.md +16 -1
  47. package/dist/resources/guides/workflow.md +198 -27
  48. package/dist/resources/knowledge.js +5 -0
  49. package/dist/resources/rules.js +27 -0
  50. package/dist/resources/schema-fingerprint.json +8 -0
  51. package/dist/resources/schemas/CreateBulkRowCommand.json +33 -0
  52. package/dist/resources/schemas/CreateDatasourceCommand.json +15 -6
  53. package/dist/resources/schemas/CreateDiscoveredEntitiesCommand.json +19 -8
  54. package/dist/resources/schemas/CreateEntityCommand.json +52 -12
  55. package/dist/resources/schemas/CreateMicroserviceCommand.json +26 -2
  56. package/dist/resources/schemas/CreateQueryCommand.json +56 -3
  57. package/dist/resources/schemas/CreateRowCommand.json +38 -0
  58. package/dist/resources/schemas/CreateViewCommand.json +13 -10
  59. package/dist/resources/schemas/CreateWorkflowCommand.json +95 -14
  60. package/dist/resources/schemas/GetRowsQuery.json +3 -0
  61. package/dist/resources/schemas/PreviewMicroserviceCommand.json +16 -0
  62. package/dist/resources/schemas/PreviewQueryCommand.json +5 -2
  63. package/dist/resources/schemas/PreviewWorkflowCommand.json +34 -9
  64. package/dist/resources/schemas/StepDto.json +23 -3
  65. package/dist/resources/schemas/UpdateBulkRowCommand.json +33 -0
  66. package/dist/resources/schemas/UpdateDatasourceCommand.json +15 -6
  67. package/dist/resources/schemas/UpdateEntityCommand.json +49 -10
  68. package/dist/resources/schemas/UpdateMicroserviceCommand.json +28 -3
  69. package/dist/resources/schemas/UpdateQueryCommand.json +13 -3
  70. package/dist/resources/schemas/UpdateRowCommand.json +40 -0
  71. package/dist/resources/schemas/UpdateViewCommand.json +15 -10
  72. package/dist/resources/schemas/UpdateWorkflowCommand.json +95 -14
  73. package/dist/server.js +4 -0
  74. package/dist/tools/access.js +3 -3
  75. package/dist/tools/applications.js +1 -1
  76. package/dist/tools/automations.js +63 -30
  77. package/dist/tools/common.js +39 -0
  78. package/dist/tools/components.js +16 -7
  79. package/dist/tools/datasources.js +83 -21
  80. package/dist/tools/deployments.js +34 -2
  81. package/dist/tools/dtos.js +25 -12
  82. package/dist/tools/entities.js +113 -28
  83. package/dist/tools/externalComponents.js +9 -3
  84. package/dist/tools/files.js +20 -6
  85. package/dist/tools/flows.js +110 -11
  86. package/dist/tools/folders.js +7 -2
  87. package/dist/tools/globalFunctions.js +63 -0
  88. package/dist/tools/layouts.js +103 -29
  89. package/dist/tools/microservices.js +229 -38
  90. package/dist/tools/organizations.js +5 -6
  91. package/dist/tools/pages.js +365 -100
  92. package/dist/tools/platformInfo.js +152 -11
  93. package/dist/tools/projects.js +183 -33
  94. package/dist/tools/queries.js +32 -10
  95. package/dist/tools/rows.js +159 -45
  96. package/dist/tools/screenWrite.js +59 -0
  97. package/dist/tools/screens.js +28 -0
  98. package/dist/tools/translations.js +24 -8
  99. package/dist/tools/views.js +20 -10
  100. package/dist/tools/workflows.js +33 -16
  101. package/package.json +6 -3
@@ -78,9 +78,30 @@ mümkündür ama doğrulama duvarı ve canlı canvas yalnız `page_schema_patch`
78
78
  her turda çözümlenmiş metinle EZER — anahtar yalnız `ref`'te yaşar, `data`'ya yazıp
79
79
  güvenme (`code` prop'uyla aynı davranış). Tam şekil, fallback zinciri ve `translation_*`
80
80
  tool'ları için `platform_guide('translation')`.
81
+ - **Kurulmuş bir düğümün prop TİPİNİ `set_prop_type` micro-op'u değiştirir.**
82
+ `{ op: 'set_prop_type', target: '<elementId>', prop: 'content', propType: 'translation', ref: 'dashboard.kpi.total' }`
83
+ bir `static` prop'u çeviriye, `propType: 'code'` + `ref: '<ifade>'` koda, `propType: 'static'` +
84
+ `data: <değer>` sabite bağlar. Bu bir **gateway emülasyonudur**: `replace_prop` gönderilen ham
85
+ değeri her zaman `static` Prop'una sarar ve sarmalanmış Prop nesnesini reddeder, yani prop tipini
86
+ yazamaz. Op olmadan tek yol düğümü `remove_component` + `add_component` ile yeniden kurmaktı;
87
+ 2026-09-06 koşusunda on bir etiket bu yolla kuruldu ve onlara yazan **dokuz setter iteratee'si**
88
+ platform tarafından sessizce silindi (üç KPI kartı boş kaldı). Taşıyıcı alan tipe göre değişir —
89
+ `static` değeri `data`'da, `code` ve `translation` `ref`'te taşır; eksik taşıyıcı istek
90
+ gönderilmeden reddedilir ve `page_schema_validate` aynı kararı verir.
91
+
81
92
  - **`blocks[].size`, komponentin `component_get`'teki `maxSize`'ını aşamaz.** Dikkat: katalog
82
93
  varsayılanı aşabiliyor (`page-header` default `h:8`, `maxSize` `h:4`) — varsayılanı körü körüne
83
94
  kopyalama, doğrulayıcı `"Height of 'page-header' must be between 1 and 4."` ile reddeder.
95
+ - **Kurulmuş bir düğümün boyutunu `resize_component` micro-op'u değiştirir.**
96
+ `{ op: 'resize_component', target: '<elementId>', size: { w, h } }` ızgara boyutunu,
97
+ `{ …, appearance: { heightType: 'fixed', heightString: '340px' } }` flex ölçüsünü yazar; ikisi
98
+ aynı op'ta verilebilir, `breakpoint` varsayılanı 1024'tür. Bu bir **gateway emülasyonudur**:
99
+ platformun micro-op sözlüğünde boyut yoktur ve `move_component` gövdesine konan `size` 200 dönüp
100
+ SESSİZCE düşer (bu yüzden gateway artık onu reddeder ve buraya yönlendirir). Op şemayı okuyup
101
+ sayfayı tam gövdeyle geri yazar. Ebeveynin düzeni yazdığın alanı etkisiz kılıyorsa (flex kabın
102
+ içine `size`, ızgaranın içine `appearance`) yanıt `PAGE-019` uyarısı taşır — istek yine geçer.
103
+ **Düğümü silip yeniden kurmayın:** o yol ona yazan akış setter'larını sessizce öldürür
104
+ (`FLOW-018`, aşağıda Kural 2).
84
105
  - **Aynı ızgara hücrelerini iki komponente veremezsin.** Studio'da komponent tuvale bırakıldığında
85
106
  çakışan kardeşleri aşağı itme işini studio-api'nin sayfa kurma servisi yapar
86
107
  (`SchemaHelper.reorderComponents` → `draggableElement.moveItem`); komple şema yazan `page_create`
@@ -189,14 +210,15 @@ Adım gövdesi `StepManagementSchema`: `{ stepId, name, type, kind, args, branch
189
210
  **`branches[].nextStepId` bir GUID değil, hedef adımın `stepId` STRING'idir**
190
211
  (`allSteps.TryGetValue(b.NextStepId)`).
191
212
 
192
- Bu adımda platformun sessizce yuttuğu beş tuzak var — hepsi isteği 200 döndürür, akış yine de
213
+ Bu adımda platformun sessizce yuttuğu altı tuzak var — hepsi isteği 200 döndürür, akış yine de
193
214
  ölü ya da yarım çalışır. Gateway hepsini yakalar (FLOW-004…012), ama şekli baştan doğru yazın:
194
215
 
195
216
  **1. Dal adı motorun anahtarıdır, etiket değil.** Runtime dal kimliğini `branch.name`'den üretir
196
217
  (`Runtime.svelte:329`) ve her adım `engine.next('always')` çağırır. Ad `always` değilse motor
197
218
  dalı bulamaz ve akışı **sessizce durdurur** (`flow-engine.ts:79`); Studio da zinciri
198
219
  `sourceBranch === 'always'` ile çizdiği için panelde **Start kartından sonrası boş görünür**.
199
- İstisnalar: `condition` → dal adı rule id'sidir, `confirm` → `yes|no|always`, `microservice` →
220
+ İstisnalar: `condition` → dal adı rule id'sidir (ve o id bir **GUID**'dir — aşağıdaki tuzak 6),
221
+ `confirm` → `yes|no|always`, `microservice` →
200
222
  `always` (eski akışlarda `success` de çalışır).
201
223
 
202
224
  **2. `iteratees[].type` yalnız `change` | `fill`.** `setter.ts` başka değerde hiçbir dala
@@ -226,6 +248,37 @@ key'ini düz anahtar ver, iç içe yolu `ref`'e yaz: `"parent_name": { "type": "
226
248
  anahtar her satırda taşınır. `type` sözlüğü: `item` (kaynak alanı), `code` (satır başına JS,
227
249
  satır `Row` değişkenindedir), diğerleri sabit.
228
250
 
251
+ **6. `condition` adımının gövdesi `args.rules`'tur — backend workflow'unun `rule.items` şekli
252
+ DEĞİL.** Sayfa akışını başka bir motor koşar (`ng-apps-flow/tasks/condition/condition.ts`) ve o
253
+ motor yalnız şu sözleşmeyi tanır. Workflow şekli yazılırsa istek 200 döner, adım kaydedilir,
254
+ Studio çizer ve **kural hiç değerlendirilmez** — akış dalsız kalır. Gateway `FLOW-020` ile
255
+ yakalar.
256
+
257
+ ```jsonc
258
+ { "stepId": "guard", "name": "Token var mı", "type": "condition", "kind": "normal",
259
+ "args": { "rules": [
260
+ { "id": "0198e2a1-4c31-7a10-9b44-1f0c5d2e6a01", "expression": "!!Steps.signIn.data.token", "type": "if" },
261
+ { "id": "0198e2a1-4c31-7a10-9b44-1f0c5d2e6a02", "expression": "", "type": "else" } ] },
262
+ "branches": [ { "id": "<GUID>", "name": "0198e2a1-4c31-7a10-9b44-1f0c5d2e6a01", "nextStepId": "go_home" },
263
+ { "id": "<GUID>", "name": "0198e2a1-4c31-7a10-9b44-1f0c5d2e6a02", "nextStepId": "show_error" },
264
+ { "id": "<GUID>", "name": "always", "nextStepId": "hide_spinner" } ] }
265
+ ```
266
+
267
+ > Dal adları okunabilir değil, **kuralın GUID'inin kendisi** — `branches[].name === rules[].id`.
268
+ > Okunabilir ad iki uçta birden tutmaz (aşağıdaki tabloda ilk satır).
269
+
270
+ | Kural | Neden | Kaynak |
271
+ |---|---|---|
272
+ | **`rules[].id` GERÇEK bir GUID olmalı** | Backend alanı `Guid`'e çevirir; `"ok"` gibi okunabilir bir ad isteği `The JSON value could not be converted to System.Guid. Path: $.flow.steps[N].args.rules[0].id` ile reddeder. Dal adı da aynı GUID olduğu için iki yerde birden okunabilir ad kullanamazsınız | `StepManagementSchema.cs` `StepManagementRule.Id` |
273
+ | **`id`'yi hiç vermemek dalı sessizce ÖLDÜRÜR** | Alan boşsa backend `Guid.CreateVersion7()` ile kendi GUID'ini üretir; yazdığınız `branches[].name` ona asla eşleşmez, istek 200 döner ve dal hiç koşmaz. Her kural için taze GUID üretip iki yere de aynısını yazın | `StepManagementSchema.cs:549` varsayılan değer |
274
+ | **İlk kural `type:'if'` olmak ZORUNDA** | Motor zinciri yalnız `rules[0].type === 'if'` iken başlatır; değilse adım hiçbir dala girmez ve akış sessizce biter | `condition.ts` son blok |
275
+ | **Dal adı kuralın `id`'sidir** | `engine.next(rule.id)` → `Runtime.svelte:330` `branch.name`'i `branch.id`'ye çevirir → `flow-engine.ts:93` dalı adıyla arar. Her `rules[].id` için aynı adlı bir `branches[]` girdisi olmalı | `condition.ts` |
276
+ | **`else` yalnız `expression` BOŞKEN fallback'tir** | Koşul `!rule.expression && rule.type === 'else'`; dolu `else` normal karşılaştırmaya döner ve hiçbir kural tutmazsa akış dalsız biter | `condition.ts` |
277
+ | **Zincir dizi SIRASIYLA yürür** | Tutmayan kural `rules[index + 1]`'e geçer — `else-if` sırası diziden gelir, `order` alanından değil | `condition.ts` `process()` |
278
+ | `type` kümesi `if` · `else-if` · `else` | `ConditionArgumentRuleType` | `condition.types.ts` |
279
+ | **`always` YALNIZ bir dal eşleştiğinde koşar** | Motor `always`'i eşleşen dalın `completed`'ından çağırır (boş ifadeli `else` dalında ayrıca `stopped`'ta da). `rules` boşsa, `rules[0].type !== 'if'` ise ya da hiçbir ifade tutmayıp fallback `else` yoksa `engine.next('always')` **hiç çağrılmaz** — ayrıntı ve tuzağı için aşağıdaki §"Condition adımı `always` dalını…" | `condition.ts:55-61` |
280
+ | `branches` nesne biçimini de kabul eder | Dizi yerine `{ "<dal adı>": "<nextStepId>" }` yazılabilir; motor ikisini de aynı `{ id, nextStepId }` şekline eşler. Dizi biçimini tercih edin — `branches[].id` alanını orada verebiliyorsunuz | `Runtime.svelte` `mapStepBranches` |
281
+
229
282
  ```jsonc
230
283
  // akışı başlatan adım — dal adı 'always' olmak zorunda
231
284
  { "stepId": "start", "name": "Start", "type": "start", "kind": "normal", "args": {},
@@ -259,6 +312,16 @@ satır `Row` değişkenindedir), diğerleri sabit.
259
312
  > göndermeden reddeder. `element` değerini `page_schema_get` çıktısındaki `elementId` ile doğrula,
260
313
  > uydurma.
261
314
 
315
+ > **DOLU ama var olmayan `element` FLOW-017'nin KAPSAMI DIŞINDADIR — onu FLOW-024 söyler.**
316
+ > FLOW-017 yalnız alanın boş olduğunu görür; henüz kurulmamış bir düğümün id'sini yazarsan alan
317
+ > dolu görünür, kural susar ve platform `element`i **null** kaydeder — sonuç aynıdır: adım koşar,
318
+ > hiçbir şey yazmaz (ölçüm: 2026-09-09 koşusu `S-14`, `search_hint` düğümü akıştan sonra
319
+ > kurulmuştu). Gateway `flow_create`/`flow_update`/`flow_patch` sırasında barındırıcının şemasını
320
+ > okuyup hedefi arar ve bulamazsa **FLOW-024 (uyarı)** düşürür; yazmayı durdurmaz, çünkü "önce
321
+ > akışı kur, sonra düğümü" meşru bir sıradır. Doğru sıra: düğümü `page_schema_patch add_component`
322
+ > ile kur, sonra akışı `flow_update` ile **aynı gövdeyle** yeniden kaydet — kaydedilen `element`
323
+ > null olduğu için `flow_patch`'in `[element=…]` seçicisi onu bulamaz.
324
+
262
325
  Satır eşlemeli tablo yüklemesinin tam şekli (yükleniyor bayrağı → uç → satırlar + toplam kayıt)
263
326
  `ngapps://examples/flow-load-table`'dadır; setter'ın kritik iki iteratee'si şöyle görünür:
264
327
 
@@ -279,7 +342,67 @@ Satır eşlemeli tablo yüklemesinin tam şekli (yükleniyor bayrağı → uç
279
342
 
280
343
  `errorSteps` verirsen (start dışında en az bir adım) hata anında `engine.onError` true döner ve
281
344
  **ana akışın kalan adımları hiç çalışmaz** — yükleniyor bayrağını kapatmak, hata mesajını
282
- göstermek error flow'un işidir (`alert` adımı: `args: { message, alertType: 'error' }`).
345
+ göstermek error flow'un işidir.
346
+
347
+ > **4xx de hatadır ve `always` dalı seni KURTARMAZ.** `microservice.ts` `.catch` bloğu
348
+ > reddedilen her istekte koşar — 401/403/404/422 dâhil. Sonrası iki yoldan biridir ve ikisi de
349
+ > "ekranda hiçbir şey olmadı" ile biter:
350
+ >
351
+ > 1. **Hata yolu VARSA** `engine.onError` true döner ve `.finally` `return` eder —
352
+ > `engine.next('always')` **hiç çağrılmaz**. Yani `always` ile `errorSteps` birbirini dışlar;
353
+ > ikisini birden kuramazsın, hata yolu ana akışın devamını devralır.
354
+ > 2. **Hata yolu YOKSA** `always` koşar, ama storage artık `{ error, data }`'dır ve
355
+ > `Steps.<ad>.data` başarı gövdesi değil **hata gövdesidir**. Sıradaki `condition`
356
+ > `!!Steps.signIn.data.token` gibi bir ifadeyi `undefined` üzerinden değerlendirir, sessizce
357
+ > diğer dala düşer ve `else` dalı bağlı değilse akış orada biter.
358
+ >
359
+ > **`errorSteps` yalnız `start` taşıyorsa hata yolu YOKTUR:** `hasErrorFlow`
360
+ > `errorTasks.some(t => t.type !== 'start')` arar. Start'ın ardına en az bir adım bağla —
361
+ > gateway `FLOW-021` ile uyarır.
362
+ >
363
+ > **Mobilde `errorSteps` diye bir şey YOK.** `ScreenRuntime.tsx` `mapFlowsToMachine` yalnız
364
+ > `tasks` üretir (web `Runtime.svelte` ayrıca `errorTasks` verir), yani mobil uygulamada
365
+ > `hasErrorFlow` her zaman false: hata yolu hiç koşmaz ve akış yukarıdaki 2. yoldan —
366
+ > `always` + hata gövdeli storage — devam eder. Mobil sayfada hata yolunu `condition
367
+ > Steps.<adımAdı>.error` ile kur.
368
+
369
+ > **Hata metni ÜÇ ayrı yerde durur ve ikisi kullanıcıya gösterilecek metin DEĞİLDİR.**
370
+ > `onError` hata akışını koşturmadan önce `machine.setStorage('error', errorInfo)` yapar
371
+ > (`flow-engine.ts`), yani hata akışının adımları `Steps.error`'ı okuyabilir:
372
+ >
373
+ > | İfade | Ne taşır | Kaynak |
374
+ > |---|---|---|
375
+ > | **`Steps.error.message`** | **Ucun kendi mesajı** — `err.response.data.message`; boşsa HTTP durum metnine düşer. **Kullanıcıya gösterilecek metin budur.** Yanında `Steps.error.code` · `Steps.error.step.name` (patlayan adım) · `Steps.error.step.data` (ucun gövdesi) gelir | `microservice.ts` `err.reason \|\| HttpStatusText[err.code]` → `flow-engine.ts` `onError` |
376
+ > | `Steps.<adımAdı>.error` | Yalnız HTTP **durum metni** — 401 için `'Unauthorized'`. Hata OLDUĞUNU anlamaya yarar, göstermeye değil | `microservice.ts` `.catch` `HttpStatusText[err.code]` |
377
+ > | `Steps.<adımAdı>.data` | Ucun **hata gövdesi** (`err.response.data`) — başarı gövdesi değil. `.message` alanı burada da vardır | `microservice.ts` `.catch` |
378
+ >
379
+ > **`alert` adımının `message`'ı DÜZ METİNDİR — ifade yazarsan ekranda o ifade görünür.**
380
+ > `alert.ts` `task.argument.message`'ı doğrudan `toast.error()`'a geçirir ve `args` hiçbir yerde
381
+ > değerlendirilmez (`Runtime.svelte` `argument: step.args`): `{ "type": "alert", "args": {
382
+ > "message": "Steps.error.message", "alertType": "error" } }` kullanıcıya harfi harfine
383
+ > `Steps.error.message` gösterir. Sabit bir uyarı yetiyorsa `alert` en ucuz adımdır; **ucun ne
384
+ > dediğini göstereceksen bir `setter` (ya da `code`) adımı zorunludur** — `value.type: 'code'`
385
+ > ifadeyi orada gerçekten çalıştırır:
386
+ >
387
+ > ```jsonc
388
+ > // errorSteps: start → bu setter. Metni bir notification/text komponentine yazar.
389
+ > { "stepId": "show_error", "name": "Hatayı göster", "type": "setter", "kind": "normal",
390
+ > "args": { "iteratees": [
391
+ > { "type": "change", "element": "error_note", "property": "content", "source": "component",
392
+ > "mapping": { "source": "", "mappings": {} },
393
+ > "value": { "type": "code", "ref": "Steps.error?.message ?? 'İşlem başarısız'" } }] },
394
+ > "branches": [{ "id": "<GUID>", "name": "always", "nextStepId": null }] }
395
+ > ```
396
+
397
+ > **Hangi yolu seçeceksin — `errorSteps` mi, ana akışta `condition` mı?** İkisi birden
398
+ > kurulamaz (yukarıdaki 1. madde), yani bu bir tercih değil bir karar:
399
+ >
400
+ > | | `errorSteps` | ana akışta `condition Steps.<adımAdı>.error` |
401
+ > |---|---|---|
402
+ > | Ana akışın kalanı | **koşmaz** — hata akışı devralır | koşar, dalı sen seçersin |
403
+ > | Yükleniyor bayrağı · modal kapatma · tablo tazeleme | hata akışında **yeniden kurmak zorundasın**; kurmazsan bayrak açık kalır | zaten ana akışta duruyor |
404
+ > | Hata metni | `Steps.error.message` (yukarıdaki tablo) | `Steps.<adımAdı>.data.message`, hata olduğunu `Steps.<adımAdı>.error` söyler |
405
+ > | Ne zaman doğru | hata anında akış **tamamen başka bir yola** sapacaksa (başarısız giriş, yetkisiz sayfa) | kalan adımların çoğu hata hâlinde de koşacaksa — **form gönderimlerinin varsayılanı budur** (Kural 7) |
283
406
 
284
407
  Zorunlu filtresi olan uçlarda (`patient-*`, `doctor-appointments` gibi `isRequired: true`
285
408
  filtreli microservice'ler) parametreyi adımda ver, yoksa uç `MissingParameterException` atar:
@@ -319,11 +442,81 @@ Tarif: `platform_guide('middleware')`.
319
442
  - Adım hedef akışı **bekler** (`await engine.run`), sonra `always` dalından devam eder.
320
443
  - Gateway boş/yarım `args`'ı **FLOW-014** ile yakalar.
321
444
 
445
+ > **`code` adımının gövdesi `args.value` alanındadır — düz JS metni olarak.** Runtime onu
446
+ > `(async () => { <value> })()` içinde koşturur (`code.ts:18`), yani `await` serbesttir ve dönüş
447
+ > değeri akışa taşınmaz; etkiyi `Page`/`state`/komponent yazarak üretirsin. **Alan boşsa adım
448
+ > hata vermeden NO-OP olur** ve akış hiçbir şey yapmadan devam eder.
449
+ >
450
+ > ```jsonc
451
+ > { "stepId": "sync_filters", "name": "sync_filters", "type": "code", "kind": "normal",
452
+ > "args": { "value": "state.page.month = flt_month.value" },
453
+ > "branches": [{ "id": "<GUID>", "name": "always", "nextStepId": "reload" }] }
454
+ > ```
455
+ >
456
+ > `args.expression` / `args.script` gibi uydurma anahtarlar `StepManagementArgument` sözleşmesinde
457
+ > YOKTUR ve platform onları **sessizce düşürür**: 2026-09-05 koşusunda akış koştu, filtre hiçbir
458
+ > şey yapmadı ve teşhis ~10 dakika sürdü. Gateway artık ikisini birden yakalar — **FLOW-019**
459
+ > gövdesiz `code` adımını istek gitmeden reddeder, akış yazmasından sonra da gövdeyi geri okuyup
460
+ > farkı `silentlyDropped` ile bildirir.
461
+ >
462
+ > **Çoğu zaman `code` adımına hiç gerek yok:** filtre değerini sayfa değişkenine yazmak yerine
463
+ > değeri doğrudan komponent prop'undan okuyun (`value: { type: 'property', ref: { source:
464
+ > 'component', element: 'flt_month', property: 'value' } }`, ya da bir `code` ifadesinde
465
+ > `flt_month.value`) ve olayı doğrudan yükleme akışına bağlayın — ara adım kırılgan bir katman ekler.
466
+
467
+ > **Tek bir ifadeyi düzeltmek için tüm gövdeyi yeniden göndermeyin: `flow_patch` var.** Dizi
468
+ > öğelerinde indeks yerine seçici kullanın —
469
+ > `/steps[stepId=paint]/args/iteratees[element=tbl_funnel]/mapping/mappings/share/ref`. Adım ve
470
+ > iteratee sırası platformda değişebilir; indeksle yazılan patch yanlış öğeye dokunur.
471
+
472
+ > **`states` bir prop DEĞİL düğüm alanıdır** (`isWrapper`, `isScrollable`, `isSticky`,
473
+ > `isDroppable`, `isFloating`, `isQuickStack`): `replace_prop` onu yazamaz. Kurulmuş bir düğümde
474
+ > değiştirmek için `resize_component`'e `states` verin —
475
+ > `{ op: 'resize_component', target: 'row_kpi', states: { isWrapper: false } }`. Eskiden tek yol
476
+ > düğümü silip yeniden kurmaktı ve 09-05 koşusunda bunun bedeli 20 setter'ın kopmasıydı.
477
+ > **`isWrapper: true` olan her kapsayıcıya runtime `min-height: 80vh` veriyor** — dokuz düğüm
478
+ > sayfaya ~1600px ölü boşluk eklemişti; `style` ile ezilemez, gerekmiyorsa `false` bırakın.
479
+
322
480
  **Sözlükler** (`StepType.cs`, `StepParameterValueType.cs` — hepsi küçük harf):
323
481
  `start · microservice · setter · setcookie · validation · condition · redirect · alert ·
324
482
  confirm · flow-trigger · storage · navigate · code · download · end`
325
483
  değer tipleri: `static · page · property · storage · variable · code · item · state`
326
484
 
485
+ > **Sözlükte olmak KOŞACAĞI anlamına gelmez — koşan tipler barındırıcıdan gelir.** Motor yedi
486
+ > tip register eder, gerisini uygulama ekler. Bir sayfa TEK bir uygulamaya aittir
487
+ > (`Page.ApplicationId`) ve uygulamanın tipi `web` ya da `mobile`'dır (`ApplicationType.cs`), yani
488
+ > sayfanın barındırıcısı bellidir — matris "hangi tip sende çalışır"ı söyler, iki barındırıcıda
489
+ > birden koşmayı değil (web `Runtime.svelte`, mobil `ScreenRuntime.tsx`):
490
+ >
491
+ > | Adım tipi | Web | Mobil | Nerede register ediliyor |
492
+ > |---|---|---|---|
493
+ > | `start` · `setter` · `code` · `condition` · `microservice` · `validation` · `flow-trigger` | ✅ | ✅ | `flow-engine.ts` — motorun kendisi |
494
+ > | `alert` · `redirect` · `setcookie` · `storage` | ✅ | ✅ | uygulama; mobilde `redirect` navigate, `setcookie` storage uygulamasına eşlenir |
495
+ > | `confirm` · `download` | ✅ | ❌ | yalnız `Runtime.svelte` |
496
+ > | `navigate` | ❌ | ✅ | yalnız `ScreenRuntime.tsx` — web'deki karşılığı `redirect` |
497
+ > | `end` · `query` | ❌ | ❌ | hiçbir yerde — üstelik `query` backend sözlüğünde de yok (`StepType.cs` onu saymaz), yalnız frontend enum'unda duruyor |
498
+ >
499
+ > **Register edilmemiş bir tipe gelen akış SESSİZCE durur.** `flow-engine.ts` `process()`
500
+ > callback bulamazsa hiçbir şey yapmaz: `next()` yok, `completed`/`stopped` yok, hata yok, log
501
+ > yok. Akışın kalanı ölür — üstelik `run()`'ın promise'i hiç çözülmediği için
502
+ > `flowRunFinished()` koşmaz: `__ngAppsFlowActivity.running` sıfıra inmez ve
503
+ > `page_screenshot`'ın hazır bekleyişi her eylemde bütçesini sonuna kadar yakar.
504
+ >
505
+ > Gateway iki hâli **kendisi düzeltir** ve `gatewayNormalized` ile söyler: `navigate` → `redirect`
506
+ > (argüman şekli aynı, `redirect` iki barındırıcıda da koşar) ve ölü `end` adımı gövdeden düşer
507
+ > (ona işaret eden dal `nextStepId: null` olur). `FLOW-023` bunların arkasındaki emniyet kemeridir:
508
+ > `end`/`query` kalırsa **bloklar**, iki barındırıcıya birden özel tip taşıyan gövdeyi uyarır. Tek
509
+ > barındırıcıya özel bir tipi (`confirm`/`download`) tek başına kullanmak kusur değildir — gateway
510
+ > sayfanın hangi uygulamada olduğunu bilmez, doğru tipi bu matristen SEN seçersin.
511
+ >
512
+ > **`end` adımı bir tuzaktır.** Studio kataloğunda bir tip olarak duruyor
513
+ > (`event-actions.constants.ts` `FlowTaskType.End`; ekleme menüsü onu eler) ve eski akışlar bu
514
+ > düğümü taşıyabiliyor (`event-actions.mappers.ts`) — ama Studio'nun varsayılan şeması End
515
+ > düğümü YARATMAZ (`DEFAULT_SCHEMA` `endId`'yi üretir ve kullanmaz) ve motor onu register etmez. Düğümün VARLIĞI zararsızdır; zararlı olan **bir dalın oraya işaret
516
+ > etmesidir** — akış o adıma girer ve çıkamaz. Akış, son adımın dalına `nextStepId: null`
517
+ > verilerek bitirilir. İronik olan şu: var olmayan bir `stepId`'ye işaret etmek daha güvenlidir,
518
+ > çünkü motor hedefi bulamayınca `completed`/`stopped`'ı düzgün çağırır.
519
+
327
520
  > `code` adımının içinde ve `value.type:'code'` ifadelerinde kullanılabilen bağlam
328
521
  > (`Page.*`, komponentlere adıyla erişim, `state.page/app`, `Steps`, `self`/`item`/`event`,
329
522
  > `cookie.get/set`, `await`) ve `value.type` çözümleme tablosu için
@@ -355,6 +548,10 @@ Toplam kayıt sayısı `Total-Count` header'ından gelir (`TableMicroserviceExec
355
548
  `isSingle: false` olduğu sürece) — `totalRows`'u yukarıdaki guard'lı ifadeyle yaz. Gateway
356
549
  parametreleri geçmeyi unutursan `FLOW-013` ile uyarır.
357
550
 
551
+ Ucun gerçekten `Total-Count` döndüğünü sayfayı kurmadan **önce** doğrula:
552
+ `microservice_execute(...)` yanıtındaki `headers['total-count']` dolu olmalı; boşsa `sort`/`limit`
553
+ verilmemiş bir tablo kaynağı vardır ve sayfa göstergesi `NaN / N` olur (ölçüm: İK koşusu M-8/E-4).
554
+
358
555
  > **`replace_prop` Prop sarmalı DEĞİL ham değer alır** — `PageSchemaMicroOpApplier.cs:77`
359
556
  > `new Prop(PropValueType.Static) { Data = op.Value }` kurar. `{ "type": "static", "data": true }`
360
557
  > gönderirsen sarmalın kendisi `Data`'ya yazılır ve platform onu `Dictionary<string, Prop>` diye
@@ -394,8 +591,9 @@ Türkçe başlık `props.title` / `props.text` / `props.label` / `props.pageTitl
394
591
  > Aynı sebeple akış ifadelerinde komponente **`name` ile** erişilir (`tbl_mng.selectedRow`);
395
592
  > `elementId` ile eşit tutmazsan `element:` alanı çalışır ama ifadedeki değişken `undefined` kalır.
396
593
  >
397
- > Gateway bunu **PAGE-009** ile `add_component`, `page_update(schema)` ve `layout_update(views)`
398
- > yollarında istek gitmeden reddeder.
594
+ > Gateway bunu **PAGE-009** ile HER sayfa ve layout yazma yolunda istek gitmeden reddeder:
595
+ > `page_create`, `page_update(schema)`, `add_component`, `layout_create`, `layout_update(views)`
596
+ > ve `layout_patch`.
399
597
 
400
598
  ## Kural 1d — Sayfa iskeleti: `page_container` ve boyutlandırma
401
599
 
@@ -633,13 +831,18 @@ aynı prop grubunu o da alır. Grup katalogda tanımlıdır, yani şemadan kurul
633
831
 
634
832
  | Prop | Değerler (varsayılan **kalın**) | Ne yapar |
635
833
  |---|---|---|
636
- | `layout` | **`flex`** · `grid` | `flex`: akış düzeni. `grid`: 12 kolonluk ızgara (Kural 1f). |
834
+ | `layout` | **`flex`** · `grid` | `flex`: akış düzeni. `grid`: 12 kolonluk mutlak ızgara (Kural 1f). Grubu taşıyan kaplar: `container`/`page_container` · `form` (2026-09-01'den) · `box`/`aibox` · `layout` ve `widget-area` (2026-09-03) — hepsinde değer kümesi `flex·grid`; yalnız `box-section` tek değerlidir (`flex`). `box`/`aibox`'ın eski üçüncü değeri `cssgrid` (akan gerçek `display:grid`) 2026-09-02'de seçenek olmaktan çıktı: yeni yazımda `invalid-enum-value` verir, o değerle kayıtlı düğümler çizilmeye devam eder. |
637
835
  | `direction` | **`row`** · `column` | `flex-direction`; hangi hizalamanın ANA eksen olduğunu belirler. |
638
836
  | `horizontalAlign` | **`left`** · `center` · `right` · `stretch` | `row`'da ana eksen (`justify-content`), `column`'da çapraz eksen (`align-items`). |
639
837
  | `verticalAlign` | **`top`** · `middle` · `bottom` · `stretch` | `row`'da çapraz eksen, `column`'da ana eksen (rolleri `horizontalAlign` ile takas). |
640
838
  | `size` | **`shrink`** · `grow` | Çocuklara `flex: 0 1 auto` / `1 1 auto`. |
641
- | `gap` | `none` · `small` · **`medium`** · `large` | 0 / 8 / 16 / 32px. |
642
- | `wrap` | **kapalı** · açık | `flex-wrap`; kapalıyken taşan çocuklar sıkışır, alta inmez. |
839
+ | `gap` | `none` · `small` · **`medium`** · `large` **ya da ham CSS** | Preset'ler tema değişkenidir: 0 / `var(--ng-spacing-sm)` / `--ng-spacing-md` / `--ng-spacing-lg` (varsayılan temada 8/16/32px). Alan `allowCustom`'dır: `"18px"` ya da `"var(--ng-spacing-xl)"` gibi ham değer de geçer. `box-section`/`aibox`'taki sayısal `gapCustom` doluysa preset'i ezer. |
840
+ | `wrap` | kapalı · **açık** | `flex-wrap`. **Varsayılan 2026-09-01'de açık oldu:** `row` + `w:12` çocuklar artık alt satıra sararak dikey akar. Kapatırsan taşan çocuklar tek satıra sıkışır. |
841
+
842
+ **`form` da bu grubun tamamını taşır (2026-09-01):** varsayılanları container ile aynı
843
+ (`flex` · `row` · `left` · `top` · `shrink` · `medium` · `wrap` açık). "Form'a layout yazılmaz"
844
+ eski bilgidir. (Platform kataloğu güncel şemayı `component_get`'te göstermiyorsa kayıt
845
+ yenilenmemiştir — ARC-024.)
643
846
 
644
847
  `stretch` iki eksende farklı anlama gelir: **ana** eksende `space-between` (çocukları uçlara
645
848
  dağıtır), **çapraz** eksende gerçekten doldurur.
@@ -654,27 +857,37 @@ prop'una bakar (`isFlexLayout`). İki sonucu var:
654
857
  ızgara alırsın — Studio'dan sürüklenen kardeş kapsayıcı flex gelirken seninki grid kalır.
655
858
 
656
859
  Gateway'in kendi eklediği kök kapsayıcı (`page_create`'te şemada `page_container` yoksa) bu yüzden
657
- seti açıkça taşır: `flex` · `row` · `left` · `top` · `shrink` · `medium`. **`row` + `wrap` kapalı
658
- demek, alt alta düşünülmüş `w: 12` çocukların TEK SATIRA sıkışması demek** — dikey akan bir sayfa
659
- istiyorsan kapsayıcıya `direction: 'column'` yaz (ya da `layout: 'grid'`e dön).
860
+ seti açıkça taşır: `flex` · `row` · `left` · `top` · `shrink` · `medium` · `wrap` açık.
861
+ `row` + `wrap` açık + `w: 12` çocuklar alt satıra sararak dikey akar; net bir dikey akış
862
+ istiyorsan yine de `direction: 'column'` yazmak daha okunur (ya da `layout: 'grid'`e dön).
863
+ **`wrap`'i kapatırsan** alt alta düşünülmüş `w: 12` çocuklar TEK SATIRA sıkışır.
660
864
 
661
865
  **Ne zaman flex?** Yan yana buton/aksiyon grubu, toolbar, rozet dizisi, ortalanmış giriş kartı,
662
866
  "kalanı doldursun" yerleşimleri. Alt alta akan form alanları için ızgara da doğrudur — orada flex
663
867
  istiyorsan `direction: 'column'` şart, `row` ile alanlar tek satıra sıkışır.
664
868
 
665
- Dört şeyi bilmeden flex kurma:
666
-
667
- 1. **Genişlik yine 12 kolondur — içerik genişliği DEĞİL.** Çocuğun `blocks[].size.w`'si
668
- `row`'da `flex-basis: w/12`, `column`'da `width: w/12` olur (`gap` payı formülden düşülür,
669
- böylece `w` toplamı 12 olan satır tam kapanır). Bu bilinçli bir tercih: ızgara ↔ flex geçişi
670
- kayıpsızdır ve genişlik şemadan ifade edilebilir kalır. `column` + `horizontalAlign: 'stretch'`
671
- tek istisnadır — orada çocuk tam genişlik olur.
869
+ Beş şeyi bilmeden flex kurma:
870
+
871
+ 1. **Çocuğun genişliği `appearance.widthType`'tan gelir (2026-09-01); 12 kolon yalnız
872
+ FALLBACK'tir.** Dört cevap, öncelik sırasıyla:
873
+ - `widthType: 'fill'` — kabın tüm genişliği (satırda `flex:1 1 100%`, sütunda
874
+ `align-self:stretch`); string taşımaz, taşırsa bile ezer.
875
+ - `widthType: 'fixed'` + `widthString` — sabit ölçü, serbest CSS uzunluğu (`"240px"`,
876
+ `"50%"`, `"10vw"`, `calc(...)`). Geçersiz string sessizce düşer ve span'a dönülür.
877
+ - `widthType: 'auto'` — içerik kadar (`flex: 0 1 auto`).
878
+ - `widthType` YOK (eski kayıtlar) — `blocks[].size.w` payı: `row`'da `flex-basis: w/12`,
879
+ `column`'da `width: w/12` (`gap` payı düşülür; `column` + `horizontalAlign:'stretch'`
880
+ istisna — tam genişlik). Izgara ↔ flex geçişi bu fallback sayesinde kayıpsızdır.
881
+ Şemayı sen kuruyorsan çocuğa `widthType`'ı AÇIKÇA yaz. Studio'nun kendi bırakma kuralı:
882
+ kapsayıcı türü kaplar (`container`/`box`/`form`) `'fixed'` + `"50%"`, diğer her şey `'auto'`.
672
883
  2. **`position.y` artık yalnız SIRADIR.** Flex çocuklar `y`, sonra `x` sırasına göre dizilir;
673
884
  aradaki boşluklar çizilmez. Yine de bitişik ver (0,1,2…): kapsayıcı ileride `grid`'e
674
885
  döndürülürse şema geçerli kalsın. Gateway bu yüzden **flex kapsayıcının İÇİNDE PAGE-010
675
886
  boşluk uyarısı üretmez** (ızgarada üretmeye devam eder).
676
887
  3. **Flex kapsayıcının yüksekliği içeriğine oturur.** `verticalAlign`'ın dağıtacak boşluğu
677
- olması için `appearance.heightType: 'fixed'` + `size.h` vermelisin. **İstisna
888
+ olması için `appearance.heightType: 'fixed'` vermelisin — ölçü `heightString`'den
889
+ (serbest CSS uzunluğu, ör. `"30vh"`), o yoksa `size.h × 10px`'ten gelir. `heightString`
890
+ yalnız `heightType:'fixed'` iken okunur; tek başına yazmak hiçbir şey yapmaz. **İstisna
678
891
  `page_container`**: `states.isWrapper: true` olduğu için zaten `min-height: 80vh` alır
679
892
  (Kural 1f), dolayısıyla dikey hizalama onda kutu boyu vermeden de çalışır. Üçüncü yol
680
893
  `heightType: 'fill'` (2026-08-28): çocuk kabın bıraktığı yeri alır — sütunda `flex: 1 1 auto`,
@@ -684,6 +897,10 @@ Dört şeyi bilmeden flex kurma:
684
897
  4. **`layout: 'grid'` iken diğer altı prop ÖLÜDÜR.** Katalogda tanımlı oldukları için
685
898
  doğrulama duvarı susar, renderer da okumaz: ızgara kapsayıcıya `direction: 'column'` yazmak
686
899
  sessizce hiçbir şey yapmaz. Gateway bunu **PAGE-015 (uyarı)** ile söyler.
900
+ 5. **Flex düzeni kırılım BİLMEZ.** `direction`/`gap`/hizalama masaüstü ve mobilde AYNIDIR;
901
+ sıra ve span'lar Desktop (1024) bloğundandır — Studio, mobil kırılım düzenlenirken flex
902
+ bölgeyi salt-okunur kilitler. Kırılım bilen yalnız `blocks[]` ve `appearance.hideOn`'dur;
903
+ mobilde farklı davranış gereken satır için `layout: 'grid'` kullan (Kural 1f mobil notu).
687
904
 
688
905
  ```jsonc
689
906
  // Ortalanmış giriş kartı — style ile CSS hack'i YOK, kabın kendi düzeni
@@ -701,6 +918,33 @@ Dört şeyi bilmeden flex kurma:
701
918
  { "op": "replace_prop", "target": "row_actions", "prop": "gap", "value": "small" }
702
919
  ```
703
920
 
921
+ **Mini tarif — sıkı dizilim (küçük bileşenleri dip dibe getirmek).** Buton/rozet/ikon dizisi
922
+ gibi bitişik duracak elemanlar için ÜÇ şart birden gerekir; biri eksikse arada boşluk kalır:
923
+
924
+ 1. Her çocuğa **`appearance.widthType: 'auto'`** — içerik kadar yer kaplar (`flex: 0 1 auto`).
925
+ Yazılmazsa eski 12 kolon payı geçerlidir ve çocuk kolonunu doldurur, bitişemez.
926
+ 2. Kaba **`gap: 'none'`** (ya da ham ince değer: `"2px"`, `"4px"` — alan `allowCustom`).
927
+ 3. Her çocuğa **`appearance.marginType: 'none'`** — en çok atlanan şart: `marginType`
928
+ yazılmamış düğüm `normal` sayılır ve her kenara 4px basar (`spacing.ts`
929
+ `CANVAS_NORMAL_MARGIN`), yani `gap:'none'` olsa bile komşular arasında 8px kalır.
930
+
931
+ ```jsonc
932
+ // Bitişik rozet dizisi: kap + iki çocuktan biri (diğeri aynı desen)
933
+ { "elementId": "badge_row", "type": "container", "kind": "component",
934
+ "props": { "layout": { "type": "static", "data": "flex" },
935
+ "direction": { "type": "static", "data": "row" },
936
+ "gap": { "type": "static", "data": "none" } },
937
+ "children": [
938
+ { "elementId": "badge_1", "type": "button", "kind": "component",
939
+ "appearance": { "widthType": "auto", "marginType": "none", "heightType": "auto" },
940
+ "blocks": [{ "breakpoint": 1024, "size": { "w": 2, "h": 4 }, "position": { "x": 0, "y": 0 } }] }
941
+ ] }
942
+ ```
943
+
944
+ "Şu ikisi bitişik, şu uzak" gibi eşit olmayan aralıklar için `gap` yetmez (kapta tekildir) —
945
+ bitişik elemanları `gap:'none'` bir İÇ kapsayıcıda gruplayıp grupları dış kapta normal
946
+ `gap` ile diz.
947
+
704
948
  > Düzen işi `style` prop'una AİT DEĞİL. Kural 1h'deki `style` reçeteleri tema/görsellik içindir;
705
949
  > `display:flex` gibi düzen kurallarını oraya yazmak kabın kendi düzeniyle çakışır (PAGE-014'teki
706
950
  > ölçüm: string style ile kart 66px'e çöktü). Yerleşim bu prop grubuyla, tema `style` ile kurulur.
@@ -776,8 +1020,10 @@ UYGULAMAZ** — koordinatları aynen kaydeder (aşağı itme yalnız `page_creat
776
1020
  > - Ekranda sürekli `Expression error: Cannot read properties of null (reading '0')` toast'ı çıkıyor
777
1021
  > (iç repeater bir kez `item` olmadan da değerlendiriliyor).
778
1022
  >
779
- > **Gateway bunu `PAGE-013` ile istek gitmeden reddediyor** (`page_create` / `page_update(schema)` ve
780
- > `page_schema_patch`'te op'lar UYGULANDIKTAN SONRAKİ ağaca bakarak).
1023
+ > **Gateway bunu `PAGE-013` ile istek gitmeden reddediyor** — `page_create` / `page_update(schema)` /
1024
+ > `layout_create` / `layout_update(views)` / `layout_patch` gönderilen ağaca, `page_schema_patch` hem
1025
+ > eklenen alt ağaca hem op'lar UYGULANDIKTAN SONRAKİ ağaca bakarak. Layout'ta da geçerlidir: kırılma
1026
+ > `_repeaterValues` geri yazmasındadır, barındıranın sayfa mı layout mu olduğuna bakmaz.
781
1027
  >
782
1028
  > **Yapılacak:** satır içi listeyi tek bir `textarea`'da **satır başına bir öğe** olarak topla
783
1029
  > (`options_text`), kaydederken `split('\n')`'le böl. Ölçülmüş çalışan sürüm: anket editörü
@@ -805,6 +1051,14 @@ yapmanın yolu budur — ölçülmüş kurallar (anket-doldur, 2026-08-19):
805
1051
  > Bu kural TEMA/GÖRSELLİK içindir. Çocukların dizilimi (ortalama, yaslama, aralık, "kalanı
806
1052
  > doldur") `style` ile değil kapsayıcının kendi `layout` prop grubuyla kurulur — Kural 1f-2.
807
1053
 
1054
+ > **Stil anahtarları camelCase'dir: `minHeight`, `fontSize`, `letterSpacing`.** Kebab-case anahtar
1055
+ > (`min-height`) platform tarafından KABUL EDİLİR, `page_schema_get` ile aynen geri okunur ve
1056
+ > runtime onu HİÇ UYGULAMAZ; tek işaret tarayıcı konsolundaki tek satırdır ve `page_screenshot`
1057
+ > konsolu döndürmez. 2026-09-05 koşusunun en pahalı yanlış teşhisi buydu (~15 dk) ve yanlış teşhis
1058
+ > düğümleri silip yeniden kurmaya, o da 20 setter'ın sessizce kopmasına yol açtı. Gateway artık
1059
+ > kebab-case anahtarı camelCase'e çevirir ve `gatewayNormalized` ile bildirir; seçici anahtarlara
1060
+ > (`&__body`, `:hover`, `--ng-*`) dokunmaz.
1061
+
808
1062
  **1. Temayı tek yerden çevir: `page_container.style` içinde `--ng-*` değişkenlerini ez.**
809
1063
  Runtime bütün renk/tipografi/form tokenlarını CSS değişkeni olarak basar
810
1064
  (`Theme.svelte`: `--ng-accent-primary`, `--ng-accent-hover`, `--ng-background-surface`,
@@ -991,7 +1245,7 @@ budur — bir preset ya da `componentStyles` sizin değerinizi eziyorsa `styleLa
991
1245
  | Değişiklik | Tool | Neden |
992
1246
  |---|---|---|
993
1247
  | Komponent ekle/sil/taşı, prop, binding, event | `page_schema_patch` | Doğrulama duvarı + açık Studio canvas'ına SignalR canlı yayını + eşzamanlı yazar kilidi yalnız bu yolda çalışır |
994
- | Ad, slug, browserTitle, layout, roller, middleware | `page_update` | Üstbilgi alanları micro-op sözlüğünde yok |
1248
+ | Ad, slug, browserTitle, `browserTitleKey`, layout, roller, middleware | `page_update` | Üstbilgi alanları micro-op sözlüğünde yok. `browserTitleKey` sekme başlığını çeviri anahtarına bağlar: verilirse başlık sözlükten çözülür, boşsa `browserTitle` kullanılır. Başlık sayfa yüklenirken çözülür ve canlı dil değişimini izlemez (`platform_guide('translation')`) |
995
1249
 
996
1250
  Platformun `PUT /pages/...` ucu **replace**'tir: gönderilmeyen `schema`/`events`/`roleIds`
997
1251
  alanları silinir. Gateway bu yüzden `page_update`'te sayfayı önce okur, yalnız verdiğin
@@ -1018,6 +1272,17 @@ Yanıt üç alan döner: `applied` (uygulanan op sayısı), **`appliedPatch`** (
1018
1272
  olarak hangi JSON Pointer'a ne yaptığı — RFC 6902 biçiminde; "gerçekten yalnız o komponente mi
1019
1273
  dokunuldu" sorusunun kanıtı) ve **`newVersion`** (`UpdatedAt.Ticks`).
1020
1274
 
1275
+ > **`appliedPatch` gövdesi VARSAYILAN OLARAK iskelettir.** Girdi `{ op, path }` döner; eklenen
1276
+ > düğüm `component` altında yalnız `id`/`elementId`/`name`/`type`/`children` taşır. Kanıt zaten
1277
+ > path'tedir — `value` senin aynı turda gönderdiğin gövdenin yankısıdır ve tek bir `set_event`
1278
+ > yaması bile düğümün bütün prop'larını geri getiriyordu. Aynı kural `page_create` yanıtındaki
1279
+ > `schema` için de geçerlidir (çözülmüş layout düğümleri dâhil). Tam gövde: `view: 'full'`.
1280
+ >
1281
+ > **"Kaydedildi mi" sorusunu `appliedPatch` zaten cevaplamıyor** (aşağıdaki `silentlyDropped`
1282
+ > bölümü): yama 200 döner ve pointer'ı gösterir, ama alanın kayda geçtiğini söylemez. Onu gateway
1283
+ > yamadan sonra şemayı okuyup gönderdiğinle karşılaştırarak söyler. Kırpma bu yüzden kanıt kaybı
1284
+ > değildir; `view: 'full'`e yalnız gerçekten platformun ham gövdesini incelerken düş.
1285
+
1021
1286
  **Sürüm kontrolü platformda var ama gateway bugün KULLANMIYOR.** Platform gövdede `baseVersion`
1022
1287
  (long tick) kabul eder ve tutmazsa 409 verir; `page_state.baseVersion` bu değeri döner.
1023
1288
  `page_schema_patch` bu alanı geçirmez — bayat plan koruması gateway tarafında
@@ -1204,9 +1469,21 @@ altın örneklerde** — şemayı ve akışları oradan kopyalayıp uyarla:
1204
1469
  | Detay / profil | `ngapps://examples/page-detail-profile` |
1205
1470
  | Dinamik form (alanlar veriden) | `ngapps://examples/page-dynamic-form` (Kural 1g + 1h) |
1206
1471
 
1207
- > Gateway iki yarım kurulumu yakalar: **FORM-001** (`form` eklendi, `submit` bağlanmadı) ve
1472
+ > Gateway üç yarım kurulumu yakalar: **FORM-001** (`form` eklendi, `submit` bağlanmadı),
1208
1473
  > **FORM-002** (akışta POST/PUT adımı var ama hiç `source:'body'` parametresi yok → gövde boş
1209
- > gider). `project_overview` ayrıca proje düzeyi boşlukları döndürür (`coverageGaps`): kimlik
1474
+ > gider) ve **FORM-003** (formun `submit`'i bağlı ama içindeki butonun `click`'i bağlı değil).
1475
+ >
1476
+ > **FORM-003 neden var:** `button` varsayılan olarak `type="button"` çizilir; form içinde bile
1477
+ > HTML form submit'i HİÇ doğmaz — butona tıklamak da alanda Enter da uca istek göndermez ve
1478
+ > **hiçbir hata çıkmaz**. `ngapps://examples/page-sign-in`'i harfiyen izleyen bir giriş sayfası bu
1479
+ > yüzden tıklanamaz kalıyordu (ölçüldü 2026-09-05); teşhis yalnız gerçek tıklamayla mümkün oldu
1480
+ > (`page_screenshot` `actions`). İki çözümden biri yeter:
1481
+ >
1482
+ > 1. Butona **`props.type = 'submit'`** ver — katalogdaki `type` prop'u bu değeri destekliyor ve
1483
+ > idiomatik yol budur.
1484
+ > 2. Ya da AYNI akışı butonun `click` olayına da bağla.
1485
+ >
1486
+ > `submit` olayını forma bağlamak tek başına yetmez; gateway ikisi de yoksa uyarır. `project_overview` ayrıca proje düzeyi boşlukları döndürür (`coverageGaps`): kimlik
1210
1487
  > middleware'i var ama giriş sayfası yok, yazma ucu var ama sayfa yok.
1211
1488
 
1212
1489
  **Adlandırma:** çoğul = liste sayfası, tekil = form sayfası (`Departmanlar` / `Departman`).
@@ -1235,6 +1512,17 @@ maxLength/pattern — sözlük `component_get`'te) orada listelenir.
1235
1512
  ] }
1236
1513
  ```
1237
1514
 
1515
+ **Mesajı çeviriye bağlamak: `t:` öneki.** `message` düz bir metindir ve translation prop'u ALMAZ;
1516
+ çeviri anahtarı mesajın kendisine önek olarak yazılır:
1517
+
1518
+ ```jsonc
1519
+ { "type": "required", "message": "t:form.emailRequired", "value": true, "isActive": true }
1520
+ ```
1521
+
1522
+ Runtime `t:` ile başlayan mesajı anahtar sayar ve normal fallback zincirinden çözer (aktif dil →
1523
+ varsayılan dil → anahtarın kendisi); önek yoksa metin olduğu gibi gösterilir. Çözüm çalışma
1524
+ anında olduğu için şema varsayılanlarına dokunmak gerekmez.
1525
+
1238
1526
  Geçerli `type` değerleri: `required` · `email` · `regex` · `max-length` · `min-length` ·
1239
1527
  `max-value` · `min-value` · `value-options`. Üç tuzak ölçüldü (crm koşumu B-9):
1240
1528
 
@@ -1268,18 +1556,29 @@ start → [setter] page_container.isLoading = true
1268
1556
  └─ else → [setter] CREATE etiketleri
1269
1557
  ```
1270
1558
 
1271
- > **Condition adımı `always` dalını kural dallarıyla BİRLİKTE taşır.** `always` her hâlükârda koşar
1272
- > (yükleniyor bayrağını kapatmanın yolu budur), kural dalları (`branch.name` = **rule id**) eşleşene
1273
- > göre. `rules`: `{ id, expression, type:'if' }` + `{ id, expression:'', type:'else' }`.
1559
+ > **Condition adımı `always` dalını kural dallarıyla BİRLİKTE taşır** — ama `always` her hâlükârda
1560
+ > KOŞMAZ. Motor onu yalnız **eşleşen dalın** bitişinden çağırır:
1561
+ >
1562
+ > ```ts
1563
+ > // condition.ts — always'in TEK çıkışı eşleşen dalın callback'idir
1564
+ > engine.next(rule.id, { ...definition, completed: () => engine.next('always', definition) })
1565
+ > ```
1566
+ >
1567
+ > Yani şu üç hâlde `always` **hiç** koşmaz: `rules` boş · `rules[0].type !== 'if'` · hiçbir ifade
1568
+ > tutmuyor ve boş ifadeli bir `else` yok. **Yükleniyor bayrağını `always` ile kapatıyorsan
1569
+ > zinciri boş ifadeli `else` ile kapatmak ZORUNDASIN** — yukarıdaki iskelette `else` dalı tam da
1570
+ > bu yüzden var. `else`'siz bir zincirde ifade tutmazsa bayrak açık kalır ve sayfa sonsuza kadar
1571
+ > yükleniyor görünür. Gateway `FLOW-020` bunu uyarıyla söyler.
1274
1572
  >
1275
- > **`rules[].id` GERÇEK bir GUID olmalı** — okunabilir bir ad (`"rule_ok"`) verirsen istek
1276
- > `The JSON value could not be converted to System.Guid. Path: $.flow.steps[N].args.rules[0].id`
1277
- > ile reddedilir. Dal adı da o GUID'in kendisidir (`branch.name === rule.id`), yani iki yerde de
1278
- > okunabilir ad kullanamazsın.
1573
+ > Sözleşmenin tamamı (GUID id zorunluluğu, dal adı = rule id, `else` fallback kuralı, zincir
1574
+ > sırası) aşağıdaki tuzak 6'da.
1279
1575
  >
1280
- > **Kural ifadesi patlarsa akış hata vermez, kuralı EŞLEŞMEMİŞ sayar** ve sessizce `else`'e
1281
- > gider. Bu yüzden condition ifadeleri guard'lı yazılır — özellikle `Page.user` okuyanlar
1282
- > (Kural 8).
1576
+ > **Kural ifadesi patlarsa kural EŞLEŞMEMİŞ sayılır** — `runCode` `undefined` döner,
1577
+ > `Boolean(undefined)` false'tur — ve zincir bir sonraki kurala, oradan `else`'e geçer. Ama bu
1578
+ > tamamen sessiz değildir: hata işaretlenir ve sıradaki `engine.next` onu tüketip **hata yolu
1579
+ > varsa akışı oraya saptırır** (`flow-engine.ts` `consumeExpressionError`), yani beklediğin dal
1580
+ > yerine `errorSteps` koşar. Bu yüzden condition ifadeleri guard'lı yazılır — özellikle
1581
+ > `Page.user` okuyanlar (Kural 8).
1283
1582
 
1284
1583
  **İki ayrı ucun sonucunu tek tabloya yazmak:** `mapping` tek kaynak alır, o yüzden birleştirmeyi
1285
1584
  `value.type: 'code'` ile yap — ilgisiz kayıtları da orada süz:
@@ -1337,20 +1636,24 @@ start → [setter] btn_save.isLoading = true
1337
1636
 
1338
1637
  - **Upsert deseni:** tek POST ucu hem create hem update yapar — `id` query-string'den, alanlar
1339
1638
  `source:'body'`'den. Entity başına iki uç yazmaya gerek yok.
1340
- - **Hata yolunu `errorSteps` ile kurma**, `condition Steps.<adımAdı>.error` ile kur: `errorSteps`
1341
- ana akışın kalanını hiç çalıştırmaz, yükleniyor bayrağı açık kalır.
1639
+ - **Form akışında hata yolunu `condition Steps.<adımAdı>.error` ile kur**, `errorSteps` ile
1640
+ değil: `errorSteps` ana akışın kalanını devralır, yani yükleniyor bayrağını kapatma · modal'ı
1641
+ kapatma · tabloyu tazeleme adımlarını hata akışında yeniden kurman gerekir — kurmazsan bayrak
1642
+ açık kalır. İki yolun karşılaştırması ve hata metninin nerede durduğu Kural 1c'nin §hata yolu
1643
+ bloğunda.
1342
1644
  - Geri bildirim `notification` komponentiyle verilir (`kind` · `header` · `content` · `timeout` ·
1343
1645
  `showCloseButton` · `style`), `alert` adımıyla değil.
1344
1646
 
1345
1647
  > **`isHidden`: ŞEMAYA yazmak ile SETTER ile yazmak AYRI yollardır.** Runtime tarafı ortak:
1346
1648
  > `RenderComponent` her düğümü `{#if !props.isHidden}` ile çizer, yani akıştaki setter
1347
- > `isHidden` yazınca komponent katalogda o prop'u tanımlamasa bile gizlenir. **Şema yolu ise
1348
- > katalogla sınırlıdır:** `props.isHidden`'ı düğüme yazmak, komponent onu tanımlamıyorsa
1349
- > `unknown-prop` ile REDDEDİLİR (81 kaydın 36'sı tanımlar; ölçüm 2026-08-25). Tanımlamayanlar
1350
- > arasında sık kullanılanlar: **`text` · `table` · `datatable` · `dropdown` · `multi-select` ·
1351
- > `tag-input` · `tabs` · `checkbox` · `switch` · `switcher` · `card` · `modal` · `drawer` ·
1352
- > `repeater` · `notification` · `toast` · `datepicker` ailesi · `breadcrumb-title`.**
1353
- > Tek doğru kaynak `component_get` + `page_schema_validate` kuru koşusudur.
1649
+ > `isHidden` yazınca komponent katalogda o prop'u tanımlamasa bile gizlenir. **Şema yolu
1650
+ > katalogla sınırlıdır** ama artık neredeyse her komponent tanımlıyor: **101 kaydın 99'u**
1651
+ > (ölçüm 2026-09-08; 2026-08-26'daki toplu ekleme öncesi 81'de 37 idi). Tanımlamayan yalnız
1652
+ > iki kap var: **`slot` · `widget-area`** — onların içindekini gizlemek için sarmalayıcı
1653
+ > kullan. Eskiden tanımlamayan `text` · `table` · `datatable` · `dropdown` · `multi-select` ·
1654
+ > `tag-input` · `tabs` · `notification` · `toast` · `datepicker` ailesi gibi komponentlere
1655
+ > artık doğrudan `props.isHidden` yazılabilir; sırf gizlemek için `container`'a sarmak
1656
+ > GEREKMEZ. Tek doğru kaynak `component_get` + `page_schema_validate` kuru koşusudur.
1354
1657
  >
1355
1658
  > **`appearance.isHidden` bir kaçış yolu DEĞİLDİR:** kaydedilir ama runtime hiçbir yerde okumaz
1356
1659
  > (renderer yalnız `props.isHidden`'a bakar) — komponent görünmeye devam eder. Başlangıçta gizli
@@ -1540,6 +1843,21 @@ hiçbiri** bağlı değildi; üç portalın da rol menüsü boş çıkıyor, pan
1540
1843
  > `Host: <org>.<suffix>` + `X-Environment` başlıklarıyla. Ham `curl` yerine tool'u kullanmak
1541
1844
  > yeterlidir.
1542
1845
  >
1846
+ > **Yerleşimi görüntüsüz ölçmek.** Taşma/çakışma şüphesinde `page_screenshot(layoutReport: true)`:
1847
+ > `layoutSummary.overflowing` boş değilse içeriğine sığmayan kutu, `outsideViewport` boş değilse
1848
+ > hiç görünmeyen düğüm var; `zeroSized` çizilmemiş komponenti gösterir. Runtime düğümleri
1849
+ > `data-element-id` basmıyorsa yanıt `layoutReportUnavailable` der — tool tahmin üretmez.
1850
+ >
1851
+ > **Üye kimliğiyle doğrulama (`asUser`).** Kapının kapalı olduğunu görmek yarısıdır; açık
1852
+ > olduğunu görmek diğer yarısı. `page_runtime_get` ve `page_screenshot` aynı `asUser` alanını
1853
+ > alır: uygulamanın kendi giriş ucundan (`microservice_execute`) aldığın üye jetonunu verirsin,
1854
+ > gateway o jetonu **hem `Authorization` başlığında hem `NG_AccessToken` çerezinde** taşır.
1855
+ > Çerez okuyan bir middleware ile başlık okuyan bir middleware aynı çağrıdan geçer — iki tool da
1856
+ > aynı kimlik kanalını kullanır. `page_screenshot` ayrıca `cookies: {ad: değer}` ile uygulamanın
1857
+ > kendi oturum çerezlerini enjekte eder. `asUser`/`cookies` ile `anonymous` birlikte verilemez;
1858
+ > gateway'in servis jetonu (varsayılan kimlik) uygulamanın `users` tablosuna çözülmeyebilir —
1859
+ > korumalı sayfada boş panel görüyorsan önce `identity` alanına bak, sonra `asUser` ile tekrarla.
1860
+ >
1543
1861
  > `page.roleIds` bu dalla birlikte de kapı değildir: rol ayrımını sayfa değil **uç** yapar.
1544
1862
 
1545
1863
  > **Rol okurken dizi bekleme.** `set-user` DTO'su `roles` dizisini çoğu kurulumda düşürür
@@ -1603,6 +1921,24 @@ micro-op hattına dönün.
1603
1921
  > setter'ı sessizce hiçbir şey yazmadı — sayfa boş liste ile açıldı, konsolda hata yok.
1604
1922
  > `flow_update` ile aynı gövdeyi yeniden kaydetmek düzeltti. Kural: **şemadan bir düğüm sildiysen,
1605
1923
  > o düğümü hedefleyen her flow'u yeniden kaydet.**
1924
+ >
1925
+ > **Akış silen yama artık istek gönderilmeden REDDEDİLİR.** Platform referanssız kalan akışı
1926
+ > KALICI siler; uyarı yamadan sonra geldiğinde "önce yedekle" tavsiyesi uygulanamıyordu (09-05
1927
+ > koşusunda sekiz adımlı giriş akışı sıfırdan yeniden yazıldı). Aynı kontrol `set_event` ve
1928
+ > `remove_event` için de çalışır — bir olayın bağını değiştirmek de akış öldürür ve o yolda hiç
1929
+ > uyarı yoktu. Silinmesini bilerek istiyorsanız çağrıyı `allowFlowDeletion: true` ile tekrarlayın;
1930
+ > yanıt `deletedFlows` ile hangi akışların gittiğini listeler.
1931
+ >
1932
+ > **FLOW-018 artık silinen düğümün ALT AĞACINI da görüyor.** Eskiden yalnız op'un kendi
1933
+ > `target`'ına bakılıyordu: bir kapsayıcı silinince ÇOCUĞUNA yazan setter sessizce kopuyor ve
1934
+ > uyarı hiç çıkmıyordu (09-05, `sign_in_form` → `btn_sign_in`).
1935
+ >
1936
+ > **Gateway artık bunu söylüyor (FLOW-018).** `remove_component` içeren bir yamada gateway sayfanın
1937
+ > akışlarını okur ve silinen düğümü hedefleyen setter adımlarını uyarı olarak listeler: hangi akış,
1938
+ > hangi adım, hangi hedef. Uyarıyı gördüğünde yapılacak tek şey `flow_update` ile aynı gövdeyi
1939
+ > yeniden kaydetmektir. Ters yönü (`silinen düğümün referans verdiği` akışların platformca kalıcı
1940
+ > silinmesi) ayrı bir uyarı kapsar; ikisi birlikte çıkabilir. Ölçüm 2026-09-04 (benchmark 4. koşu):
1941
+ > uyarı yokken aynı tuzak ~8 dakika teşhis aldı ve yalnız ekran görüntüsüyle görüldü.
1606
1942
 
1607
1943
  ## Bilinen sınırlar
1608
1944
 
@@ -1667,10 +2003,11 @@ micro-op hattına dönün.
1667
2003
  `isHidden` prop'u yok (Kural 1g'deki sarmalayıcı çözümü). Bir prop'u kullanmadan önce
1668
2004
  `component_get`'e bak; eksikse ürün sahibine katalog güncellemesi olarak bildir.
1669
2005
 
1670
- - **`container.title` `states.isWrapper: true` iken başlık ÇİZMEZ.** Wrapper kaplar salt düzen
2006
+ - **`container.title` varsayılanı boştur ve `states.isWrapper: true` iken başlık ÇİZMEZ.** Başlıksız
2007
+ normal container da `__header` bloğu üretmez; bant istiyorsan `title` değerini açıkça yaz.
2008
+ Wrapper kaplar salt düzen
1671
2009
  elemanıdır; başlığı da `__header` bloğunu da render etmez (klinik ölçümü: `__header` sayısı 0).
1672
- `isWrapper: false` olan kapta başlık normal çizilir (İK ölçümü: başlık verilmeyen kapta
1673
- varsayılan "Container title" göründü). Kart başlığını garantiye almak istiyorsan kabın
2010
+ `isWrapper: false` olan ve başlığı açıkça verilen kapta başlık normal çizilir. Kart başlığını garantiye almak istiyorsan kabın
1674
2011
  `title`'ına güvenme, **içine bir `text` komponenti** koy.
1675
2012
  - **Kod prop'unun ifadesi `ref`'te ve `{{ }}` sarılı olmalı.** `{type:'code', ref:"'' + Row.value"}`
1676
2013
  (süssüz) ekrana ifadenin **kendisini** basar; ifade yalnız `data`'da olursa prop **boş**