@appaflytech/zeyrek-ai-mcp 0.0.3 → 0.0.4

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/.env.example +21 -7
  2. package/.env.production.example +42 -0
  3. package/README.md +14 -7
  4. package/dist/config.js +113 -47
  5. package/dist/knowledge/componentCatalog.js +474 -31
  6. package/dist/knowledge/componentEvents.js +150 -12
  7. package/dist/knowledge/errorPath.js +64 -0
  8. package/dist/knowledge/normalize.js +12 -1
  9. package/dist/knowledge/outlines.js +6 -0
  10. package/dist/knowledge/rules.js +184 -3
  11. package/dist/knowledge/styleResolve.js +485 -0
  12. package/dist/observability/trace.js +4 -0
  13. package/dist/platform/http.js +3 -0
  14. package/dist/platform/responses.js +80 -4
  15. package/dist/platform/screenshot.js +106 -0
  16. package/dist/resources/examples/layout-app-shell.json +368 -0
  17. package/dist/resources/examples/microservice-table-list.json +1 -0
  18. package/dist/resources/examples/middleware-authenticate.json +243 -47
  19. package/dist/resources/examples/workflow-basic.json +24 -2
  20. package/dist/resources/guides/component.md +6 -3
  21. package/dist/resources/guides/external-component.md +129 -0
  22. package/dist/resources/guides/layout.md +147 -0
  23. package/dist/resources/guides/microservice.md +35 -0
  24. package/dist/resources/guides/middleware.md +10 -0
  25. package/dist/resources/guides/page.md +132 -14
  26. package/dist/resources/guides/workflow.md +127 -2
  27. package/dist/server.js +2 -0
  28. package/dist/tools/externalComponents.js +285 -0
  29. package/dist/tools/layouts.js +48 -6
  30. package/dist/tools/microservices.js +2 -2
  31. package/dist/tools/pages.js +98 -30
  32. package/dist/tools/platformInfo.js +2 -2
  33. package/dist/tools/screens.js +36 -0
  34. package/dist/tools/workflows.js +3 -3
  35. package/package.json +4 -1
