@appaflytech/zeyrek-ai-mcp 0.0.7 → 0.0.8

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 (35) hide show
  1. package/dist/knowledge/backendConstraints.js +3 -0
  2. package/dist/knowledge/componentCatalog.js +59 -15
  3. package/dist/knowledge/normalize.js +459 -23
  4. package/dist/knowledge/propTypes.js +5 -4
  5. package/dist/knowledge/rules.js +110 -5
  6. package/dist/knowledge/styleResolve.js +6 -1
  7. package/dist/platform/http.js +10 -2
  8. package/dist/platform/screenshot.js +80 -8
  9. package/dist/platform/strictArguments.js +32 -0
  10. package/dist/platform/writeDiff.js +6 -3
  11. package/dist/resources/examples/page-sign-in.json +1 -1
  12. package/dist/resources/guides/component.md +1 -1
  13. package/dist/resources/guides/expressions.md +6 -0
  14. package/dist/resources/guides/getting-started.md +2 -0
  15. package/dist/resources/guides/layout.md +6 -0
  16. package/dist/resources/guides/microservice.md +105 -5
  17. package/dist/resources/guides/page.md +81 -18
  18. package/dist/resources/guides/translation.md +11 -10
  19. package/dist/resources/guides/workflow.md +50 -1
  20. package/dist/resources/schema-fingerprint.json +2 -2
  21. package/dist/resources/schemas/CreateMicroserviceCommand.json +4 -1
  22. package/dist/resources/schemas/CreateWorkflowCommand.json +4 -1
  23. package/dist/resources/schemas/PreviewMicroserviceCommand.json +4 -1
  24. package/dist/resources/schemas/PreviewWorkflowCommand.json +4 -1
  25. package/dist/resources/schemas/StepDto.json +4 -1
  26. package/dist/resources/schemas/UpdateMicroserviceCommand.json +4 -1
  27. package/dist/resources/schemas/UpdateWorkflowCommand.json +4 -1
  28. package/dist/server.js +2 -1
  29. package/dist/tools/flows.js +9 -3
  30. package/dist/tools/layouts.js +4 -4
  31. package/dist/tools/microservices.js +70 -6
  32. package/dist/tools/pages.js +127 -57
  33. package/dist/tools/queries.js +72 -6
  34. package/dist/tools/workflows.js +9 -5
  35. 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
@@ -116,6 +122,11 @@ servis formunda görünür ve `microservice_get` outline görünümünde geri ok
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
124
  adım adım executionItems ağacı döner.
125
+ Çağrı parametrelerini (sorgu parametresi, filtre değeri) üst düzey `parameters: { ad: değer }`
126
+ ile ver; gateway GET'te query-string'e, POST/PUT'ta gövdeye koyar. Değeri NULL bağlanan query
127
+ parametresini debug yanıtı `unboundParameters` ile söyler — ağaçta o satırın `value` anahtarı
128
+ görünmez. Tool'un tanımadığı bir üst düzey argüman artık reddedilir (GW-005); 2026-09-15'e
129
+ kadar `parameters` hata vermeden atılıyordu ve dört farklı filtre aynı gövdeyi döndürdü (G-13).
119
130
 
120
131
  ## Yanıt sözleşmesi (DTO) uca DEĞİL workflow'a bağlanır (MS-018, block)
121
132
 
@@ -159,8 +170,9 @@ DTO'dan süzülmez (WF-026 — `platform_guide('workflow')` §Hata yolu). Sözle
159
170
  ## Asgari doğru table-source microservice (platformda doğrulanmış kurallar)
160
171
 
161
172
  - `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`.
173
+ `get · create · update · delete · upsert · count · bulk-create · bulk-update · bulk-delete`.
174
+ **`list` yoktur** — liste için `operation: 'get'` + `isSingle: false`, tek kayıt için
175
+ `isSingle: true`.
164
176
  - **`upsert` vardır ve tercih edilmelidir.** Aynı formun hem yeni kayıt hem düzenleme
165
177
  yapmasını iki ayrı uç + akışta condition dallanmasıyla kurmayın: tek `operation: 'upsert'`
166
178
  ucu yeterlidir. Aynı işlem workflow **table step**'inde de desteklenir
@@ -174,9 +186,42 @@ DTO'dan süzülmez (WF-026 — `platform_guide('workflow')` §Hata yolu). Sözle
174
186
 
175
187
  > **Upsert'te üretilen alan tuzağı.** `operation: 'upsert'` UPDATE yolunda da alan ifadelerini
176
188
  > 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.
