@appaflytech/zeyrek-ai-mcp 0.0.7 → 0.0.9

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 (54) hide show
  1. package/README.md +27 -21
  2. package/dist/config.js +2 -0
  3. package/dist/knowledge/backendConstraints.js +3 -0
  4. package/dist/knowledge/componentCatalog.js +172 -33
  5. package/dist/knowledge/componentEvents.js +35 -4
  6. package/dist/knowledge/errorPath.js +1 -1
  7. package/dist/knowledge/normalize.js +474 -23
  8. package/dist/knowledge/propTypes.js +5 -4
  9. package/dist/knowledge/rules.js +113 -6
  10. package/dist/knowledge/styleResolve.js +52 -5
  11. package/dist/knowledge/themeResolve.js +936 -0
  12. package/dist/knowledge/themeRules.js +221 -0
  13. package/dist/knowledge/themeSchema.js +335 -0
  14. package/dist/platform/domSummary.js +144 -0
  15. package/dist/platform/http.js +10 -2
  16. package/dist/platform/screenshot.js +492 -29
  17. package/dist/platform/screenshotMemory.js +55 -0
  18. package/dist/platform/strictArguments.js +32 -0
  19. package/dist/platform/writeDiff.js +6 -3
  20. package/dist/resources/examples/layout-app-shell.json +1 -1
  21. package/dist/resources/examples/page-sign-in.json +1 -1
  22. package/dist/resources/examples/theme-brand.json +162 -0
  23. package/dist/resources/guides/component.md +8 -2
  24. package/dist/resources/guides/expressions.md +6 -0
  25. package/dist/resources/guides/getting-started.md +6 -0
  26. package/dist/resources/guides/layout.md +36 -26
  27. package/dist/resources/guides/microservice.md +160 -6
  28. package/dist/resources/guides/page.md +165 -59
  29. package/dist/resources/guides/theme.md +210 -0
  30. package/dist/resources/guides/translation.md +13 -10
  31. package/dist/resources/guides/workflow.md +141 -7
  32. package/dist/resources/schema-fingerprint.json +2 -2
  33. package/dist/resources/schemas/CreateMicroserviceCommand.json +4 -1
  34. package/dist/resources/schemas/CreateWorkflowCommand.json +4 -1
  35. package/dist/resources/schemas/PreviewMicroserviceCommand.json +4 -1
  36. package/dist/resources/schemas/PreviewWorkflowCommand.json +4 -1
  37. package/dist/resources/schemas/StepDto.json +4 -1
  38. package/dist/resources/schemas/UpdateMicroserviceCommand.json +4 -1
  39. package/dist/resources/schemas/UpdateWorkflowCommand.json +4 -1
  40. package/dist/server.js +52 -42
  41. package/dist/tools/components.js +8 -0
  42. package/dist/tools/designSchemaPatch.js +152 -0
  43. package/dist/tools/flows.js +24 -21
  44. package/dist/tools/layouts.js +8 -8
  45. package/dist/tools/microservices.js +74 -10
  46. package/dist/tools/pages.js +275 -332
  47. package/dist/tools/projects.js +66 -9
  48. package/dist/tools/queries.js +72 -6
  49. package/dist/tools/schemaOps.js +280 -0
  50. package/dist/tools/screenWrite.js +2 -1
  51. package/dist/tools/screens.js +0 -7
  52. package/dist/tools/themes.js +163 -0
  53. package/dist/tools/workflows.js +10 -6
  54. package/package.json +1 -1
@@ -77,6 +77,12 @@ servis formunda görünür ve `microservice_get` outline görünümünde geri ok
77
77
  > (`'all'`) ve SQL'de ona bakan bir dal. Aynı davranış workflow query adımında da geçerli;
78
78
  > oradaki anlatım `platform_guide('workflow')` "Query adımı" bölümünde.
79
79
  >
80
+ > **`(@p = '' OR kolon = @p)` YAZMA** — "filtre boşsa hepsini getir" dalı hiçbir zaman
81
+ > çalışmaz, çünkü boş değer `NULL` bağlanır. Değeri form bileşeninden gelen parametrede
82
+ > dokunulmamış alanın `null` ya da `''` göndermesi fark etmez; ikisi de `NULL` olur
83
+ > (2026-09-15 bulgusu: pano 0 kart, `COALESCE`/`IS NULL` ile 141). Gateway bu kalıbı
84
+ > `query_create`/`query_update`/`query_preview`'da **QRY-007** (warn) ile işaretler.
85
+ >
80
86
  > **KAPSAM UYARISI: query kaynaklı ucun parametreleri ÇAĞRIDAN gelir.** Middleware'in
81
87
  > kurduğu `user` bu yola girmez — kimlik/tenant kapsamını gövdeye yazan bir uç
82
88
  > sahtelenebilir (ölçüldü 2026-09-07). Kapsam sunucuda zorlanacaksa sorguyu bir workflow
@@ -115,7 +121,15 @@ servis formunda görünür ve `microservice_get` outline görünümünde geri ok
115
121
 
116
122
  5. **Doğrulama akışı:** `microservice_preview` (kaydetmeden) → kaydet → `microservice_execute`
117
123
  (gerçek runtime yolu) → sonuç beklenmedikse **aynı parametrelerle `microservice_debug`** —
118
- adım adım executionItems ağacı döner.
124
+ adım adım executionItems ağacı döner. Zamanlanmış (`schedule-job`) uçta sıra farklıdır:
125
+ `workflow_preview` → `microservice_debug` → `microservice_schedule_control` `trigger` →
126
+ `microservice_job_history`; geçmişteki `completed` başarı kanıtı DEĞİLDİR (§Zamanlanmış işin
127
+ gerçek sonucunu görmek).
128
+ Çağrı parametrelerini (sorgu parametresi, filtre değeri) üst düzey `parameters: { ad: değer }`
129
+ ile ver; gateway GET'te query-string'e, POST/PUT'ta gövdeye koyar. Değeri NULL bağlanan query
130
+ parametresini debug yanıtı `unboundParameters` ile söyler — ağaçta o satırın `value` anahtarı
131
+ görünmez. Tool'un tanımadığı bir üst düzey argüman artık reddedilir (GW-005); 2026-09-15'e
132
+ kadar `parameters` hata vermeden atılıyordu ve dört farklı filtre aynı gövdeyi döndürdü (G-13).
119
133
 
120
134
  ## Yanıt sözleşmesi (DTO) uca DEĞİL workflow'a bağlanır (MS-018, block)
121
135
 
@@ -159,8 +173,9 @@ DTO'dan süzülmez (WF-026 — `platform_guide('workflow')` §Hata yolu). Sözle
159
173
  ## Asgari doğru table-source microservice (platformda doğrulanmış kurallar)
160
174
 
161
175
  - `operation` enum'u **tam olarak** şudur ([MicroserviceTableOperation](../../../../zeyrek-backend/src/NgApps.Domain/Contexts/Studio/Microservices/MicroserviceTableOperation.cs)):
162
- `get · create · update · delete · upsert · count`. **`list` yoktur** — liste için
163
- `operation: 'get'` + `isSingle: false`, tek kayıt için `isSingle: true`.
176
+ `get · create · update · delete · upsert · count · bulk-create · bulk-update · bulk-delete`.
177
+ **`list` yoktur** — liste için `operation: 'get'` + `isSingle: false`, tek kayıt için
178
+ `isSingle: true`.
164
179
  - **`upsert` vardır ve tercih edilmelidir.** Aynı formun hem yeni kayıt hem düzenleme
165
180
  yapmasını iki ayrı uç + akışta condition dallanmasıyla kurmayın: tek `operation: 'upsert'`
