@appaflytech/zeyrek-ai-mcp 0.0.36 → 0.0.40

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 (210) hide show
  1. package/.env.example +15 -0
  2. package/README.md +5 -5
  3. package/dist/config.js +37 -0
  4. package/dist/index.js +2 -0
  5. package/dist/knowledge/access.js +802 -0
  6. package/dist/knowledge/backendConstraints.js +18 -1
  7. package/dist/knowledge/case.js +41 -0
  8. package/dist/knowledge/componentCatalog.js +524 -53
  9. package/dist/knowledge/componentEvents.js +132 -7
  10. package/dist/knowledge/connectDataBind.js +851 -0
  11. package/dist/knowledge/connectDataDefinition.js +344 -0
  12. package/dist/knowledge/connectDataExecute.js +93 -0
  13. package/dist/knowledge/connectDataRules.js +128 -0
  14. package/dist/knowledge/decision.js +121 -0
  15. package/dist/knowledge/expressionPrelude.js +60 -0
  16. package/dist/knowledge/layoutSummary.js +140 -2
  17. package/dist/knowledge/microserviceParameters.js +58 -0
  18. package/dist/knowledge/mining.js +94 -0
  19. package/dist/knowledge/mobileComponentCatalog.js +876 -0
  20. package/dist/knowledge/mobileComponentEvents.js +259 -0
  21. package/dist/knowledge/mobileStore.js +396 -0
  22. package/dist/knowledge/normalize.js +240 -72
  23. package/dist/knowledge/outlines.js +36 -0
  24. package/dist/knowledge/platform/lint.js +385 -0
  25. package/dist/knowledge/platform/mobile.js +142 -0
  26. package/dist/knowledge/platform/mobileLayoutLint.js +324 -0
  27. package/dist/knowledge/platform/mobileRules.js +231 -0
  28. package/dist/knowledge/platform/mobileSpacing.js +82 -0
  29. package/dist/knowledge/platform/mobileTabs.js +108 -0
  30. package/dist/knowledge/platform/nativeBoxModel.js +44 -0
  31. package/dist/knowledge/platform/profile.js +25 -0
  32. package/dist/knowledge/platform/resolve.js +122 -0
  33. package/dist/knowledge/platform/web.js +39 -0
  34. package/dist/knowledge/policy.js +18 -4
  35. package/dist/knowledge/process.js +510 -0
  36. package/dist/knowledge/reservedTables.js +79 -0
  37. package/dist/knowledge/rowFilter.js +43 -0
  38. package/dist/knowledge/rules.js +485 -22
  39. package/dist/knowledge/schemaGuard.js +11 -0
  40. package/dist/knowledge/styleResolve.js +64 -10
  41. package/dist/knowledge/styleResolveNative.js +529 -0
  42. package/dist/knowledge/themeResolve.js +3 -3
  43. package/dist/observability/trace.js +13 -5
  44. package/dist/platform/argumentAliases.js +128 -0
  45. package/dist/platform/bodyFile.js +103 -0
  46. package/dist/platform/capture/actions.js +398 -0
  47. package/dist/platform/capture/browser.js +1 -0
  48. package/dist/platform/capture/capture.js +683 -0
  49. package/dist/platform/capture/console-log.js +122 -0
  50. package/dist/platform/capture/index.js +11 -0
  51. package/dist/platform/capture/readiness.js +103 -0
  52. package/dist/platform/capture/summary.js +77 -0
  53. package/dist/platform/capture/target.js +5 -0
  54. package/dist/platform/capture/targets/studio-mobile.js +15 -0
  55. package/dist/platform/capture/targets/studio-web.js +5 -0
  56. package/dist/platform/diagnostics.js +10 -4
  57. package/dist/platform/domSummary.js +1 -1
  58. package/dist/platform/errors.js +163 -10
  59. package/dist/platform/http.js +29 -8
  60. package/dist/platform/impersonation.js +45 -0
  61. package/dist/platform/jsonArray.js +24 -0
  62. package/dist/platform/responses.js +39 -5
  63. package/dist/platform/schemaReadMemory.js +60 -0
  64. package/dist/platform/secrets.js +68 -2
  65. package/dist/platform/strictArguments.js +68 -12
  66. package/dist/platform/tokenVault.js +187 -4
  67. package/dist/resources/examples/bpm-gorev-kontrol.json +294 -0
  68. package/dist/resources/examples/bpm-iso-dokuman.json +228 -0
  69. package/dist/resources/examples/bpm-satinalma.json +376 -0
  70. package/dist/resources/examples/connect-data-convert-verify.json +107 -0
  71. package/dist/resources/examples/connect-data-dropdown-roles.json +66 -0
  72. package/dist/resources/examples/connect-data-edit-m2m.json +73 -0
  73. package/dist/resources/examples/connect-data-query-params.json +50 -0
  74. package/dist/resources/examples/connect-data-table-entity.json +147 -0
  75. package/dist/resources/examples/connect-data-workflow-microservice.json +65 -0
  76. package/dist/resources/examples/middleware-authenticate.json +1 -1
  77. package/dist/resources/examples/mobile-app-shell-drawer.json +244 -0
  78. package/dist/resources/examples/mobile-app-shell-stack.json +157 -0
  79. package/dist/resources/examples/mobile-app-shell-tab.json +208 -0
  80. package/dist/resources/examples/mobile-form-screen.json +365 -0
  81. package/dist/resources/examples/mobile-list-detail-form.json +703 -0
  82. package/dist/resources/examples/page-detail-profile.json +2 -1
  83. package/dist/resources/examples/page-dynamic-form.json +3 -2
  84. package/dist/resources/examples/page-flex-sections.json +1 -1
  85. package/dist/resources/examples/page-html-embed.json +1 -1
  86. package/dist/resources/examples/workflow-basic.json +8 -2
  87. package/dist/resources/guides/access.md +810 -0
  88. package/dist/resources/guides/bpm.md +691 -0
  89. package/dist/resources/guides/case.md +153 -0
  90. package/dist/resources/guides/component.md +92 -5
  91. package/dist/resources/guides/decision.md +185 -0
  92. package/dist/resources/guides/entity.md +50 -8
  93. package/dist/resources/guides/expressions.md +14 -5
  94. package/dist/resources/guides/functions.md +1 -1
  95. package/dist/resources/guides/getting-started.md +38 -1
  96. package/dist/resources/guides/layout.md +11 -7
  97. package/dist/resources/guides/merge.md +42 -7
  98. package/dist/resources/guides/microservice.md +88 -3
  99. package/dist/resources/guides/middleware.md +69 -5
  100. package/dist/resources/guides/mobile-layout.md +193 -0
  101. package/dist/resources/guides/mobile-publish.md +148 -0
  102. package/dist/resources/guides/mobile.md +565 -0
  103. package/dist/resources/guides/page.md +469 -82
  104. package/dist/resources/guides/theme.md +8 -2
  105. package/dist/resources/guides/translation.md +1 -1
  106. package/dist/resources/guides/workflow.md +58 -1
  107. package/dist/resources/read.js +41 -0
  108. package/dist/resources/rules.js +11 -5
  109. package/dist/resources/schema-fingerprint.json +7 -5
  110. package/dist/resources/schemas/AccessExplanationDto.json +144 -0
  111. package/dist/resources/schemas/AccessMatrixDto.json +170 -0
  112. package/dist/resources/schemas/AccessPermissionDto.json +57 -0
  113. package/dist/resources/schemas/AccessPolicyDto.json +223 -0
  114. package/dist/resources/schemas/AccessResourceDto.json +86 -0
  115. package/dist/resources/schemas/AppSessionDto.json +53 -0
  116. package/dist/resources/schemas/ConnectDataDescribeDto.json +344 -0
  117. package/dist/resources/schemas/ConnectDataRequestModel.json +202 -0
  118. package/dist/resources/schemas/ConnectDataResultDto.json +421 -0
  119. package/dist/resources/schemas/ConnectorCredentialDto.json +70 -0
  120. package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +37 -0
  121. package/dist/resources/schemas/CreateAccessPolicyCommand.json +209 -0
  122. package/dist/resources/schemas/CreateDecisionCommand.json +221 -0
  123. package/dist/resources/schemas/CreateEntityCommand.json +118 -0
  124. package/dist/resources/schemas/CreateMicroserviceCommand.json +13 -1
  125. package/dist/resources/schemas/CreateProcessDefinitionCommand.json +1337 -0
  126. package/dist/resources/schemas/CreateQueryCommand.json +4 -0
  127. package/dist/resources/schemas/CreateWorkflowCommand.json +146 -1
  128. package/dist/resources/schemas/DataJobDto.json +6 -1
  129. package/dist/resources/schemas/DecisionDto.json +302 -0
  130. package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +28 -0
  131. package/dist/resources/schemas/ExplainAccessQuery.json +33 -0
  132. package/dist/resources/schemas/GenerateProcessDocumentCommand.json +116 -0
  133. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +24 -0
  134. package/dist/resources/schemas/MicroserviceDto.json +15 -3
  135. package/dist/resources/schemas/OrganizationProjectDto.json +343 -0
  136. package/dist/resources/schemas/OrganizationSchemaDto.json +196 -0
  137. package/dist/resources/schemas/OrganizationSchemaVersionDto.json +207 -0
  138. package/dist/resources/schemas/PatchProcessDefinitionCommand.json +76 -0
  139. package/dist/resources/schemas/PreviewMicroserviceCommand.json +10 -1
  140. package/dist/resources/schemas/PreviewWorkflowCommand.json +25 -1
  141. package/dist/resources/schemas/ProcessDefinitionDto.json +1480 -0
  142. package/dist/resources/schemas/ProcessSimulationDto.json +96 -0
  143. package/dist/resources/schemas/SaveConnectorCredentialCommand.json +90 -0
  144. package/dist/resources/schemas/SaveProcessEventSourceCommand.json +61 -0
  145. package/dist/resources/schemas/SaveProcessMailSourceCommand.json +89 -0
  146. package/dist/resources/schemas/SignProcessTaskCommand.json +73 -0
  147. package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +92 -0
  148. package/dist/resources/schemas/StartMobileBuildCommand.json +54 -0
  149. package/dist/resources/schemas/StepDto.json +25 -1
  150. package/dist/resources/schemas/UpdateAccessPolicyCommand.json +209 -0
  151. package/dist/resources/schemas/UpdateDecisionCommand.json +216 -0
  152. package/dist/resources/schemas/UpdateEntityCommand.json +118 -0
  153. package/dist/resources/schemas/UpdateMicroserviceCommand.json +13 -1
  154. package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +1357 -0
  155. package/dist/resources/schemas/UpdateQueryCommand.json +4 -0
  156. package/dist/resources/schemas/UpdateViewCommand.json +0 -2
  157. package/dist/resources/schemas/UpdateWorkflowCommand.json +146 -1
  158. package/dist/resources/schemas/UpsertMobileStoreSettingsCommand.json +333 -0
  159. package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +1339 -0
  160. package/dist/server.js +31 -1
  161. package/dist/tools/access.js +369 -2
  162. package/dist/tools/accessOperations.js +135 -0
  163. package/dist/tools/accessPolicies.js +170 -0
  164. package/dist/tools/accessRuntime.js +203 -0
  165. package/dist/tools/applications.js +63 -3
  166. package/dist/tools/bpm.js +343 -0
  167. package/dist/tools/bpmGovernance.js +266 -0
  168. package/dist/tools/bpmOperations.js +230 -0
  169. package/dist/tools/bpmReporting.js +154 -0
  170. package/dist/tools/bpmRouting.js +150 -0
  171. package/dist/tools/bpmRuntime.js +396 -0
  172. package/dist/tools/case.js +178 -0
  173. package/dist/tools/caseRuntime.js +89 -0
  174. package/dist/tools/common.js +19 -6
  175. package/dist/tools/components.js +118 -14
  176. package/dist/tools/connectData.js +450 -0
  177. package/dist/tools/connectors.js +193 -0
  178. package/dist/tools/datasources.js +3 -3
  179. package/dist/tools/decisions.js +206 -0
  180. package/dist/tools/dependencies.js +1 -1
  181. package/dist/tools/deployments.js +38 -3
  182. package/dist/tools/designSchemaPatch.js +34 -9
  183. package/dist/tools/dtos.js +36 -5
  184. package/dist/tools/entities.js +79 -12
  185. package/dist/tools/environments.js +1 -1
  186. package/dist/tools/externalComponents.js +26 -5
  187. package/dist/tools/flows.js +119 -13
  188. package/dist/tools/folders.js +1 -1
  189. package/dist/tools/imports.js +5 -2
  190. package/dist/tools/layouts.js +50 -17
  191. package/dist/tools/merges.js +2 -2
  192. package/dist/tools/microservices.js +77 -25
  193. package/dist/tools/mobile.js +280 -0
  194. package/dist/tools/mobileStore.js +572 -0
  195. package/dist/tools/modules.js +48 -0
  196. package/dist/tools/organizationStructure.js +137 -0
  197. package/dist/tools/pages.js +467 -100
  198. package/dist/tools/platformInfo.js +28 -8
  199. package/dist/tools/profileWrite.js +92 -0
  200. package/dist/tools/projects.js +71 -9
  201. package/dist/tools/rows.js +69 -13
  202. package/dist/tools/schemaOps.js +61 -1
  203. package/dist/tools/screenWrite.js +36 -7
  204. package/dist/tools/screens.js +95 -5
  205. package/dist/tools/templates.js +26 -1
  206. package/dist/tools/translations.js +1 -1
  207. package/dist/tools/variables.js +11 -5
  208. package/dist/tools/workflows.js +10 -5
  209. package/package.json +2 -2
  210. package/dist/platform/screenshot.js +0 -1162