189
+ > güncellemede **sessizce değişir** (İK koşusu M-3: EMP-0005 → EMP-0006). Çözüm alan bazlı
190
+ > **`writeOn: create | update | always`**'dir (varsayılan `always`) ve 2026-09-04'ten beri
191
+ > platformdadır: üretilen alana `writeOn: 'create'` verirsen upsert'in UPDATE dalı o alanı satıra
192
+ > hiç koymaz (`ToRow(entityDto, writeOn)` iki satırı ayrı kurar). `writeOn` YALNIZ
193
+ > `operation: 'upsert'` ucunda anlamlıdır — başka operasyonda `always` dışında bir değer
194
+ > **MS-017** ile reddedilir. Gateway **MS-016** ile uyarır; alan `writeOn: 'create'` taşıyorsa
195
+ > susar. **`writeOn` bir "kısmi güncelleme" bayrağı DEĞİLDİR** — hangi DALIN yazacağını söyler,
196
+ > "gövdede gelmeyen alanı atla" demez (aşağıdaki §Güncelleme sözleşmesi).
197
+ - **Toplu yazma için `bulk-create` / `bulk-update` / `bulk-delete` vardır.** Aynı tabloya N satır
198
+ yazmak için akışta döngü kurmayın: bu üç uç gövdedeki **JSON diziyi** tek çağrıda işler
199
+ ([TableMicroserviceExecutor](../../../../zeyrek-backend/src/NgApps.Domain/Contexts/Studio/Microservices/Executors/TableMicroserviceExecutor.cs)).
200
+ Kurallar:
201
+ - Gövde bir **dizi** olur; her eleman bir satırdır. `bulk-delete` gövdesinde yalnız birincil
202
+ anahtar alanı aranır ve tek bir `IN` filtresine çevrilir.
203
+ - `filters` **kullanılmaz** — satırlar ve anahtarlar gövdeden okunur.
204
+ - Tek istekte en fazla **1000 satır** (`EntityConstants.BulkRowLimit`); aşılırsa
205
+ `BulkRowLimitException` (422).
206
+ - Parti **hep-ya-hiç**tir: bir satır doğrulamadan geçmezse `BulkRowFailedException` hangi
207
+ satırın (index) patladığını söyler ve hiçbir satır yazılmaz.
208
+ - **Dosya alanı olan tablolarda `bulk-create`/`bulk-update` da desteklenir** — her satırın
209
+ dosya (ve çoka-çok dosya) alanı, tekil `create`/`update` ile **aynı format** üzerinden
210
+ (`{ type, fileName, value, clone, override, id }`) satır satır `fileService`'e gönderilir;
211
+ artık dosya alanı yüzünden 422 dönmez. Aynı entegrasyon workflow **table step**'inde de
212
+ vardır (satır dizisindeki dosya değeri de aynı formatı izler).
213
+ - **Bulk upsert yoktur.**
214
+ - `bulk-update` gövdesinde birincil anahtar **bulunur ve zorunludur** — tekil `update`'in
215
+ aksine satırı belirleyen filtre değil, gövdedeki anahtardır. `UpdateRowsAsync` bu anahtarı
216
+ `SET` listesinden dışlayıp `WHERE`'e taşır, bu yüzden alan listesinde görünmesi doğrudur ve
217
+ GENERATED ALWAYS koruması onu es geçer.
218
+ - Workflow **table step**'inde de aynı üç işlem vardır; orada satır dizisi adımın `expression`
219
+ alanından (JS) gelir, gövdeden değil. **Ancak studio'nun workflow table formunda bu
220
+ `expression` alanı için editör henüz yoktur** — akış adımında bulk seçilirse satır dizisi
221
+ girilemez ve adım `MissingParameterException` ile düşer. v1'de bulk yalnız microservice
222
+ ucundan kullanılır.
223
+ - Adımda tanımlı ama değeri boş bırakılan alanlar gövdeden/expression'dan gelen veriyi
224
+ **ezmez**; yalnız değeri dolu olan sabitler her satıra uygulanır.
180
225
  - **`count` vardır** — kayıt sayısı için tüm satırları çekip uzunluğuna bakmayın; panel/KPI
181
226
  sayaçlarının doğru şekli budur.
182
227
  - Gövde kökünde **`entityId` zorunlu**. (Eski "kökte datasourceId zorunlu" bilgisi yanlıştı —
@@ -233,6 +278,61 @@ DTO'dan süzülmez (WF-026 — `platform_guide('workflow')` §Hata yolu). Sözle
233
278
  }
234
279
  ```
235
280
 
281
+ ## Güncelleme sözleşmesi: yazılan alan kümesi TANIMDAN gelir, gövdeden DEĞİL
282
+
283
+ `create` / `update` / `upsert` ucunda **SET listesi `fields[]`'tir**; gövde yalnız o alanların
284
+ DEĞERİNİ taşır. Studio'daki karşılığı "Oluşturulacak veya güncellenecek alanlar" ekranındaki
285
+ **İstek alanları** listesidir: önce hangi alanların yazılacağı seçilir, gövde sonra gelir.
286
+ İki yön de sessizdir, ikisi de 200 döner (ölçüm: 2026-09-15, `emrah/test`, `development`):
287
+
288
+ | Tanımda (`fields[]`) | Gövdede | Sonuç |
289
+ |---|---|---|
290
+ | alan **var** | anahtar **yok** | kolona **NULL yazılır** — eski değer silinir |
291
+ | alan **var** | anahtar var | değer yazılır |
292
+ | alan **yok** | anahtar var | **yok sayılır** — o alan bu uçtan hiç değiştirilemez |
293
+
294
+ Kod yolu: gövdede anahtar yoksa alan `null` değerini alır
295
+ (`MicroserviceRuntimeCacheDto.SetSourceEntityFieldAndFilterValue`, `:145-148`), `ToRow` Default
296
+ modlu **bütün** alanları sözlüğe basar (`:169-175`) ve `UpdateRowAsync` SET listesini doğrudan o
297
+ sözlüğün anahtarlarından kurar (`PostgreSqlEntityConnector.cs:262-273`; null → `DBNull.Value`).
298
+ "Yalnız gövdede gelen alanı yaz" diye bir bayrak **yoktur**.
299
+
300
+ Ölçülen üç vaka (`pu_test`: `title` · `owner` · `status`):
301
+
302
+ | # | Uç tanımı | Gövde | Sonuç |
303
+ |---|---|---|---|
304
+ | 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) |
305
+ | 2 | `fields: [status]` | `{ id, status, title: '…', owner: '…' }` | 200 — yalnız `status` değişti; listelenmeyen iki alan **yazılmadı** |
306
+ | 3 | `fields: [title, owner, status]`, `status` kolonu NOT NULL | `{ id, title }` | **400 `RowValidationFailed`** ("Please complete all required fields") — satır hiç değişmedi |
307
+
308
+ Yani kolon `nullable` ise kayıp **sessizdir**, NOT NULL ise istek **gürültülü** düşer. Kanban'da
309
+ kart sürüklemek, satır içi tek alan düzenlemek, "arşivle" düğmesi — hepsi 1. satırın tuzağıdır:
310
+ kart doğru sütuna geçer, başlığı ve sorumlusu silinir.
311
+
312
+ ### Kısmi güncellemenin üç doğru yolu
313
+
314
+ 1. **Eylem başına uç (varsayılan tercih).** Kartı taşıyan uç yalnız `status` yazar, başlığı
315
+ düzenleyen uç yalnız `title`: `fields[]` = o eylemin gerçekten yazdığı alanlar. Fazladan okuma
316
+ yok, eşzamanlı düzenlemede birbirini ezme yok, yetki yüzeyi de dar kalır (2. ölçüm bunun
317
+ kanıtıdır — uç, gövdeye konan `title`/`owner`'ı yok sayar).
318
+ 2. **Tam form ucunda gövde de tam olur.** Düzenleme formu kaydediyorsa gövde `fields[]`'teki HER
319
+ alanı taşır; kullanıcının dokunmadığı alan da forma yüklendiği hâliyle geri gönderilir. Uçta
320
+ yapılacak bir şey yoktur — eksik gönderen istemci alanı siler.
321
+ 3. **Oku-birleştir (alan kümesi ancak çağrı anında belliyse).** Akışta önce `table get`, sonra
322
+ `table update`; gelmeyen alan okunan değere düşer:
323
+
324
+ ```js
325
+ typeof input.title !== 'undefined' && input.title !== null ? input.title : read_row.data.title
326
+ ```
327
+
328
+ Ölçüldü: `{ id, status }` gövdesiyle `title` ve `owner` korundu, `status` yazıldı. Bedeli iki
329
+ veritabanı turu ve **son yazan kazanır** — okuma ile yazma arasında başkasının yazdığı alan
330
+ geri alınır. Bu yüzden 1. yol varken bu yola girilmez.
331
+
332
+ > Aynı kural workflow **table step**'inde birebir geçerlidir; orada değer gövdeden değil
333
+ > ifadeden gelir ve `undefined` dönen ifade de `null` yazar —
334
+ > `platform_guide('workflow')` §"Table update adımı".
335
+
236
336
  ## İlişki alanı: `row_list` NESNE döner, uç/step JOIN'siz DÜZ FK döner
237
337
 
238
338
  Aynı alan iki yoldan farklı şekilde gelir ve bu fark sessizce yanlış sonuç üretir:
@@ -78,16 +78,18 @@ 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.
86
+ (gateway emülasyonu; mevcut `data`'yı tasarım yedeği olarak taşır). Taşıyıcı alan tipe göre
87
+ değişir — `static` değeri `data`'da, `code` ve `translation` `ref`'te taşır; eksik taşıyıcı istek
88
+ gönderilmeden reddedilir ve `page_schema_validate` aynı kararı verir. Eski yol
89
+ `remove_component` + `add_component` idi: 2026-09-06 koşusunda on bir etiket bu yolla kuruldu ve
90
+ onlara yazan **dokuz setter iteratee'si** sessizce silindi; 2026-09-15'te (G-04) aynı zincir bir
91
+ `daterangepicker` yer tutucusunu çeviriye bağlarken tekrarlandı — setter hedefi elementId değil
92
+ komponentin iç kimliğidir (aşağıda Kural 2, `FLOW-018`).
91
93
 
92
94
  - **`blocks[].size`, komponentin `component_get`'teki `maxSize`'ını aşamaz.** Dikkat: katalog
93
95
  varsayılanı aşabiliyor (`page-header` default `h:8`, `maxSize` `h:4`) — varsayılanı körü körüne
@@ -394,6 +396,14 @@ göstermek error flow'un işidir.
394
396
  > "branches": [{ "id": "<GUID>", "name": "always", "nextStepId": null }] }
395
397
  > ```