166
181
  ucu yeterlidir. Aynı işlem workflow **table step**'inde de desteklenir
@@ -174,9 +189,42 @@ DTO'dan süzülmez (WF-026 — `platform_guide('workflow')` §Hata yolu). Sözle
174
189
 
175
190
  > **Upsert'te üretilen alan tuzağı.** `operation: 'upsert'` UPDATE yolunda da alan ifadelerini
176
191
  > yeniden değerlendirir; sicil/kod gibi üretilen bir değeri `source: 'expression'` ile yazarsan
177
- > güncellemede **sessizce değişir** (İK koşusu M-3: EMP-0005 → EMP-0006). Alan bazlı
178
- > `writeOn: create | update | always` platformda **yok**; o gelene kadar oluşturma ve güncelleme
179
- > için iki ayrı uç kurun. Gateway **MS-016** ile uyarır.
192
+ > güncellemede **sessizce değişir** (İK koşusu M-3: EMP-0005 → EMP-0006). Çözüm alan bazlı
193
+ > **`writeOn: create | update | always`**'dir (varsayılan `always`) ve 2026-09-04'ten beri
194
+ > platformdadır: üretilen alana `writeOn: 'create'` verirsen upsert'in UPDATE dalı o alanı satıra
195
+ > hiç koymaz (`ToRow(entityDto, writeOn)` iki satırı ayrı kurar). `writeOn` YALNIZ
196
+ > `operation: 'upsert'` ucunda anlamlıdır — başka operasyonda `always` dışında bir değer
197
+ > **MS-017** ile reddedilir. Gateway **MS-016** ile uyarır; alan `writeOn: 'create'` taşıyorsa
198
+ > susar. **`writeOn` bir "kısmi güncelleme" bayrağı DEĞİLDİR** — hangi DALIN yazacağını söyler,
199
+ > "gövdede gelmeyen alanı atla" demez (aşağıdaki §Güncelleme sözleşmesi).
200
+ - **Toplu yazma için `bulk-create` / `bulk-update` / `bulk-delete` vardır.** Aynı tabloya N satır
201
+ yazmak için akışta döngü kurmayın: bu üç uç gövdedeki **JSON diziyi** tek çağrıda işler
202
+ ([TableMicroserviceExecutor](../../../../zeyrek-backend/src/NgApps.Domain/Contexts/Studio/Microservices/Executors/TableMicroserviceExecutor.cs)).
203
+ Kurallar:
204
+ - Gövde bir **dizi** olur; her eleman bir satırdır. `bulk-delete` gövdesinde yalnız birincil
205
+ anahtar alanı aranır ve tek bir `IN` filtresine çevrilir.
206
+ - `filters` **kullanılmaz** — satırlar ve anahtarlar gövdeden okunur.
207
+ - Tek istekte en fazla **1000 satır** (`EntityConstants.BulkRowLimit`); aşılırsa
208
+ `BulkRowLimitException` (422).
209
+ - Parti **hep-ya-hiç**tir: bir satır doğrulamadan geçmezse `BulkRowFailedException` hangi
210
+ satırın (index) patladığını söyler ve hiçbir satır yazılmaz.
211
+ - **Dosya alanı olan tablolarda `bulk-create`/`bulk-update` da desteklenir** — her satırın
212
+ dosya (ve çoka-çok dosya) alanı, tekil `create`/`update` ile **aynı format** üzerinden
213
+ (`{ type, fileName, value, clone, override, id }`) satır satır `fileService`'e gönderilir;
214
+ artık dosya alanı yüzünden 422 dönmez. Aynı entegrasyon workflow **table step**'inde de
215
+ vardır (satır dizisindeki dosya değeri de aynı formatı izler).
216
+ - **Bulk upsert yoktur.**
217
+ - `bulk-update` gövdesinde birincil anahtar **bulunur ve zorunludur** — tekil `update`'in
218
+ aksine satırı belirleyen filtre değil, gövdedeki anahtardır. `UpdateRowsAsync` bu anahtarı
219
+ `SET` listesinden dışlayıp `WHERE`'e taşır, bu yüzden alan listesinde görünmesi doğrudur ve
220
+ GENERATED ALWAYS koruması onu es geçer.
221
+ - Workflow **table step**'inde de aynı üç işlem vardır; orada satır dizisi adımın `expression`
222
+ alanından (JS) gelir, gövdeden değil. **Ancak studio'nun workflow table formunda bu
223
+ `expression` alanı için editör henüz yoktur** — akış adımında bulk seçilirse satır dizisi
224
+ girilemez ve adım `MissingParameterException` ile düşer. v1'de bulk yalnız microservice
225
+ ucundan kullanılır.
226
+ - Adımda tanımlı ama değeri boş bırakılan alanlar gövdeden/expression'dan gelen veriyi
227
+ **ezmez**; yalnız değeri dolu olan sabitler her satıra uygulanır.
180
228
  - **`count` vardır** — kayıt sayısı için tüm satırları çekip uzunluğuna bakmayın; panel/KPI
181
229
  sayaçlarının doğru şekli budur.
182
230
  - Gövde kökünde **`entityId` zorunlu**. (Eski "kökte datasourceId zorunlu" bilgisi yanlıştı —
@@ -233,6 +281,61 @@ DTO'dan süzülmez (WF-026 — `platform_guide('workflow')` §Hata yolu). Sözle
233
281
  }