@@ -0,0 +1,147 @@
1
+ # Layout Rehberi (yerleşim kabuğu)
2
+
3
+ Sayfaların paylaştığı kabuk: yan menü, üst şerit, kullanıcı menüsü ve sayfa içeriğinin
4
+ gireceği delik. Şema gövdesi ve koordinat modeli için `platform_guide('page')` (Kural 1,
5
+ 1d, 1f); komponent sözleşmeleri için `platform_guide('component')`.
6
+
7
+ ## Karar kuralları
8
+
9
+ **1. Layout, tek `slot` içeren SIRADAN bir komponent ağacıdır.** Ayrı bir dil yoktur:
10
+ sayfada kullandığın her kap, her prop, her `appearance` alanı burada da geçerlidir. Araç
11
+ kutusu layout modülünde süzülmez — tüm katalog kullanılabilir.
12
+
13
+ **2. Şema alanının adı `schema` değil `views`.** (LAY-001)
14
+
15
+ **3. Micro-op hattı YOKTUR.** `page_schema_patch`'in layout karşılığı yok (backend'de
16
+ `PATCH /layouts/.../schema` ucu yok). Değişiklik `layout_update` ile yazılır; gateway kaydı
17
+ önce okur ve yalnız verdiğin alanları değiştirir, `views` göndermezsen ağaç korunur.
18
+ Okumak için `layout_schema_get` (summary kipi hedef bulmak için yeterlidir).
19
+
20
+ **4. TEK slot.** Sayfa şeması **her** slot'a ayrı ayrı kopyalanır — iki slot, sayfayı iki
21
+ kez çizer ve `elementId`'leri tekrarlar. (LAY-002 / LAY-006)
22
+
23
+ **5. Kabuğu `container` + `slot` ile kur.** `layout` monoliti (sabit logo/menü/kullanıcı
24
+ bandı — yalnız doldurulur) ve `layout-shell` + `layout-region` ikilisi **emekliye
25
+ ayrıldı**; kayıtlı kalırlar ve mevcut layout'lar çalışır, ama yeni kabuk `container` ile
26
+ kurulur. Sebebi: `container` zaten `layout-region`'ın yaptığının üstünü yapıyor (flex/ızgara
27
+ düzeni, hizalama, gap, wrap), region'ın kattığı tek şey `dock` sözlüğüydü.
28
+
29
+ **5a. Kök, sayfanın `page_container`'ının yerleşim ikizidir.** Studio'nun yeni layout
30
+ iskeleti (`getDefaultLayout`) tam olarak şunu kurar ve sen de aynısını yazmalısın:
31
+ `states.isWrapper: true` (çerçevesiz, başlıksız, saydam) + `appearance.heightType: 'fill'`
32
+ + `blocks` x0/w12. Sarmalayıcı olmayan bir kök, kenarlıklı beyaz bir kart olarak çizilir.
33
+
34
+ **5b. Tam ekran kabuk `heightType: 'fill'` ile kurulur (2026-08-28).** Yükseklik ne
35
+ içerikten (`auto`) ne satır sayısından (`fixed`) gelir — ebeveynin bıraktığı yerden gelir:
36
+ ızgara çocuğunda `height:100%`, flex sütununda `flex:1 1 auto`, flex satırında
37
+ `align-self:stretch`. **Kök** düğümde ekranı doldurur (100vh). Yan menünün tam boy olması
38
+ ve slot'un artan alanı kaplaması bununla ifade edilir; `fixed` + tahmini `size.h` ile
39
+ yapılan eski numaraya artık gerek yok.
40
+
41
+ **6. Konum `blocks[]`'ten gelir, prop'tan değil.** `layout-region`'daki `dock` bölgenin
42
+ NEREDE durduğunu değil, BİÇİMİNİ söyler (dikey kolon / yatay şerit / dolgu yüzeyi). Hem
43
+ runtime hem Studio tuvali bölgeleri 12 kolonluk ızgarayla yerleştirir.
44
+
45
+ **7. Mobilde flex YIĞILMAZ.** Runtime'ın mobil kuralı yalnız ızgara özelliklerini ezer.
46
+ Telefonda alt alta düşmesi gereken satırı `layout:'grid'` ile kur; `layout:'flex'` +
47
+ `direction:'row'` telefonda da yan yana kalır (375px'te ölçüldü: ızgara çocukları 375/375px
48
+ alt alta, flex çocukları 49/310px yan yana).
49
+
50
+ **8. Doğrulama tek yerden:** `page_runtime_get`. Layout'un kendi serve URL'i yoktur —
51
+ gerçek çıktıyı, layout'a bağlı bir sayfanın runtime paketinde görürsün (slot yerleşimi
52
+ dahil). Slot kurgusu için ayrıca `page_state` → `layoutSlots`.
53
+
54
+ ## Mini tarif — uygulama kabuğu
55
+
56
+ Yan menü + üst şerit + içerik. Studio'nun `getDefaultLayout` iskeletiyle birebir aynı ağaç;
57
+ `layout_create`'in `views` alanına doğrudan verilebilir (`resource: layout-app-shell`).
58
+
59
+ ```
60
+ container isWrapper:true heightType:'fill' layout:'flex' direction:'row' gap:'none'
61
+ │ verticalAlign:'stretch' block w12 x0
62
+ ├ container heightType:'fill' layout:'flex' direction:'column' block w2 ← yan menü
63
+ │ └ navigations block w12
64
+ └ container heightType:'fill' layout:'flex' direction:'column' block w10 x2
65
+ ├ container layout:'flex' direction:'row' states.isSticky block w12 h6
66
+ │ └ breadcrumb block w12 h3
67
+ └ slot heightType:'fill' block w12 y6
68
+ ```
69
+
70
+ - **Genişlik kolon payıdır:** yan menü `w:2` = 12'de 2 → 1440px tasarım genişliğinde 240px.
71
+ Sabit piksel istiyorsan `appearance.widthPx: 240` (yalnız flex çocuğunda okunur; ızgara
72
+ kabında okunmaz).
73
+ - **Yükseklik `fill`'dir**, `size.h` değil: kök ekranı doldurur, yan menü ve içerik sütunu
74
+ kökün bıraktığını, slot da üst şeritten artanı alır. Slot'un grid satırı ayrıca açık
75
+ uçludur (`y / auto`), yani sayfa uzadıkça slot uzar.
76
+ - **Kaydırma:** `fill` + `states.isScrollable:true` verilen düğüm büyümek yerine İÇİNDE
77
+ kaydırır — uzun içerikte kabuğu sabit tutmanın yolu budur.
78
+ - **MOBİL UYARISI:** bu ağaç `flex` olduğu için telefonda YIĞILMAZ (Kural 7) — 375px'te yan
79
+ menü ince bir şerite iner. Telefonda alt alta düşmesi gereken bir kabuk için kökü
80
+ `layout:'grid'` kur; o zaman `fill`'in flex anlamı (`flex:1 1 auto`) devreye girmez,
81
+ yükseklik yine `blocks` satırlarından gelir. İkisini aynı anda veren bir yol bugün yok.
82
+ - Menü satırlarını biçimlendirme: **bugün mümkün değil.** `navigations`'ın `style` prop'u
83
+ ölüdür (bkz. `component_get('navigations')`).
84
+
85
+ ## Mini tarif — menüde aktif satır
86
+
87
+ `navigations.activeHref` boş bırakılırsa menü tarayıcıdaki adrese uyan satırı kendi bulur;
88
+ hiç ayar gerekmez. Elle sürmek istersen satırın `href`'iyle aynı biçimde yaz (ham metin,
89
+ bağlantı nesnesi ya da tam yol).
90
+
91
+ ## Kötü / iyi
92
+
93
+ ```jsonc
94
+ // KÖTÜ — iki slot: sayfa iki kez çizilir, elementId'ler çakışır
95
+ { "views": [{ "type": "container", "children": [
96
+ { "type": "slot" }, { "type": "slot" }
97
+ ]}]}
98
+
99
+ // KÖTÜ — sarmalayıcı olmayan, sabit yükseklikli kök: ekranı doldurmaz,
100
+ // kenarlıklı beyaz bir kart olarak çizilir ve yüksekliği tahmini h'den gelir
101
+ { "type": "container", "states": { "isDroppable": true },
102
+ "appearance": { "heightType": "fixed" },
103
+ "blocks": [{ "breakpoint": 1024, "size": { "w": 7, "h": 83 }, "position": { "x": 2, "y": 0 } }] }
104
+
105
+ // İYİ — sarmalayıcı, tam genişlikte, ekranı dolduran kök (Kural 5a/5b)
106
+ { "type": "container",
107
+ "states": { "isDroppable": true, "isWrapper": true },
108
+ "appearance": { "heightType": "fill", "marginType": "none", "paddingType": "none" },
109
+ "props": { "layout": { "type": "static", "data": "flex" },
110
+ "direction": { "type": "static", "data": "row" } },
111
+ "blocks": [{ "breakpoint": 1024, "size": { "w": 12, "h": 80 }, "position": { "x": 0, "y": 0 } }] }
112
+
113
+ // KÖTÜ — telefonda 49px'lik yan menü: row flex mobilde yığılmaz (Kural 7)
114
+ // Mobilde alt alta düşmesi gerekiyorsa kökü layout:'grid' kur — bedeli, fill'in
115
+ // flex anlamının devreye girmemesi.
116
+ ```
117
+
118
+ ## Sayfayı layout'a bağlama
119
+
120
+ `page_update` ile `layoutId`. Bağlı sayfanın kendi şeması slot'un içine girer; sayfanın
121
+ kök `page_container`'ı korunur.
122
+
123
+ ## Bilinen boşluklar (2026-08-28)
124
+
125
+ Bunlar **motorun** eksikleri, senin hatan değil — bir tasarımı karşılayamıyorsan sebebi
126
+ bunlardan biri olabilir:
127
+
128
+ | Eksik | Sonuç |
129
+ |---|---|
130
+ | ~~`heightType` yalnız `auto` \| `fixed`~~ | **28 Ağustos'ta kapandı:** üçüncü değer `fill` (Kural 5b) |
131
+ | ~~Kök kap viewport'a kilitlenmez~~ | **28 Ağustos'ta kapandı:** `fill` verilmiş kök 100vh (Kural 5b) |
132
+ | Flex mobilde yığılmaz | Kural 7 — kabuk bugün bir MASAÜSTÜ kabuğudur |
133
+ | Kırılıma duyarlı prop yok | `direction`/`gap` masaüstü ve mobilde AYNI; yalnız `blocks` ve `appearance.hideOn` kırılım bilir |
134
+ | Mobil çekmece (overlay) yok | Telefonda içeriğin üstüne binen panel kurulamaz |
135
+
136
+ ---
137
+
138
+ **Kaynaklar** — `zeyrek-frontend`: `apps/studio/src/features/layout/layout.functions.ts`
139
+ (`injectPageSchemaIntoSlots`, `getResolvedLayoutViews`), `apps/studio/src/features/page/api/page.mappers.ts`
140
+ (`mapPageLayoutToPreview`), `packages/ng-apps-ui/src/shared/render/render.functions.ts`
141
+ (`resolveNodeBoxStyle` — slot'un `y / auto` satırı, flex çocuk genişliği),
142
+ `packages/ng-apps-ui/src/shared/render/RenderComponent.svelte` (mobil media kuralı),
143
+ `packages/ng-apps-screen-builder/src/flex-layout.ts` (`getFlexChildStyle` — `widthPx` ve
144
+ `heightType:'fill'` dalı; tuval ve runtime aynı fonksiyonu okur),
145
+ `apps/studio/src/features/layout/layout.functions.ts` (`getDefaultLayout` — Kural 5a/5b'deki iskelet),
146
+ `docs/components/layout-composition.md`, `docs/specs/2026-08-28-free-layout-responsive-design.md`,
147
+ `docs/issues.md` ARC-031 / BE-002 / CQ-143.
@@ -1,5 +1,38 @@
1
1
  # Microservice Rehberi
2
2
 
3
+ ## Ucun NE İŞE YARADIĞI yazılır (MS-014, block)
4
+
5
+ Servis listesinde `name` ve `slug` var, "bu uç ne işe yarar" yok. Her microservice bir
6
+ `description` taşır; gateway açıklamasız gövdeyi platforma göndermez.
7
+
8
+ ```jsonc
9
+ { "name": "anket-kaydet", "slug": "anket-kaydet", "source": "workflow",
10
+ "description": "Anket başlığı ve sorularını tek çağrıda kaydeder; oluşan anketin id'sini döner." }
11
+ ```
12
+
13
+ **Çağıranın ne elde ettiğini yaz**, kaynağın şeklini değil: `"workflow kaynaklı POST ucu"`
14
+ gövdeden zaten okunuyor. Alan kalıcıdır, çalışma zamanını etkilemez, Studio'da servis formunda
15
+ görünür ve elle düzeltilebilir. Adım düzeyindeki karşılığı `platform_guide('workflow')` §WF-024.
16
+
17
+ ## Tuzağı varsa `caution` yaz (opsiyonel, kural yok)
18
+
19
+ `description` ucun ne işe yaradığını söyler; `caution` **çağıranın bilmezse yanılacağı şeyi**
20
+ söyler. Hiçbir kural zorlamaz — ucun gerçek bir tuzağı yoksa alanı hiç yazma.
21
+
22
+ - **Anonim uç** — "bu uç auth istemiyor; kimlik kapsaması yalnız bağlı middleware'de yapılır"
23
+ - **Sayfalama yok** — "limit uygulanmaz, tablo büyüdükçe yanıt büyür"
24
+ - **Atomik değil** — "çok adımlı yazım middleware'siz atomik değil; yarıda kalırsa kısmi yazar"
25
+ - **Sessiz boş dönüş** — "filtre eşleşmezse 404 değil boş dizi döner"
26
+
27
+ ```jsonc
28
+ { "name": "urun-listesi", "httpMethod": "Get",
29
+ "description": "Aktif ürünleri ada göre sıralı döner.",
30
+ "caution": "Sayfalama yok; katalog büyüdükçe yanıt boyutu doğrusal büyür." }
31
+ ```
32
+
33
+ Doldurma metin yazma: `"bu uç veri döner, dikkat"` hiçbir şey eklemez. Alan inerttir, Studio'da
34
+ servis formunda görünür ve `microservice_get` outline görünümünde geri okunur.
35
+
3
36
  ## Karar kuralları
4
37
 
5
38
  1. **Kaynak seçimi sırası: `table` → `view` → `workflow` → `query`.**
@@ -297,6 +330,8 @@ kabul etti. Ayrıntı ve doğru rol kapısı tarifi: `platform_guide('middleware
297
330
 
298
331
  ## Sık hatalar
299
332
 
333
+ - `description` boş (MS-014, block — uç listesinde ne işe yaradığı okunamaz)
334
+
300
335
  - `selectFields: []` + join → tüm alanlar + katlanmış maliyet (MS-001, block)
301
336
  - `operation: 'list'` → geçersiz; `get` + `isSingle: false` kullanın (PLT-006)
302
337
  - Üst seviye `entityId` eksik (MS-004, warn)
@@ -13,6 +13,12 @@ kavramı olmayan bir üründe auth middleware'i, ayar tablosu olmayan bir ürün
13
13
  kur** — giriş sayfası + korunacak her uç ve her sayfa `middlewareIds`'e bağlı. Kısmi koruma,
14
14
  sistemi güvenli gösterip açık bırakır.
15
15
 
16
+ ## Açıklama zorunlu
17
+
18
+ Middleware de bir workflow'dur: her adımı ve middleware'in kendisi `description` taşımak
19
+ zorundadır (**WF-024**, block). Guard'larda bu özellikle önemli — "neden reddediyor" sorusunun
20
+ cevabı adımın ifadesinde gizlidir. Ayrıntı ve yazım ölçütü: `platform_guide('workflow')` §WF-024.
21
+
16
22
  ## Çalışma modeli — bunları bilmeden middleware yazma
17
23
 
18
24
  1. **Sıra ada bağlıdır.** Bağlı middleware'ler **ada göre alfabetik** koşar — uç yolunda
@@ -54,6 +60,10 @@ sistemi güvenli gösterip açık bırakır.
54
60
  6. Middleware transaction içinde koşar: hedef başarısız olursa middleware'in yazdıkları da
55
61
  geri alınır (microservice yolu).
56
62
 
63
+ > Gateway middleware tanımına da hata yolunu otomatik ekler (start adımına `global-error` dalı +
64
+ > 500 dönen `gatewayOnError` response adımı). Guard middleware'i patladığında ham exception
65
+ > yerine denetimli bir gövde döner. Ayrıntı: `platform_guide('workflow')` → "Hata yolu".
66
+
57
67
  ## Yalnız middleware'de geçerli step'ler
58
68
 
59
69
  | Step | Ne yapar | Kritik alanlar |
@@ -155,10 +155,13 @@ olaylarında **boştur** (ölçüm 2026-08-25, kaynak okuması: `flow-machine.ts
155
155
 
156
156
  Pratik kural: **`payload` anahtarları aynı zamanda prop adlarıdır; akışta `self.<anahtar>` ile
157
157
  oku** (ya da `value { type:'property', ref:{ source:'component', element, property } }` ile).
158
- `event.data` yalnız `params` ile gelen olaylarda dolar — pratikte `html` komponentinin
159
- `fireEvent` aksiyonları (`platform_guide('html')`). Sessiz hata örneği: sayfalama akışını
160
- `event.data.limit`'e bağlarsan sorgu `LIMIT null OFFSET null` ile koşar ve **tüm satırları**
161
- döndürür; hata yok, sayfalama yok.
158
+ `event.data` yalnız `params` ile gelen olaylarda dolar — `html` komponentinin `fireEvent`
159
+ aksiyonları (`platform_guide('html')`) ve **2026-08-27 kuşağı komponentler** (`eventcalendar` ·
160
+ `importwizard` · `kanban` · `signature` · `stepper`): bu kuşak `params`'ı da gönderir, yani
161
+ yükleri `event.data.<anahtar>` ile okunur; `kanban` ve `eventcalendar` yükü `self`'e HİÇ
162
+ yazmaz (`component_get` çıktısındaki sözleşme notları ayrımı söyler). Sessiz hata örneği:
163
+ sayfalama akışını `event.data.limit`'e bağlarsan sorgu `LIMIT null OFFSET null` ile koşar ve
164
+ **tüm satırları** döndürür; hata yok, sayfalama yok.
162
165
 
163
166
  | Komponent · olay | Yük (aynı zamanda prop adları) | Akışta nereden okunur |
164
167
  |---|---|---|
@@ -175,7 +178,12 @@ döndürür; hata yok, sayfalama yok.
175
178
  | `tabs.tab` | `{ selected }` | `self.selected` |
176
179
  | `chart.dataPointClick` | `{ name, seriesName, value, dataIndex, row }` | `self.row` (kaynak satırın tamamı) |
177
180
  | `repeater.change` | `{ selectedIndex }` | `item` / `Row` satır bağlamı |
178
- | `html` aksiyonu (`fireEvent`) | ikinci argüman | **`event.data`** (tek istisna) |
181
+ | `html` aksiyonu (`fireEvent`) | ikinci argüman | **`event.data`** |
182
+ | `kanban.cardMove` | `{ id, row, from, to }` | **`event.data`** (self'e yazılmaz) |
183
+ | `eventcalendar.eventMove` | `{ id, row, start, end, previousStart, previousEnd }` | **`event.data`** (self'e yazılmaz) |
184
+ | `importwizard.complete` | `{ rows, errors, fileName, total }` | `self.rows`… ya da `event.data` |
185
+ | `stepper.stepChange` | `{ currentStep, previous }` | `self.currentStep` ya da `event.data` |
186
+ | `signature.change` | `{ value }` | `self.value` ya da `event.data.value` |
179
187
 
180
188
  Adım gövdesi `StepManagementSchema`: `{ stepId, name, type, kind, args, branches }`.
181
189
  **`branches[].nextStepId` bir GUID değil, hedef adımın `stepId` STRING'idir**
@@ -462,8 +470,8 @@ gateway **PAGE-011 / LAY-004** ile istek gitmeden reddeder.
462
470
 
463
471
  | slug | Olaylar | Ayırt edici prop'lar |
464
472
  |---|---|---|
465
- | `table` | 8: `mount · pagination · filter · sort · select · selectionChange · doubleClick · rowChange` | `rowSelection` (none/single/multi), `selectedRow`/`selectedRows`, `filterMode` (panel/hide/row), `filters`, `showSort`, `sortField`/`sortType`, `isEditable`, `showColumnMenu`, `actions`, `emptyIcon/emptyText/emptyActions`, `style` (root/thead/th/tr/td) |
466
- | `datatable` | 1: `mount` | `title`, `locale` (tr/en), `tableStyle` (zebra/plain), kolonlarda `isFilterable`/`isSortable`/`colorMap`/`prefix`/`suffix`/`precision`/`thousands`/`dateFormat`/`imageShape`, `filters`/`sortField`/`sortType` (hidden), `actions` |
473
+ | `table` | 8: `mount · pagination · filter · sort · select · selectionChange · doubleClick · rowChange` | `rowSelection` (none/single/multi), `selectedRow`/`selectedRows`, `filterMode` (panel/hide/row), `filters`, `showSort`, `sortField`/`sortType`, `isEditable`, `showColumnMenu`, `actions`, `emptyIcon/emptyText/emptyActions`, `style` (root/thead/th/tr/td), satır bazlı `_columns`/`_actions` ezmesi (Kural 1e-2) |
474
+ | `datatable` | 1: `mount` | `title`, `locale` (tr/en), `tableStyle` (zebra/plain — satır zemini varyantı), **`style`** (parça bazlı CSS: `&__toolbar`/`&__title`/`&__head`/`&__th`/`&__body`/`&__row`/`&__td`), kolonlarda `isFilterable`/`isSortable`/`colorMap` (statik)/`prefix`/`suffix`/`precision`/`thousands`/`dateFormat`/`imageShape`/**`progress`** kolon tipi, `filters`/`sortField`/`sortType` (hidden), `actions` |
467
475
 
468
476
  **`datatable`'ın tek olay yayması eksiklik değil, tasarım.** Filtre paneli, sıralama ve sayfalama
469
477
  komponentin İÇİNDE: kullanıcı süzdüğünde/sıraladığında/sayfa değiştirdiğinde `datatable` önce
@@ -487,8 +495,18 @@ olarak gider ve ancak `enableDynamicFilters: true` olan bir uçta karşılık bu
487
495
  düzenleme (`isEditable`) ve boş-durum aksiyonları verir.
488
496
  - *Satır seçimi / çift tıkla forma geçiş / hücre düzenleme gerekiyor mu?* → **`table`**; bunlar
489
497
  `datatable`'da yok.
490
- - *Zengin kolon biçimlendirme (renk haritası, para birimi öneki, tarih formatı, avatar) gerekiyor
491
- mu?* → **`datatable`**; kolon şeması bunun için tasarlandı.
498
+ - *Sayı/tarih/görsel biçimlendirme (para birimi öneki, ondalık, binlik ayracı, tarih formatı,
499
+ avatar şekli) gerekiyor mu?* → **`datatable`**; `prefix`/`suffix`/`precision`/`thousands`/
500
+ `dateFormat`/`imageShape` yalnız onun kolon şemasındadır, `table`'da yoktur.
501
+ - *Renk gerekiyor mu?* → **ikisi de yapar ama farklı şeyi.** `datatable`'ın `colorMap`'i
502
+ **statiktir**: hücrenin kendi değerine bakar; satır verisinde rengi taşırsan (`{ label, color }`)
503
+ dinamik de olur. `table` ise satır bazında **koşullu** renklendirir (`_columns` ezmesi,
504
+ Kural 1e-2). İkisinin de `style` prop'u vardır (datatable'ınki 2026-08-27'de eklendi), yani
505
+ renk uğruna stilden vazgeçmen gerekmiyor.
506
+ - *Hücrenin içinde ilerleme çubuğu ya da yüzde halkası mı istiyorsun?* → **`datatable`**, kolon
507
+ tipi `progress`. `progressVariant` çubuk/halka, `thresholds` yüzdeye göre renk, `captionField`
508
+ ise BAŞKA bir satır alanını çubuğun üstüne yazar — ad ve çubuk tek hücrede durur. `table`'da
509
+ böyle bir kolon tipi yok.
492
510
 
493
511
  **Bu, katalogdaki her komponent için geçerlidir:** hangi komponenti kullanacağını
494
512
  `component_list`/`component_get` ile keşfedip kendin seçersin, rehberdeki örnekler tercih değil
@@ -504,6 +522,48 @@ görünür, kullanıcı süzer, hiçbir şey olmaz. Ya süzme arayüzünü kapat
504
522
  > eklersen elementId aynı olsa bile o bağ kopar ve flow'u yeniden kaydetmen gerekir. Tipi
505
523
  > baştan doğru seç.
506
524
 
525
+ ## Kural 1e-2 — Satır bazlı renk `table`'da: `_columns` ezmesi
526
+
527
+ `table` her hücreyi çizerken **satırın kendi `_columns` sözlüğünü** kolon tanımıyla birleştirir
528
+ (`column-view.ts` `resolveColumn` → `Table.svelte:1207` `<TableCell {column}>`). Yani bir satır
529
+ kendi `appearance`/`isBold`'unu dayatabilir; renk, satırın herhangi bir alanından hesaplanır:
530
+
531
+ ```jsonc
532
+ // setter: rows'a yazmadan önce satırları zenginleştir
533
+ { "type": "change", "element": "tbl", "property": "rows", "source": "component",
534
+ "mapping": { "source": "", "mappings": {} },
535
+ "value": { "type": "code", "ref": "(Steps.listele?.data || []).map(function (r) { return Object.assign({}, r, { _columns: [null, null, null, null, { appearance: r.durum === 'İptal' ? 'error' : 'success' }] }); })" } }
536
+ ```
537
+
538
+ **`_columns` bir DİZİDİR ve konum kolonun `columns` dizisindeki İNDEKSİDİR** — yukarıdaki dört
539
+ `null`, hedef kolon 5. sırada (indeks 4) olduğu için var; ezilmeyen kolonlar atlanır.
540
+ `buildViewColumns` `index`'i `isHidden` kolonlar elenmeden ÖNCE atar, yani **görünür sıra değil
541
+ tanım sırası**: gizli bir `id` kolonu indeksleri kaydırmaz, kullanıcının kolon menüsünden kolon
542
+ gizlemesi de kaydırmaz (`hideColumns` `buildViewColumns`'dan SONRA uygulanır).
543
+ Aynı kalıp `_actions[i]` ile satır bazlı **aksiyon** ezmesinde de çalışır (tek bir satırda "Sil"i
544
+ `{ "isHidden": true }` ile gizlemek gibi).
545
+
546
+ > Sözleşme frontend'de belgeli ve testli: `column-view.ts` `resolveColumn` + `column-view.spec.ts`
547
+ > (`applies the row's own override fields on top`, `matches the override by the column definition
548
+ > order`). Ezilebilen alanlar `appearance` · `options` · `isBold`. CSV export'u da bu ezmeyi
549
+ > uygular, yani ekranda gördüğün renk dışa aktarımda da tutarlıdır.
550
+
551
+ İki renk yolu var:
552
+
553
+ | istenen | yol |
554
+ |---|---|
555
+ | Hazır palet | `tag` kolonu + `appearance`: `primary · info · success · warning · error`; `_columns` ile satır satır değişir |
556
+ | Serbest hex | `tags` kolonu + satır verisinde `[{ "value": "kritik", "color": "#da1e28" }]` |
557
+
558
+ **`style` yazarken iki ayrı kural geçerli:** `style.root` emotion'dır (camelCase çalışır, iç
559
+ seçici yazılabilir); `style.thead|th|tr|td` ise satır içi `style` attribute'una basılır
560
+ (`toStyleString` anahtarı olduğu gibi yazar) — bu dördünde **kebab-case zorunludur**, `fontSize`
561
+ sessizce düşer, `font-size` çalışır.
562
+
563
+ > `datatable`'da `style` prop'u **yoktur** (ne şemada ne `.svelte`'de) ve `_columns` ezmesi de
564
+ > yoktur; `tableStyle` CSS değil `zebra`/`plain` varyantıdır. Tabloya CSS ya da koşullu satır
565
+ > rengi gerekiyorsa **`table`**.
566
+
507
567
  ## Kural 1f — Koordinat modeli ve layout-slot yerleşimi
508
568
 
509
569
  Izgara kapsayıcıya görelidir ve **bir satır ≈ 10px**'tir; `w` 12 kolon. Her komponentin
@@ -616,7 +676,11 @@ Dört şeyi bilmeden flex kurma:
616
676
  3. **Flex kapsayıcının yüksekliği içeriğine oturur.** `verticalAlign`'ın dağıtacak boşluğu
617
677
  olması için `appearance.heightType: 'fixed'` + `size.h` vermelisin. **İstisna
618
678
  `page_container`**: `states.isWrapper: true` olduğu için zaten `min-height: 80vh` alır
619
- (Kural 1f), dolayısıyla dikey hizalama onda kutu boyu vermeden de çalışır.
679
+ (Kural 1f), dolayısıyla dikey hizalama onda kutu boyu vermeden de çalışır. Üçüncü yol
680
+ `heightType: 'fill'` (2026-08-28): çocuk kabın bıraktığı yeri alır — sütunda `flex: 1 1 auto`,
681
+ satırda `align-self: stretch` — yani "şu kutu kalan alanı kaplasın" artık tahmini bir `h`
682
+ olmadan yazılabilir. Tam ekran kabuk kurmanın yolu da budur: `platform_guide('layout')`
683
+ Kural 5b.
620
684
  4. **`layout: 'grid'` iken diğer altı prop ÖLÜDÜR.** Katalogda tanımlı oldukları için
621
685
  doğrulama duvarı susar, renderer da okumaz: ızgara kapsayıcıya `direction: 'column'` yazmak
622
686
  sessizce hiçbir şey yapmaz. Gateway bunu **PAGE-015 (uyarı)** ile söyler.
@@ -799,11 +863,11 @@ yapmazsan ekran "platform mavisi" görünür; marka rengine çevirmek tek satır
799
863
  | Küme | Değişkenler |
800
864
  |---|---|
801
865
  | Vurgu | `--ng-accent-primary` · `--ng-accent-hover` · `--ng-accent-muted` · `--ng-accent-subtle` |
802
- | Zemin | `--ng-background-canvas` · `--ng-background-surface` · `--ng-background-surface-alt` · `--ng-background-readonly` · `--ng-background-disabled` · `--ng-background-overlay` |
866
+ | Zemin | `--ng-background-canvas` · `--ng-background-surface` · `--ng-background-surface-alt` · `--ng-background-readonly` · `--ng-background-disabled` · `--ng-background-overlay` · `--ng-background-inverse` (zıt zemin: aydınlıkta koyu, karanlıkta açık) |
803
867
  | Metin | `--ng-text-primary` · `--ng-text-secondary` · `--ng-text-disabled` |
804
- | Çerçeve / köşe / gölge | `--ng-border-primary` · `--ng-border-secondary` · `--ng-rounded` · `--ng-shadow-none/sm/md/lg` |
868
+ | Çerçeve / köşe / gölge | `--ng-border-primary` · `--ng-border-secondary` · `--ng-rounded` (= `--ng-radius-md`) · `--ng-radius-sm/lg/pill` (temanın köşe kademeleri; sm/lg `--ng-rounded`'ın oranı) · `--ng-shadow-none/sm/md/lg` |
805
869
  | Tipografi | `--ng-font` · `--ng-font-size` · `--ng-font-weight` |
806
- | Boşluk | `--ng-spacing` · `--ng-spacing-small` · `--ng-spacing-large` |
870
+ | Boşluk | `--ng-spacing` · `--ng-spacing-xs/sm/md/lg/xl` (kademeler; builder'ın gap preset'leri sm/md/lg'yi okur — custom properties'te de bunları kullan) · `--ng-spacing-small` · `--ng-spacing-large` (eski adlar) |
807
871
  | Durum | `--ng-status-success` · `--ng-status-warning` · `--ng-status-error` · `--ng-status-info` |
808
872
  | Form (girdilerin tamamı) | `--ng-form-color` / `-disabled` / `-placeholder` / `-invalid` · `--ng-form-background` / `-disabled` / `-readonly` · `--ng-form-border` / `-focus` / `-disabled` / `-invalid` · `--ng-form-radius-sm/md/lg` · `--ng-form-height-sm/md/lg` · `--ng-form-padding-x-sm/md/lg` · `--ng-form-font-family` · `--ng-form-font-size-sm/md/lg` · `--ng-form-label-color` / `-font-weight` / `-font-size-*` / `-margin-bottom-*` · `--ng-form-default-size` |
809
873
 
@@ -879,6 +943,47 @@ kaplar — yardım metni opsiyonelse bunu hesaba kat.
879
943
  > `input` `display:none` olduğu için klavye odağı görünmez. Klavyeyle gezilecek ekranlarda
880
944
  > `&__item-input:focus-visible + &__item-text` için de bir dış çizgi tanımlayın.
881
945
 
946
+ ### 1h-4 — Stilin ÜÇ kanalı ve efektif sonucu okuma
947
+
948
+ `style` tek kanal değildir; bir düğümün ekranda aldığı görünüm üç ayrı yerden gelir ve hangisini
949
+ kullanacağını bilmek gereksiz CSS yazmayı önler:
950
+
951
+ | Kanal | Ne için | Nereden yazılır |
952
+ |---|---|---|
953
+ | `appearance` (düğüm alanı) | dış/iç boşluk, genişlik-yükseklik, z-index, opaklık, taşma, **sarmalayıcının** zemini/çerçevesi/gölgesi | `page_schema_patch` ile düğümün `appearance` alanı |
954
+ | Şema prop'ları | yerleşim (`layout`/`direction`/`gap`/`gridColumns`), semantik renkler, boyut (`size`) | `replace_prop` |
955
+ | `style` prop'u | geri kalan her şey; iç seçiciler, pseudo-class, `--ng-*` token ezme | `replace_prop` / `set_binding` |
956
+
957
+ **Dikkat — `appearance` kutunun İÇİNİ boyamaz.** Yüzey stili düğüm sarmalayıcısına
958
+ (`.ng-app__node-item`) yazılır; `box` gibi kendi zeminini çizen bir komponentin içi bundan
959
+ etkilenmez. Tipli stil modeline göçen komponentler bu boşluğu kapatır: `box` (ve emekli
960
+ takma adı `aibox`: `surfaceStyle` · `typographyStyle` · `hoverStyle` · `sectionStyle`),
961
+ `container` (aynı üçlü + `headerStyle`), `text` (`typographyStyle` · `surfaceStyle`;
962
+ hover/bölge yok), `statcard` (üçlü + `valueStyle`) ve `icon` (yalnız `hoverStyle`;
963
+ yüzey/tipografi kararları çip ve boyut prop'larında) — `component_get('<slug>')` sözleşmeyi
964
+ verir, göçen komponentlerin listesi `TYPED_STYLE_COMPONENTS` kaydıdır.
965
+
966
+ **Stil yazma SIRASI (tipli gruplu komponentlerde):** önce tipli gruplar — kararın alanı
967
+ grupta varsa ORAYA yaz; grubun karşılamadığı her CSS kararı `style` kanalına (studio'daki
968
+ "custom properties" alanının veri kanalı budur). Tipli alan dururken aynı özelliği `style`'a
969
+ yazma: `style` kazanır ama panel alanı etkisiz görünür ve tasarımcı yanılır.
970
+ Bileşenin BİLDİRDİĞİ parçalara (`&__header`, `&__body` …) da yazabilirsin: `style: { '&__header':
971
+ { minHeight: '0' } }` — studio'daki "custom properties" bölümünün Hedef seçicisi bunun panel
972
+ yüzüdür. Boyut alanlarında METİN `'0'` gerçek seçimdir (bileşenin taban dolgusunu/yüksekliğini
973
+ kaldırır); sayı `0` "seçilmedi" sayılır.
974
+ **Katman sırası (K4):** tema `componentStyles` → `$preset` TABANDIR (varsayılan verir);
975
+ tipli grup alanları temayı alan alan EZER; düğümün serbest `style`'ı en üstte SON SÖZDÜR.
976
+ Boyut alanları CSS uzunluğu metnidir (`'18px'`, `'1.2em'`); `radius` tek alanda preset
977
+ anahtarı YA DA CSS uzunluğu kabul eder.
978
+ **Tema preset'i bağlamak:** `style: { $preset: 'card' }` (adlar `project_get` → `theme.presets`;
979
+ `componentStyles[type]` referanssız uygulanır). Bir alanı temaya bırakmak = o alanı hiç yazmamak;
980
+ studio'da bunun adı "Temaya dön"dür. Tema değerini gruba kopyalama — bağ kopar, tema değişince izlemez.
981
+
982
+ **Efektif stili okumak:** `page_runtime_get(resolveStyles: true)` her düğüme `resolvedStyle`
983
+ (kaskad çözülmüş birleşik stil) ve `styleLayers` (`{ typedGroups, componentStyle, preset, own }`)
984
+ ekler. "Bu kutu neden böyle görünüyor / benim yazdığım stil neden görünmüyor" sorusunun cevabı
985
+ budur — bir preset ya da `componentStyles` sizin değerinizi eziyorsa `styleLayers` söyler.
986
+
882
987
  ## Kural 2 — Şema değişikliği `page_update` ile YAPILMAZ
883
988
 
884
989
  İki ayrı yazma yolu vardır ve karıştırılmaz:
@@ -1337,6 +1442,10 @@ bağlanır, filtre `source:'expression'` + `value:'user.id'` olur.
1337
1442
  | Kimlik alanları | `email` · `password` |
1338
1443
  | Dosya | `fileUpload` (dropzone) · `fileInput` · `filebutton` · `file-viewer` |
1339
1444
  | Liste | `table` · `datatable` (Kural 1e) · `repeater` (kart listesi VE dinamik form — Kural 1g) · `tree-view` (hiyerarşi; `isSearchable` ile istemci içi arama) · `timeline` (tarihli olay akışı) |
1445
+ | Pano / takvim | `kanban` (durum kolonlarında sürüklenebilir kartlar; taşıma `cardMove` ile `event.data`'dan okunur, kalıcılık AKIŞIN işidir) · `eventcalendar` (ay/hafta/gün; veri `rows`'a akışla yazılır, `monthChange` tazeleme kancasıdır, `month` 1-tabanlı) |
1446
+ | Kayıt detayı | `detailview` — tek kaydı etiket/değer ızgarası basar (alan başına format, iç içe alan yolu); OLAY YAYMAZ, tıklanabilirlik gerekiyorsa kapsayıcıya sar |
1447
+ | Adımlı akış | `stepper` — tabs gibi her adım kendi droppable görünümünü taşır; ileri gidiş `canProceed` kapısıyla (`{{ form1.isValid === true }}` biçimi), adım `currentStep` setter'ıyla sürülür |
1448
+ | İmza / CSV içe aktarma | `signature` (değer PNG data-URL ya da ham SVG; `change` setter'lı) · `importwizard` (3 adımlı sihirbaz; temiz+hatalı satırlar `complete` yükünde — satırları uca yazmak akışın işidir) |
1340
1449
  | Sayfalama (tablo dışı listeler için) | `pagination` — offset/limit yazar, `self.offset`/`self.limit` ile sorguya bağlanır |
1341
1450
  | Grafik / KPI | `chart` (ECharts: bar·line·area·pie·donut + waterfall/histogram/pareto/bullet/tornado/radial) · `statcard` (tek KPI + trend + sparkline) · `progress` |
1342
1451
  | Kapsayıcı / düzen | `container` (ızgara ya da flex — Kural 1f-2) · `form` (düzen prop'u YOK, çocuklar ızgara koordinatıyla dizilir) · `tabs` · `collapsible-container` · `accordion` |
@@ -1344,7 +1453,7 @@ bağlanır, filtre `source:'expression'` + `value:'user.id'` olur.
1344
1453
  | Katman | `modal` · `drawer` (şema kökünde — Kural 1d) |
1345
1454
  | Geri bildirim | `notification` (sayfa içi kutu) · `toast` (geçici bildirim; isOpen yükselen kenarı) · `form-validation-errors` · `confirm-bar` · `result` (işlem sonucu ekranı) · `emptystate` (boş liste; ikon YA DA görsel + aksiyon düğmeleri) |
1346
1455
  | Gezinme / başlık | `breadcrumb` · `breadcrumb-title` (başlık + daralan iz) · `page-header` · `header` · `sidebar` · `auth-layout` · `language-selector` (dil değiştirici — çoklu dil kurulumu `platform_guide('translation')`) |
1347
- | Sunum | `text` · `html` · `image` · `video` · `icon` · `qrcode` · `tags` · `avatar` · `badge` (sayaç “99+” / nokta / durum metni) · `carousel` · `iframe` (sandbox’lı gömme) · `jsonviewer` (katlanabilir JSON; JSON METNİNİ de parse eder) · `audit-basic` · `audit-history` |
1456
+ | Sunum | `text` · `html` · `image` · `video` · `icon` (Material/Phosphor glif; çip + rozet + spin + click/href) · `qrcode` · `tags` · `avatar` · `badge` (sayaç “99+” / nokta / durum metni) · `carousel` · `iframe` (sandbox’lı gömme) · `jsonviewer` (katlanabilir JSON; JSON METNİNİ de parse eder) · `audit-basic` · `audit-history` |
1348
1457
 
1349
1458
  > **`chart` komponenti VAR (2026-08-25 ölçümü) — bu satır düzeltildi.** Rehberin önceki hâli
1350
1459
  > "grafik komponenti yoktur, `html` + `loadScript` kullanın" diyordu; `component_list` çıktısında
@@ -1515,6 +1624,15 @@ micro-op hattına dönün.
1515
1624
  style + Kural 1h deseniyle yeniden kurulunca kart 400px'e ve ortaya oturdu. Gateway bunu **PAGE-014 (uyarı)** ile
1516
1625
  yanıtın `layoutWarnings` alanında bildirir. **Düzeni `style` ile kurmaya hiç kalkışma:**
1517
1626
  ortalama/yaslama/aralık işi kapsayıcının kendi `layout: 'flex'` prop grubuna aittir (Kural 1f-2).
1627
+ - **Menüde "bulunduğun sayfa" vurgusu: `sidebar`/`layout`'ta `activeHref`, `header`'da
1628
+ `activeNavHref`.** `sidebar` ve `layout` menü ağacını ortak renderer'la çizer: `activeHref` boşken
1629
+ tarayıcıdaki adrese uyan satırı kendileri vurgular, prop yazılırsa o satır vurgulanır (akıştan
1630
+ setter ile de yazılabilir). Eşleşmenin iki tarafı da `getHref`'ten geçtiği için satırın ham
1631
+ `href`'ini, bağlantı seçicisinin nesnesini ya da tam yolu yazmak aynı satırı gösterir. `header`
1632
+ yalnız prop'a bakar (adrese bakmaz), `qm-header` ise yalnız adrese bakar (prop'u yoktur). Menü
1633
+ satırları `style`'ın `&__navigations*` parçalarıyla biçimlendirilemez ve menü olay yaymaz —
1634
+ vurgu dışında bir "seçili satır" davranışı kurulamaz. Ayrıntı:
1635
+ `component_get('sidebar' | 'layout' | 'header' | 'qm-header')`.
1518
1636
  - **`isHidden` olmayan komponent listesine `dropdown` da dahildir** (`text`, `multi-select`,
1519
1637
  `dropdown`). Koşullu göstermek için `isHidden`'ı olan bir `container`'a sar (Kural 1g).
1520
1638
  - **Ana sayfayı TAŞIYAMAZSIN — `application_update.homepageId` kalıcı DEĞİL ve yanıtı yanıltıcı.**
@@ -2,6 +2,65 @@
2
2
 
3
3
  Buradaki kurallar gerçek projelerde (qm, hr) **deneyerek doğrulandı**.
4
4
 
5
+ ## Her adım NE İŞE YARADIĞINI yazar (WF-024, block)
6
+
7
+ Studio kanvası adımın **adını** gösterir, ne yaptığını göstermez. 11 satırlık bir `code` adımının
8
+ işini anlamak için ifadeyi satır satır okumak gerekir — akışı devralan geliştirici için bu, her
9
+ seferinde tersine mühendisliktir. Bu yüzden **her adım** ve **akışın kendisi** bir `description`
10
+ taşır; gateway açıklamasız gövdeyi platforma göndermez.
11
+
12
+ ```jsonc
13
+ { "name": "anket-kaydet", "type": "workflow",
14
+ "description": "Anket başlığını ve sorularını tek çağrıda kaydeder.",
15
+ "steps": [
16
+ { "id": "<GUID>", "name": "createSurvey", "type": "table",
17
+ "description": "Anket başlığını surveys'e yazar; oluşan id sonraki adımlarda kullanılır.",
18
+ "rule": { … } }
19
+ ] }
20
+ ```
21
+
22
+ - **Mekanizmayı değil NİYETİ yaz.** `"assignments tablosunu status='Aktif' ile okur"` koddan zaten
23
+ okunuyor ve hiçbir şey eklemiyor. `"kullanıcının asal pozisyonunu seçer; birden çok aktif atama
24
+ varsa is_primary olanı tercih eder, o da yoksa ilkini alır"` — yazılmadıkça hiçbir yerde
25
+ bulunmayan bilgi budur.
26
+ - **`start` ve `end` muaftır** (kural onları hiç sormaz): ikisi de yapısal işaretçi, açıklanacak
27
+ davranışları yok.
28
+ - **Gateway'in enjekte ettiği `gatewayOnError` adımı kendi açıklamasını taşır** — o adımı sen
29
+ yazmadığın için kural seni onun yüzünden durdurmaz.
30
+ - Alan **kalıcıdır ve çalışma zamanını etkilemez**: motor okumaz, yalnız Studio'da kanvas
31
+ düğümünde ⓘ ikonu + popover olarak ve adım panelinde düzenlenebilir metin olarak görünür.
32
+ İnsan da düzeltebilir.
33
+ - `preview` bu kuralın dışındadır — kaydedilmeyen bir denemede açıklama aranmaz.
34
+ - Aynı disiplin ucun kendisi için de geçerli: `platform_guide('microservice')` §MS-014.
35
+
36
+ ## Tuzağı varsa `caution` yaz (opsiyonel, kural yok)
37
+
38
+ `description` "bu adım ne işe yarar" sorusunu cevaplar. `caution` bambaşka bir soruyu cevaplar:
39
+ **"burada neye dikkat etmek gerekiyor."** İkisi ayrı alandır çünkü tek metne sıkıştırıldıklarında
40
+ ikisi de okunmaz olur — açıklama uyarı listesine döner, uyarı da açıklamanın içinde kaybolur.
41
+
42
+ **Bu alanı hiçbir kural zorlamaz ve doldurma metin kabul edilmez.** Adımın gerçek bir tuzağı
43
+ yoksa alanı hiç yazma. Yazmaya değer olanlar:
44
+
45
+ - **İdempotent değil** — "iki kez çalışırsa çift atama yazar; çağıran tarafta koruma yok"
46
+ - **Kapsam bu adımda değil** — "tenant filtresi burada yok, kimlik kapsaması bağlı middleware'de"
47
+ - **Dış bağımlılık** — "ödeme sağlayıcısına senkron çağrı; sağlayıcı yavaşlarsa istek bekler"
48
+ - **Sessiz düşen dal** — "eşleşme bulunamazsa hata dönmez, boş dizi döner"
49
+
50
+ ```jsonc
51
+ { "id": "<GUID>", "name": "createAssignment", "type": "table",
52
+ "description": "Kullanıcıyı seçilen pozisyona atar.",
53
+ "caution": "İki kez çalışırsa çift atama yazar; çağıran tarafta idempotency koruması yok." }
54
+ ```
55
+
56
+ **Yazılmayacaklar:** `"bu adım veriyi okur, dikkat"` gibi mekanizma tekrarı, `description`'ın
57
+ başka kelimelerle yazılmışı, `"dikkatli olun"` gibi genel tavsiye. Bunlar kanvastaki ⚠ işaretini
58
+ değersizleştirir — işaretin tüm değeri **seyrek** olmasından gelir.
59
+
60
+ Alan `description` gibi inerttir: motor okumaz. Studio'da kanvas düğümünde kehribar ⚠ ikonu,
61
+ adım panelinde düzenlenebilir metin, analiz belgesinde ayrı satır olarak görünür; `workflow_get`
62
+ outline görünümünde geri okunur — sonraki oturumda tuzağı tam gövdeyi almadan görürsün.
63
+
5
64
  ## Karar kuralları
6
65
 
7
66
  1. **DTO önce (yalnız workflow tipinde).** `type='workflow'` için girdi/çıktı sözleşmesini
@@ -49,6 +108,55 @@ Bağlanmamış step çalışmaz. `start` tek branch taşır; normal step'ler `su
49
108
  `condition` koşul branch'leri + `else`; terminal step'ler (`response`, `convert`, `revert`,
50
109
  `end`) **branch taşımaz**.
51
110
 
111
+ ## Hata yolu
112
+
113
+ **Gateway workflow ve middleware tanımlarına hata yolunu otomatik ekler** (yanıtta
114
+ `gatewayNormalized`): start adımına `type: 'global-error'` dalı + 500 dönen bir `response` adımı
115
+ (`gatewayOnError`). Kendi hata gövdeni istiyorsan bu adımın `bodyExpression`'ını değiştir ya da
116
+ tanımı kendi global-error dalınla gönder — dal **bağlıysa** (`nextStepId` doluysa) gateway
117
+ dokunmaz. `nextStepId: null` ile bağlanmamış gönderirsen gateway ikinci bir dal eklemez, mevcut
118
+ dalını enjekte ettiği hata adımına bağlar.
119
+
120
+ **Fail dallarını tek tek bağlamana gerek yok.** Bir adımın `fail` dalı bağlanmamışsa
121
+ (`nextStepId: null`) ya da hiç yoksa, hata **kendiliğinden** global-error dalına gider. `fail`
122
+ dalını yalnız o adıma özel bir telafi akışı (örn. "kayıt yoksa oluştur") istiyorsan bağla.
123
+
124
+ Global-error dalında `error` değişkeni tanımlıdır:
125
+
126
+ ```jsonc
127
+ { "code": "RowAlreadyExists", "message": "…", "detail": "…", "type": "…",
128
+ "step": { "id": "…", "name": "writeRow", "type": "table", "data": …, "input": [ … ] } }
129
+ ```
130
+
131
+ `error.step` hangi adımın patladığını söyler — hata yanıtına koymadan önce iki kez düşün, iç
132
+ yapıyı dışarı sızdırır.
133
+
134
+ **Kurallar ve sınırlar:**
135
+
136
+ - **Global-error dalı TEK olmalı ve START adımında durmalı.** Başka adıma koyulan dal hiç
137
+ çalışmaz, platform 400 döner. Gateway bunu `WF-022` ile istek gitmeden reddeder.
138
+ - **Hata `response` adımı ReturnDto'ya bağlı değildir.** Global-error modunda platform sözleşmeyi
139
+ atlar, gövde serbesttir — `returnDtoId` ile çakışmaz.
140
+ - **Loop içi hatalar global-error dalına GİTMEZ.** Loop çocuk bağlamı bu yönlendirmeyi kapatır;
141
+ loop adımının kendi `fail` dalını bağla.
142
+ - **`automation` tipinde `response` adımı yasaktır**, bu yüzden gateway bu tipe enjeksiyon
143
+ yapmaz. Hata yolunu kendin yaz: global-error dalını `revert` (tetikleyen satır işlemini geri
144
+ alır) ya da `email`/`table` adımına bağla. Bağlı hata yolu yoksa `WF-021` uyarır.
145
+ - **Hata yolu hiç yoksa** akış patladığında platform 500 + ham exception mesajı döner. Çalışır,
146
+ ama çıktı denetimsizdir ve lokalize değildir.
147
+
148
+ > **Preview HTTP 200 dönerken bile akış patlamış olabilir.** `executionSuccess` yalnız SON adıma
149
+ > bakar; ortada patlayıp hata dalından dönen akış `true` gösterir. Gateway tüm adımları (iç içe
150
+ > `children` ve tetiklenen `automations` dâhil) tarar ve hata bulursa yanıta `runtimeErrors`
151
+ > alanını ekler — `workflow_preview` ve `automation_preview` yanıtlarında bu alanı kontrol et.
152
+ >
153
+ > **`runtimeErrors` her tool'da aynı şeyi ölçmez.** Alan yalnız `steps[]` taşıyan yanıtlarda adım
154
+ > adım tarama yapar. `microservice_execute` / `microservice_debug` / `microservice_preview`
155
+ > yanıtlarında gateway sadece `httpStatus >= 400` durumunu bulgu sayar: bu tool'lar adım ağacını
156
+ > `executionItems` olarak döner ve o düğümlerde hata işareti yoktur, yani HTTP 200 gövdesine gömülü
157
+ > bir microservice hatası `runtimeErrors` üretmez. Orada `body`'yi kendin oku ve şüpheliyse
158
+ > `microservice_debug` ile adım adım bak. `query_preview` yanıtları hiç kapsanmaz.
159
+
52
160
  ## Condition step kuralları
53
161
 
54
162
  Koşullar `rule.items[]` dizisindedir ve **sırayla** değerlendirilir; ilk `true` olan dala
@@ -151,6 +259,11 @@ Tek uçta "başlık + N alt kayıt" yazmanın yolu budur (anket + sorular + seç
151
259
 
152
260
  - `rule` içinde `datasourceId` + `entityId` zorunlu; `selectFields`/`filters` öğelerinde
153
261
  **`fieldId` zorunlu** (yalnız `fieldSlug` yetmez → `EvaluateWorkflowStepFailedException`).
262
+ - **`selectFields` = akışın gerçekten OKUDUĞU alanlar.** Entity'nin tüm alanlarını işaretlemek
263
+ her satırı gereksiz büyütür (join'li adımda maliyet katlanır) ve adımın ne ürettiğini
264
+ okunamaz hâle getirir. Sonraki adımların, filtrelerin ve response `bodyExpression`'ının
265
+ dokunmadığı alanları çıkarın — gateway bunları **WF-023 (warn)** ile adıyla sayar. Çıktıyı
266
+ bütün olarak geçiriyorsanız (`{ list: rows.data }`) kural susar.
154
267
  - **Table step JOIN kurabilir** ve alias/path disiplini microservice'inkiyle **birebir aynıdır**
155
268
  (Studio aynı form bileşenlerini kullanır): `joins[].path` = `<kökEntityId>.<ilişkiAlanı…>`,
156
269
  join'li `selectFields[].path` = kök id olmadan aynı zincir, kök alanlarda `path` yok.
@@ -243,12 +356,15 @@ zorunludur (`CreateWorkflowCommandValidator`: `When(ReturnDtoId.HasValue) → Re
243
356
  ```jsonc
244
357
  {
245
358
  "name": "Product Lookup", "slug": "product-lookup", "type": "workflow",
359
+ "description": "Ada göre tek ürün getirir; bulunamazsa hata yoluna düşer.",
246
360
  "parameterDtoId": "<girdi DTO id>", "parameterBehavior": "object",
247
361
  "returnDtoId": "<çıktı DTO id>", "returnBehavior": "object",
248
362
  "steps": [
249
363
  { "id": "<GUID-1>", "name": "Start", "type": "start",
250
- "branches": [{ "id": "<GUID-2>", "name": "start", "type": "success", "nextStepId": "<GUID-3>" }] },
364
+ "branches": [{ "id": "<GUID-2>", "name": "start", "type": "success", "nextStepId": "<GUID-3>" },
365
+ { "id": "<GUID-7>", "name": "global-error", "type": "global-error", "nextStepId": "<GUID-8>" }] },
251
366
  { "id": "<GUID-3>", "name": "load_product", "type": "table",
367
+ "description": "input.productName ile ürünü okur; tek kayıt bekler.",
252
368
  "rule": { "stepType": "table", "datasourceId": "<datasource id>", "entityId": "<entity id>",
253
369
  "operation": "get", "isSingle": true,
254
370
  "selectFields": [{ "alias": "<entity slug>", "fieldId": "<alan id>", "fieldSlug": "name" }],
@@ -258,9 +374,16 @@ zorunludur (`CreateWorkflowCommandValidator`: `When(ReturnDtoId.HasValue) → Re
258
374
  "limit": { "name": "limit", "offset": "offset", "source": "query-string" } },
259
375
  "branches": [{ "id": "<GUID-4>", "name": "success", "type": "success", "nextStepId": "<GUID-5>" },
260
376
  { "id": "<GUID-6>", "name": "fail", "type": "fail", "nextStepId": null }] },
377
+ // ^ bağlanmamış fail dalı: hata global-error dalına gider
261
378
  { "id": "<GUID-5>", "name": "product_response", "type": "response",
379
+ "description": "Bulunan ürünün adını 200 ile döner.",
262
380
  "rule": { "bodyExpression": "{ name: load_product.data.name }", "httpStatusCode": 200,
263
- "headerExpression": "{ }" }, "branches": [] }
381
+ "headerExpression": "{ }" }, "branches": [] },
382
+ { "id": "<GUID-8>", "name": "onError", "type": "response",
383
+ "description": "Akış patlarsa hata kodunu ve mesajını 500 ile döner.",
384
+ "rule": { "stepType": "response", "httpStatusCode": 500,
385
+ "bodyExpression": "({ code: error.code, message: error.message })" },
386
+ "branches": [] }
264
387
  ]
265
388
  }
266
389
  ```
@@ -315,6 +438,8 @@ zorunludur (`CreateWorkflowCommandValidator`: `When(ReturnDtoId.HasValue) → Re
315
438
  - Automation'da `isQueue`/`operation` eksik ya da returnDtoId verilmiş (WF-010, block)
316
439
  - `fields` ∩ `selectFields` çakışması (WF-012, warn) · join'li table'da boş selectFields (WF-007, warn)
317
440
  - Join'li table step'inde kök `rule.alias` boş (WF-019, block — Postgres 42601'in sebebi)
441
+ - Akışta hiç okunmayan `selectFields` alanı (WF-023, warn — gereksiz taşınan sütun)
442
+ - Adımda ya da akışta `description` yok (WF-024, block — Studio'da adım okunamaz kalır)
318
443
  - İlişki alanının içini okuyan ifade (WF-020, warn — join yoksa alan düz FK'dır)
319
444
  - `set-user` adımı olmadan `user` okuyan middleware (MW-003, warn)
320
445