396
398
 
399
+ > **`confirm` adımının diyalog çerçevesi de yazılabilir.** `args` dört düz metin alanı taşır:
400
+ > `message` (gövde), `title` (başlık), `confirmText` (onay düğmesi, `yes` dalı), `cancelText`
401
+ > (vazgeç düğmesi, `no` dalı). Son üçü isteğe bağlıdır; boş bırakılan alan dile göre sabit metne
402
+ > düşer ("Emin misiniz?" / "Evet" / "Hayır" — `dialog.functions.ts` `resolveDialogFrame`).
403
+ > Dördü de `alert`'in `message`'ı gibi DÜZ METİNDİR (FLOW-022 hepsini denetler):
404
+ > `{ "type": "confirm", "args": { "message": "Bu risk ve 4 aksiyonu silinecek.", "title": "Riski sil",
405
+ > "confirmText": "Sil", "cancelText": "Vazgeç" } }`.
406
+
397
407
  > **Hangi yolu seçeceksin — `errorSteps` mi, ana akışta `condition` mı?** İkisi birden
398
408
  > kurulamaz (yukarıdaki 1. madde), yani bu bir tercih değil bir karar:
399
409
  >
@@ -893,6 +903,16 @@ Beş şeyi bilmeden flex kurma:
893
903
  aradaki boşluklar çizilmez. Yine de bitişik ver (0,1,2…): kapsayıcı ileride `grid`'e
894
904
  döndürülürse şema geçerli kalsın. Gateway bu yüzden **flex kapsayıcının İÇİNDE PAGE-010
895
905
  boşluk uyarısı üretmez** (ızgarada üretmeye devam eder).