234
282
  ```
235
283
 
284
+ ## Güncelleme sözleşmesi: yazılan alan kümesi TANIMDAN gelir, gövdeden DEĞİL
285
+
286
+ `create` / `update` / `upsert` ucunda **SET listesi `fields[]`'tir**; gövde yalnız o alanların
287
+ DEĞERİNİ taşır. Studio'daki karşılığı "Oluşturulacak veya güncellenecek alanlar" ekranındaki
288
+ **İstek alanları** listesidir: önce hangi alanların yazılacağı seçilir, gövde sonra gelir.
289
+ İki yön de sessizdir, ikisi de 200 döner (ölçüm: 2026-09-15, `emrah/test`, `development`):
290
+
291
+ | Tanımda (`fields[]`) | Gövdede | Sonuç |
292
+ |---|---|---|
293
+ | alan **var** | anahtar **yok** | kolona **NULL yazılır** — eski değer silinir |
294
+ | alan **var** | anahtar var | değer yazılır |
295
+ | alan **yok** | anahtar var | **yok sayılır** — o alan bu uçtan hiç değiştirilemez |
296
+
297
+ Kod yolu: gövdede anahtar yoksa alan `null` değerini alır
298
+ (`MicroserviceRuntimeCacheDto.SetSourceEntityFieldAndFilterValue`, `:145-148`), `ToRow` Default
299
+ modlu **bütün** alanları sözlüğe basar (`:169-175`) ve `UpdateRowAsync` SET listesini doğrudan o
300
+ sözlüğün anahtarlarından kurar (`PostgreSqlEntityConnector.cs:262-273`; null → `DBNull.Value`).
301
+ "Yalnız gövdede gelen alanı yaz" diye bir bayrak **yoktur**.
302
+
303
+ Ölçülen üç vaka (`pu_test`: `title` · `owner` · `status`):
304
+
305
+ | # | Uç tanımı | Gövde | Sonuç |
306
+ |---|---|---|---|
307
+ | 1 | `fields: [title, owner, status]` | `{ id, status: 'done' }` | 200 — `status` değişti, **`title` ve `owner` NULL** oldu (`entity_audit_trails` ikisini de yalnız `previousValue` ile gösteriyor) |
308
+ | 2 | `fields: [status]` | `{ id, status, title: '…', owner: '…' }` | 200 — yalnız `status` değişti; listelenmeyen iki alan **yazılmadı** |
309
+ | 3 | `fields: [title, owner, status]`, `status` kolonu NOT NULL | `{ id, title }` | **400 `RowValidationFailed`** ("Please complete all required fields") — satır hiç değişmedi |
310
+
311
+ Yani kolon `nullable` ise kayıp **sessizdir**, NOT NULL ise istek **gürültülü** düşer. Kanban'da
312
+ kart sürüklemek, satır içi tek alan düzenlemek, "arşivle" düğmesi — hepsi 1. satırın tuzağıdır:
313
+ kart doğru sütuna geçer, başlığı ve sorumlusu silinir.
314
+
315
+ ### Kısmi güncellemenin üç doğru yolu
316
+
317
+ 1. **Eylem başına uç (varsayılan tercih).** Kartı taşıyan uç yalnız `status` yazar, başlığı
318
+ düzenleyen uç yalnız `title`: `fields[]` = o eylemin gerçekten yazdığı alanlar. Fazladan okuma
319
+ yok, eşzamanlı düzenlemede birbirini ezme yok, yetki yüzeyi de dar kalır (2. ölçüm bunun
320
+ kanıtıdır — uç, gövdeye konan `title`/`owner`'ı yok sayar).
321
+ 2. **Tam form ucunda gövde de tam olur.** Düzenleme formu kaydediyorsa gövde `fields[]`'teki HER
322
+ alanı taşır; kullanıcının dokunmadığı alan da forma yüklendiği hâliyle geri gönderilir. Uçta
323
+ yapılacak bir şey yoktur — eksik gönderen istemci alanı siler.
324
+ 3. **Oku-birleştir (alan kümesi ancak çağrı anında belliyse).** Akışta önce `table get`, sonra
325
+ `table update`; gelmeyen alan okunan değere düşer:
326
+
327
+ ```js
328
+ typeof input.title !== 'undefined' && input.title !== null ? input.title : read_row.data.title
329
+ ```
330
+
331
+ Ölçüldü: `{ id, status }` gövdesiyle `title` ve `owner` korundu, `status` yazıldı. Bedeli iki
332
+ veritabanı turu ve **son yazan kazanır** — okuma ile yazma arasında başkasının yazdığı alan
333
+ geri alınır. Bu yüzden 1. yol varken bu yola girilmez.
334
+
335
+ > Aynı kural workflow **table step**'inde birebir geçerlidir; orada değer gövdeden değil
336
+ > ifadeden gelir ve `undefined` dönen ifade de `null` yazar —
337
+ > `platform_guide('workflow')` §"Table update adımı".
338
+
236
339
  ## İlişki alanı: `row_list` NESNE döner, uç/step JOIN'siz DÜZ FK döner
237
340
 
238
341
  Aynı alan iki yoldan farklı şekilde gelir ve bu fark sessizce yanlış sonuç üretir:
@@ -590,6 +693,57 @@ Yeni işte tanımı `typeParameters.schedule` ile yazın ve kaydetmeden önce
590
693
  `cronExpression`'ı değiştiren PUT/PATCH `422 ScheduleCronConflict` alır — sessizce eski cron
591
694
  kalmaz. `typeParameters: null` işi zamanlayıcıdan siler.
592
695
 
696
+ ### Zamanlanmış işin gerçek sonucunu görmek: geçmiş `failed` yazar, adım izini önizleme verir
697
+
698
+ Zamanlanmış koşu Quartz işi `MicroserviceJob` içinde koşar: `ExecuteAsync(microservice, BOŞ
699
+ parametre)`. 2026-09-16'dan beri iki hata yolu da geçmişe `failed` olarak düşer ve transaction geri
700
+ alınır:
701
+
702
+ | Geçmişte görünen | Gerçekte olan |
703
+ |---|---|
704
+ | `failed`, `errorMessage: "Step 'boom' failed: <sebep>"` | fail dalı bağlanmamış adım patladı; motor `WorkflowStepFailedException` fırlattı |
705
+ | `failed`, `errorMessage: "The job's workflow returned 500 (JavaScriptException): <mesaj>"` | akış bağlı global-error dalından (gateway'in eklediği `gatewayOnError` dâhil) 2xx dışı sonuç döndü; `MicroserviceJob` `ScheduleJobWorkflowFailedException` fırlattı |
706
+ | `failed`, başka mesaj | motor dışı exception: tanım/cache hatası, sonsuz döngü, canlı `revert`, DTO davranış uyumsuzluğu |
707
+ | `completed`, beklenen süre | akış 2xx ile bitti, yazmalar commit edildi — kalıcı etkiyi yine `row_list` ile say |
708
+ | satır yok | koşu veto edildi (deployment/şablon kurulumu sürüyor) ya da zamanlayıcıda kayıt yok — `microservice_schedule_get` |
709
+
710
+ Eski davranış (2026-09-16 öncesi imaj): adım hatası exception değildi; geçmişe `completed` + kısa
711
+ süre + boş `errorMessage` yazılır, yazmalar commit edilirdi — "90 ms'de tamamlandı ama hiçbir şey
712
+ yapmadı". Karşındaki platform hâlâ bunu yapıyorsa imaj eskidir.
713
+
714
+ Dört gerçek daha:
715
+
716
+ - **Geçmiş satırı `{ status, triggeredAt, completedAt, durationMs, errorMessage }`'dan ibarettir.**
717
+ Mesaj patlayan adımı ve sebebini söyler ama adım adım girdi/çıktı vermez; son koşunun adım izini
718
+ döndüren bir MCP aracı **yoktur** (açık backend işi P-19c). Adım izi için aynı tanımı önizlemede
719
+ koştur.
720
+ - **Zamanlanmış koşunun bağlamı boştur:** girdi yok (`MicroserviceExecuteParameter.Create()`),
721
+ middleware koşmaz (`user` yok), `request` yok, `context`/`triggerSource` kurulmaz. Akış
722
+ `input.x`, `user.id`, `request.*`, `context.language` okuyorsa zamanlanmış koşuda hepsi
723
+ `undefined`'dır — önizlemede geçen akış bu yüzden zamanlayıcıda patlayabilir.
724
+ - `microservice_schedule_get` içindeki `lastExecution` aynı geçmiş satırıdır.
725
+ - Geçmiş mesajları resx varsayılanıyla (İngilizce) yazılır; iş iş parçacığında istek dili yoktur.
726
+
727
+ **Sıra — tetiklemeden önce gör** (Studio'daki akış "Önizleme"si + servis önizlemesinin MCP karşılığı):
728
+
729
+ 1. **Akışı `workflow_preview` ile koştur** (`platform_guide('workflow')` §Önizleme çıktısını
730
+ okumak): `workflow_get` gövdesini olduğu gibi ver, **`previewPayload` VERME** — iş girdisiz
731
+ koşar, önizleme de girdisiz koşsun. Akış `parameterDtoId` taşıyorsa zamanlanmış koşuda DTO boş
732
+ bağlanır; zorunlu alanı olan DTO'yu zamanlanmış akışa bağlama. Hata varsa `runtimeErrors` +
733
+ `steps[].hasError` / `errorMessage` / `input[]` patlayan adımı ve sebebini verir.
734
+ 2. **Kayıtlı ucu `microservice_debug` ile koştur:**
735
+ `{ application: '<app slug>', microservice: '<uç slug>', method: 'POST' }` — schedule-job için
736
+ de slug yolu çalışır (tip denetimi yok), rollback'lidir, `httpStatus` akışın kendi sonucudur
737
+ (4xx/5xx'te gateway `runtimeErrors` ekler) ve `executionItems` ağacı adım adım girdi/çıktıyı
738
+ verir. Düğümlerde hata bayrağı yoktur: patlayan adımın `output`'u boştur, hata gövdesi hata
739
+ dalındaki response adımının çıktısındadır. Fark: debug ucun middleware'lerini de koşturur,
740
+ zamanlayıcı koşturmaz.
741
+ 3. **`microservice_preview({ id })`** en kısa "geçer mi" sondasıdır: yanıt `httpStatus` + gövdeden
742
+ ibarettir, adım ağacı DÖNMEZ (`executionItems` yalnız `/debug/` yolunda).
743
+ 4. Ancak sonra `microservice_schedule_control` `trigger` → `microservice_job_history`. `failed`
744
+ ise mesaj patlayan adımı ve sebebini söyler; adım adım girdi/çıktı gerekiyorsa 1-2'ye dön.
745
+ `completed` ise kalıcı etkiyi `row_list` ile say.
746
+
593
747
  ## Sık hatalar
594
748
 
595
749
  - `description` boş (MS-014, block — uç listesinde ne işe yaradığı okunamaz)
@@ -78,27 +78,33 @@ 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.**
81
+ - **Kurulmuş bir düğümün prop TİPİNİ yerinde değiştir — düğümü yeniden KURMA.** İki eş yol var:
82
+ `{ op: 'replace_prop', target: '<elementId>', prop: 'content', value: { type: 'translation', ref: 'dashboard.kpi.total' } }`
83
+ (applier `code` ve `translation` sarmalını tipiyle korur; gateway 2026-09-15'e kadar translation
84
+ sarmalını reddediyordu) ya da
82
85
  `{ 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
-
92
- - **`blocks[].size`, komponentin `component_get`'teki `maxSize`'ını aşamaz.** Dikkat: katalog
93
- varsayılanı aşabiliyor (`page-header` default `h:8`, `maxSize` `h:4`) — varsayılanı körü körüne
94
- kopyalama, doğrulayıcı `"Height of 'page-header' must be between 1 and 4."` ile reddeder.
86
+ (platformun kendi op'u — 2026-09-14'e kadar gateway emülasyonuydu — ve yamanın geri kalanıyla
87
+ aynı PATCH'te gider; mevcut `data`'yı tasarım yedeği olarak taşır). Taşıyıcı alan tipe göre
88
+ değişir — `static` değeri `data`'da, `code` ve `translation` `ref`'te taşır; eksik taşıyıcı istek
89
+ gönderilmeden reddedilir ve `page_schema_validate` aynı kararı verir. Eski yol
90
+ `remove_component` + `add_component` idi: 2026-09-06 koşusunda on bir etiket bu yolla kuruldu ve
91
+ onlara yazan **dokuz setter iteratee'si** sessizce silindi; 2026-09-15'te (G-04) aynı zincir bir
92
+ `daterangepicker` yer tutucusunu çeviriye bağlarken tekrarlandı — setter hedefi elementId değil
93
+ komponentin iç kimliğidir (aşağıda Kural 2, `FLOW-018`).
94
+
95
+ - **`blocks[].size`'ı komponentin `component_get`'teki `minSize`..`maxSize` aralığında tut.** Dikkat:
96
+ katalog varsayılanı aşabiliyor (`page-header` default `h:8`, `maxSize` `h:4`) — varsayılanı körü
97
+ körüne kopyalama. Sınır grid kanvasının kuralıdır; API 2026-09-15'ten beri reddetmez, ama aşan
98
+ blok kanvasta boyutlandırılamaz (aşağıda Kural 7 "Boyut tuzağı").
95
99
  - **Kurulmuş bir düğümün boyutunu `resize_component` micro-op'u değiştirir.**
96
100
  `{ op: 'resize_component', target: '<elementId>', size: { w, h } }` ızgara boyutunu,
97
101
  `{ …, 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
+ aynı op'ta verilebilir, `breakpoint` varsayılanı 1024'tür. Gateway op'u platformun
103
+ `set_block {target, breakpoint, size}` + `set_appearance {target, appearance}` +
104
+ `set_states {target, states}` op'larına çevirir ve yamanın geri kalanıyla **aynı PATCH'te** yollar
105
+ (o üç op'u doğrudan da gönderebilirsin). `move_component` gövdesine konan `size` gateway'de
106
+ reddedilir ve buraya yönlendirir; konumu belirli bir breakpoint'e yazmak için
107
+ `move_component`'e `breakpoint` ver. Ebeveynin düzeni yazdığın alanı etkisiz kılıyorsa (flex kabın
102
108
  içine `size`, ızgaranın içine `appearance`) yanıt `PAGE-019` uyarısı taşır — istek yine geçer.
103
109
  **Düğümü silip yeniden kurmayın:** o yol ona yazan akış setter'larını sessizce öldürür
104
110
  (`FLOW-018`, aşağıda Kural 2).
@@ -164,8 +170,10 @@ uymayan adı **PAGE-008** ile istek gitmeden reddeder.
164
170
  **Olay KALDIRMAK için `remove_event` kullan.** Platformun `set_event` micro-op'u olay silemez:
165
171
  `eventValue: null` verildiğinde kayıt `{ name: 'click' }` olarak (değersiz) kalır, diziden
166
172
  çıkmaz — şemada ölü bağ artığı bırakır. `{ "op": "remove_event", "target": "<elementId>",
167
- "event": "click" }` gateway emülasyonudur: şemayı okur, olayı diziden çıkarır ve sayfayı tam
168
- gövdeyle geri yazar (yanıtta `removedEvents`). Sayfa düzeyi olaylar için `target: "page"`.
173
+ "event": "click" }` platformun op'udur: olayı diziden çıkarır (yanıtta `removedEvents`); olmayan
174
+ bir olayı kaldırmaya çalışmak `missing-event` ile 422 döner. Sayfa düzeyi olaylar için
175
+ `target: "page"`. Referanssız kalan akış silineceği için `allowFlowDeletion` kuralı (G-21) bu op'ta
176
+ da geçerlidir.
169
177
 
170
178
  **Olay YÜKÜ komponente özeldir ve `component_get` onu da veriyor** (`events[].payload`, gerekirse
171
179
  `events[].reads`) — **ama yük akışa `event.data` ile GELMEZ.** Motor emit'in `data`'sını önce
@@ -394,6 +402,14 @@ göstermek error flow'un işidir.
394
402
  > "branches": [{ "id": "<GUID>", "name": "always", "nextStepId": null }] }