@@ -4,6 +4,10 @@ Studio'nun tuval tarafı. Buradaki kurallar controller + komut kodundan doğrula
4
4
  (`zeyrek-mcp/docs/13-faz2-envanter.md` §4); veri/mantık tarafı için
5
5
  `platform_guide('entity')`, `('microservice')`, `('workflow')`.
6
6
 
7
+ > **Mobil uygulamada** (`type: 'mobile'`) şema ve micro-op'lar aynıdır ama yerleşim yalnız flex'tir
8
+ > (blocks çizilmez), görünüm tipli gruplarla yazılır ve `page_create` kökü flex sütun kurar —
9
+ > `platform_guide('mobile')`. Bu rehberdeki "dar ekran" notları WEB uygulamasının telefon kırılımıdır.
10
+
7
11
  **Asgari komponent gövdesi** — her yazma yolunda, her düğümde (çocuklar dahil); ayrıntı Kural 1:
8
12
 
9
13
  ```jsonc
@@ -38,8 +42,14 @@ blokları 1h-3'te).
38
42
 
39
43
  ## Kural 1 — Komponent gövdesinin şekli (`DesignComponentManagementSchema`)
40
44
 
41
- Sayfa şeması **micro-op hattından** kurulur; `page_create`'e satır içi `schema` vermek de
42
- mümkündür ama doğrulama duvarı ve canlı canvas yalnız `page_schema_patch`'te çalışır.
45
+ Sayfa şeması **micro-op hattından** kurulur; `page_create`/`page_update`'e satır içi `schema`
46
+ vermek de mümkündür ve 2026-09-20'den beri o yol da **katalog doğrulamasından geçer** (TB-123):
47
+ gateway geçersiz enum değerini `invalid-enum-value`, olmayan prop'u `unknown-prop`, geçersiz
48
+ `appearance` jetonunu `invalid-appearance` ile **istek gitmeden** reddeder — platformun micro-op
49
+ ucundaki duvarıyla aynı kural kimlikleri. Micro-op hattının hâlâ iki üstünlüğü var: **canlı
50
+ canvas** (SignalR) ve ATOMİK op listesi. Gateway'in tam şema denetimi ÜST DÜZEY prop'lara ve
51
+ `appearance` jetonlarına bakar (platformun duvarı da öyle: `surfaceStyle.contentOverflow` gibi iç
52
+ alanlar denetlenmez); katalog okunamazsa denetim susar ve yazma eskisi gibi geçer.
43
53
 
44
54
  ```jsonc