906
+ **Dizi sırası çizimi belirlemez** — `children[]` içindeki yer değil `y` kazanır; `y`'si eşit
907
+ iki çocukta dizi sırası kazanır. Bu yüzden flex kapta sıralamanın yolu `index`'tir:
908
+ `add_component`/`move_component`'e verilen `index` **görünen sıradaki yeri** belirler ve gateway
909
+ kardeşlerin konumunu Studio'nun flex tuvali gibi yeniden yazar (x=0, alt alta birikimli y;
910
+ yanıtın `gatewayNormalized` notu hangi `y`'nin değiştiğini söyler). Grafiğin üstüne başlık:
911
+ `{ "op": "add_component", "parent": "<flex kap>", "index": 0, "component": { … } }` — koordinat
912
+ hesaplamak gerekmez, `remove_component` + `add_component` hiç gerekmez. **Izgara kapta `index`
913
+ çizimi değiştirmez** (her çocuk kendi `position`'ında mutlak çizilir): sırayı
914
+ `move_component { target, position: { x, y } }` ile ver; sona eklemeyen `index` orada
915
+ **PAGE-033 (uyarı)** üretir.
896
916
  3. **Flex kapsayıcının yüksekliği içeriğine oturur.** `verticalAlign`'ın dağıtacak boşluğu
897
917
  olması için `appearance.heightType: 'fixed'` vermelisin — ölçü `heightString`'den
898
918
  (serbest CSS uzunluğu, ör. `"30vh"`), o yoksa `size.h × 10px`'ten gelir. `heightString`
@@ -1010,7 +1030,8 @@ UYGULAMAZ** — koordinatları aynen kaydeder (aşağı itme yalnız `page_creat
1010
1030
  > sarmalayıcıya bağla (yukarıdaki `box_multi`). Sarmalayıcıyı **`isWrapper:false`** kur
1011
1031
  > (Kural 1f: wrapper = `min-height:80vh`) ve çerçevesini `style` ile kapat:
1012
1032
  > `{ background:'transparent', border:'none', '&__body': { padding:'0px' } }`.
1013
- > `appearance.isHidden` diye bir kaçış yolu YOKTUR — runtime onu hiç okumaz.
1033
+ > `appearance.isHidden` diye bir alan YOKTUR (2026-09-15'te platformdan kaldırıldı, S-16) —
1034
+ > görünürlük yalnız `props.isHidden`'dır.
1014
1035
 
1015
1036
  > **Satır BAŞINA doğrulama kurulamaz.** `validations` komponentin kendisindedir, tüm satırlar
1016
1037
  > onu paylaşır — "3. soru zorunlu, 4. soru değil" şemayla ifade edilemez. Zorunluluk satır
@@ -1559,6 +1580,19 @@ alanına dokunur. **Komponenti silip yeniden eklemeyin:** `remove_component` + `
1559
1580
  turu o komponente bağlı setter hedeflerini koparır (Kural 1e sonundaki not) ve komponentin tek
1560
1581
  referans verdiği akışları kalıcı olarak sildirir (Kural 2'deki yan etki notu).
1561
1582
 
1583
+ **Kurulu düğümün appearance'ını değiştirmek: `set_appearance`.** Aynı gerekçeyle appearance için
1584
+ de silip yeniden ekleme gerekmez:
1585
+
1586
+ ```jsonc
1587
+ { "op": "set_appearance", "target": "box_summary", "appearance": { "heightType": "auto", "tooltip": null } }
1588
+ ```
1589
+
1590
+ Op **birleştirir**: yalnız verilen alanları yazar, gerisini korur; `null` alanı temizler. Alan
1591
+ adları `page_schema_get`'in döndürdüğü appearance anahtarlarıdır; bilinmeyen alan
1592
+ `unknown-appearance-field`, tanınmayan enum değeri (`heightType: "tall"`) 400 ile reddedilir.
1593
+ **`isHidden` bir appearance alanı DEĞİLDİR** — gizlemek için `replace_prop { prop: 'isHidden' }`
1594
+ ya da `set_binding { prop: 'isHidden' }` (Kural 7a'daki `isHidden` notu).
1595
+
1562
1596
  **Kip ayrımı query parametresidir:** aynı sayfa `?id=<kayıt>` ile edit, parametresiz create.
1563
1597
 
1564
1598
  `mount` akışı (`page_container` ya da `form` üzerine bağlanır):
@@ -1671,10 +1705,12 @@ start → [setter] btn_save.isLoading = true
1671
1705
  > artık doğrudan `props.isHidden` yazılabilir; sırf gizlemek için `container`'a sarmak
1672
1706
  > GEREKMEZ. Tek doğru kaynak `component_get` + `page_schema_validate` kuru koşusudur.
1673
1707
  >
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.
1708
+ > **Appearance'ta `isHidden` alanı YOKTUR** (2026-09-15'te platformdan kaldırıldı, S-16): renderer
1709
+ > yalnız `props.isHidden`'a bakar. Başlangıçta gizli bildirim şemada
1710
+ > `"props": { "isHidden": { "type": "static", "data": true } }` ile kurulur, açılışı akıştaki
1711
+ > setter yapar. `add_component` / `page_update` gövdesine yine de `appearance.isHidden` konursa
1712
+ > gateway değeri `props.isHidden`'a taşır ve `gatewayNormalized`'da bildirir (prop zaten verilmişse
1713
+ > o kazanır); `set_appearance` ile `isHidden` yazmak istek gönderilmeden reddedilir.
1678
1714
 
1679
1715
  ### 7b — Liste → form geçişi
1680
1716
 
@@ -1864,6 +1900,18 @@ hiçbiri** bağlı değildi; üç portalın da rol menüsü boş çıkıyor, pan
1864
1900
  > hiç görünmeyen düğüm var; `zeroSized` çizilmemiş komponenti gösterir. Runtime düğümleri
1865
1901
  > `data-element-id` basmıyorsa yanıt `layoutReportUnavailable` der — tool tahmin üretmez.
1866
1902
  >
1903
+ > **Sürükle-bırakı gerçekten sınamak (G-09).** `kanban` ve `eventcalendar` iç öğelerine
1904
+ > `<elementId>__<rol>` kimliği yazar (tablonun `__row-<n>`'i ile aynı kalıp):
1905
+ > kart `kb_board__card-<id>`, kolon `kb_board__col-<value>`; etkinlik `cal_plan__event-<id>`,
1906
+ > gün `cal_plan__day-<yyyy-MM-dd>`, tüm gün hücresi `cal_plan__allday-<yyyy-MM-dd>`, saat dilimi
1907
+ > `cal_plan__slot-<yyyy-MM-dd>T<HH>`. `id`, `idField`'ın değeridir (yoksa `card-<sıra>` /
1908
+ > `event-<sıra>`); tarih yereldir. Tıklama yolu `click` ile, taşıma `drag` ile sınanır:
1909
+ > `actions: [{ action:'drag', target:'kb_board__card-42', to:'kb_board__col-Assigned' }]`.
1910
+ > `actionsReport[].flowRuns`/`requests` sıfırsa `cardMove`/`eventMove` bağı kurulu görünse de
1911
+ > taşıma kalıcılaşmamıştır. Kaynak `draggable` değilse (`isDraggable`/`isEditable` kapalı)
1912
+ > adım `applied:false` ve notla döner. Çok günlü etkinlik aynı kimliği her gün hücresinde taşır;
1913
+ > hedef ilk eşleşmedir.
1914
+ >
1867
1915
  > **Üye kimliğiyle doğrulama (`asUser`).** Kapının kapalı olduğunu görmek yarısıdır; açık
1868
1916
  > olduğunu görmek diğer yarısı. `page_runtime_get` ve `page_screenshot` aynı `asUser` alanını
1869
1917
  > alır: uygulamanın kendi giriş ucundan (`microservice_execute`) aldığın üye jetonunu verirsin,
@@ -1951,10 +1999,22 @@ micro-op hattına dönün.
1951
1999
  >
1952
2000
  > **Gateway artık bunu söylüyor (FLOW-018).** `remove_component` içeren bir yamada gateway sayfanın
1953
2001
  > 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ı
2002
+ > hangi adım, hangi hedef. Ters yönü (`silinen düğümün referans verdiği` akışların platformca kalıcı
1956
2003
  > silinmesi) ayrı bir uyarı kapsar; ikisi birlikte çıkabilir. Ölçüm 2026-09-04 (benchmark 4. koşu):
1957
2004
  > uyarı yokken aynı tuzak ~8 dakika teşhis aldı ve yalnız ekran görüntüsüyle görüldü.
2005
+ >
2006
+ > **Mekanizma ve onarım (2026-09-15, G-04).** Setter iteratee'si hedefini elementId olarak DEĞİL,
2007
+ > komponentin iç kimliği olarak saklar (`step_iterates.DesignComponentId`, FK `SetNull`); düğüm
2008
+ > yeni kimlikle yazılınca eski satır silinir ve alan boşalır — `flow_get` iteratee'yi `element`'siz
2009
+ > döndürür. Bu yüzden **`flow_get`'ten okunan gövdeyi aynen yeniden kaydetmek onarmaz** (element
2010
+ > zaten yok; `flow_update` FLOW-017'ye takılır). İki güvence var: (1) backend artık AYNI yamada
2011
+ > silinip aynı `elementId` ile eklenen düğüme (alt ağaç dahil) eski kimliğini geri veriyor, bağ
2012
+ > kopmuyor; (2) gateway yamadan sonra akışları yeniden okur ve kopan her iteratee için yanıtta
2013
+ > `flowTargetsLost` ile hazır onarımı verir:
2014
+ > `flow_patch` `{ op: 'add', path: '/steps[stepId=s4]/args/iteratees/0/element', value: 'f_range' }`.
2015
+ > `flow_get` de hedefsiz setter iteratee'sini `flowWarnings` (`FLOW-017 (okuma)`) ile gösterir.
2016
+ > Ayrı isteklerle (önce sil, sonra ekle) yapılan yeniden kurulum ve komple şema yazan `page_update`
2017
+ > bu korumanın DIŞINDADIR — prop tipini değiştirmek için düğümü hiç yeniden kurmayın.
1958
2018
 
1959
2019
  ## Bilinen sınırlar
1960
2020
 
@@ -2029,8 +2089,11 @@ micro-op hattına dönün.
2029
2089
  (süssüz) ekrana ifadenin **kendisini** basar; ifade yalnız `data`'da olursa prop **boş**
2030
2090
  render edilir. `set_binding` micro-op'u eski sürümlerde `data`'ya yazıyordu — bu davranış
2031
2091
  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.
2092
+ kontrol etmen gerekirse `replace_prop` ile
2093
+ `value: {"type":"code","ref":"{{ … }}"}` yaz — code tipi ve `ref` olduğu gibi kaydedilir
2094
+ (platform code prop'unda `data`'yı saklamaz).
2095
+ `replace_prop`'a `expression` alanı verme: platform onu yalnız `set_binding`'de okur (gateway
2096
+ code sarmalına çevirir).
2034
2097
 
2035
2098
  - **Ana sayfa sayfadan değil application'dan değişir:** `page_create/update` gövdesindeki
2036
2099
  `isHomepage` platformca yok sayılır (tasarım). Doğru yol `application_update` →
@@ -81,22 +81,23 @@ backend'de zaten tanımlı, Studio'nun "Translate" aksiyonunun ürettiği şekil
81
81
  "value": { "type": "translation", "ref": "home.welcome", "data": "Hoş geldiniz" } }
82
82
  ```
83
83
 
84
- > **`replace_prop` ile kurulamaz, `set_prop_type` ile kurulur.** Micro-op applier her `value`'yu
85
- > `Static` (ifade verilmişse `Code`) Prop'a çevirir (`PageSchemaMicroOpApplier.cs:77`);
86
- > `translation` sarmalı gönderildiğinde sarmal `Data`'nın içine düşer ve prop sessizce bozulur.
87
- > Gateway `replace_prop`'un bu kullanımını **reddeder**. Kurulu bir prop'u çeviriye bağlamanın
88
- > yolu `set_prop_type`'tır:
84
+ > **Yukarıdaki `replace_prop` sarmalı olduğu gibi çalışır (2026-09-15, G-04).** Micro-op applier
85
+ > `{type, ref, data}` sözlüğünün tipini korur ve `Translation` Prop'unu `Ref` + metin `Data` ile
86
+ > kurar (`PageSchemaMicroOpApplier.ApplyOne`). Gateway bu kullanımı eskiden reddedip komponenti
87
+ > yeniden eklemeyi öneriyordu; o öneri uygulanınca düğüme yazan setter iteratee'lerinin `element`'i
88
+ > silindi (bir panoda alt başlık ve tarih aralığı boş kaldı). Ref'siz translation sarmalı istek
89
+ > gönderilmeden reddedilir; nesne `data` silinir. Eş yol `set_prop_type`'tır — mevcut `data`'yı
90
+ > tasarım yedeği olarak taşır ve layout ağacında (micro-op hattı yok) tek yoldur:
89
91
  >
90
92
  > ```jsonc
91
93
  > { "op": "set_prop_type", "target": "welcome_text", "prop": "text",
92
94
  > "propType": "translation", "ref": "home.welcome" }
93
95
  > ```
94
96
  >
95
- > Gateway şemayı okur, prop'un TİPİNİ yerinde değiştirir ve sayfayı tam gövdeyle geri yazar —
96
- > düğümün olay/setter bağları korunur. Eski yol (`remove_component` + `add_component`) düğümü
97
- > yeniden kurduğu için ona yazan setter iteratee'lerini sessizce öldürüyordu; artık gerekmiyor.
98
- > Taşıyıcı alan tipe göre değişir: `static` → `data`, `code` ve `translation` → `ref`. Eksik
99
- > taşıyıcı istek platforma gitmeden reddedilir ve `page_schema_validate` aynı kararı verir.
97
+ > İkisi de düğümün olay/setter bağlarını korur. **Prop tipini değiştirmek için
98
+ > `remove_component` + `add_component` yapmayın.** Taşıyıcı alan tipe göre değişir: `static` →
99
+ > `data`, `code` ve `translation` → `ref`. Eksik taşıyıcı istek platforma gitmeden reddedilir ve
100
+ > `page_schema_validate` aynı kararı verir.
100
101
  >
101
102
  > Ölçüldü 2026-09-08 (yerel `dil-dogrulama/dil-app`): kurulu bir `statcard`'ın `title` prop'u tek
102
103
  > `set_prop_type` op'uyla `f11.tam` anahtarına bağlandı; gerçek ekranda `tr` → "iki dilde dolu",
@@ -375,6 +375,22 @@ Tek uçta "başlık + N alt kayıt" yazmanın yolu budur (anket + sorular + seç
375
375
  başka alana ihtiyacı varsa bunları **`returnFields`** ile iste
376
376
  (`{ alias, fieldId, fieldSlug }` öğeleriyle) — `selectFields` write operasyonunda sessizce
377
377
  yok sayılır.
378
+ - **Bulk yazma (`bulk-create`/`bulk-update`/`bulk-delete`) table step'te de vardır**, ama tek
379
+ satırlık `create`/`update`'in aksine satır dizisi `fields`'tan değil, adımın **`expression`**
380
+ alanının (JS) sonucundan gelir: `rule.expression` bir **dizi** döndürmeli, dizideki her
381
+ nesnenin anahtarı alan **slug**'ıdır (fieldId değil). `bulk-delete`'te dizi ya birincil anahtar
382
+ değerlerinin düz listesi (`{ value: [...] }` şeklinde tek elemanlı sarmalayıcı) ya da her öğesi
383
+ `{ <pk slug>: <değer> }` olan nesne dizisidir. Tek istekte en fazla 1000 satır
384
+ (`EntityConstants.BulkRowLimit`); aşarsa `BulkRowLimitException`. `fields`'ta tanımlı ve
385
+ **değeri dolu** bir sabit varsa her satıra uygulanır; boş bırakılan alanlar `expression`
386
+ sonucundaki satırı ezmez.
387
+ - **Dosya (ve çoka-çok dosya) alanı `bulk-create`/`bulk-update`'te desteklenir** — microservice
388
+ tarafındaki `bulk-create`/`bulk-update` ile **aynı format**
389
+ (`{ type, fileName, value, clone, override, id }`); her satırın dosya değeri ayrı ayrı
390
+ `fileService`'e yüklenir, dosya alanı olan tabloda bulk artık reddedilmez.
391
+ - **Studio'nun workflow table formunda bulk için `expression` editörü henüz yoktur** — akışı
392
+ UI'dan kurarken bulk operasyonu seçilemez; `rule.expression`'ı JSON/`workflow_patch` ile elle
393
+ yazmak gerekir, yoksa adım `MissingParameterException` ile düşer.
378
394
  - **Çıktının ŞEKLİ `isSingle`'a bağlıdır** (ölçüm: 09-04 koşusu Y-15). `isSingle: true` ise
379
395
  `<step>.data` nesnedir ve `<step>.data.id` çalışır; **`isSingle: false` ise `data` bir
380
396
  DİZİDİR** — `<step>.data.id` `undefined` olur, akış `RowValidationFailed` ile düşer ve hata
@@ -395,6 +411,38 @@ Tek uçta "başlık + N alt kayıt" yazmanın yolu budur (anket + sorular + seç
395
411
  > **Gateway table step'lerinde `path`/`sort`/`limit` eksiklerini otomatik dolduruyor**
396
412
  > (yanıtta `gatewayNormalized`). Dolu değerlere dokunmaz; yanlış path yine WF/MS block'una takılır.
397
413
 
414
+ ## Table update adımı: yazılan alan kümesi ADIMDAN gelir
415
+
416
+ `operation: 'update'` (aynısı `create`/`upsert`) adımında SET listesi `rule.fields[]`'tir.
417
+ `PrepareParameters` listedeki **her** alanın ifadesini değerlendirir (`TableStep.cs:508-530`),
418
+ `undefined` dönen sonucu `Parse` `null`a çevirir (`CommonFieldType.cs:59`) ve satır sözlüğü
419
+ süzgeçsiz kurulur (`TableStep.ByUpdateAsync`, `:254-262`). İki sonucu vardır:
420
+
421
+ - Gövdede o anahtar yoksa `input.x` **`undefined`**'dır ve kolona **NULL yazılır** — eski değer
422
+ silinir, yanıt yine 200'dür.
423
+ - Adımda listelenmeyen alan hiçbir gövdeyle **değiştirilemez**.
424
+
425
+ Ölçüm (2026-09-15): `fields: [title, owner, status]` olan bir update adımına `{ id, status }`
426
+ gövdesi gitti; uç 200 döndü, `status` değişti, `title` ve `owner` **NULL** oldu. Microservice
427
+ `update` ucuyla birebir aynı davranış; tam tablo ve kısmi güncellemenin üç doğru yolu
428
+ `platform_guide('microservice')` §"Güncelleme sözleşmesi"nde.
429
+
430
+ Gateway bunun tanımdan görülebilen hâlini **WF-031 (warn)** ile keser: yazma adımı akışın giriş
431
+ DTO'sunda OLMAYAN bir `input.x` alanını bağlıyorsa o ifade kesinlikle `undefined` döner ve kural
432
+ eksik alanları adıyla sayar. Uyarıyı susturmanın doğru yolu adımı **eylem başına daraltmaktır**
433
+ (`fields[]` yalnız o eylemin yazdığı alanlar); alanı DTO'ya eklemek yalnız izin verir, çağıranın
434
+ her istekte göndereceğini garanti etmez.
435
+
436
+ Akışta kısmi güncelleme gerekiyorsa **oku-birleştir**: önce `table get`, sonra `table update`,
437
+ gelmeyen alan okunan değere düşsün —
438
+
439
+ ```js
440
+ typeof input.title !== 'undefined' && input.title !== null ? input.title : read_row.data.title
441
+ ```
442
+
443
+ (ölçüldü: `{ id, status }` gövdesiyle `title`/`owner` korundu). Bedeli fazladan bir okuma turu ve
444
+ **son yazan kazanır**; eylem başına ayrı uç kurmak hâlâ daha ucuz ve daha güvenlidir.
445
+
398
446
  ## Response step'i gövdeyi `bodyExpression`'dan üretir
399
447
 
400
448
  `rule: { dataTransferObjectId }` **hiçbir şey döndürmez** (`result: null`). Çalışan şekil:
@@ -615,7 +663,8 @@ Kaydedilen sözleşme üç tüketim yolunda da aynı şekilde bağlanır:
615
663
  |---|---|
616
664
  | `queryStepSourceType: 'library'` (workflow query adımı) | Adımın ifadeleri: `rule.expression` (nesne literali) ya da `rule.queryParameters[]` — ikisi de **JavaScript ifadesidir**; istek girdisini (`input.year`), önceki adım çıktısını **ve bağlı middleware'in `user.*` değişkenini** okur |
617
665
  | Query kaynaklı microservice (`source: 'query'`) | Yalnız çağrının kendisi: **body → query-string → header** (`MicroserviceExecuteParameter.FindValue`); ifade YOKTUR, `user.*` bu yola girmez |
618
- | `query_preview` | `parameters` alanı — davranış yukarıdaki ikisiyle aynıdır |
666
+ | `query_preview` | Komutun `definition.parameters[].value` alanı; tool'un üst düzey `parameters` argümanı (ad → değer) gateway'de oraya bağlanır. Değer almayan parametre yanıtta `unboundParameters` ile listelenir |
667
+ | `microservice_execute` / `microservice_debug` | Üst düzey `parameters` (ad → değer): gateway GET/DELETE'te query-string'e, POST/PUT'ta gövdeye koyar — yukarıdaki query kaynaklı uç satırının okuduğu yer. `microservice_debug` NULL bağlanan parametreyi `unboundParameters` ile adıyla verir |
619
668
 
620
669
  > **Boş string parametre `NULL`'a çevrilir.** `Query.NormalizePreviewParameters` değeri normalize
621
670
  > ederken boş ya da yalnız boşluktan oluşan değeri `null` yapar. `WHERE status = {{durum}}` yazıp
@@ -1,6 +1,6 @@
1
1
  {
2
- "hash": "7702a2d5d690f0d9b1aeab42e9253096f68dfaca040952b07d6c2911f1a37a7f",
3
- "generatedAt": "2026-09-14T09:08:38.743Z",
2
+ "hash": "cafe3a6b082c7828cece4eaa1c0115ba28581f157eac3288abcf068c7f6f8533",
3
+ "generatedAt": "2026-09-15T10:24:42.398Z",
4
4
  "types": 185,
5
5
  "files": 107,
6
6
  "note": "Şemaları üreten backend kaynaklarının parmak izi. Elle düzenlemeyin: 'npm run generate-schemas' yazar, 'npm run schema:check' denetler.",
@@ -322,7 +322,10 @@
322
322
  "get",
323
323
  "delete",
324
324
  "upsert",
325
- "count"
325
+ "count",
326
+ "bulk-create",
327
+ "bulk-update",
328
+ "bulk-delete"
326
329
  ],
327
330
  "type": "string",
328
331
  "format": ""
@@ -805,7 +805,10 @@
805
805
  "get",
806
806
  "delete",
807
807
  "upsert",
808
- "count"
808
+ "count",
809
+ "bulk-create",
810
+ "bulk-update",
811
+ "bulk-delete"
809
812
  ],
810
813
  "type": "string",
811
814
  "format": ""
@@ -341,7 +341,10 @@
341
341
  "get",
342
342
  "delete",
343
343
  "upsert",
344
- "count"
344
+ "count",
345
+ "bulk-create",
346
+ "bulk-update",
347
+ "bulk-delete"
345
348
  ],
346
349
  "type": "string",
347
350
  "format": ""
@@ -650,7 +650,10 @@
650
650
  "get",
651
651
  "delete",
652
652
  "upsert",
653
- "count"
653
+ "count",
654
+ "bulk-create",
655
+ "bulk-update",
656
+ "bulk-delete"
654
657
  ],
655
658
  "type": "string",
656
659
  "format": ""
@@ -555,7 +555,10 @@
555
555
  "get",
556
556
  "delete",
557
557
  "upsert",
558
- "count"
558
+ "count",
559
+ "bulk-create",
560
+ "bulk-update",
561
+ "bulk-delete"
559
562
  ],
560
563
  "type": "string",
561
564
  "format": ""
@@ -307,7 +307,10 @@
307
307
  "get",
308
308
  "delete",
309
309
  "upsert",
310
- "count"
310
+ "count",
311
+ "bulk-create",
312
+ "bulk-update",
313
+ "bulk-delete"
311
314
  ],
312
315
  "type": "string",
313
316
  "format": ""
@@ -805,7 +805,10 @@
805
805
  "get",
806
806
  "delete",
807
807
  "upsert",
808
- "count"
808
+ "count",
809
+ "bulk-create",
810
+ "bulk-update",
811
+ "bulk-delete"
809
812
  ],
810
813
  "type": "string",
811
814
  "format": ""
package/dist/server.js CHANGED
@@ -19,6 +19,7 @@ import { registerKnowledgeResources } from './resources/knowledge.js';
19
19
  import { registerSchemaResources } from './resources/schemas.js';
20
20
  import { registerRuleResources } from './resources/rules.js';
21
21
  import { instrumentServer } from './observability/trace.js';
22
+ import { rejectUnknownArguments } from './platform/strictArguments.js';
22
23
  import { registerProjectTools } from './tools/projects.js';
23
24
  import { registerEntityTools } from './tools/entities.js';
24
25
  import { registerDependencyTools } from './tools/dependencies.js';
@@ -72,7 +73,7 @@ const moduleRegistrations = {
72
73
  revision: registerRevisionTools
73
74
  };
74
75
  export function createGatewayServer(platform) {
75
- const server = instrumentServer(new McpServer({ name: 'ngapps', version: '0.1.0' }), platform.config);
76
+ const server = rejectUnknownArguments(instrumentServer(new McpServer({ name: 'ngapps', version: '0.1.0' }), platform.config));
76
77
  for (const moduleName of platform.config.modules) {
77
78
  moduleRegistrations[moduleName](server, platform);
78
79
  }
@@ -1,7 +1,7 @@
1
1
  import { fetchHost, flowPath, hostParam, hostUpdateBody, unwrap } from './screens.js';
2
2
  import { guid, projectParam, environmentParam, readOnly, writes, updates, destructive, microOps } from './common.js';
3
3
  import { evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/policy.js';
4
- import { normalizeFlowSteps, unknownSetterTargetWarnings, withNormalization } from '../knowledge/normalize.js';
4
+ import { normalizeFlowSteps, targetlessSetterWarnings, unknownSetterTargetWarnings, withNormalization } from '../knowledge/normalize.js';
5
5
  import { flowBodyWarnings } from '../knowledge/coverage.js';
6
6
  import { PlatformApiError } from '../platform/errors.js';
7
7
  import { handle } from '../platform/responses.js';
@@ -243,10 +243,16 @@ export function registerFlowTools(server, platform) {
243
243
  }));
244
244
  server.registerTool('flow_get', {
245
245
  title: 'Akış getir',
246
- description: 'Tek akışı adımlarıyla getirir; yeni akış yazarken en iyi şablon budur.',
246
+ description: "Tek akışı adımlarıyla getirir; yeni akış yazarken en iyi şablon budur. Hedefi silinmiş (element'siz) setter iteratee'si varsa yanıttaki flowWarnings söyler — o adım koşar ama hiçbir şeye yazmaz.",
247
247
  inputSchema: { project: projectParam, environment: environmentParam, host: hostParam, applicationId: guid, hostId: guid, flowId: flowIdParam },
248
248
  annotations: readOnly
249
- }, handle(config, ({ project, environment, host, applicationId, hostId, flowId }) => platform.tenant('GET', project, flowPath(host, applicationId, hostId, flowId), { environment })));
249
+ }, handle(config, async ({ project, environment, host, applicationId, hostId, flowId }) => {
250
+ const response = await platform.tenant('GET', project, flowPath(host, applicationId, hostId, flowId), { environment });
251
+ const warnings = targetlessSetterWarnings(response);
252
+ if (warnings.length === 0 || !response || typeof response !== 'object' || Array.isArray(response))
253
+ return response;
254
+ return { ...response, flowWarnings: warnings };
255
+ }));
250
256
  server.registerTool('flow_create', {
251
257
  title: 'Akış oluştur',
252
258
  description: "Sayfa/layout/komponent için olay akışı oluşturur. Platform bu uçta barındırıcının TAM gövdesini de ister (akış ile şemadaki olay bağı tek işlemde tutarlı kalsın diye); gateway barındırıcıyı okuyup gövdeye kendisi koyar. Akışı bir komponent olayına bağlamak ayrı adımdır: page_schema_patch ile set_event {target, event, eventValue: <flow id>}.",
@@ -2,7 +2,7 @@ import { guid, projectParam, environmentParam, pagingParams, readOnly, writes, u
2
2
  import { resolvePatchOperations } from '../knowledge/outlines.js';
3
3
  import { patchResult, baseVersionOf } from '../platform/patch.js';
4
4
  import fastJsonPatch from 'fast-json-patch';
5
- import { normalizeComponentDefaults, componentDefaultsNote, flexSizeWarnings, verticalGapWarnings, withNormalization } from '../knowledge/normalize.js';
5
+ import { normalizeComponentDefaults, componentDefaultsNote, flexSizeWarnings, spacingLayerWarnings, verticalGapWarnings, withNormalization } from '../knowledge/normalize.js';
6
6
  import { fetchHost, layoutUpdateBody, schemaParam, eventsParam, modeParam, summarize, findNode } from './screens.js';
7
7
  import { assertScreenWriteGuards, screenCatalogNotes } from './screenWrite.js';
8
8
  import { applyPropTypesToSchema, isSetPropTypeOp, setPropTypeErrors } from '../knowledge/propTypes.js';
@@ -93,7 +93,7 @@ export function registerLayoutTools(server, platform) {
93
93
  assertScreenWriteGuards('layout', { schema: views, events });
94
94
  const fixed = normalizeComponentDefaults(views);
95
95
  const catalogNotes = await screenCatalogNotes(platform, project, environment, applicationId, views);
96
- const gaps = [...verticalGapWarnings(views ?? [], 'LAY-003 (uyarı)'), ...flexSizeWarnings(views ?? [], 'LAY-007 (uyarı)', 'LAY-008 (uyarı)')];
96
+ const gaps = [...verticalGapWarnings(views ?? [], 'LAY-003 (uyarı)'), ...flexSizeWarnings(views ?? [], 'LAY-007 (uyarı)', 'LAY-008 (uyarı)'), ...spacingLayerWarnings(views ?? [], 'LAY-010 (uyarı)', 'LAY-011 (uyarı)')];
97
97
  const response = await platform.tenant('POST', project, `/layouts/${applicationId}`, {
98
98
  environment,
99
99
  body: { name, views: views ?? [], events: events ?? [], parameters: parameters ?? null }
@@ -126,7 +126,7 @@ export function registerLayoutTools(server, platform) {
126
126
  const catalogNotes = await screenCatalogNotes(platform, project, environment, applicationId, overrides.views);
127
127
  const body = layoutUpdateBody({ ...current, applicationId }, overrides);
128
128
  await platform.tenant('PUT', project, `/layouts/${applicationId}/${id}`, { environment, body });
129
- const gaps = overrides.views === undefined ? [] : [...verticalGapWarnings(body.views, 'LAY-003 (uyarı)'), ...flexSizeWarnings(body.views, 'LAY-007 (uyarı)', 'LAY-008 (uyarı)')];
129
+ const gaps = overrides.views === undefined ? [] : [...verticalGapWarnings(body.views, 'LAY-003 (uyarı)'), ...flexSizeWarnings(body.views, 'LAY-007 (uyarı)', 'LAY-008 (uyarı)'), ...spacingLayerWarnings(body.views, 'LAY-010 (uyarı)', 'LAY-011 (uyarı)')];
130
130
  const summary = { id, changed: Object.keys(overrides), preserved: { viewNodes: body.views.length } };
131
131
  if (gaps.length > 0)
132
132
  summary.layoutWarnings = gaps;
@@ -193,7 +193,7 @@ export function registerLayoutTools(server, platform) {
193
193
  body: { baseVersion: baseVersionOf(document, 'layout_get'), ops: netOps }
194
194
  });
195
195
  const written = patchResult(saved, finalDocument);
196
- const gaps = [...verticalGapWarnings(body.views, 'LAY-003 (uyarı)'), ...flexSizeWarnings(body.views, 'LAY-007 (uyarı)', 'LAY-008 (uyarı)')];
196
+ const gaps = [...verticalGapWarnings(body.views, 'LAY-003 (uyarı)'), ...flexSizeWarnings(body.views, 'LAY-007 (uyarı)', 'LAY-008 (uyarı)'), ...spacingLayerWarnings(body.views, 'LAY-010 (uyarı)', 'LAY-011 (uyarı)')];
197
197
  const summary = {
198
198
  id,
199
199
  applied: patchOps.length,