395
403
  > ```
396
404
 
405
+ > **`confirm` adımının diyalog çerçevesi de yazılabilir.** `args` dört düz metin alanı taşır:
406
+ > `message` (gövde), `title` (başlık), `confirmText` (onay düğmesi, `yes` dalı), `cancelText`
407
+ > (vazgeç düğmesi, `no` dalı). Son üçü isteğe bağlıdır; boş bırakılan alan dile göre sabit metne
408
+ > düşer ("Emin misiniz?" / "Evet" / "Hayır" — `dialog.functions.ts` `resolveDialogFrame`).
409
+ > Dördü de `alert`'in `message`'ı gibi DÜZ METİNDİR (FLOW-022 hepsini denetler):
410
+ > `{ "type": "confirm", "args": { "message": "Bu risk ve 4 aksiyonu silinecek.", "title": "Riski sil",
411
+ > "confirmText": "Sil", "cancelText": "Vazgeç" } }`.
412
+
397
413
  > **Hangi yolu seçeceksin — `errorSteps` mi, ana akışta `condition` mı?** İkisi birden
398
414
  > kurulamaz (yukarıdaki 1. madde), yani bu bir tercih değil bir karar:
399
415
  >
@@ -600,8 +616,8 @@ Türkçe başlık `props.title` / `props.text` / `props.label` / `props.pageTitl
600
616
  > `elementId` ile eşit tutmazsan `element:` alanı çalışır ama ifadedeki değişken `undefined` kalır.