45
55
  { "elementId": "page_header_1", "kind": "component", "type": "<component_list slug'ı>",
@@ -125,9 +135,9 @@ mümkündür ama doğrulama duvarı ve canlı canvas yalnız `page_schema_patch`
125
135
  **Düğümü silip yeniden kurmayın:** o yol ona yazan akış setter'larını sessizce öldürür
126
136
  (`FLOW-018`, aşağıda Kural 2).
127
137
  - **Aynı ızgara hücrelerini iki komponente veremezsin.** Studio'da komponent tuvale bırakıldığında
128
- çakışan kardeşleri aşağı itme işini studio-api'nin sayfa kurma servisi yapar
129
- (`SchemaHelper.reorderComponents` → `draggableElement.moveItem`); komple şema yazan `page_create`
130
- / `page_update` o servisten geçmez, yani verdiğin koordinatlar olduğu gibi kayda girer ve tuvalde
138
+ çakışan kardeşleri aşağı itme işini Studio'nun ızgara motoru yapar
139
+ (`@ng-apps/screen-builder` `moveItem`); komple şema yazan `page_create`
140
+ / `page_update` o motordan geçmez, yani verdiğin koordinatlar olduğu gibi kayda girer ve tuvalde
131
141
  komponentler üst üste biner. **Gateway aynı algoritmayı şemanın tamamına uyguluyor** (kök dizi +
132
142
  her `children`, yalnız desktop `blocks[]` girdisi; `x`/`w`/`h` ve dizi sırası korunur, çakışanın
133
143
  `position.y`'si aşağı kayar) ve yanıtta `gatewayNormalized: "çakışan komponentler ızgarada aşağı
@@ -164,10 +174,9 @@ mümkündür ama doğrulama duvarı ve canlı canvas yalnız `page_schema_patch`
164
174
  ## Kural 1b — `page_build` YOK
165
175
 
166
176
  Entity'den otomatik sayfa üreten `page_build` tool'u **kaldırıldı**. Platformun
167
- `POST /pages/{app}/build/{page}` ucu üretimi `PageBuilderApiOptions:Url` adresindeki **ayrı bir
168
- servise** delege ediyor (`PageBuilderApiClient`); dev'de `localhost:4000`, prod'da `studio-api…`.
169
- O servis bu ürünün parçası değil ve ayakta olmayabilir. Sayfa/komponent şemasının tam karşılığı
170
- platformda zaten var → doğru yol yukarıdaki micro-op hattıdır.
177
+ `POST /pages/{app}/build/{page}` ucu ve onun delege ettiği sayfa üretme servisi de 2026-09-18'de
178
+ kaldırıldı; otomatik sayfa üretimi yok. Sayfa/komponent şemasının tam karşılığı platformda zaten
179
+ var → doğru yol yukarıdaki micro-op hattıdır.
171
180
 
172
181
  ## Kural 1c — Komponente VERİ bağlama: `mount` olayı + flow (setter adımı)
173
182
 
@@ -249,7 +258,7 @@ döndürür; hata yok, sayfalama yok. (Bağımsız `pagination` komponenti bu tu
249
258
  | `dropdown.search` · `multi-select.search` | `{ searchText }` | `self.searchText` |
250
259
  | `tabs.tab` | `{ selected }` | `self.selected` ya da `event.data.selected` |
251
260
  | `chart.dataPointClick` | `{ name, seriesName, value, dataIndex, row }` | `self.row` (kaynak satırın tamamı) |
252
- | `repeater.change` · `repeater.select` | `{ selectedIndex }` | `self.selectedIndex` · `self.selectedItem` (akış satır bağlamında koşmaz, `item`/`Row` boş) |
261
+ | `repeater.change` · `repeater.select` | `{ selectedIndex }` | `self.selectedIndex` · `self.selectedItem` (akış satır bağlamında koşmaz, `item`/`Row` boş); satırdaki bir düğmenin akışında setter'ın yazım kapsamı OLAYIN kaynağı değil HEDEFİN ağaçtaki yeriyle belirlenir — satırın şablonu içindeki bir hedefe satır bazlı (`_repeaterValues`), dışındaki bir hedefe (modal, sayfa düzeyi metin) sayfa kapsamında yazılır (ZM-08, 2026-09-26); iç içe repeater zaten `PAGE-013` ile reddedilir, ama bir DIŞ repeater'ın şablonuna İÇ satırdan yazan bir akış varsa yazım dış satırın değil İÇ satırın kimliğiyle anahtarlanır — bilinen açık, PAGE-013 riski düşük tutar |
253
262
  | `html` aksiyonu (`fireEvent`) | ikinci argüman | **`event.data`** |
254
263
  | `kanban.cardMove` | `{ id, row, from, to }` | **`event.data`** (self'e yazılmaz) |
255
264
  | `kanban.cardAdd` · `kanban.cardRemove` | `{ column }` · `{ id, row, column }` | **`event.data`** (self'e yazılmaz; kart eklenmez/gizlenmez, akış yazıp `rows`'u tazeler) |
@@ -272,7 +281,8 @@ dalı bulamaz ve akışı **sessizce durdurur** (`flow-engine.ts:79`); Studio da
272
281
  `sourceBranch === 'always'` ile çizdiği için panelde **Start kartından sonrası boş görünür**.
273
282
  İstisnalar: `condition` → dal adı rule id'sidir (ve o id bir **GUID**'dir — aşağıdaki tuzak 6),
274
283
  `confirm` → `yes|no|always`, `microservice` →
275
- `always` (eski akışlarda `success` de çalışır).
284
+ `always` (eski akışlarda `success` de çalışır) **+ `error`** — bağlarsan hata anında `always`
285
+ yerine ondan sürer (2026-09-26, ADR 0004; ayrıntı aşağıdaki §"4xx de hatadır").
276
286
 
277
287
  **2. `iteratees[].type` yalnız `change` | `fill`.** `setter.ts` başka değerde hiçbir dala
278
288
  girmez — adım çalışır, hiçbir şey yapmaz. `change` tek prop'a değer yazar, `fill` diziyi
@@ -398,36 +408,42 @@ Satır eşlemeli tablo yüklemesinin tam şekli (yükleniyor bayrağı → uç
398
408
  göstermek error flow'un işidir.
399
409
 
400
410
  > **4xx de hatadır ve `always` dalı seni KURTARMAZ.** `microservice.ts` `.catch` bloğu
401
- > reddedilen her istekte koşar — 401/403/404/422 dâhil. Sonrası iki yoldan biridir ve ikisi de
402
- > "ekranda hiçbir şey olmadı" ile biter:
411
+ > reddedilen her istekte koşar — 401/403/404/422 dâhil — ve storage'a HER ZAMAN
412
+ > `{ data: null, headers: null, error: { status, message, body } }` yazar (ADR 0004,
413
+ > 2026-09-26 — eskiden hata gövdesi `data`'ya yazılıyordu). `.finally` sonra ÜÇ yoldan birini
414
+ > dener, SIRAYLA:
403
415
  >
404
- > 1. **Hata yolu VARSA** `engine.onError` true döner ve `.finally` `return` eder —
405
- > `engine.next('always')` **hiç çağrılmaz**. Yani `always` ile `errorSteps` birbirini dışlar;
406
- > ikisini birden kuramazsın, hata yolu ana akışın devamını devralır.
407
- > 2. **Hata yolu YOKSA** `always` koşar, ama storage artık `{ error, data }`'dır ve
408
- > `Steps.<ad>.data` başarı gövdesi değil **hata gövdesidir**. Sıradaki `condition`
409
- > `!!Steps.signIn.data.token` gibi bir ifadeyi `undefined` üzerinden değerlendirir, sessizce
410
- > diğer dala düşer ve `else` dalı bağlı değilse akış orada biter.
416
+ > 1. **Adımın kendi `error` dalı BAĞLIYSA** akış oradan sürer — `always` **hiç koşmaz**
417
+ > (`hasBranch('error')`). Bu dal `Steps.<ad>.error` okuyabilir; ana akışın kalanı hiç çalışmaz.
418
+ > 2. **`error` dalı yoksa, `errorSteps` VARSA** `engine.onError` true döner ve `.finally` `return`
419
+ > eder — yine `always` koşmaz, flow'un hata akışı devralır (aşağıdaki karşılaştırma tablosu).
420
+ > 3. **İkisi de yoksa** `always` koşar, ama `Steps.<ad>.data` artık **NULL**dır — başarı gövdesi
421
+ > değil, hiç gövde değil. Sıradaki bir `condition` `!!Steps.signIn.data.token` gibi bir
422
+ > ifadeyi `null.token` üzerinden dener; `runCode` hatayı yutar (K2), ifade `undefined` döner ve
423
+ > akış sessizce diğer dala düşer — `else` dalı bağlı değilse akış orada biter. `data`yı bir
424
+ > liste prop'una yazan bir setter artık patlayan bir nesne değil `null` görür (repeater/table
425
+ > `null`'ı boş liste sayar), ama hata OLDUĞUNU fark etmeden geçer — gateway bunu **FLOW-038**
426
+ > ile uyarır.
411
427
  >
412
428
  > **`errorSteps` yalnız `start` taşıyorsa hata yolu YOKTUR:** `hasErrorFlow`
413
429
  > `errorTasks.some(t => t.type !== 'start')` arar. Start'ın ardına en az bir adım bağla —
414
430
  > gateway `FLOW-021` ile uyarır.
415
431
  >
416
- > **Mobilde `errorSteps` diye bir şey YOK.** `ScreenRuntime.tsx` `mapFlowsToMachine` yalnız
417
- > `tasks` üretir (web `Runtime.svelte` ayrıca `errorTasks` verir), yani mobil uygulamada
418
- > `hasErrorFlow` her zaman false: hata yolu hiç koşmaz ve akış yukarıdaki 2. yoldan —
419
- > `always` + hata gövdeli storage — devam eder. Mobil sayfada hata yolunu `condition
420
- > Steps.<adımAdı>.error` ile kur.
432
+ > **Mobil uygulama:** mobil runtime (`@ng-apps/ui-native` `NativeRuntime`) `errorSteps`'i web gibi
433
+ > `errorTasks`'a eşler — hata yolu iki platformda aynı koşar.
421
434
 
422
- > **Hata metni ÜÇ ayrı yerde durur ve ikisi kullanıcıya gösterilecek metin DEĞİLDİR.**
435
+ > **Hata metni birden çok yerde durur, hepsi aynı şey değildir (2026-09-26 itibarıyla, ADR 0004).**
423
436
  > `onError` hata akışını koşturmadan önce `machine.setStorage('error', errorInfo)` yapar
424
- > (`flow-engine.ts`), yani hata akışının adımları `Steps.error`'ı okuyabilir:
437
+ > (`flow-engine.ts`), yani hata akışının adımları `Steps.error`'ı okuyabilir; patlayan adımın
438
+ > KENDİ storage'ı ise (bağlı `error` dalı da `always` da) `Steps.<adımAdı>.error` nesnesini taşır:
425
439
  >
426
440
  > | İfade | Ne taşır | Kaynak |
427
441
  > |---|---|---|
428
- > | **`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` |
429
- > | `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]` |
430
- > | `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` |
442
+ > | **`Steps.error.message`** | **Yalnız `errorSteps` içinde** — ucun kendi mesajı, `err.reason` → HTTP durum metni → `'Service request failed'`. Yanında `Steps.error.code` · `Steps.error.step.name` (patlayan adım) · `Steps.error.step.data` (ucun hata gövdesi; parametre ifadesi patladıysa `null`) gelir | `microservice.ts` `err.reason \|\| HttpStatusText[err.code]` → `flow-engine.ts` `onError` |
443
+ > | `Steps.<adımAdı>.error.message` | Patlayan adımın KENDİ storage'ında ucun mesajı — `error` dalında da `always`'te de okunur, sıra aynı: `err.reason` → HTTP durum metni → `'Service request failed'` | `microservice.ts` `.catch` |
444
+ > | `Steps.<adımAdı>.error.status` | HTTP durum KODU (sayı) — parametre ifadesi patladıysa `null` | `microservice.ts` `.catch` |
445
+ > | `Steps.<adımAdı>.error.body` | Ucun hata gövdesi — eski `Steps.<adımAdı>.data`'nın taşındığı yer | `microservice.ts` `.catch` |
446
+ > | `Steps.<adımAdı>.data` | **HER ZAMAN `null`**'dır — başarı gövdesi değil, hiçbir gövde değil (eskiden hata gövdesiydi) | `microservice.ts` `.catch` |
431
447
  >
432
448
  > **`alert` adımının `message`'ı DÜZ METİNDİR — ifade yazarsan ekranda o ifade görünür.**
433
449
  > `alert.ts` `task.argument.message`'ı doğrudan `toast.error()`'a geçirir ve `args` hiçbir yerde
@@ -455,15 +471,24 @@ göstermek error flow'un işidir.
455
471
  > `{ "type": "confirm", "args": { "message": "Bu risk ve 4 aksiyonu silinecek.", "title": "Riski sil",
456
472
  > "confirmText": "Sil", "cancelText": "Vazgeç" } }`.
457
473
 
458
- > **Hangi yolu seçeceksin — `errorSteps` mi, ana akışta `condition` mı?** İkisi birden
459
- > kurulamaz (yukarıdaki 1. madde), yani bu bir tercih değil bir karar:
474
+ > **Hangi yolu seçeceksin — adımın kendi `error` dalı mı, `errorSteps` mi, ana akışta
475
+ > `condition` mı?** Bir hatada sırayla denenir (yukarıdaki 4xx bölümü) — üçü birden TANIMLI
476
+ > olabilir ama aynı anda yalnız biri koşar: **`error` dalı bağlıysa o kazanır**, yoksa
477
+ > `errorSteps`, ikisi de yoksa `always` + `condition`:
460
478
  >
461
- > | | `errorSteps` | ana akışta `condition Steps.<adımAdı>.error` |
462
- > |---|---|---|
463
- > | Ana akışın kalanı | **koşmaz** — hata akışı devralır | koşar, dalı sen seçersin |
464
- > | 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 |
465
- > | Hata metni | `Steps.error.message` (yukarıdaki tablo) | `Steps.<adımAdı>.data.message`, hata olduğunu `Steps.<adımAdı>.error` söyler |
466
- > | 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) |
479
+ > | | adımın kendi `error` dalı (g-02) | `errorSteps` | ana akışta `condition !!Steps.<adımAdı>.error` |
480
+ > |---|---|---|---|
481
+ > | Kapsam | yalnız BU adımın hatası | akıştaki HERHANGİ bir adımın hatası | yalnız BU adımın hatası |
482
+ > | Ana akışın kalanı | **koşmaz** — bu dal devralır | **koşmaz** — hata akışı devralır | koşar, dalı sen seçersin |
483
+ > | Yükleniyor bayrağı · modal kapatma · tablo tazeleme | dalda **yeniden kurmak zorundasın** | hata akışında **yeniden kurmak zorundasın**; kurmazsan bayrak açık kalır | zaten ana akışta duruyor |
484
+ > | Hata metni | `Steps.<adımAdı>.error.message` | `Steps.error.message` (yukarıdaki tablo) | `Steps.<adımAdı>.error.message`; hata olduğunu `!!Steps.<adımAdı>.error` söyler — `.error.status` parametre ifadesi patladıysa `null`dur, o yolu KAÇIRIR |
485
+ > | Ne zaman doğru | hata anında BU adımın sonrası **tamamen başka bir yola** sapacaksa | 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) |
486
+ >
487
+ > **"Ana akışta condition" sütunu `.error`'u okumalı, `.data`'yı değil.** `Steps.<adımAdı>.data`'nın
488
+ > DOLU/BOŞ olmasına bakarak başarı/hata ayırmak (`data.token` gibi bir alanın varlığını yoklamak)
489
+ > artık yanıltıcıdır — `data` hata anında zaten `null`'dur (yukarıdaki §"4xx de hatadır"). Böyle bir
490
+ > `condition`'ı gateway **FLOW-038** ile uyarır; adıma ayrıca bir `error` dalı bağlayın (aşağıdaki
491
+ > 7c örneği) — `error` dalı bağlıysa uyarı düşer, çünkü hata artık `always`'e hiç uğramaz.
467
492
 
468
493
  Zorunlu filtresi olan uçlarda (`patient-*`, `doctor-appointments` gibi `isRequired: true`
469
494
  filtreli microservice'ler) parametreyi adımda ver, yoksa uç `MissingParameterException` atar:
@@ -540,21 +565,22 @@ Tarif: `platform_guide('middleware')`.
540
565
 
541
566
  **Sözlükler** (`StepType.cs`, `StepParameterValueType.cs` — hepsi küçük harf):
542
567
  `start · microservice · setter · setcookie · validation · condition · redirect · alert ·
543
- confirm · flow-trigger · storage · navigate · code · download · function · end`
568
+ confirm · flow-trigger · storage · navigate · code · download · function · share · end · bpm`
569
+ (`bpm` argümanı `value` içinde JSON metnidir — `platform_guide('bpm')` §Yürütme ve kutu)
544
570
  değer tipleri: `static · page · property · storage · variable · code · item · state`
545
571
 
546
572
  > **Sözlükte olmak KOŞACAĞI anlamına gelmez — koşan tipler barındırıcıdan gelir.** Motor yedi
547
573
  > tip register eder, gerisini uygulama ekler. Bir sayfa TEK bir uygulamaya aittir
548
574
  > (`Page.ApplicationId`) ve uygulamanın tipi `web` ya da `mobile`'dır (`ApplicationType.cs`), yani
549
575
  > sayfanın barındırıcısı bellidir — matris "hangi tip sende çalışır"ı söyler, iki barındırıcıda
550
- > birden koşmayı değil (web `Runtime.svelte`, mobil `ScreenRuntime.tsx`):
576
+ > birden koşmayı değil (web `Runtime.svelte`, mobil uygulama `@ng-apps/ui-native` `NativeRuntime`):
551
577
  >
552
- > | Adım tipi | Web | Mobil | Nerede register ediliyor |
578
+ > | Adım tipi | Web | Mobil uygulama | Nerede register ediliyor |
553
579
  > |---|---|---|---|
554
- > | `start` · `setter` · `code` · `condition` · `microservice` · `validation` · `flow-trigger` · `function` | ✅ | ✅ | `flow-engine.ts` — motorun kendisi (`function` mobilde kayıtlıdır ama mobil runtime `Functions`'ı boş kurar — K11, çağrı çalışmaz) |
555
- > | `alert` · `redirect` · `setcookie` · `storage` | ✅ | ✅ | uygulama; mobilde `redirect` navigate, `setcookie` storage uygulamasına eşlenir |
556
- > | `confirm` · `download` | ✅ | ❌ | yalnız `Runtime.svelte` |
557
- > | `navigate` | ❌ | ✅ | yalnız `ScreenRuntime.tsx` — web'deki karşılığı `redirect` |
580
+ > | `start` · `setter` · `code` · `condition` · `microservice` · `validation` · `flow-trigger` · `function` · `bpm` | ✅ | ✅ | `flow-engine.ts` — motorun kendisi (`bpm` adımı Studio'da yalnız web uygulamasında listelenir) |
581
+ > | `alert` · `redirect` · `setcookie` · `storage` | ✅ | ✅ | barındırıcı; mobilde `setcookie` cihaz deposuna yazar (`MOB-005`) |
582
+ > | `confirm` · `download` · `share` | ✅ | ✅ | `Runtime.svelte` · `runtime-tasks.ts` (`download` mobilde açma/paylaşım — `MOB-005`; `share` aşağıda) |
583
+ > | `navigate` | ❌ | ✅ (`redirect` takma adı) | web'deki karşılığı `redirect`; gateway `redirect`'e çevirir |
558
584
  > | `end` · `query` | ❌ | ❌ | hiçbir yerde — üstelik `query` backend sözlüğünde de yok (`StepType.cs` onu saymaz), yalnız frontend enum'unda duruyor |
559
585
  >
560
586
  > **Register edilmemiş bir tipe gelen akış SESSİZCE durur.** `flow-engine.ts` `process()`
@@ -590,6 +616,19 @@ değer tipleri: `static · page · property · storage · variable · code · it
590
616
  > adımında ve `{{ }}` bağlamasında `Functions.<ad>(…)` ile de çağrılır; gateway bilinmeyen adı ve
591
617
  > fazla argümanı FLOW-025 ile uyarır. Tam gövde `ngapps://examples/flow-call-function`, kurallar
592
618
  > **`platform_guide('functions')`**.
619
+ >
620
+ > **`share` adımı ("Share")** metin ve bağlantı paylaşır: `args: { title, message, path }`. `title`
621
+ > DÜZ METİNDİR (ifade yazılırsa FLOW-022 uyarır); `message` ve `path` `storage.value` gibi
622
+ > İFADEDİR — düz metin tırnak ister (`"'Şuna bak'"`, `"'https://…/urun/' + Page.queryParams.id"`).
623
+ > Bağlantının adı `url` DEĞİL `path`'tir: backend `Title`/`Message`/`Path` alanlarında saklar ve
624
+ > tanımadığı anahtarı 200 ile sessizce düşürür (FLOW-035 `url`/`link`/`text`'i ve tırnaksız adresi
625
+ > bloklar). Bağlantı ŞEMALI TAM ADRES olmalı (`https://…`, `mailto:…`): şemasız değer (`www.…`,
626
+ > `urun/42`) adres sayılmaz, metnin sonuna eklenir. İfadenin sonucu yalnız dize ya da sayıysa
627
+ > paylaşılır; `false` (`koşul && 'x'`), `null` ve nesne boş sayılır. Cihazda paylaşım sayfası açılır
628
+ > (Android'de bağlantı metnin sonuna eklenir), web'de tarayıcının paylaşım penceresi, yoksa ya da
629
+ > reddederse metin + bağlantı panoya yazılır ve bildirim çıkar. Tek dal `always`: iOS'ta sayfa
630
+ > kapanınca, Android'de seçici açılır açılmaz (RN seçicinin kapanışını bildirmez) sürer; metin de
631
+ > bağlantı da boşsa hiçbir şey açılmaz, akış hemen sürer.
593
632
 
594
633
  > Bir microservice adımı kaydedildiğinde platform `args.microserviceSchema`'yı
595
634
  > (parameterScheme + returnScheme + `isReturnArray`) kendisi doldurur — `flow_get` ile geri
@@ -629,6 +668,175 @@ verilmemiş bir tablo kaynağı vardır ve sayfa göstergesi `NaN / N` olur (öl
629
668
  > bildirir, ama ham HTTP'de bu kural sana ait.) `add_component`'te ise sarmal ZORUNLUdur — iki
630
669
  > op'un şekli birbirinin tersidir, karıştırma.
631
670
 
671
+ ## Kural 1c-3 — Prop'u veri kaynağına BAĞLAMA (`connectData`)
672
+
673
+ Liste, seçenek, grafik serisi ya da ağaç verisi bir TABLODAN geliyorsa akış yazılmaz: prop veri kaynağına bağlanır.
674
+ Değer `{ type: 'connectData', ref: <tanım>, data: <son satırlar> }` olur; runtime satırları
675
+ `POST /connect-data/{uygulama}/{sayfa}/{element}/{prop}` ucundan okur, politika her istekte uygulanır.
676
+
677
+ | İş | Yol |
678
+ |---|---|
679
+ | Tablo/datatable satırları, dropdown/select seçenekleri, grafik serisi, kanban kartları — kaynak tablo, okuyan query, **liste dönen workflow (dönüş DTO'su array) ya da okuyan microservice** olsa da | **Bağla** (bu kural) |
680
+ | Tek kayıt yükleyip forma doldurmak, bir düğmeyle tetiklenen yazma/işlem, çıktısı birden çok bileşene dağıtılan sonuç | **Akış** (Kural 1c) |
681
+
682
+ **Tek çağrı.** `connect_data_bind` bileşenin bağlanabilir prop kaydını ve kaynağın kabiliyetini
683
+ okur, tanımı kurar, bulguları istek gitmeden verir ve tek atomik yamayla yazar:
684
+
685
+ ```json
686
+ { "applicationId": "<app>", "pageId": "<sayfa>", "element": "tbl_musteri", "prop": "rows",
687
+ "source": { "kind": "entity", "id": "<entity id>" },
688
+ "fields": ["ad", "soyad", "tckn", "puan", "sube"], "expand": { "sube": ["ad"] },
689
+ "filterable": ["ad", "tckn", "puan"], "searchable": ["ad", "soyad"], "pageSize": 25 }
690
+ ```
691
+
692
+ Layout'taki bileşende `pageId` yerine `layoutId`, widget içindekinde `componentId`. Yanıt `touched`, `newVersion` ve `runtime.propType: 'connectData'` döner. Önce `dryRun: true` ile
693
+ tanımı, op'ları ve bulguları görebilirsiniz. **Var olan bağlantıyı değiştirmek** için aynı prop ve kaynakla
694
+ yalnız değişen parçayı verin (`{ …, pageSize: 50 }`): verilmeyen parçalar kayıtlı tanımdan taşınır, alan
695
+ listesi değişmediyse kolonlar (genişlik, biçim, başlık) yeniden yazılmaz. Kolonların filtre düğmesi (`isFilterable`)
696
+ yalnız `filterable` listesindeki alanlarda açılır. Tam örnekler: `ngapps://examples/connect-data-table-entity`,
697
+ `ngapps://examples/connect-data-dropdown-roles`, `ngapps://examples/connect-data-query-params` (parametreli query +
698
+ `$ad`), `ngapps://examples/connect-data-workflow-microservice`, `ngapps://examples/connect-data-edit-m2m`,
699
+ `ngapps://examples/connect-data-convert-verify` (convert/detach + kullanıcı gözüyle doğrulama). Resource okuyamayan
700
+ istemcide: `platform_guide({ resource: 'ngapps://examples/<ad>' })`.
701
+
702
+ **Keşif sırası.**
703
+
704
+ 1. `component_get` → `connectData.props`: hangi prop bağlanır (`rows`, `options`, `series[].data`).
705
+ Kayıt yoksa bileşen bağlanamaz (CD-001).
706
+ 2. `connect_data_sources` → kaynak listesi: `entity` · `query` (yalnız okuyan) · `view` · `workflow` · `microservice`.
707
+ Entity dışı kaynak yazmaz. Query, view ve workflow tanımı filtre/sıralama/searchable taşımaz (CD-013); microservice
708
+ yalnız dinamik filtreli tablo ucunda süzer ve aranır, parametreli sıralamada sıralar (describe `canFilter`/`sortable`,
709
+ CD-002). Microservice'te `searchable`'a yalnız `icontains` işleçli korumasız metin alanı konur; tablonun arama metni
710
+ bu alanlar üzerinde OR "içerir" filtresine çevrilip uca dinamik filtre olarak gider.
711
+ Describe `source.pageMode: client` ise sunucu sayfalamaz, runtime ilk 1000 satırı alıp tarayıcıda süzer (CD-007); view
712
+ `textSearch`: arama istekteki `search` ile, aramasız liste boş. `isSchemaSampled`: DTO'suz query ilk satırdan okundu.
713
+ 3. `connect_data_describe { source }` → alan başına efektif kabiliyet (`access` · `operators` · `sortable` · `canWrite` ·
714
+ `relation` · `enum`). İşleci ve sıralamayı alan tipinden tahmin etmeyin, buradan okuyun.
715
+
716
+ **İki bağlanma biçimi.**
717
+
718
+ - **Alan listesi (`fieldsFrom`)** — `datatable.rows`, `table.rows`, `repeater.rows`. Kolon listesi (`columns`) bileşenin
719
+ KENDİ kolon şemasından üretilir (metin → `text`, sayı → `number`, boolean → `switch`, ilişki → `relation` yoksa `tag`,
720
+ enum → `tag`); `expand` yolu (`sube.ad`) satıra okunur ama kolon OLMAZ — hedefte gösterilecek alanlar `lookup`'a yazılır.
721
+ Yeniden bağlamada kolonların sırası ve ayarları korunur.
722
+ - **Roller** — dropdown/radio `{ label, value, … }`, grafik `{ category, value, series, … }`. Verilmezse
723
+ yalnız ZORUNLU roller önerilir: `value` ← birincil anahtar, `label` ← görünen alan, diğer zorunlu rol
724
+ türündeki ilk alan. İsteğe bağlı roller (`caption`, `color`, `isDisabled`, `series`…) açıkça verilmedikçe
725
+ boş kalır. Zorunlu rol doldurulamazsa bind aday listesiyle durur; rolü besleyemeyen alan tipi uyarıdır
726
+ (CD-016). Runtime satırları rol adlarına da yazar (`option.label ← ad`). Kaydında `fields` olan bileşende
727
+ (grafik `categoryField`, kanban `titleField`) alan adı o prop'lara aynı yamada yazılır; kanban `columns`
728
+ kolon rolünün enum değerlerinden üretilir (yeniden bağlamada elle girilen `wipLimit` korunur). Grafiğin
729
+ `aggregate`'i yazılmaz — satır başına tekrar eden kategori için elle ayarlayın (CD-015).
730
+ **Role ilişki alanının kendisi** (`category: 'kategori'`, o2m/self) eşlenirse runtime ve kanvas satırda o alanı
731
+ hedefin görünen alanına (`relation.targetDisplay`, datatable hücresindeki metin) çevirir — hem rolde hem alanın
732
+ kendisinde, çünkü `categoryField` satırı alan adıyla okur (39/03 U-3; önce eksen `[object Object]` çiziyordu). Kimlik
733
+ gerekiyorsa alt yolu eşleyin (`value: 'kategori.id'`); m2m dizisi çevrilmez.
734
+ - **Sayfalamayan bileşen bütün sonucu okur** (bileşen tanımında `isPaginated` yok — grafik, dropdown, stat card,
735
+ kanban…; 39/03 U-5): `pageSize` verilmezse tanımın sayfa boyu **1000** (motorun tek okuma tavanı) yazılır, kayıtlı eski
736
+ 25 de ezilir. Kaynak 1000'i aşarsa satırlar kesilir ve runtime bunu `runtimeErrors`'a `ConnectDataRowsTruncated` olarak
737
+ yazar; kanvas şeridi "Sayfalamıyor: N satırın M satırını gösterir" der.
738
+ - **Stat Card ana değeri** (39/03 U-13): birincil bağlanabilir prop `rows` (rol `value: number`); kart `valueAggregate`
739
+ (`first` · `count` · `sum` · `avg` · `min` · `max`) özetini basar, `count` kaynağın `meta.total`'ıdır. `sparkline`
740
+ ikincil prop olarak bağlanır. Bağlama `valueAggregate`'i yazmaz — `page_schema_patch set_prop` ile seçin.
741
+
742
+ **Alan kimliği kalıcıdır.** `slug` sayfanın gördüğü ad, `id` fiziksel alan: kaynakta ad değişse de yanıt kayıtlı adla
743
+ gelir, sayfa kırılmaz. Elle yazılan tanımda her alana `id` koyun (`connect_data_bind` kendisi yazar).
744
+
745
+ **Tanımın şekli (kısa).** `version: 1` · `source { kind, id, slug? }` · `fields [{ id, slug, expand? }]` ·
746
+ `filter` (backend Filter ağacı) · `sort [{ field, ascending }]` · `page { mode: 'offset'|'cursor'|'client', limit }` ·
747
+ `language` (`request` · `default` · `xx`) · `mapping { roles, columns? }` ·
748
+ `behavior { filterable, searchable, edit { create, update, delete, bulk, saveMode, editable, defaults, writeMode }, refresh }`.
749
+ Davranış, rol ve sıralama yalnız SEÇİLİ alanlardan olur; `editable` yalnız seçili kök alanlardan (CD-004).
750
+ Tanım filtresinde `"$ad"` yazılan değer istekteki `params.ad`'dan bağlanır. `cursor` (keyset) yalnız entity'de, tek
751
+ sıralama anahtarıyla; sonraki sayfa `page.after` ← `meta.page.nextCursor`.
752
+
753
+ **Politika arayüzü ezer.** Politikanın gizlediği alan yanıtta YOKTUR ve kolonu çizilmez, maskeli alan maskeli gelir —
754
+ ayrıca gizlemeye gerek yok. **Maske önkoşulu:** maske alanın depolama korumasından gelir — alan önce
755
+ `entity_protection_update { field, protection: { mode: 'masked', mask } }` ile korunmalı; korumasız alana politika
756
+ `fieldAccess: 'masked'` verilirse değer DÜZ döner (ACC-017 warn). Maskeli gören özne o alanı SÜZEMEZ (describe
757
+ `canFilter: false`, `operators: []` — koruma varsayılanı maskelidir, yetkili kullanıcı için de; 39/05). Açık (`plain`) gören
758
+ öznede korumalı alanda yalnız `eq` · `neq` · `in` · `notin`,
759
+ sıralama yok; `contains` istek gitmeden durur (CD-002).
760
+
761
+ ### İlişki, enum, dosya ve Lookup
762
+
763
+ - İlişki alanı satırda hedef nesnesi olarak gelir: `sube: { id, ad }` (hangi hedef alanların geleceği
764
+ `expand` ile seçilir). `datatable`'da kolon tipi `relation`, hücre görünen alanı basar; m2m ilk üç
765
+ chip + `+N`; kullanıcı hedef tabloyu okuyamıyorsa hücre kilitli `—` (lookup ayarı etkisiz, CD-011 warn).
766
+ - Enum alanı `tag` kolonunda seçeneğin etiketi ve rengiyle çizilir.
767
+ - İlişki alanının seçim listesi aynı motordan gelir: runtime `op: 'lookup'` (hedefin anahtarı, görünen
768
+ alanı ve ilk düz alanları türetilir). Özelleştirmek için alanın `lookup` alt tanımı:
769
+ `{ slug: 'sube', lookup: { fields: [{ slug: 'ad' }, { slug: 'kod' }], behavior: { filterable: ['kod'], searchable: ['ad'] }, sort, page, mode: 'drawer' } }`.
770
+ `lookup` yalnız ilişki alanında olur ve içinde ikinci `lookup` olmaz (CD-011).
771
+ - `datatable`'da ilişki hücresi ve filtre değeri lookup panelinden seçilir (o2m tekil, m2m çoklu) — akış yazılmaz.
772
+ - **Seçim bileşeninin kendi lookup'ı** (`dropdown`, `multi-select`; `component_get.connectData.props.options.lookup`):
773
+ tanımın KÖK `lookup`'ı, alan lookup'ıyla aynı biçimde ama kaynağın kendi alanlarıyla
774
+ (`{ ..., lookup: { fields: [{ slug: 'kod' }], behavior: { filterable: ['sehir'] }, mode: 'drawer' } }`;
775
+ `fields` boşsa platform türetir). Açıkken alanın sağında ↗ çıkar ve pencere bileşenin kaynağını kolonlarla
776
+ listeler (multi-select'te çoklu seçim, önceki seçimler üstte). `connect_data_bind` entity kaynağına yeni
777
+ bağlamada açık yazar, `lookup: null` kapatır; kök lookup'sız eski bağlantı kapalı kalır. Ana tanımın filtresi
778
+ pencerede de geçerlidir. İstek alansız `op: 'lookup'` (`connect_data_execute`); yalnız entity kaynağında (CD-013).
779
+ - Dosya alanı (describe `type: 'file'`, `operators: [isnull, isnotnull]`, sıralanmaz) satırda dosya nesnesi olarak gelir:
780
+ `{ id, display_name, path, size, extension, content_type, folder_id, absolute_path, … }`; boş dosya/ilişki alanı `null` ya da satırda hiç yok (runtime `list` anahtarı atar — ikisini de boş sayın; 39/05)
781
+ (38/M-6 backend düzeltmesiyle; eski imaj `{ absolute_path: '', type: 'ref' }` döndürüyordu). Dosya alanı
782
+ `edit.editable`'a KONAMAZ — bağlı bileşen dosyayı satır içinde yazmaz, platform kaydı 422
783
+ `connect-data-editable-file` ile reddeder (CD-018, bind dryRun'da block). Dosyayı `file_upload` + `row_update` yazar.
784
+
785
+ ### Yazma (`edit`)
786
+
787
+ - Tanım: `edit { create, update, delete, bulk, saveMode: 'row'|'cell'|'batch', editable: [kök alanlar], defaults, writeMode: 'atomic'|'partial' }`.
788
+ `row` satırı ✓ ile kaydeder; `cell` var olan satırda yalnız çift tıklanan hücreyi açar, ✓/✕ hücrenin içindedir ve
789
+ ✓ yalnız o alanı yazar (yeni satır yine bütün olarak kaydedilir); `batch` değişiklikleri taslakta biriktirip birlikte kaydeder.
790
+ Bağlı `datatable`'ın düzenleme prop'larını runtime tanım ∧ politikadan yazar; hücre yalnız `editable` ∧ `canWrite`
791
+ alanda açılır. Kaydetmek için akış YOK: `save` bağlantı uca gider (create → update → delete).
792
+ - Yazmayı önden denemek: `connect_data_execute { applicationId, pageId, element, prop, op: 'update', rows: [{ id, durum: 'pasif' }], asUserId }`
793
+ — bileşenin isteğinin aynısı, GERÇEKTEN yazar; tanımın izin vermediği istek gitmeden durur (CD-012). İlişki alanı
794
+ `'<id>'`, m2m `['<id>', …]` (tam liste); hedefte olmayan kimlik 404 `ConnectDataRelationTargetNotFound` (`field`).
795
+ `atomic` (varsayılan) TEK transaction'dır: `rowIndex` gönderilen dizinin indeksi — HİÇBİR satır yazılmadı.
796
+ `writeMode: 'partial'` (yalnız built-in veritabanı): yazılabilenler yazılır, yanıt `errors[{ row, field, code, message }]`.
797
+ - **Zorunlu alan** (describe `isRequired`) create'te boş/eksik, update'te `null`/boş gönderilirse istek gitmeden kaynağa
798
+ değil motorda düşer: `422 BulkRowFailed`, `field` alanın slug'ı, `rowIndex` satır, iç neden `ConnectDataFieldRequired`
799
+ ("Başlık alanı zorunludur."); `partial`'da yalnız o satır `errors[]`'a (39/03 U-8 — önce veritabanının NOT NULL'u
800
+ alan adsız `400 RowValidationFailed` veriyordu). `defaults` create'te eksik alanı doldurur.
801
+ - `defaults` yalnız create'te, istemci değeri ezer. Politikanın vermediği eylem ya da alan etkisizdir (CD-006).
802
+ Anonim sayfada açık yazma oturumsuz ziyaretçiye yazma verir (CD-017). Denetim izinde kaynak `CONNECT_DATA`.
803
+
804
+ **Doğrulama tarifi.** `connect_data_get` (tanım + drift) → `page_screenshot` (satırlar çizildi mi, `runtimeErrors`
805
+ boş mu; bağlama hatası `flowId: 'connect-data'`) → `connect_data_execute { op: 'list' }` (runtime yanıtı;
806
+ `connect_data_preview` Studio ucudur, sayfa kapısını koşmaz). **Kompleks + `enforce` projede** gateway kimliği uygulama
807
+ öznesi değildir: runtime araçları (`connect_data_execute`, `microservice_execute`/`_debug`, `page_runtime_get`,
808
+ `page_screenshot`) kimliksiz 401 `AppSessionRequired` alır. **Parolasız kullanıcı gözü:** aynı çağrıya
809
+ `asUserId: '<users satır id'si>'` — istek gateway kimliği + `impersonated-user-id` başlığıyla gider, özne o kullanıcıdır
810
+ (politika, satır kapsamı, maske); `page_screenshot` sayfanın bütün isteklerine başlığı ekler (Studio "View as user"
811
+ seçicisinin aynısı). Yalnız development/dal: production'da platform başlığı düşürür, gateway isteği göndermez.
812
+ Oturum açılmış üye gerekiyorsa `access_session_create` + `asUser`. **Kaynağın kendi grant'i** (kompleks + `enforce`,
813
+ ölçüldü 39/05): entity kaynağı `entity.read` (+ yazmada `entity.create/update/delete`, ilişki hedefi için hedefin `entity.read`)
814
+ ister; workflow kaynağı `workflow.execute` (`workflow:<id>`), microservice kaynağı `microservice.execute` (`microservice:<id>`)
815
+ ister — yoksa bağlantı 403 `ForbiddenException`. Query kaynağı (ve query kaynaklı uç) kapsamlı tabloya dokunuyorsa her
816
+ kullanıcıda 403 `AccessQueryNotScoped` (ACC-005): kullanıcıya özel liste için entity ya da tablo ucu, özet rapor için sorguyu
817
+ `isUnrestricted` yapın. `connect_data_convert`'in açtığı uç da ayrı `microservice.execute` ister (sayfanın bağlantısı istemez).
818
+ Kullanıcıda `page.view` yoksa sayfa ve bağlantı 403
819
+ döner — politikaya `page:<sayfa>` grant'i ekleyin. `connect_data_bind { asUserId }` CD-006/CD-011 uyarılarını o
820
+ kullanıcının gözüyle üretir (gateway kimliği yöneticidir, bu iki uyarıyı hiç görmez).
821
+
822
+ **Yasaklar.**
823
+
824
+ - Bağlı prop'a `set_binding`, `set_prop_type` ya da `{ type: … }` sarmallı `replace_prop` yazılmaz;
825
+ flow setter bağlı prop'u hedeflemez. Runtime setter'ı atlar ve bileşene hiçbir şey yazılmaz (CD-005).
826
+ Yalnız örnek satırları değiştirmek için `replace_prop { value: [ham satırlar] }`.
827
+ - Bağlantıyı kaldırmak `remove_connect_data { target, path }`: son satırlar static veri olarak kalır.
828
+ - Platformun yönettiği tabloda (organizasyon, rol tabloları) düzenleme açılamaz (CD-003); `users` sistem tablosu
829
+ değildir, `password` alanı kabiliyette hiç yoktur.
830
+ - Kaynaktan silinmiş alan tanımda kalırsa uyarı; `connect_data_bind { fixDrift: true }` her yerden (filtre, expand dahil) düşürür (CD-008).
831
+ - Aynı sayfada aynı kaynağa 5+ bağlantı uyarıdır (CD-010).
832
+ - **Parametreli kaynak** (query/workflow/microservice): `connect_data_describe` `source.parameters` bağlantının doldurabileceği parametreleri verir; `connect_data_bind { params: { yil: 2026, sube: '$secilenSube' } }` sabit değer ya da sayfa değişkeni yazar. Bilinmeyen ad block (CD-013); `$ad` `page_state`'te yoksa warn (CD-014, runtime `null` gönderir). Önizleme `$ad`'ı sayfa durumundan DOLDURMAZ:
833
+ `connect_data_preview { request: { params: { yil: 2026 } } }` verin — verilmezse null bağlanır, sonuç sessizce boş döner — ya da null'da filtresini düşüren workflow'da TÜM satırlar döner (39/05)
834
+ (CD-014 warn, sayfa değişkeninin başlangıç değerini önerir). Entity/view parametre almaz — filtre değerinde `$ad` kullanın.
835
+ - **Microservice'e çevirmek** (`connect_data_convert { …, name, slug }`, yalnız sayfa): tanım microservice'e taşınır, prop `{ version: 1, microserviceId }` olur; flow aynı veriyi `microservice_execute { body: { op: 'list' } }` ile alır. Çevrilmiş prop'u bind reddeder: önce `connect_data_detach` (başka yerde kullanılıyorsa 409).
836
+
837
+ Kuralların tamamı, uygulandığı yer ve kaynağıyla: `ngapps://rules/connect-data` (resource okuyamayan istemcide
838
+ `platform_guide({ resource: 'ngapps://rules/connect-data' })`).
839
+
632
840
  ## Kural 1c-2 — Komponentin `name`'i JS TANIMLAYICISI olmalı (yoksa sayfadaki tüm ifadeler ölür)
633
841
 
634
842
  Runtime, ifade çalıştırmadan önce sayfadaki **her** komponent için bir değişken üretir:
@@ -693,9 +901,10 @@ Tire yerine `_` kullan.
693
901
  `isScrollable: true`, `appearance.heightType: 'auto'`). `container`, `form` ve `repeater` de aynı
694
902
  şekilde kapsayıcıdır — onların içine de `parent` vererek eklenir.
695
903
 
696
- Sayfa düzeyi olaylar (page load vb.) **bu kapsayıcıya** bağlanır:
697
- `set_event { target: 'page_container', event: 'mount', eventValue: <flow id> }`.
698
- `page_container`'ın kendi `isLoading` prop'u vardır — sayfa açılış akışında yükleniyor bayrağı olarak
904
+ Sayfa düzeyi olaylar (page load vb.) **bu kapsayıcıya DEĞİL, sayfanın kendi `events`'ine**
905
+ bağlanır: `page_update { events: [{ name: 'mount', value: <flow id> }] }` (Kural 1d-2).
906
+ `page_container` Studio'nun Layers ağacında görünmez; ona `set_event` ile bağlanan akış çalışır ama
907
+ tasarımcıda hiçbir yerden bulunamaz. `page_container`'ın kendi `isLoading` prop'u vardır — sayfa açılış akışında yükleniyor bayrağı olarak
699
908
  onu kullan. Bir `container` olduğu için **düzen prop'larını da alır**: çocuklarını ızgara yerine
700
909
  akışla (ortalama, sağa yaslama, aralık) dizmek istiyorsan `layout: 'flex'` — Kural 1f-2.
701
910
 
@@ -738,9 +947,10 @@ gateway **PAGE-011 / LAY-004** ile istek gitmeden reddeder.
738
947
 
739
948
  - Bağlama yolu `set_event` DEĞİLDİR (o komponent hedefler): sayfada `page_create` /
740
949
  `page_update`'in `events` parametresi, layout'ta `layout_create/update`'in `events`'i.
741
- - Sayfa açılış akışı için yerleşik desen `page_container.mount`'tur (Kural 1d) — ikisi de
742
- çalışır; **`destroy` için tek yol buradaki sayfa olayıdır** (interval temizliği, "ayrılmadan
743
- kaydet" uyarısı gibi işler).
950
+ - **Sayfa açılış akışı buraya, sayfanın `mount` olayına bağlanır.** `page_container.mount` da
951
+ koşar ama kullanma: kapsayıcı Studio'nun Layers ağacında görünmez, bağ tasarımcıda bulunamaz
952
+ (Kural 1d). **`destroy` için de tek yol buradaki sayfa olayıdır** (interval temizliği,
953
+ "ayrılmadan kaydet" uyarısı gibi işler).
744
954
  - Akış kilidi burada da geçerlidir (Kural 3): önce bütün `flow_create`'ler, sonra events bağı.
745
955
  - Mevcut bağları `page_get` gösterir (`events[].value` = flow id).
746
956
 
@@ -896,14 +1106,15 @@ kullanır: `page.mappers.ts` `mapPageLayoutToPreview` — layout varsa çizilen
896
1106
  >
897
1107
  > Kural: **`isWrapper` yalnız gerçekten sayfa boyu kaplaması gereken kap içindir**
898
1108
  > (`page_container` böyledir ve öyle kalmalı). Sadece "başlık/çerçeve çizilmesin" diye wrapper
899
- > yapma — o iş `container` için de `form` için de `style` ile yapılır (`background:'transparent'`,
900
- > `border:'none'`, `padding:0`). `form`'un eski `hideBorders`/`hidePadding` prop'ları
1109
+ > yapma — çerçevesiz kutu için `box` kullan; `container`'da çerçeve ve zemin `surfaceStyle` ile
1110
+ > kapatılır (dört `border*` yönü `'none'`, `background:'transparent'` — §Kap seçimi), `form`'da
1111
+ > `style` ile (`background:'transparent'`, `border:'none'`, `padding:0`). `form`'un eski `hideBorders`/`hidePadding` prop'ları
901
1112
  > 2026-09-09'da şemadan kaldırıldı — bileşen onları hiçbir zaman okumuyordu, no-op'tu. Yanlış
902
1113
  > `isWrapper`'ı düğümü silmeden düzelt: `resize_component { target, states: { isWrapper: false } }`
903
1114
  > (platforma `set_states` olarak gider). `remove_component` + `add_component` ona yazan akış
904
1115
  > setter'larını öldürür (`FLOW-018`).
905
1116
 
906
- Mobil not: <768px'te ızgara tek kolona düşer (`RenderComponent` media query'si) — y sırası
1117
+ Dar ekran notu: <768px'te ızgara tek kolona düşer (`RenderComponent` media query'si) — y sırası
907
1118
  dikey sıraya dönüşür, x/w yok sayılır. Yerleşimi masaüstü (breakpoint 1024) bloğuyla kur.
908
1119
 
909
1120
  ## Kural 1f-2 — Kapsayıcı düzeni: `flex` (varsayılan) ↔ `grid`
@@ -921,7 +1132,7 @@ aynı prop grubunu o da alır. Grup katalogda tanımlıdır, yani şemadan kurul
921
1132
  | `horizontalAlign` | **`left`** · `center` · `right` · `stretch` | `row`'da ana eksen (`justify-content`), `column`'da çapraz eksen (`align-items`). |
922
1133
  | `verticalAlign` | **`top`** · `middle` · `bottom` · `stretch` | `row`'da çapraz eksen, `column`'da ana eksen (rolleri `horizontalAlign` ile takas). |
923
1134
  | `size` | **`shrink`** · `grow` | Çocuklara `flex: 0 1 auto` / `1 1 auto`. |
924
- | `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. |
1135
+ | `gap` | `none` · `small` · **`medium`** · `large` | Preset'ler tema değişkenidir: 0 / `var(--ng-spacing-sm)` / `--ng-spacing-md` / `--ng-spacing-lg` (varsayılan temada 8/16/32px). Yalnız bu dört preset geçer: `"18px"` gibi ham değer platform doğrulamasında `invalid-enum-value` ile reddedilir. `box-section`/`aibox`'taki sayısal `gapCustom` doluysa preset'i ezer. |
925
1136
  | `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. |
926
1137
 
927
1138
  **`form` da bu grubun tamamını taşır (2026-09-01):** varsayılanları container ile aynı
@@ -997,10 +1208,10 @@ Beş şeyi bilmeden flex kurma:
997
1208
  4. **`layout: 'grid'` iken diğer altı prop ÖLÜDÜR.** Katalogda tanımlı oldukları için
998
1209
  doğrulama duvarı susar, renderer da okumaz: ızgara kapsayıcıya `direction: 'column'` yazmak
999
1210
  sessizce hiçbir şey yapmaz. Gateway bunu **PAGE-015 (uyarı)** ile söyler.
1000
- 5. **Flex düzeni kırılım BİLMEZ.** `direction`/`gap`/hizalama masaüstü ve mobilde AYNIDIR;
1001
- sıra ve span'lar Desktop (1024) bloğundandır — Studio, mobil kırılım düzenlenirken flex
1211
+ 5. **Flex düzeni kırılım BİLMEZ.** `direction`/`gap`/hizalama masaüstü ve dar ekranda AYNIDIR;
1212
+ sıra ve span'lar Desktop (1024) bloğundandır — Studio, dar ekran kırılımı düzenlenirken flex
1002
1213
  bölgeyi salt-okunur kilitler. Kırılım bilen yalnız `blocks[]` ve `appearance.hideOn`'dur;
1003
- mobilde farklı davranış gereken satır için `layout: 'grid'` kullan (Kural 1f mobil notu).
1214
+ dar ekranda farklı davranış gereken satır için `layout: 'grid'` kullan (Kural 1f dar ekran notu).
1004
1215
 
1005
1216
  ```jsonc
1006
1217
  // Ortalanmış giriş kartı — style ile CSS hack'i YOK, kabın kendi düzeni
@@ -1023,7 +1234,7 @@ gibi bitişik duracak elemanlar için ÜÇ şart birden gerekir; biri eksikse ar
1023
1234
 
1024
1235
  1. Her çocuğa **`appearance.widthType: 'auto'`** — içerik kadar yer kaplar (`flex: 0 1 auto`).
1025
1236
  Yazılmazsa eski 12 kolon payı geçerlidir ve çocuk kolonunu doldurur, bitişemez.
1026
- 2. Kaba **`gap: 'none'`** (ya da ham ince değer: `"2px"`, `"4px"` — alan `allowCustom`).
1237
+ 2. Kaba **`gap: 'none'`** (ham ince değer yazılamaz — `"2px"` platformda 422; ince aralık gerekiyorsa çocuğa `appearance.marginType: 'custom'`).
1027
1238
  3. Her çocuğa **`appearance.marginType: 'none'`** — en çok atlanan şart: `marginType`
1028
1239
  yazılmamış düğüm `normal` sayılır ve her kenara 4px basar (`spacing.ts`
1029
1240
  `CANVAS_NORMAL_MARGIN`), yani `gap:'none'` olsa bile komşular arasında 8px kalır.
@@ -1100,8 +1311,9 @@ UYGULAMAZ** — koordinatları aynen kaydeder (aşağı itme yalnız `page_creat
1100
1311
  > Ölçüm: `radio`, `textarea`, `container`, `form`, `button` **var**; `text` ve `multi-select`
1101
1312
  > **yok**. Çözüm: gizlenecek komponenti `isHidden`'ı olan bir `container`'a sar ve koşulu
1102
1313
  > sarmalayıcıya bağla (yukarıdaki `box_multi`). Sarmalayıcıyı **`isWrapper:false`** kur
1103
- > (Kural 1f: wrapper = `min-height:80vh`) ve çerçevesini `style` ile kapat:
1104
- > `{ background:'transparent', border:'none', '&__body': { padding:'0px' } }`.
1314
+ > (Kural 1f: wrapper = `min-height:80vh`) ve çerçevesini `surfaceStyle` ile kapat (dört
1315
+ > `border*` yönü `'none'`, `background:'transparent'` — §Kap seçimi); gövde dolgusu
1316
+ > `style`'da kalır: `{ '&__body': { padding:'0px' } }`.
1105
1317
  > `appearance.isHidden` diye bir alan YOKTUR (2026-09-15'te platformdan kaldırıldı, S-16) —
1106
1318
  > görünürlük yalnız `props.isHidden`'dır.
1107
1319
  >
@@ -1132,15 +1344,22 @@ UYGULAMAZ** — koordinatları aynen kaydeder (aşağı itme yalnız `page_creat
1132
1344
  > SONRAKİ ağaca bakarak. Layout'ta da geçerlidir: kırılma
1133
1345
  > `_repeaterValues` geri yazmasındadır, barındıranın sayfa mı layout mu olduğuna bakmaz.
1134
1346
  >
1347
+ > **Mobil uygulamada** başlıklar altında gruplu liste (güne/kategoriye göre) iç içe repeater
1348
+ > gerektirmez: tek `grouped-list` (`groupBy` + başlık `{{ Section.title }}` + öğe şablonu) —
1349
+ > `platform_guide('mobile')` §Güne göre görev listesi. PAGE-013 mesajı mobilde bunu önerir.
1350
+ >
1135
1351
  > **Yapılacak:** satır içi listeyi tek bir `textarea`'da **satır başına bir öğe** olarak topla
1136
1352
  > (`options_text`), kaydederken `split('\n')`'le böl. Ölçülmüş çalışan sürüm: anket editörü
1137
1353
  > (`anket-admin/anket-duzenle`). Gerçekten satır satır düzenleme gerekiyorsa iç listeyi AYRI bir
1138
1354
  > sayfada/modalda yönet — iç içe repeater bunu karşılamıyor.
1139
1355
 
1140
1356
  > **Repeater satırını akıştan silmek/eklemek:** repeater ÇOCUĞUNDAKİ bir butonun click akışında
1141
- > `RowIndex` doğru satırı verir, ve hedef repeater'ın KENDİSİ olduğu için `rows`'a yazmak serbesttir
1142
- > (`isRepeaterSelf` dalı satır-değeri yoluna girmez). Ölçülmüş desen:
1143
- > `q_list.rows = (q_list.rows || []).filter(function(r, i){ return i !== RowIndex; })`.
1357
+ > tıklanan satır olay makineye gitmeden SEÇİLİR, yani `q_list.selectedIndex` o satırdır — her runtime
1358
+ > sürümünde. Hedef repeater'ın KENDİSİ olduğu için `rows`'a yazmak serbesttir (`isRepeaterSelf` dalı
1359
+ > satır-değeri yoluna girmez). Desen:
1360
+ > `q_list.rows = (q_list.rows || []).filter(function(r, i){ return i !== q_list.selectedIndex; })`.
1361
+ > `RowIndex` de aynı satırı verir, ama yalnız ARC-121 düzeltmesini (2026-09-27) taşıyan runtime'da;
1362
+ > eskisinde akışta tanımsızdı.
1144
1363
  > Ekleme aynı şekilde `.slice()` + `push` ile yapılır; satır numarasını ekledikten sonra yeniden ata.
1145
1364
 
1146
1365
  **Gönderimi saklamak:** satır başına normalize kayıt yerine tek `TEXT` kolonda anlık görüntü
@@ -1149,6 +1368,45 @@ kendisi nesneye çevirir; Studio tablosunda `[object Object]` görünmesi normal
1149
1368
  (`platform_guide('entity')` → "TEXT kolonunda JSON"). Ekranın görsel tarafı için **Kural 1h**:
1150
1369
  tema `--ng-*` değişkenleriyle tek yerden çevrilir, `html` kaçış kapısına gerek kalmaz.
1151
1370
 
1371
+ ## Kural 1g-2 — Mobil davranış: dört ayar, dört ayrı sahip
1372
+
1373
+ Mobil listelerin davranışı TEK bileşende toplanmaz. Hangi ayarın kimde olduğunu bilmeden yazılan
1374
+ şema sessizce çalışmaz — prop var olmayan bileşene yazılır ve `page_schema_patch` onu düşürmez,
1375
+ çizer yok sayar.
1376
+
1377
+ | Davranış | Sahip bileşen | Prop / olay |
1378
+ |---|---|---|
1379
+ | Çekerek yenile | `scroll-view` (listeyi SARAN kap) | `refreshable: true` + `refresh` olayı |
1380
+ | Sonsuz kaydırma | `scroll-view` | `scrollEnd` olayı (prop yok) |
1381
+ | Kaydırma eylemleri | `repeater` | `swipeActions: [{ key, label, tone, icon }]` + `swipe` olayı, payload `{ key, index, row }` |
1382
+ | Satıra dokunma | `repeater` | `itemPressable: true` + `itemPress` olayı, payload `{ index, row }` |
1383
+
1384
+ Çekerek yenileyi `repeater`'a yazmak en sık hatadır: liste kendi kaydırmasını yapmaz, onu saran
1385
+ `scroll-view` yapar. `repeater` kökteyse (kap yok) yenileme hiç çizilmez.
1386
+
1387
+ Satıra dokunmanın kartın `click`'iyle yapılan eski yolu mobilde bırakıldı: `itemPress` satır
1388
+ bağlamını payload'da taşır ve seçimi (`selectedIndex`) de yazar, yani akış satırı
1389
+ `<tekrarlayıcı>.selectedItem` ile okumaya devam eder.
1390
+
1391
+ ### `redirect.presentation`
1392
+
1393
+ `redirect` adımının `args.presentation` alanı ekranın NASIL açıldığını söyler ve kapalı bir küme
1394
+ alır:
1395
+
1396
+ - `push` — yığına ekler, geri düğmesi önceki ekrana döner (**varsayılan**; alan yazılmayabilir)
1397
+ - `modal` — alttan açılan ayrı bir sunum; geri onu kapatır, altındaki ekran yerinde kalır
1398
+ - `replace` — geçerli ekranı değiştirir, geçmişe girmez (giriş → ana ekran için doğru olan)
1399
+
1400
+ Küme dışı bir değer **MOB-008** uyarısıdır ve backend doğrulayıcısı 422 döner. Alanı hiç yazmamak
1401
+ kusur değildir.
1402
+
1403
+ **Sekme hedefinde `modal` çalışmaz.** Hedef sayfa bir sekmeyse gezinme her zaman `navigate`'e
1404
+ düşer: sekme yığını kendi geçmişini tutar ve modal onu kırardı. `presentation`ı yazabilirsiniz,
1405
+ çizer sessizce yok sayar — sekme dışı bir sayfaya modal istiyorsanız o sayfayı sekme listesinden
1406
+ çıkarın.
1407
+
1408
+ Web uygulamasında alan yok sayılır; gateway web profilinde bu konuda bulgu üretmez.
1409
+
1152
1410
  ## Kural 1h — Tasarım: `style` prop'u ve tema değişkenleri
1153
1411
 
1154
1412
  Her komponentin `style` prop'u vardır ve emotion `css()` bloğudur: iç seçiciler, pseudo-class'lar
@@ -1171,6 +1429,12 @@ yapmanın yolu budur — ölçülmüş kurallar (anket-doldur, 2026-08-19):
1171
1429
  > düğümleri silip yeniden kurmaya, o da 20 setter'ın sessizce kopmasına yol açtı. Gateway artık
1172
1430
  > kebab-case anahtarı camelCase'e çevirir ve `gatewayNormalized` ile bildirir; seçici anahtarlara
1173
1431
  > (`&__body`, `:hover`, `--ng-*`) dokunmaz.
1432
+ >
1433
+ > **İSTİSNA — satır içi stil dalları (TB-129).** Kural `style` prop'unun emotion bloğu olan
1434
+ > dalları içindir. `table.style.thead|th|tr|td` ise DOM'un `style` attribute'una string olarak
1435
+ > basılır ve orada **kebab-case zorunludur**; gateway bu dört alanın altına 2026-09-20'den beri
1436
+ > hiç dokunmaz (`INLINE_STYLE_FIELDS_BY_TYPE`). `table.style.root` ve öteki komponentler eskisi
1437
+ > gibi çevrilir. Çeviri bu dalları da camelCase yapınca stil sessizce düşüyordu (S2-39).
1174
1438
 
1175
1439
  **1. Temayı tek yerden çevir: `page_container.style` içinde `--ng-*` değişkenlerini ez.**
1176
1440
  Runtime bütün renk/tipografi/form tokenlarını CSS değişkeni olarak basar
@@ -1330,6 +1594,20 @@ kullanacağını bilmek gereksiz CSS yazmayı önler:
1330
1594
  | Şema prop'ları | yerleşim (`layout`/`direction`/`gap`/`gridColumns`), semantik renkler, boyut (`size`) | `replace_prop` |
1331
1595
  | `style` prop'u | geri kalan her şey; iç seçiciler, pseudo-class, `--ng-*` token ezme | `replace_prop` / `set_binding` |
1332
1596
 
1597
+ **Kap seçimi — çerçeve tasarımdan gelir, kaptan değil.** `container`'ın tabanı kart yüzeyidir:
1598
+ tema her `container`'a 1px `--ng-border-primary` çerçeve verir. Salt düzen için açılan kaplar
1599
+ (satır, sütun, başlık şeridi, buton grubu, gövde/iki sütun kabuğu) bu yüzden ekranda iç içe
1600
+ çerçeveler olarak görünür — kullanıcı bunu hata olarak bildirdi (qm `qms-dokumanlar`,
1601
+ 2026-09-30: başlık şeridi, buton grubu ve içerik sütunu çerçeveli çizildi).
1602
+
1603
+ 1. Tasarımda o kutunun **çerçevesi yoksa `box` kullan** — `box`'ın tabanında çerçeve yoktur.
1604
+ 2. `container` kullanmak zorundaysan (başlık bandı, `isLoading`, `page_container`, mevcut düğüm)
1605
+ çerçeveyi **`surfaceStyle` ile kapat**, `style`'a ham `border` yazma:
1606
+ `replace_prop { target, prop: 'surfaceStyle', value: { borderTop: 'none', borderRight: 'none', borderBottom: 'none', borderLeft: 'none' } }`.
1607
+ Tek yön kalacaksa (ör. ayraç çizgisi) yalnız o yönü boş bırak. Zemin de istenmiyorsa
1608
+ aynı gruba `background: 'transparent'`.
1609
+ 3. Çerçeveyi yalnız tasarımda **gerçekten kart olan** kutu taşır (tablo kartı, özet kartı).
1610
+
1333
1611
  **Dikkat — `appearance` kutunun İÇİNİ boyamaz.** Yüzey stili düğüm sarmalayıcısına
1334
1612
  (`.ng-app__node-item`) yazılır; `box` gibi kendi zeminini çizen bir komponentin içi bundan
1335
1613
  etkilenmez. Tipli stil modeline göçen komponentler bu boşluğu kapatır: `box` (ve emekli
@@ -1389,6 +1667,16 @@ alanları silinir. Gateway bu yüzden `page_update`'te sayfayı önce okur, yaln
1389
1667
  alanları değiştirip tam gövdeyi geri yazar (`layout_update` ve `component_update` de aynı).
1390
1668
  Yine de `schema` parametresini elle doldurursan okuduğun hâli değil, **verdiğin liste** yazılır.
1391
1669
 
1670
+ **`page_update` sürümsüz yazmaz (PAGE-039).** Platform sayfa PUT'unu `baseVersion` olmadan reddeder.
1671
+ `baseVersion` vermezsen gateway bu sayfada en son gördüğü sürümü gönderir: `page_get` ya da
1672
+ `page_schema_get` yanıtındaki `version`, `page_schema_patch`'in `newVersion`'ı ya da önceki
1673
+ `page_update`'in yazdığı sürüm. Hiçbiri yoksa istek **gönderilmez** — önce oku. Sayfa o sürümden beri
1674
+ değiştiyse (Studio'da açık sekme, başka ajan) 409 döner; mesaj güncel sürümü ve son yazmayı (zaman,
1675
+ kullanıcı, kaynak, revizyon) söyler ve hiçbir şey yazılmaz. Doğru yol yeniden okuyup değişikliği
1676
+ `page_schema_patch` ile yapmaktır; aradaki değişikliği bilerek ezmek için `force: true` son sürümü
1677
+ okuyup onun üstüne yazar ve yanıt `forced: true` taşır. Sebep: Bitki Bakım koşusunda sürümsüz bir
1678
+ tam gövde yazımı ana sayfayı ve profili boşalttı, akışları sildirdi (14:02).
1679
+
1392
1680
  ### Atomiklik: 500 KB'lık şemada tek komponente dokunmak
1393
1681
 
1394
1682
  `page_schema_patch` şemanın tamamını göndermez — yalnız op'ları gönderir. Tam gövde ne LLM
@@ -1398,7 +1686,8 @@ ve geri yazar. Sözleşme (`PatchPageSchemaCommandHandler`):
1398
1686
  | Aşama | Ne yapar |
1399
1687
  |---|---|
1400
1688
  | Kilit | Sayfa başına 60 sn'lik dağıtık kilit; başka bir aktör yazıyorsa istek `PageSchemaLockedException` ile döner (409) |
1401
- | Sürüm | `baseVersion` verdiysen `UpdatedAt.Ticks` ile karşılaştırılır; tutmazsa `PageSchemaVersionConflictException` (409) ve **hiçbir op uygulanmaz** |
1689
+ | Sürüm | `baseVersion` verdiysen `UpdatedAt.Ticks` ile karşılaştırılır; tutmazsa `PageSchemaVersionConflictException` (409) ve **hiçbir op uygulanmaz** — bütün op'lar etkisizse hariç (aşağıdaki "Tekrar" satırı) |
1690
+ | Tekrar | `add_component`'in `elementId`'si zaten varsa ve düğüm istenen ebeveynde, istenen içerikle duruyorsa op **etkisizdir** (hata değil; konum karşılaştırılmaz). Hedef düğümü değiştirmeyen `replace_prop`/`set_*`/`add_event` da etkisizdir. Bütün op'lar etkisizse yama yazılmaz, sürüm artmaz ve eski `baseVersion`'a rağmen 200 `noop: true` döner; sürüm eskiyse `stale: true` ve `newVersion` sizin gönderdiğinizdir. Aynı id'de farklı düğüm hâlâ 422 `duplicate-id`'dir |
1402
1691
  | Uygulama | Her op hedefini ağaçta arar (`Find` → JSON Pointer) ve **yalnız o düğüme** dokunur; kardeşler, üst düğümler ve diğer alt ağaçlar bit düzeyinde korunur |
1403
1692
  | Hep-ya-hiç | Op'lar önce bellekte uygulanır; **tek bir hata** (bilinmeyen op, bulunamayan hedef, tekrar eden elementId, doğrulama) tüm komutu düşürür — kısmi yazma yoktur |
1404
1693
  | Doğrulama | Tam şema `PageSchemaValidator`'dan geçer (422 + alan bazlı `errors`) |
@@ -1407,7 +1696,18 @@ ve geri yazar. Sözleşme (`PatchPageSchemaCommandHandler`):
1407
1696
 
1408
1697
  Yanıt üç alan döner: `applied` (uygulanan op sayısı), **`appliedPatch`** (her op'un şemada tam
1409
1698
  olarak hangi JSON Pointer'a ne yaptığı — RFC 6902 biçiminde; "gerçekten yalnız o komponente mi
1410
- dokunuldu" sorusunun kanıtı) ve **`newVersion`** (`UpdatedAt.Ticks`).
1699
+ dokunuldu" sorusunun kanıtı) ve **`newVersion`** (`UpdatedAt.Ticks`). Etkisiz op varsa `unchanged`
1700
+ (elementId listesi) ve `alreadyApplied` cümlesi gelir; hepsi etkisizse `noop: true`.
1701
+
1702
+ **Yanıt kaybolursa aynı gövdeyle tekrarla.** Ağ koptu, zaman aşımı oldu ve yamanın uygulanıp
1703
+ uygulanmadığını bilmiyorsan aynı `page_schema_patch`'i **aynı gövde ve aynı `baseVersion`** ile
1704
+ yeniden gönder. Uygulanmışsa `noop: true` + `alreadyApplied` döner, hiçbir şey ikinci kez yazılmaz;
1705
+ uygulanmamışsa yama şimdi uygulanır. `add_component` (gövdede `id` verme — platform her istekte yeni
1706
+ `id` üretir, karşılaştırma onu yok sayar), aynı değeri yazan `replace_prop`/`set_*` ve aynı bağı kuran
1707
+ `add_event` tekrarda etkisizdir. İki istisna: `remove_component` ikinci kez `unknown-target` döner — o
1708
+ durumda `page_schema_get` ile bak; eklediğin düğümün bir prop'unu aynı yamanın sonraki op'u
1709
+ değiştirdiyse tekrar `duplicate-id` alır. Yanıt `stale: true` taşıyorsa sayfa sürümünden beri başka bir
1710
+ yazımla da değişmiştir: devam etmeden önce yeniden oku. `idempotencyKey` yoktur ve gerekmez.
1411
1711
 
1412
1712
  > **`appliedPatch` gövdesi VARSAYILAN OLARAK iskelettir.** Girdi `{ op, path }` döner; eklenen
1413
1713
  > düğüm `component` altında yalnız `id`/`elementId`/`name`/`type`/`children` taşır. Kanıt zaten
@@ -1427,12 +1727,29 @@ yamanın `baseVersion`'ıdır. Kontrol platformda atomiktir (satır kilidi + sü
1427
1727
  `UPDATE`'te), yani iki çağrı arasına giren yazarı da yakalar. `baseVersion` vermezsen gateway yalnız
1428
1728
  bu çağrıda zaten okuduğu sayfanın sürümünü gönderir; hiç okumadıysa yama sürümsüz gider ve son
1429
1729
  yazan kazanır. Eski `expectedUpdatedAt` hâlâ çalışır ama `baseVersion`'ın yerini tutmaz.
1730
+
1731
+ **Studio da sürüm gönderir (2026-09-26, ZM-27).** Önceden Studio'da açık kalan bir sekme, yayını
1732
+ kaçırdığı yamalarının üstüne kendi eski (bir kez boş) şemasını yazıyor ve bağı kalmayan akışları
1733
+ sildiriyordu. Artık Studio'nun her kaydı sürüm taşır: eski sekme 409 alır ve taslağını senin
1734
+ yazdığının üstüne birleştirir. `revision_list`'te yamandan sonra `source: studio` satırı görürsen bu
1735
+ artık bir üzerine yazma değil, birleştirilmiş bir kayıttır — yine de `revision_diff` ile bak.
1430
1736
  Yanıttaki `touched`, kaydı değişen elementId'leri listeler (eklenen alt ağaç dâhil).
1431
1737
 
1432
1738
  > **Büyük şemada çalışma sırası:** `mode:'summary'` ile ağacı çıkar (props yok, 500 KB'lık sayfada
1433
1739
  > bile küçük) → hedefin `elementId`'sini al → gerekiyorsa `mode:'subtree'` ile yalnız o dalı oku →
1434
1740
  > op'ları `page_schema_validate` ile kuru koştur → `page_schema_patch`. `mode:'full'` yalnız
1435
1741
  > gerçekten tüm şema gerektiğinde çağrılır.
1742
+ >
1743
+ > **`full` sığmazsa hata verir, kırpmaz.** Yanıt boyut sınırını aşan şemada `mode:'full'` hata döner
1744
+ > ve düğüm sayısını söyler. Tamamı gerekiyorsa `cursor:'0'` ile oku ve `nextCursor` gelmeyene kadar
1745
+ > sür: her parça `items[]` taşır, kalem `parentElementId` düğümünün `children[index]`'inin yerine
1746
+ > geçer (`parentElementId` yoksa kök dizinin `[index]`'i); alt ağacı sığmayan düğüm kabuk gelir
1747
+ > (`children: [{ elementId, deferred: true }]`) ve sonraki kalemler onu doldurur. Toplanan düğüm sayısı
1748
+ > `nodeCount`'a eşit değilse okuma eksiktir. Okuma arasında sayfa değişirse 409 — baştan başla.
1749
+ > `allowTruncated:true` kırpılmış gövdeyi açıkça verir (`truncated: true`); o gövdeye dayanan
1750
+ > `page_schema_patch` PAGE-038 uyarısı taşır. **`page_update`'e kırpık ya da yarım şema gönderme:**
1751
+ > PUT tam REPLACE'tir, eksik düğümler geri dönüşsüz silinir — gateway düğüm eksilten şemayı
1752
+ > `allowNodeLoss:true` olmadan PAGE-043 ile reddeder.
1436
1753
 
1437
1754
  > **`remove_component`'in yan etkisi — tek istisna.** Sayfa kaydedilirken artık hiçbir komponent
1438
1755
  > olayının, prop'unun ya da sayfa olayının referans vermediği akışlar platformca **kalıcı olarak
@@ -1478,19 +1795,24 @@ patch'te bütün `set_event`'ler:
1478
1795
  ```
1479
1796
  page_create → page_schema_patch (tüm add_component)
1480
1797
  → TÜM flow_create'ler
1481
- → TEK page_schema_patch (bütün set_event'ler)
1798
+ → TEK page_schema_patch (bütün set_event'ler) + page_update { events } (sayfanın mount'u)
1482
1799
  ```
1483
1800
 
1484
1801
  Sonradan akış eklemen gerekirse sayfayı baştan kurmana gerek yok — **bağı geçici olarak çöz**:
1485
1802
 
1486
1803
  ```jsonc
1487
- 1) page_schema_patch [{ "op":"set_event", "target":"page_container", "event":"mount", "eventValue": null }]
1804
+ 1) page_schema_patch [{ "op":"set_event", "target":"<bağlı komponent>", "event":"click", "eventValue": null }]
1805
+ page_update { events: [] } // sayfanın kendi mount/destroy bağları
1488
1806
  2) flow_create × N
1489
- 3) page_schema_patch [ bütün set_event'ler tek seferde ]
1807
+ 3) page_schema_patch [ bütün set_event'ler tek seferde ] + page_update { events: [...] }
1490
1808
  ```
1491
1809
 
1492
1810
  `eventValue: null` olay kaydını bırakıp değerini boşaltır ve kilidi açar.
1493
1811
 
1812
+ > Ölçüm 2026-09-25 (`egitim-portali` › `surecler`, Studio'nun `POST pages/<id>/flows` çağrısı):
1813
+ > sayfada `page_container.mount` ve komponent olayları bağlıyken yeni akış **reddedilmeden**
1814
+ > oluştu. Kilit artık her durumda devrede olmayabilir; yine de sıralı kuruluş güvenli yoldur.
1815
+
1494
1816
  > **Aynı kilit LAYOUT'ta da vardır** (ölçüm 2026-08-18: menüye yeni bağlantı eklerken
1495
1817
  > `POST /layouts/<id>/flows` aynı hatayı verdi). Layout'ta sıra sayfadakiyle aynıdır ve bağlar
1496
1818
  > `layout_schema_patch` ile yazılır: `flow_create × N` → TEK `layout_schema_patch` (bütün
@@ -1543,7 +1865,9 @@ isimlerine bağlanır.
1543
1865
 
1544
1866
  **Platform değişkenleri tek tek değil SET olarak tutar:** uygulama başına bir kayıt, sayfa başına
1545
1867
  bir kayıt (`variable_create` `pageId` verirsen sayfa, vermezsen uygulama düzeyi). Kayıt zaten varsa
1546
- create update'e döner. Şekil `[{ key, value }]`; `value` **herhangi bir JSON** olabilir:
1868
+ create update'e döner. Şekil `[{ key, value, storage? }]`; `value` **herhangi bir JSON** olabilir.
1869
+ State **bellektedir** — sayfa her açıldığında `value`'dan başlar; `storage: 'device' | 'secure'` yalnız
1870
+ mobil uygulamada değeri cihazda saklar (`platform_guide('mobile')` §Kalıcı state, `MOB-014`):
1547
1871
 
1548
1872
  ```jsonc
1549
1873
  variable_create({ applicationId, pageId, variables: [
@@ -1555,7 +1879,7 @@ variable_create({ applicationId, pageId, variables: [
1555
1879
  - Uç seti **komple değiştirir**; gateway mevcut seti okuyup anahtar bazında birleştirir. Kasten
1556
1880
  silmek için `replace: true`. `variable_delete` **setin tamamını** siler.
1557
1881
  - Platform `data`'yı JSON **string** olarak saklar (`"[{\"key\":…}]"`); gateway çeviriyi iki yönde
1558
- de yapar — `variable_list` sana `[{key, value}]` döndürür. Ham HTTP kullanıyorsan string
1882
+ de yapar — `variable_list` sana `[{key, value, storage?}]` döndürür. Ham HTTP kullanıyorsan string
1559
1883
  zorunludur: gövdede yalnız `data` alanı okunur, `name`/`type`/`defaultValue` gibi alanlar
1560
1884
  **sessizce yok sayılır** ve kayıt boş yazılır (istek yine 200 döner).
1561
1885
 
@@ -1714,7 +2038,8 @@ ya da `set_binding { prop: 'isHidden' }` (Kural 7a'daki `isHidden` notu).
1714
2038
 
1715
2039
  **Kip ayrımı query parametresidir:** aynı sayfa `?id=<kayıt>` ile edit, parametresiz create.
1716
2040
 
1717
- `mount` akışı (`page_container` ya da `form` üzerine bağlanır):
2041
+ `mount` akışı (sayfanın kendi `mount` olayına — `page_update { events }` — ya da `form` üzerine bağlanır;
2042
+ `page_container`'a değil, Kural 1d-2):
1718
2043
 
1719
2044
  ```
1720
2045
  start → [setter] page_container.isLoading = true
@@ -1876,15 +2201,24 @@ page_container
1876
2201
  ```
1877
2202
  start → [setter] btn.isLoading = true, btn.isDisabled = true, notification gizle
1878
2203
  → [microservice] sign-in (POST; email + password body'den)
1879
- → [condition] !!Steps.signIn.data.token
1880
- ├─ always → [setter] buton durumunu sıfırla
1881
- ├─ if → [setcookie] { name:'APP_Token', value:'Steps.signIn.data.token',
1882
- │ expiresInHours: 240 }
1883
- │ → [redirect] hedef sayfaya
1884
- └─ else → [setter] notification.content =
1885
- { type:'code', ref:'Steps.signIn.data.message || "Giriş başarısız"' }
2204
+ ├─ error → [setter] btn.isLoading=false, btn.isDisabled=false,
2205
+ │ notification.content =
2206
+ │ { type:'code', ref:'Steps.signIn.error?.message ?? "Giriş başarısız"' }
2207
+ └─ always → [condition] !!Steps.signIn.data.token
2208
+ ├─ always → [setter] buton durumunu sıfırla
2209
+ ├─ if → [setcookie] { name:'APP_Token', value:'Steps.signIn.data.token',
2210
+ │ expiresInHours: 240 }
2211
+ │ → [redirect] hedef sayfaya
2212
+ └─ else → [setter] notification.content =
2213
+ { type:'code', ref:'Steps.signIn.error?.message ?? "Giriş başarısız"' }
1886
2214
  ```
1887
2215
 
2216
+ - **`sign-in` adımının `error` dalı burada ZORUNLUDUR, süs değil.** Dal bağlı değilken bu şablonun
2217
+ `condition`'ı `Steps.signIn.data.token`'ı okur ve gateway'i **FLOW-038** ile uyarır (yukarıdaki
2218
+ "Hangi yolu seçeceksin" notu): hata anında `data` `null`'dur, token okuması sessizce `undefined`
2219
+ döner. Dal bağlanınca hata onun üzerinden sürer — `always`/`condition` yalnız BAŞARIDA çalışır,
2220
+ yani `data.token` orada gerçek bir iş kuralıdır (uç 200 dönüp token'sız yanıt verirse), hata
2221
+ algılama YÖNTEMİ değil.
1888
2222
  - **Cookie adı middleware'in okuduğu adla birebir aynı olmalı** (`cookie.APP_Token` —
1889
2223
  `platform_guide('middleware')` Tarif 1). Uyuşmazlık sessizce "hep 401" demektir.
1890
2224
  - `redirect` adımı `isRaw: true` iken `path` **çok satırlı JS bloğudur** (son ifade değeri döner) —
@@ -2074,9 +2408,14 @@ hiçbiri** bağlı değildi; üç portalın da rol menüsü boş çıkıyor, pan
2074
2408
  > (`dto_get` ile bak: genelde `role`, `role_name`, `roles_csv` vardır). Sayfa tarafında CSV
2075
2409
  > kullan — ayrıntı ve şablon `platform_guide('expressions')` §2.
2076
2410
  >
2077
- > **Uç hata dönerse `.data` dizi değildir.** 401/404 sonrası `Steps.x.data` bir nesnedir ve
2078
- > `rows || []` bunu yakalamaz (nesne truthy) → `rows.filter is not a function` /
2079
- > `(items ?? []).map is not a function`. `Array.isArray(Steps.x?.data) ? Steps.x.data : []` yaz.
2411
+ > **Uç hata dönerse `.data` NULL'dur, hata gövdesi değil (ADR 0004, 2026-09-26).** 2026-09-26'ya
2412
+ > kadar `Steps.x.data` 401/404 sonrası hata gövdesiydi ve `rows || []` bunu yakalamıyordu (nesne
2413
+ > truthy) → `rows.filter is not a function` / `(items ?? []).map is not a function`; artık uç hata
2414
+ > dönünce `data` **null**'dur ve bu adım boş veriyle koşar — `rows` gibi bir listeye bağlarsan
2415
+ > bileşen `null`'ı boş liste sayar, `Array.isArray(...) ? ... : []` sarmalamaya artık gerek yok.
2416
+ > Hata OLDUĞUNU fark etmeden geçmemek için `error` dalı bağlayın (yukarıdaki §"4xx de hatadır",
2417
+ > gateway bunu bağlı değilken **FLOW-038** ile uyarır); hata gövdesini görmek istiyorsan
2418
+ > `Steps.x.error.body`'yi okuyun.
2080
2419
 
2081
2420
  ## ⚠️ `page_schema_patch` `html` komponentini reddediyor — katalogda mükerrer kayıt (2026-08-19)
2082
2421
 
@@ -2139,6 +2478,44 @@ micro-op hattına dönün.
2139
2478
  > uyarı yoktu. Silinmesini bilerek istiyorsanız çağrıyı `allowFlowDeletion: true` ile tekrarlayın;
2140
2479
  > yanıt `deletedFlows` ile hangi akışların gittiğini listeler.
2141
2480
  >
2481
+ > **Silinen akış geri kurulabilir (2026-09-26, ZM-27).** Sayfa kaydının sildiği akış artık revizyon
2482
+ > defterine `operation: delete` satırı olarak düşer (kaynağı `studio` ya da `mcp`). `flow_restore`
2483
+ > akışı son revizyonundaki ad, slug ve adımlarla AYNI id ile yeniden kurar; olaya bağlamaz —
2484
+ > geri geldikten sonra `set_event`. İki sınır: revizyon saklama süresi (budanan akış 404) ve
2485
+ > adımların hedeflediği komponentler — akış bir komponenti tetikleyen/parametre olarak
2486
+ > gösteriyorsa o komponent şemada durmalıdır (canlı ölçüldü: `flow_restore` "Trigger Element Id"
2487
+ > dış anahtarına çarpar).
2488
+ >
2489
+ > **Boşalan sayfa: `page_restore` (2026-09-26).** Sayfayı ve akışlarını birlikte geri kurar:
2490
+ > `revision_list(module: 'page', recordId: <pageId>)` → boşalmadan önceki revizyonu bul (`source` ve
2491
+ > `correlationId` yardım eder; `revision_get` ile içeriğine bak) → `page_restore({ revisionNo })`.
2492
+ > Platform önce eksik akışları adımsız kurar, sonra şemayı eski komponent id'leriyle yazar, sonra
2493
+ > adımları yazar — akış→komponent ve olay→akış dış anahtarları bu sırayla tek transaction'da çözülür.
2494
+ > Yanıt `restoredFlows` ve `deletedFlows` (N'den sonra bağlanmış, şimdi yetim kalan) döner.
2495
+ > Revizyonu kalmamış bir akış varsa 409 ve hiçbir şey yazılmaz.
2496
+ >
2497
+ > **Doğru revizyonu seç.** Şemayı yüklemeyen komutların (`UpdateProjectCommand`,
2498
+ > `UpdateApplicationCommand`…) yazdığı sayfa revizyonları komponent taşımaz; komponenti olan sayfayı
2499
+ > böyle bir revizyona döndürmek 409 `PageRevisionEmptyException` ile reddedilir (bilerek boşaltmak için
2500
+ > `allowEmpty: true`). `correlationId`'si `PatchPageSchemaCommand` ya da `UpdatePageCommand` olan
2501
+ > revizyonu seç. İki sınır daha: N'den beri silinen layout/rol/middleware/özel komponent ya da başka
2502
+ > sayfanın aldığı slug geri yüklemeyi dış anahtar/benzersizlik hatasıyla düşürür (transaction geri
2503
+ > alınır, veri kaybı yok); yalnız başka bir akışın adımından çağrılan (olaya bağlı olmayan) akışlar
2504
+ > geri kurulmaz — onlar için `flow_restore`.
2505
+ >
2506
+ > **Koruma op listesinin SONUCUNA bakar (TB-125).** İstek atomik olduğu için doğru soru tektir:
2507
+ > yama bittiğinde bu akışa referans kalıyor mu? Aynı yamada akışı başka bir düğüme bağlayıp
2508
+ > eskisini silmek (`add_component` + `set_event` + `remove_component`) artık TEK çağrıda geçer;
2509
+ > 2026-09-20 öncesinde koruma her op'a tek başına baktığı için düğüm taşıyan her yama ikiye
2510
+ > bölünmek zorundaydı (S2-35). Akışı gerçekten referanssız bırakan yama hâlâ durur.
2511
+ >
2512
+ > **Satır aksiyonları statik şemada görünmeyebilir (TB-126).** `table`/`datatable`'ın
2513
+ > `actions[].flow` bağı `{ type: 'flow', ref }` olarak sayılır — ama bu prop'u çoğu zaman YÜKLEME
2514
+ > AKIŞI çalışma zamanında yazar (aksiyon role göre eklenip çıkarılıyorsa başka yolu da yok) ve o
2515
+ > hâlde şemada `actions` BOŞ durur. `flow_list` böyle bir barındırıcıda `unboundFlowIds` yanına
2516
+ > `mayBeBoundAtRuntime: true` koyar: "bağsız akışları temizle" refleksinden önce akışın gerçekten
2517
+ > sahipsiz olduğunu doğrulayın (S2-36).
2518
+ >
2142
2519
  > **FLOW-018 artık silinen düğümün ALT AĞACINI da görüyor.** Eskiden yalnız op'un kendi
2143
2520
  > `target`'ına bakılıyordu: bir kapsayıcı silinince ÇOCUĞUNA yazan setter sessizce kopuyor ve
2144
2521
  > uyarı hiç çıkmıyordu (09-05, `sign_in_form` → `btn_sign_in`).
@@ -2182,6 +2559,9 @@ micro-op hattına dönün.
2182
2559
  style + Kural 1h deseniyle yeniden kurulunca kart 400px'e ve ortaya oturdu. Gateway bunu **PAGE-014 (uyarı)** ile
2183
2560
  yanıtın `layoutWarnings` alanında bildirir. **Düzeni `style` ile kurmaya hiç kalkışma:**
2184
2561
  ortalama/yaslama/aralık işi kapsayıcının kendi `layout: 'flex'` prop grubuna aittir (Kural 1f-2).
2562
+ **İstisna `html`:** onun `style`'ı yazarın işaretlemesine kapsamlı giren CSS metnidir (web'de iç
2563
+ seçicili sınıf, mobilde belgenin stil bloğu) — string asıl biçimdir ve PAGE-014 onda susar. Mobilde
2564
+ `html`'e nesne `style` verme: çizilmez (MOB-002).
2185
2565
  - **Menüde "bulunduğun sayfa" vurgusu: `sidebar`/`layout`'ta `activeHref`, `header`'da
2186
2566
  `activeNavHref`.** `sidebar` ve `layout` menü ağacını ortak renderer'la çizer: `activeHref` boşken
2187
2567
  tarayıcıdaki adrese uyan satırı kendileri vurgular, prop yazılırsa o satır vurgulanır (akıştan
@@ -2231,6 +2611,13 @@ micro-op hattına dönün.
2231
2611
  elemanıdır; başlığı da `__header` bloğunu da render etmez (klinik ölçümü: `__header` sayısı 0).
2232
2612
  `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
2233
2613
  `title`'ına güvenme, **içine bir `text` komponenti** koy.
2614
+ - **`selectedRow`'un ALANLARI sarmalı korur.** `tbl.selectedRow` prop olarak çözülür ama içindeki
2615
+ alanlar `{data, type}` nesneleri olarak kalır: `mapSchemaPropToRawProp` yalnız üst düzey prop'u
2616
+ açar, satırın iç alanlarını açmaz. `tbl.selectedRow.id` bu yüzden bir **nesnedir**, ham değer
2617
+ değil — uca öyle gidince güncelleme hiçbir satırı tutmaz ve sonuç sessizdir (ölçüldü 2026-09-19:
2618
+ `PUT kredi-onayla` → **404**). İfadede `tbl.selectedRow?.id?.data ?? tbl.selectedRow?.id` yazın;
2619
+ aynısı satır eşlemesinden gelen her alan için geçerlidir (S-22).
2620
+
2234
2621
  - **Kod prop'unun ifadesi `ref`'te ve `{{ }}` sarılı olmalı.** `{type:'code', ref:"'' + Row.value"}`
2235
2622
  (süssüz) ekrana ifadenin **kendisini** basar; ifade yalnız `data`'da olursa prop **boş**
2236
2623
  render edilir. `set_binding` micro-op'u eski sürümlerde `data`'ya yazıyordu — bu davranış