601
617
  >
602
618
  > Gateway bunu **PAGE-009** ile HER sayfa ve layout yazma yolunda istek gitmeden reddeder:
603
- > `page_create`, `page_update(schema)`, `add_component`, `layout_create`, `layout_update(views)`
604
- > ve `layout_patch`.
619
+ > `page_create`, `page_update(schema)`, `add_component` (sayfa, `layout_schema_patch` ve
620
+ > `component_schema_patch` yamalarında), `layout_create`, `layout_update(views)` ve `layout_patch`.
605
621
 
606
622
  ## Kural 1d — Sayfa iskeleti: `page_container` ve boyutlandırma
607
623
 
@@ -893,6 +909,16 @@ Beş şeyi bilmeden flex kurma:
893
909
  aradaki boşluklar çizilmez. Yine de bitişik ver (0,1,2…): kapsayıcı ileride `grid`'e
894
910
  döndürülürse şema geçerli kalsın. Gateway bu yüzden **flex kapsayıcının İÇİNDE PAGE-010
895
911
  boşluk uyarısı üretmez** (ızgarada üretmeye devam eder).
912
+ **Dizi sırası çizimi belirlemez** — `children[]` içindeki yer değil `y` kazanır; `y`'si eşit
913
+ iki çocukta dizi sırası kazanır. Bu yüzden flex kapta sıralamanın yolu `index`'tir:
914
+ `add_component`/`move_component`'e verilen `index` **görünen sıradaki yeri** belirler ve gateway
915
+ kardeşlerin konumunu Studio'nun flex tuvali gibi yeniden yazar (x=0, alt alta birikimli y;
916
+ yanıtın `gatewayNormalized` notu hangi `y`'nin değiştiğini söyler). Grafiğin üstüne başlık:
917
+ `{ "op": "add_component", "parent": "<flex kap>", "index": 0, "component": { … } }` — koordinat
918
+ hesaplamak gerekmez, `remove_component` + `add_component` hiç gerekmez. **Izgara kapta `index`
919
+ çizimi değiştirmez** (her çocuk kendi `position`'ında mutlak çizilir): sırayı
920
+ `move_component { target, position: { x, y } }` ile ver; sona eklemeyen `index` orada
921
+ **PAGE-033 (uyarı)** üretir.
896
922
  3. **Flex kapsayıcının yüksekliği içeriğine oturur.** `verticalAlign`'ın dağıtacak boşluğu
897
923
  olması için `appearance.heightType: 'fixed'` vermelisin — ölçü `heightString`'den
898
924
  (serbest CSS uzunluğu, ör. `"30vh"`), o yoksa `size.h × 10px`'ten gelir. `heightString`
@@ -1010,7 +1036,8 @@ UYGULAMAZ** — koordinatları aynen kaydeder (aşağı itme yalnız `page_creat
1010
1036
  > sarmalayıcıya bağla (yukarıdaki `box_multi`). Sarmalayıcıyı **`isWrapper:false`** kur
1011
1037
  > (Kural 1f: wrapper = `min-height:80vh`) ve çerçevesini `style` ile kapat:
1012
1038
  > `{ background:'transparent', border:'none', '&__body': { padding:'0px' } }`.
1013
- > `appearance.isHidden` diye bir kaçış yolu YOKTUR — runtime onu hiç okumaz.
1039
+ > `appearance.isHidden` diye bir alan YOKTUR (2026-09-15'te platformdan kaldırıldı, S-16) —
1040
+ > görünürlük yalnız `props.isHidden`'dır.
1014
1041
 
1015
1042
  > **Satır BAŞINA doğrulama kurulamaz.** `validations` komponentin kendisindedir, tüm satırlar
1016
1043
  > onu paylaşır — "3. soru zorunlu, 4. soru değil" şemayla ifade edilemez. Zorunluluk satır
@@ -1030,8 +1057,9 @@ UYGULAMAZ** — koordinatları aynen kaydeder (aşağı itme yalnız `page_creat
1030
1057
  > (iç repeater bir kez `item` olmadan da değerlendiriliyor).
1031
1058
  >
1032
1059
  > **Gateway bunu `PAGE-013` ile istek gitmeden reddediyor** — `page_create` / `page_update(schema)` /
1033
- > `layout_create` / `layout_update(views)` / `layout_patch` gönderilen ağaca, `page_schema_patch` hem
1034
- > eklenen alt ağaca hem op'lar UYGULANDIKTAN SONRAKİ ağaca bakarak. Layout'ta da geçerlidir: kırılma
1060
+ > `layout_create` / `layout_update(views)` / `layout_patch` gönderilen ağaca; `page_schema_patch`,
1061
+ > `layout_schema_patch` ve `component_schema_patch` hem eklenen alt ağaca hem op'lar UYGULANDIKTAN
1062
+ > SONRAKİ ağaca bakarak. Layout'ta da geçerlidir: kırılma
1035
1063
  > `_repeaterValues` geri yazmasındadır, barındıranın sayfa mı layout mu olduğuna bakmaz.
1036
1064
  >
1037
1065
  > **Yapılacak:** satır içi listeyi tek bir `textarea`'da **satır başına bir öğe** olarak topla
@@ -1060,6 +1088,12 @@ yapmanın yolu budur — ölçülmüş kurallar (anket-doldur, 2026-08-19):
1060
1088
  > Bu kural TEMA/GÖRSELLİK içindir. Çocukların dizilimi (ortalama, yaslama, aralık, "kalanı
1061
1089
  > doldur") `style` ile değil kapsayıcının kendi `layout` prop grubuyla kurulur — Kural 1f-2.
1062
1090
 
1091
+ > **ÖNCE PROJE TEMASI.** Bir markayı uygulamanın birinci yolu `theme_update`'tir
1092
+ > (`platform_guide('theme')`): tema kaydı BÜTÜN sayfalara iner, `theme_get(view:'resolved')`
1093
+ > hangi değişkenin hangi değeri aldığını söyler. Aşağıdaki `page_container.style` yöntemi
1094
+ > yalnız O SAYFAYA özgü sapma içindir — her sayfada tekrarlanan bir ezme, temaya taşınmamış
1095
+ > bir karardır.
1096
+
1063
1097
  > **Stil anahtarları camelCase'dir: `minHeight`, `fontSize`, `letterSpacing`.** Kebab-case anahtar
1064
1098
  > (`min-height`) platform tarafından KABUL EDİLİR, `page_schema_get` ile aynen geri okunur ve
1065
1099
  > runtime onu HİÇ UYGULAMAZ; tek işaret tarayıcı konsolundaki tek satırdır ve `page_screenshot`
@@ -1120,6 +1154,8 @@ işareti taşıyan input'un **sonraki kardeşini** boya (`&__item-input:checked
1120
1154
  ### 1h-2 — Tema değişkeni envanteri (`Theme.svelte`, tamamı)
1121
1155
 
1122
1156
  Bunlar **runtime'da her sayfada tanımlıdır**; `style` içinde yeniden tanımlayarak eziyorsun.
1157
+ Bir projedeki GÜNCEL değerleri (ve buradaki listenin tamamını) `theme_get(view:'resolved')`
1158
+ `cssVars` alanından okursun — aşağıdaki tablo kümeleri anlatır, değerleri değil.
1123
1159
  Varsayılan palet `white.ts` (accent `#0052CC`, border `#dfe1e6`, text `#262626`) — yani hiçbir şey
1124
1160
  yapmazsan ekran "platform mavisi" görünür; marka rengine çevirmek tek satırdır.
1125
1161
 
@@ -1245,8 +1281,9 @@ kaldırır); sayı `0` "seçilmedi" sayılır.
1245
1281
  tipli grup alanları temayı alan alan EZER; düğümün serbest `style`'ı en üstte SON SÖZDÜR.
1246
1282
  Boyut alanları CSS uzunluğu metnidir (`'18px'`, `'1.2em'`); `radius` tek alanda preset
1247
1283
  anahtarı YA DA CSS uzunluğu kabul eder.
1248
- **Tema preset'i bağlamak:** `style: { $preset: 'card' }` (adlar `project_get` → `theme.presets`;
1249
- `componentStyles[type]` referanssız uygulanır). Bir alanı temaya bırakmak = o alanı hiç yazmamak;
1284
+ **Tema preset'i bağlamak:** `style: { $preset: 'card' }` (adlar `theme_get` → `presets`, sayfa
1285
+ bağlamında `page_runtime_get` → `theme.presets`; `componentStyles[type]` referanssız uygulanır).
1286
+ Preset'in kendisini yazmak/silmek `theme_update` işidir — `platform_guide('theme')`. Bir alanı temaya bırakmak = o alanı hiç yazmamak;
1250
1287
  studio'da bunun adı "Temaya dön"dür. Tema değerini gruba kopyalama — bağ kopar, tema değişince izlemez.
1251
1288
 
1252
1289
  **Efektif stili okumak:** `page_runtime_get(resolveStyles: true)` her düğüme `resolvedStyle`
@@ -1299,12 +1336,14 @@ dokunuldu" sorusunun kanıtı) ve **`newVersion`** (`UpdatedAt.Ticks`).
1299
1336
  > yamadan sonra şemayı okuyup gönderdiğinle karşılaştırarak söyler. Kırpma bu yüzden kanıt kaybı
1300
1337
  > değildir; `view: 'full'`e yalnız gerçekten platformun ham gövdesini incelerken düş.
1301
1338
 
1302
- **Sürüm kontrolü platformda var ama gateway bugün KULLANMIYOR.** Platform gövdede `baseVersion`
1303
- (long tick) kabul eder ve tutmazsa 409 verir; `page_state.baseVersion` bu değeri döner.
1304
- `page_schema_patch` bu alanı geçirmez — bayat plan koruması gateway tarafında
1305
- `expectedUpdatedAt` ile yapılır: sayfa okunur, `audit.updatedAt` karşılaştırılır, tutmazsa istek
1306
- hiç gönderilmez. Bu kontrol **iki çağrı arasına başka bir yazar girerse yakalamaz**; gerçek
1307
- korumayı isteyen ham HTTP ile `baseVersion` göndermelidir.
1339
+ **Sürüm kontrolü `baseVersion` ile yapılır.** `page_schema_get` yanıtı `version` taşır (mikrosaniye,
1340
+ STRING — sayıya çevirme); `page_schema_patch`'e `baseVersion` olarak ver. Sayfa o sürümden beri
1341
+ değiştiyse platform **409** döner ve hiçbir op uygulanmaz; yanıttaki `newVersion` bir sonraki
1342
+ yamanın `baseVersion`'ıdır. Kontrol platformda atomiktir (satır kilidi + sürüm koşulu tek
1343
+ `UPDATE`'te), yani iki çağrı arasına giren yazarı da yakalar. `baseVersion` vermezsen gateway yalnız
1344
+ 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
1345
+ yazan kazanır. Eski `expectedUpdatedAt` hâlâ çalışır ama `baseVersion`'ın yerini tutmaz.
1346
+ Yanıttaki `touched`, kaydı değişen elementId'leri listeler (eklenen alt ağaç dâhil).
1308
1347
 
1309
1348
  > **Büyük şemada çalışma sırası:** `mode:'summary'` ile ağacı çıkar (props yok, 500 KB'lık sayfada
1310
1349
  > bile küçük) → hedefin `elementId`'sini al → gerekiyorsa `mode:'subtree'` ile yalnız o dalı oku →
@@ -1319,14 +1358,15 @@ korumayı isteyen ham HTTP ile `baseVersion` göndermelidir.
1319
1358
  > `set_event` ile bağla ya da `flow_get` ile gövdesini yedekle. Ayrıntılı analiz:
1320
1359
  > `PatchPageSchemaCommandHandler` (backend) — kilit, sürüm ve hep-ya-hiç davranışının kaynağı.
1321
1360
 
1322
- ## Kural 3 — Flow yazımı barındırıcıyı da günceller
1361
+ ## Kural 3 — Flow yazımı yalnız akışı yazar, bağ ayrı yamadır
1323
1362
 
1324
- Platform `flow` uçlarında gövdede hem akışı hem barındırıcının tam güncelleme komutunu ister
1325
- (`{ flow, page | layout | component }`) — şemadaki `events[].value` = flow id bağı tek işlemde
1326
- tutarlı kalsın diye. **Silme bile gövdelidir.** Gateway barındırıcıyı okuyup gövdeye kendisi
1327
- koyar; sen yalnız akışı verirsin.
1363
+ 2026-09-14'ten beri `flow_create` / `flow_update` / `flow_delete` barındırıcının şemasına
1364
+ **dokunmaz**: gövde yalnız akıştır (silmede yalnız `id`). Eskiden gateway barındırıcının tam
1365
+ güncelleme komutunu da gönderiyordu; bu, her akış yazmasında sayfayı, layout'u ya da widget'ı
1366
+ baştan yazıyor ve açık bir Studio designer'ının kayıtlı tabanını eskitiyordu.
1328
1367
 
1329
- Akışı bir komponente bağlamak ayrı adımdır:
1368
+ Akışı bir komponente bağlamak ayrı adımdır ve akış kaydedildikten **sonra** yapılır (layout'ta
1369
+ `layout_schema_patch`, widget'ta `component_schema_patch`, op'lar aynı):
1330
1370
 
1331
1371
  ```
1332
1372
  flow_create(host:'page', hostId:<pageId>, name:'Save order', slug:'save-order', steps:[…])
@@ -1368,18 +1408,13 @@ Sonradan akış eklemen gerekirse sayfayı baştan kurmana gerek yok — **bağ
1368
1408
  `eventValue: null` olay kaydını bırakıp değerini boşaltır ve kilidi açar.
1369
1409
 
1370
1410
  > **Aynı kilit LAYOUT'ta da vardır** (ölçüm 2026-08-18: menüye yeni bağlantı eklerken
1371
- > `POST /layouts/<id>/flows` aynı hatayı verdi). Layout'un olayları komponentin kendi `events`
1372
- > dizisinde satır içidir (`{ "name": "click", "value": "<flow id>" }`) ve `page_schema_patch`
1373
- > layout'a uygulanmaz; çözüm de bu yüzden farklı yazılır:
1411
+ > `POST /layouts/<id>/flows` aynı hatayı verdi). Layout'ta sıra sayfadakiyle aynıdır ve bağlar
1412
+ > `layout_schema_patch` ile yazılır: `flow_create × N` → TEK `layout_schema_patch` (bütün
1413
+ > `set_event`'ler). Sıfırdan kurarken kilide takılmazsın (akışları bağlamadan önce yaratırsın).
1374
1414
  >
1375
- > ```
1376
- > 1) layout_update → tüm komponentlerde "events": [] (bağları çöz)
1377
- > 2) flow_create × N
1378
- > 3) layout_update → tam ağaç + bütün events'ler
1379
- > ```
1380
- >
1381
- > Sıfırdan kurarken kilide takılmazsın (akışları bağlamadan önce yaratırsın); kilit yalnız
1382
- > **mevcut ve bağlı** bir layout'a yeni akış eklerken çıkar.
1415
+ > **Yeniden ölçülmeli (2026-09-14):** iki ölçüm de flow isteğinin barındırıcıyı baştan yazdığı
1416
+ > dönemdendi. Zarfsız istekte barındırıcı yazılmadığı için kilit kalkmış olabilir; ölçülene kadar
1417
+ > kuruluş sırasını koru.
1383
1418
 
1384
1419
  ## Kural 4 — Komponent güncellemesi slug ile, silme id ile
1385
1420
 
@@ -1559,6 +1594,21 @@ alanına dokunur. **Komponenti silip yeniden eklemeyin:** `remove_component` + `
1559
1594
  turu o komponente bağlı setter hedeflerini koparır (Kural 1e sonundaki not) ve komponentin tek
1560
1595
  referans verdiği akışları kalıcı olarak sildirir (Kural 2'deki yan etki notu).
1561
1596
 
1597
+ **Kurulu düğümün appearance'ını değiştirmek: `set_appearance`.** Aynı gerekçeyle appearance için
1598
+ de silip yeniden ekleme gerekmez:
1599
+
1600
+ ```jsonc
1601
+ { "op": "set_appearance", "target": "box_summary", "appearance": { "heightType": "auto", "tooltip": null } }
1602
+ ```
1603
+
1604
+ Op **birleştirir**: yalnız verilen alanları yazar, gerisini korur; `null` alanı temizler (null
1605
+ kabul etmeyen `heightType` gibi alanlar hariç). Alan adları `page_schema_get`'in döndürdüğü
1606
+ appearance anahtarlarıdır; bilinmeyen alan `unknown-appearance-field`, tanınmayan ya da null
1607
+ kabul etmeyen değer (`heightType: "tall"`) `invalid-appearance` ile 422 döner ve yama atomik
1608
+ olduğu için hiçbir op yazılmaz. Kurulu düğümün `states` bayrakları için eşi `set_states`'tir.
1609
+ **`isHidden` bir appearance alanı DEĞİLDİR** — gizlemek için `replace_prop { prop: 'isHidden' }`
1610
+ ya da `set_binding { prop: 'isHidden' }` (Kural 7a'daki `isHidden` notu).
1611
+
1562
1612
  **Kip ayrımı query parametresidir:** aynı sayfa `?id=<kayıt>` ile edit, parametresiz create.
1563
1613
 
1564
1614
  `mount` akışı (`page_container` ya da `form` üzerine bağlanır):
@@ -1671,10 +1721,12 @@ start → [setter] btn_save.isLoading = true
1671
1721
  > artık doğrudan `props.isHidden` yazılabilir; sırf gizlemek için `container`'a sarmak
1672
1722
  > GEREKMEZ. Tek doğru kaynak `component_get` + `page_schema_validate` kuru koşusudur.
1673
1723
  >
1674
- > **`appearance.isHidden` bir kaçış yolu DEĞİLDİR:** kaydedilir ama runtime hiçbir yerde okumaz
1675
- > (renderer yalnız `props.isHidden`'a bakar) — komponent görünmeye devam eder. Başlangıçta gizli
1676
- > göstermek için ya komponenti `isHidden`'ı OLAN bir `container`'a sar (Kural 1g) ya da
1677
- > görünürlüğü sayfa açılış akışının setter'ıyla kur.
1724
+ > **Appearance'ta `isHidden` alanı YOKTUR** (2026-09-15'te platformdan kaldırıldı, S-16): renderer
1725
+ > yalnız `props.isHidden`'a bakar. Başlangıçta gizli bildirim şemada
1726
+ > `"props": { "isHidden": { "type": "static", "data": true } }` ile kurulur, açılışı akıştaki
1727
+ > setter yapar. `add_component` / `page_update` gövdesine yine de `appearance.isHidden` konursa
1728
+ > gateway değeri `props.isHidden`'a taşır ve `gatewayNormalized`'da bildirir (prop zaten verilmişse
1729
+ > o kazanır); `set_appearance` ile `isHidden` yazmak istek gönderilmeden reddedilir.
1678
1730
 
1679
1731
  ### 7b — Liste → form geçişi
1680
1732
 
@@ -1797,8 +1849,9 @@ Komponentlerin **`style` prop'u** vardır ve iç parçalara iner (`textinput.sty
1797
1849
  `&__body`). Tasarım kararlarını buradan uygula — varsayılan görünümle bırakmak zorunda değilsin.
1798
1850
 
1799
1851
  > **Boyut tuzağı:** katalog varsayılan `size`'ı `maxSize`'ı aşan komponentler var
1800
- > (`textinput` default `h:8`, `maxSize.h:5`). Varsayılanı körü körüne kopyalarsan doğrulayıcı
1801
- > reddeder; gateway aşan değeri `maxSize`'a kırpar ve `gatewayNormalized` ile bildirir.
1852
+ > (`textinput` default `h:8`, `maxSize.h:5`). Boyut sınırı grid kanvasının kuralıdır, API
1853
+ > reddetmez; ama aşan blok kanvasta boyutlandırılamaz. Gateway aşan değeri `maxSize`'a kırpar ve
1854
+ > `gatewayNormalized` ile bildirir.
1802
1855
 
1803
1856
  ## Kural 8 — Korunan sayfa: `middlewareIds` vermeden `Page.user` YOKTUR
1804
1857
 
@@ -1859,11 +1912,49 @@ hiçbiri** bağlı değildi; üç portalın da rol menüsü boş çıkıyor, pan
1859
1912
  > `Host: <org>.<suffix>` + `X-Environment` başlıklarıyla. Ham `curl` yerine tool'u kullanmak
1860
1913
  > yeterlidir.
1861
1914
  >
1915
+ > **Kareyi GÖRMEDEN doğrulamak — önce bunu dene.** `page_screenshot` her yanıtta `summaryText`
1916
+ > döner: her kök düğüm için tek satır — kimlik (tip) · görünür/GİZLİ/ekran dışı · metnin ilk 80
1917
+ > karakteri · `rows=<satır>` · `tab=<açık sekme>` · `value=<kutudaki değer>` · `BOŞ` ·
1918
+ > `errors=<sayı>`, sonda toast ve hata mesajları. "Kart göründü mü, tablo doldu mu, hata var mı"
1919
+ > sorularının cevabı budur ve **kareye gerek yoktur**: `image: 'none'` ile kare hiç çekilmez.
1920
+ > Ölçüldü (2026-09-16, 40 satırlık tablo taşıyan sayfa): özet **416** karakter, aynı sayfanın
1921
+ > `fullPage` karesi **151 276** karakter. Görsel soru (renk, hizalama, referansa sadakat) için
1922
+ > kareyi al ama küçült: `clip: { element: '<elementId>' }` yalnız o bölgeyi keser, `scale: 0.5`
1923
+ > rasteri yarıya indirir (sayfa yine 1280 CSS pikselinde çizilir — kırılım noktaları kaymaz),
1924
+ > `format: 'jpeg'` + `quality: 60` ölçekle BİRLİKTE kazandırır (tek başına neredeyse hiç).
1925
+ > Aynı sayfayı yeniden koştururken `diff: true` neyin değiştiğini listeler ve
1926
+ > `image: 'ifChanged'` değişmemiş kareyi hiç göndermez. `summary: 'full'` yapısal `domSummary`
1927
+ > dizisini de verir; runtime `data-component-type` basmıyorsa tipler yazılmaz ve `summaryNote`
1928
+ > bunu söyler.
1929
+ >
1930
+ > **Sekme, açılır kutu, form.** `actions` sözlüğünde üç fiil daha var.
1931
+ > `{ action: 'tab', target: 'tabs_settings', value: 'iot' }` sekmeyi ANAHTARIYLA seçer — sekme
1932
+ > grubunun köküne `click` göndermek DOM sırasındaki İLK sekmeye gider ve bu yüzden artık
1933
+ > reddedilir. **Etkin olmayan sekmenin içeriği DOM'da yoktur**: o sekmedeki bir düğüme yazmadan
1934
+ > ya da onu kırpmadan önce `tab` adımı koy. `{ action: 'select', target: 'dd_city', value: '34' }`
1935
+ > açılır kutudan seçer (kutu native alan değildir; `set` de aynı yola devreder) ve seçenekler
1936
+ > sorgudan geliyorsa listeyi 3 sn'ye kadar bekler — `optionsSeen: 0` görürsen kusur kutuda değil
1937
+ > sorgudadır, `query_preview` ile bak. `{ action: 'submit', target: 'frm_note' }` formu gönder
1938
+ > butonu olmadan gönderir. Her adımın raporu `matchedBy` · `activeTab` · `optionsSeen` ·
1939
+ > `waitedMs` · `submitVia` ile ne olduğunu söyler.
1940
+ >
1862
1941
  > **Yerleşimi görüntüsüz ölçmek.** Taşma/çakışma şüphesinde `page_screenshot(layoutReport: true)`:
1863
1942
  > `layoutSummary.overflowing` boş değilse içeriğine sığmayan kutu, `outsideViewport` boş değilse
1864
1943
  > hiç görünmeyen düğüm var; `zeroSized` çizilmemiş komponenti gösterir. Runtime düğümleri
1865
1944
  > `data-element-id` basmıyorsa yanıt `layoutReportUnavailable` der — tool tahmin üretmez.
1866
1945
  >
1946
+ > **Sürükle-bırakı gerçekten sınamak (G-09).** `kanban` ve `eventcalendar` iç öğelerine
1947
+ > `<elementId>__<rol>` kimliği yazar (tablonun `__row-<n>`'i ile aynı kalıp):
1948
+ > kart `kb_board__card-<id>`, kolon `kb_board__col-<value>`; etkinlik `cal_plan__event-<id>`,
1949
+ > gün `cal_plan__day-<yyyy-MM-dd>`, tüm gün hücresi `cal_plan__allday-<yyyy-MM-dd>`, saat dilimi
1950
+ > `cal_plan__slot-<yyyy-MM-dd>T<HH>`. `id`, `idField`'ın değeridir (yoksa `card-<sıra>` /
1951
+ > `event-<sıra>`); tarih yereldir. Tıklama yolu `click` ile, taşıma `drag` ile sınanır:
1952
+ > `actions: [{ action:'drag', target:'kb_board__card-42', to:'kb_board__col-Assigned' }]`.
1953
+ > `actionsReport[].flowRuns`/`requests` sıfırsa `cardMove`/`eventMove` bağı kurulu görünse de
1954
+ > taşıma kalıcılaşmamıştır. Kaynak `draggable` değilse (`isDraggable`/`isEditable` kapalı)
1955
+ > adım `applied:false` ve notla döner. Çok günlü etkinlik aynı kimliği her gün hücresinde taşır;
1956
+ > hedef ilk eşleşmedir.
1957
+ >
1867
1958
  > **Üye kimliğiyle doğrulama (`asUser`).** Kapının kapalı olduğunu görmek yarısıdır; açık
1868
1959
  > olduğunu görmek diğer yarısı. `page_runtime_get` ve `page_screenshot` aynı `asUser` alanını
1869
1960
  > alır: uygulamanın kendi giriş ucundan (`microservice_execute`) aldığın üye jetonunu verirsin,
@@ -1951,10 +2042,22 @@ micro-op hattına dönün.
1951
2042
  >
1952
2043
  > **Gateway artık bunu söylüyor (FLOW-018).** `remove_component` içeren bir yamada gateway sayfanın
1953
2044
  > akışlarını okur ve silinen düğümü hedefleyen setter adımlarını uyarı olarak listeler: hangi akış,
1954
- > hangi adım, hangi hedef. Uyarıyı gördüğünde yapılacak tek şey `flow_update` ile aynı gövdeyi
1955
- > yeniden kaydetmektir. Ters yönü (`silinen düğümün referans verdiği` akışların platformca kalıcı
2045
+ > hangi adım, hangi hedef. Ters yönü (`silinen düğümün referans verdiği` akışların platformca kalıcı
1956
2046
  > silinmesi) ayrı bir uyarı kapsar; ikisi birlikte çıkabilir. Ölçüm 2026-09-04 (benchmark 4. koşu):
1957
2047
  > uyarı yokken aynı tuzak ~8 dakika teşhis aldı ve yalnız ekran görüntüsüyle görüldü.
2048
+ >
2049
+ > **Mekanizma ve onarım (2026-09-15, G-04).** Setter iteratee'si hedefini elementId olarak DEĞİL,
2050
+ > komponentin iç kimliği olarak saklar (`step_iterates.DesignComponentId`, FK `SetNull`); düğüm
2051
+ > yeni kimlikle yazılınca eski satır silinir ve alan boşalır — `flow_get` iteratee'yi `element`'siz
2052
+ > döndürür. Bu yüzden **`flow_get`'ten okunan gövdeyi aynen yeniden kaydetmek onarmaz** (element
2053
+ > zaten yok; `flow_update` FLOW-017'ye takılır). İki güvence var: (1) backend artık AYNI yamada
2054
+ > silinip aynı `elementId` ile eklenen düğüme (alt ağaç dahil) eski kimliğini geri veriyor, bağ
2055
+ > kopmuyor; (2) gateway yamadan sonra akışları yeniden okur ve kopan her iteratee için yanıtta
2056
+ > `flowTargetsLost` ile hazır onarımı verir:
2057
+ > `flow_patch` `{ op: 'add', path: '/steps[stepId=s4]/args/iteratees/0/element', value: 'f_range' }`.
2058
+ > `flow_get` de hedefsiz setter iteratee'sini `flowWarnings` (`FLOW-017 (okuma)`) ile gösterir.
2059
+ > Ayrı isteklerle (önce sil, sonra ekle) yapılan yeniden kurulum ve komple şema yazan `page_update`
2060
+ > bu korumanın DIŞINDADIR — prop tipini değiştirmek için düğümü hiç yeniden kurmayın.
1958
2061
 
1959
2062
  ## Bilinen sınırlar
1960
2063
 
@@ -2029,8 +2132,11 @@ micro-op hattına dönün.
2029
2132
  (süssüz) ekrana ifadenin **kendisini** basar; ifade yalnız `data`'da olursa prop **boş**
2030
2133
  render edilir. `set_binding` micro-op'u eski sürümlerde `data`'ya yazıyordu — bu davranış
2031
2134
  düzeltildi, ama daha önce kurulmuş sayfalarda kalıntı olabilir. Prop nesnesini birebir
2032
- kontrol etmen gerekirse `remove_component` + `add_component` ile
2033
- `{"type":"code","ref":"{{ … }}","data":"<yedek>"}` yaz; `replace_prop` her zaman `static` kurar.
2135
+ kontrol etmen gerekirse `replace_prop` ile
2136
+ `value: {"type":"code","ref":"{{ … }}"}` yaz — code tipi ve `ref` olduğu gibi kaydedilir
2137
+ (platform code prop'unda `data`'yı saklamaz).
2138
+ `replace_prop`'a `expression` alanı verme: platform onu yalnız `set_binding`'de okur (gateway
2139
+ code sarmalına çevirir).
2034
2140
 
2035
2141
  - **Ana sayfa sayfadan değil application'dan değişir:** `page_create/update` gövdesindeki
2036
2142
  `isHomepage` platformca yok sayılır (tasarım). Doğru yol `application_update` →