@appaflytech/zeyrek-ai-mcp 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/.env.example +8 -3
  2. package/.env.production.example +2 -0
  3. package/README.md +229 -193
  4. package/dist/config.js +20 -2
  5. package/dist/knowledge/backendConstraints.js +897 -0
  6. package/dist/knowledge/componentCatalog.js +196 -42
  7. package/dist/knowledge/coverage.js +43 -0
  8. package/dist/knowledge/flowGuards.js +30 -0
  9. package/dist/knowledge/guideSections.js +49 -0
  10. package/dist/knowledge/hydrate.js +97 -0
  11. package/dist/knowledge/languageCodes.js +36 -0
  12. package/dist/knowledge/languages.js +105 -0
  13. package/dist/knowledge/layoutSummary.js +10 -0
  14. package/dist/knowledge/localization.js +51 -0
  15. package/dist/knowledge/normalize.js +449 -16
  16. package/dist/knowledge/outlines.js +136 -12
  17. package/dist/knowledge/propTypes.js +80 -0
  18. package/dist/knowledge/rules.js +618 -43
  19. package/dist/knowledge/schemaGuard.js +199 -0
  20. package/dist/knowledge/styleResolve.js +63 -4
  21. package/dist/observability/trace.js +27 -3
  22. package/dist/platform/errors.js +46 -4
  23. package/dist/platform/http.js +33 -4
  24. package/dist/platform/patch.js +27 -0
  25. package/dist/platform/refs.js +104 -0
  26. package/dist/platform/responses.js +9 -2
  27. package/dist/platform/screenshot.js +527 -16
  28. package/dist/platform/secrets.js +40 -0
  29. package/dist/platform/tokenVault.js +89 -0
  30. package/dist/platform/writeDiff.js +281 -0
  31. package/dist/resources/examples/layout-app-shell.json +1 -1
  32. package/dist/resources/examples/microservice-table-list.json +2 -4
  33. package/dist/resources/examples/page-dynamic-form.json +2 -2
  34. package/dist/resources/examples/page-flex-sections.json +99 -0
  35. package/dist/resources/examples/page-sign-in.json +3 -2
  36. package/dist/resources/guides/component.md +69 -8
  37. package/dist/resources/guides/entity.md +404 -16
  38. package/dist/resources/guides/expressions.md +44 -2
  39. package/dist/resources/guides/external-component.md +9 -0
  40. package/dist/resources/guides/getting-started.md +77 -7
  41. package/dist/resources/guides/layout.md +53 -10
  42. package/dist/resources/guides/microservice.md +226 -13
  43. package/dist/resources/guides/middleware.md +89 -1
  44. package/dist/resources/guides/page.md +383 -46
  45. package/dist/resources/guides/translation.md +126 -8
  46. package/dist/resources/guides/view.md +16 -1
  47. package/dist/resources/guides/workflow.md +198 -27
  48. package/dist/resources/knowledge.js +5 -0
  49. package/dist/resources/rules.js +27 -0
  50. package/dist/resources/schema-fingerprint.json +8 -0
  51. package/dist/resources/schemas/CreateBulkRowCommand.json +33 -0
  52. package/dist/resources/schemas/CreateDatasourceCommand.json +15 -6
  53. package/dist/resources/schemas/CreateDiscoveredEntitiesCommand.json +19 -8
  54. package/dist/resources/schemas/CreateEntityCommand.json +52 -12
  55. package/dist/resources/schemas/CreateMicroserviceCommand.json +26 -2
  56. package/dist/resources/schemas/CreateQueryCommand.json +56 -3
  57. package/dist/resources/schemas/CreateRowCommand.json +38 -0
  58. package/dist/resources/schemas/CreateViewCommand.json +13 -10
  59. package/dist/resources/schemas/CreateWorkflowCommand.json +95 -14
  60. package/dist/resources/schemas/GetRowsQuery.json +3 -0
  61. package/dist/resources/schemas/PreviewMicroserviceCommand.json +16 -0
  62. package/dist/resources/schemas/PreviewQueryCommand.json +5 -2
  63. package/dist/resources/schemas/PreviewWorkflowCommand.json +34 -9
  64. package/dist/resources/schemas/StepDto.json +23 -3
  65. package/dist/resources/schemas/UpdateBulkRowCommand.json +33 -0
  66. package/dist/resources/schemas/UpdateDatasourceCommand.json +15 -6
  67. package/dist/resources/schemas/UpdateEntityCommand.json +49 -10
  68. package/dist/resources/schemas/UpdateMicroserviceCommand.json +28 -3
  69. package/dist/resources/schemas/UpdateQueryCommand.json +13 -3
  70. package/dist/resources/schemas/UpdateRowCommand.json +40 -0
  71. package/dist/resources/schemas/UpdateViewCommand.json +15 -10
  72. package/dist/resources/schemas/UpdateWorkflowCommand.json +95 -14
  73. package/dist/server.js +4 -0
  74. package/dist/tools/access.js +3 -3
  75. package/dist/tools/applications.js +1 -1
  76. package/dist/tools/automations.js +63 -30
  77. package/dist/tools/common.js +39 -0
  78. package/dist/tools/components.js +16 -7
  79. package/dist/tools/datasources.js +83 -21
  80. package/dist/tools/deployments.js +34 -2
  81. package/dist/tools/dtos.js +25 -12
  82. package/dist/tools/entities.js +113 -28
  83. package/dist/tools/externalComponents.js +9 -3
  84. package/dist/tools/files.js +20 -6
  85. package/dist/tools/flows.js +110 -11
  86. package/dist/tools/folders.js +7 -2
  87. package/dist/tools/globalFunctions.js +63 -0
  88. package/dist/tools/layouts.js +103 -29
  89. package/dist/tools/microservices.js +229 -38
  90. package/dist/tools/organizations.js +5 -6
  91. package/dist/tools/pages.js +365 -100
  92. package/dist/tools/platformInfo.js +152 -11
  93. package/dist/tools/projects.js +183 -33
  94. package/dist/tools/queries.js +32 -10
  95. package/dist/tools/rows.js +159 -45
  96. package/dist/tools/screenWrite.js +59 -0
  97. package/dist/tools/screens.js +28 -0
  98. package/dist/tools/translations.js +24 -8
  99. package/dist/tools/views.js +20 -10
  100. package/dist/tools/workflows.js +33 -16
  101. package/package.json +6 -3
@@ -14,9 +14,41 @@ kendi arayüz dili (`i18n.md`, yalnız frontend'i ilgilendirir) ile **karıştı
14
14
  `key`'e bakar).
15
15
  - Proje **dilleri** ve **varsayılan dil** application değil **proje** düzeyinde ayardır
16
16
  (`project_get`/`project_update` `languages`/`defaultLanguage`) ve tam **ISO 639-1** kod
17
- kümesinden seçilir (`ProjectConstants.SupportedLanguages`, ~184 kod). `translation_update`'e
18
- proje dillerinde olmayan bir kod verirsen platform isteği reddeder — önce `project_update` ile
19
- dili ekle.
17
+ kümesinden seçilir (`ProjectConstants.SupportedLanguages`, ~184 kod).
18
+
19
+ ## ÖNCE DİLLERİ KUR — yoksa çeviri yazmak hiçbir işe yaramaz
20
+
21
+ > **Gateway'den açılan projede bu iş halledilmiştir (PRJ-003).** Platformun create ucu dil alanı
22
+ > taşımadığı için `project_create` projeyi dilsiz doğuruyordu; gateway artık create'ten hemen sonra
23
+ > dilleri kuruyor — verdiğinizi, vermezseniz tek dillik `['en']` + `'en'` tohumunu — ve bunu
24
+ > `gatewayNormalized` ile bildiriyor. Başka dil istiyorsanız `project_create`'e `languages`
25
+ > yazın. Aşağısı **eski projeler** ve gateway dışından açılmış projeler için geçerlidir.
26
+
27
+ **Platform tarafında yeni proje dilsiz doğar:** `CreateOrganizationProjectCommand`
28
+ `Languages`/`DefaultLanguage` alanı taşımaz, ikisi de `null` başlar. Bu durumda:
29
+
30
+ 1. `translation_update` yazmayı **kabul eder ve 200 döner** — satırlar veritabanına düşer.
31
+ 2. Runtime sayfa paketi `languages: null` taşır, aktif dil hiç seçilemez ve fallback zinciri
32
+ anahtarın kendisine düşer: ekranda `dashboard.card.candidate_statistics` yazar.
33
+ 3. **Hiçbir katman uyarmaz.** Backend dil kodunu yalnız 184 kodluk ISO sözlüğüne göre doğrular
34
+ (TR-001), projenin `languages` listesine göre **doğrulamaz** — iki aggregate arasında bağ yoktur.
35
+
36
+ Ölçüldü: 2026-09-09 1. koşu `S-15` — 15 anahtar × 2 dil (200) + 15 prop bağı (200), ekranda her
37
+ etiket ham anahtar. Koşunun en pahalı sessiz düşmesi; yalnız gerçek karede görülüyor.
38
+
39
+ ```jsonc
40
+ // SIRA: önce bu
41
+ { "id": "<project id>", "languages": ["en", "tr"], "defaultLanguage": "en" } // project_update
42
+ // sonra çeviriler
43
+ { "applicationId": "<app id>", "keys": [ /* … */ ] } // translation_update
44
+ ```
45
+
46
+ Gateway bu bağı kurar: `translation_update` yazmadan önce projeyi okur ve liste boşsa, kod listede
47
+ yoksa ya da `defaultLanguage` kurulu değilse **TR-002 caution** düşürür (yazmayı durdurmaz).
48
+ `project_get` ve `project_overview` yanıtlarındaki `localization` bloğu durumu doğrudan söyler —
49
+ `runtimeResolvesTranslations: false` ise çeviri yazmanın ekranda karşılığı olmayacak demektir.
50
+ Platform dil alanlarını `null` olduklarında serileştirmede düşürdüğü için ham yanıtta `languages`
51
+ diye bir alan **hiç görünmez**; blok tam olarak bunu görünür kılmak için var.
20
52
 
21
53
  ## Araçlar
22
54
 
@@ -49,12 +81,36 @@ backend'de zaten tanımlı, Studio'nun "Translate" aksiyonunun ürettiği şekil
49
81
  "value": { "type": "translation", "ref": "home.welcome", "data": "Hoş geldiniz" } }
50
82
  ```
51
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:
89
+ >
90
+ > ```jsonc
91
+ > { "op": "set_prop_type", "target": "welcome_text", "prop": "text",
92
+ > "propType": "translation", "ref": "home.welcome" }
93
+ > ```
94
+ >
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.
100
+ >
101
+ > Ölçüldü 2026-09-08 (yerel `dil-dogrulama/dil-app`): kurulu bir `statcard`'ın `title` prop'u tek
102
+ > `set_prop_type` op'uyla `f11.tam` anahtarına bağlandı; gerçek ekranda `tr` → "iki dilde dolu",
103
+ > `en` → "filled in both", `de` → "in beiden gefuellt" ve dil değişimi sayfa yenilenmeden geçti.
104
+
52
105
  - **Anahtar `ref`'tedir, `data` DEĞİL.** Runtime her `getNodeProps` turunda `data`'yı
53
106
  `resolveTranslation(...)` sonucuyla EZER (`code` prop'larıyla aynı davranış — `page.md` Kural 1c
54
107
  bunun `code` karşılığını anlatır). `ref`'i okumadan/yazmadan `data`'ya güvenme; `data` yalnız
55
108
  Studio'nun sakladığı önceki statik değerdir (fallback/geri dönüş amaçlı, çözümlemeyi ETKİLEMEZ).
56
109
  - **Fallback zinciri:** aktif dil → projenin varsayılan dili → anahtarın kendisi. Boş string de
57
110
  "eksik" sayılır ve zincirde bir alt basamağa düşer — bir anahtar hiçbir zaman boş render olmaz.
111
+ Zincirin sonu anahtarın kendisi olduğu için **proje dilsizken ekran boş değil, ham anahtar dolu
112
+ görünür** — hata gibi durmadığı için gözden kaçar. Kaynak: `resolveRuntimeLanguage` +
113
+ `resolveTranslation` (`zeyrek-frontend/packages/ng-apps-screen-builder/src/translation.functions.ts`).
58
114
  - Hangi prop'lar çeviriye bağlanabilir platformca kısıtlanmaz (Studio'da yalnız `text`/`long-text`
59
115
  tipli prop'larda "Translate" aksiyonu gösterilir, ama şema düzeyinde her static prop aynı Prop
60
116
  şeklini kabul eder).
@@ -67,14 +123,76 @@ Son kullanıcı dili `language-selector` komponentiyle değiştirir (`platform_g
67
123
  prop'lar sayfa yenilenmeden yeniden çözülür — ayrı bir akış kurmana gerek yok, komponenti sayfaya
68
124
  eklemen yeterli. Projede dil tanımlı değilse (`languages` boşsa) liste boş çizilir.
69
125
 
126
+ ## İstek dili: nereden gelir, hangi ifadede ne denir
127
+
128
+ Her istekte dil şu sırayla çözülür: **`X-Data-Language` başlığı → `Accept-Language` → projenin
129
+ varsayılan dili**. Bölgeli kodlar dil kısmına indirilir (`tr-TR` → `tr`). Aynı değer hem satır
130
+ okumasında (companion tablosu), hem sayfa çevirilerinde, hem ifadelerde kullanılır.
131
+
132
+ | Dünya | Aktif dil | Anahtar çözme | Nerede |
133
+ |---|---|---|---|
134
+ | UI flow (tarayıcı) | `machine` state'i | `t(key)` | `platform_guide('expressions')` §2 |
135
+ | Backend (workflow/microservice ifadesi) | `context.language` | `Loc(key)` | `platform_guide('workflow')` |
136
+ | Ham SQL | — | `{{language}}` ayrılmış parametresi | `platform_guide('microservice')` |
137
+
138
+ `t()` ve `Loc()` **aynı fallback zincirini** izler: aktif dil → projenin varsayılan dili →
139
+ anahtarın kendisi; boş string de "eksik" sayılır. İsimlerin farklı olmasının tek sebebi iki ayrı
140
+ motor olmasıdır (tarayıcı JS'i ve Jint).
141
+
142
+ > **Backend motorunun kültürü de istek dilinden gelir.** `tr` isteğinde `tr-TR`, diğer her dilde
143
+ > `en-US`. Eskiden `tr-TR`'ye sabitti; sabit biçim istiyorsan kültüre güvenme.
144
+
145
+ ## Sayfa sekmesi başlığı: `browserTitleKey`
146
+
147
+ `page_update` / `page_create` `browserTitle` (düz metin) yanında `browserTitleKey` alır. Anahtar
148
+ verilmişse sekme başlığı çeviri sözlüğünden çözülür; anahtar boşsa `browserTitle` olduğu gibi
149
+ kullanılır. İkisi de boşsa sayfa adı yazılır.
150
+
151
+ > **Sekme başlığı SAYFA YÜKLENİRKEN çözülür, canlı dil değişimini İZLEMEZ.** Ölçüldü 2026-09-08
152
+ > (yerel runtime): `language-selector` ile dil değiştirildiğinde sayfa içeriği ve layout başlığı
153
+ > anında yeni dile geçiyor, sekme başlığı ise açılıştaki dilde kalıyor; aynı dille sayfa yeniden
154
+ > yüklendiğinde başlık doğru geliyor. Sebep `Runtime.svelte`'te dilin bir `const` olarak bir kez
155
+ > çözülmesi. Sekme başlığının dilini doğrulayacaksan **yeniden yükleyerek** doğrula.
156
+
157
+ ## Doğrulama mesajları: `t:` öneki
158
+
159
+ Komponent şemasındaki `validations[].message` bir **düz metindir** ve translation prop'u ALMAZ.
160
+ Çeviriye bağlamak için mesajın kendisine `t:` öneki konur:
161
+
162
+ ```jsonc
163
+ { "op": "set_validations", "target": "email_input",
164
+ "validations": [{ "type": "required", "message": "t:form.emailRequired", "isActive": true }] }
165
+ ```
166
+
167
+ Runtime `t:` ile başlayan bir mesajı anahtar sayar ve normal zincirden çözer; önek yoksa metin
168
+ olduğu gibi gösterilir. Çözüm çalışma anında olduğu için şema varsayılanlarını değiştirmek
169
+ gerekmez — `schema.ts`'e dokunulmaz, katalog yeniden yüklenmez.
170
+
70
171
  ## Bilinen sınırlar
71
172
 
72
173
  - **Flow adımlarında Translation kaynağı YOK.** `value.type` sözlüğünde (`platform_guide('page')`
73
174
  Kural 1c sonundaki tablo) `translation` yoktur — bir flow adımının **değerini** doğrudan bir
74
175
  çeviri anahtarına bağlayamazsın, yalnız sayfa şemasındaki statik prop'lar bağlanabilir. İfadeden
75
- okumak gerekiyorsa `t('anahtar')` Jexl fonksiyonu kullanılır (`platform_guide('expressions')`).
76
- - **Runtime sayfa yanıtı sunucu tarafında önbelleklidir** (Postgres `caches` tablosu). Bir
77
- `translation_update`/`project_update(languages)` çağrısından hemen sonra `page_runtime_get`
78
- eski değerleri gösterebilir — önbellek süresi dolana kadar. Acil doğrulama gerekiyorsa
79
- `translation_list` ile yazma tarafını, `page_runtime_get` ile okuma tarafını AYRI doğrula.
176
+ okumak gerekiyorsa **UI flow kodunda** `t('anahtar')` kullanılır: bu tarayıcı tarafındaki
177
+ `runCode` önsözünde tanımlı bir yardımcıdır, backend ifade yüzeyinde (Jint) karşılığı YOKTUR —
178
+ backend tarafının karşılığı `Loc()`'tur (`platform_guide('workflow')`). İkisinin de fallback
179
+ zinciri aynı: aktif dil → varsayılan dil → anahtarın kendisi
180
+ (`platform_guide('expressions')` §2).
181
+ - ~~Runtime sayfa yanıtı önbellekten eski çeviri döndürebilir.~~ **Çözüldü — 2026-09-08:**
182
+ `translation_update` artık uygulamanın bütün sayfalarının önbellek satırlarını düşürüyor
183
+ (`RefreshPageCacheWhenTranslationChangedCommand`), okuma yolu cache-aside olduğu için bir
184
+ sonraki `page_runtime_get` taze değeri kurar. Yazmadan hemen sonra okuyabilirsin.
80
185
  - Mobil runtime (`apps/mobile`) bu özelliği henüz beslemiyor — yalnız web.
186
+
187
+ ## Backend doğrulama sınırları (TR-001)
188
+
189
+ `translation_update` gövdesi platforma gitmeden denetlenir (block): `keys[].key` boş olamaz ve
190
+ tekil; `values`'taki dil kodları ISO 639-1 iki harfli küçük kod (`tr`, `en`, `de`… — `tr-TR`
191
+ reddedilir; 184 kodluk liste `ngapps://rules/backend-constraints` içinde); `deletedKeys` boş anahtar
192
+ taşıyamaz. Proje dilleri de aynı listeyle doğrulanır (`project_update`, PRJ-002).
193
+
194
+ **Bu listenin doğrulamadığı şey:** kodun PROJENİN `languages` listesinde olup olmadığı.
195
+ `UpdateTranslationsCommandValidator`, `UpsertTranslationKeyCommandValidator` ve
196
+ `Translation.ValidateState()` üçü de yalnız `ProjectConstants.SupportedLanguages`'a bakar; hiçbiri
197
+ `Project.Languages`'ı okumaz. Bağı **yalnız gateway** kurar (TR-002, `warn`) — Studio UI'dan ya da
198
+ doğrudan API'den yazan bir istemci için boşluk açık kalır.
@@ -4,6 +4,11 @@ View, **serbest metin araması** içindir: seçtiğin alanlar bir analizörden g
4
4
  projeksiyon tablosuna indekslenir. **Raporlama/agregasyon aracı değildir** — özet/sayım için
5
5
  `query_create` (raw SQL) + query kaynaklı microservice kullan.
6
6
 
7
+ > **İki bağımsız koşu da bu yanlış beklentiyle başladı** (09-02 M-3; 2026-09-04'ün iki koşusunda
8
+ > da tekrarladı): "view" adı SQL VIEW'ünü çağrıştırıyor, oysa buradaki view bir **arama
9
+ > indeksidir**. Karar tek satır: *sayı/özet mi istiyorsun* → `query_create`; *metin arıyor
10
+ > musun* → `view_create`. Tool açıklamaları da bunu söyler.
11
+
7
12
  Gövde şemaları: `ngapps://schemas/CreateViewCommand` · `ngapps://schemas/UpdateViewCommand`.
8
13
 
9
14
  ## Kuruluş sırası
@@ -35,6 +40,10 @@ Gövde şemaları: `ngapps://schemas/CreateViewCommand` · `ngapps://schemas/Upd
35
40
  - `analyzer` verilmezse platform `ViewAnalyzer.Default()` kullanır — Türkçe içerikte
36
41
  `language: 'turkish'` + `tokenFilters: ['lowercase','asciifolding']` açıkça verilmelidir, yoksa
37
42
  "Talep" ile "talep" ya da "yazilim" ile "yazılım" eşleşmez.
43
+ - `asciifolding` PostgreSQL `unaccent()` ile **hem indeks hem sorgu** tarafında uygulanır: filtre
44
+ açıkken "Ayse" araması "Ayşe" satırını döndürür. Filtreyi var olan bir view'a **sonradan**
45
+ eklersen eski projeksiyon satırları aksansız metni taşımaz — `view_rebuild` çağırmadan eşleşme
46
+ olmaz.
38
47
  - `fields[].weight` sıralama ağırlığıdır (`double`): başlık 2, gövde 1 gibi.
39
48
  - `fields[].viewEntityAlias`, `entities[].alias` ile birebir eşleşmeli. Eşleşmezse alan **sessizce
40
49
  ilk tabloya** bağlanır (`ViewSourceMapper.Map` → `tablesByAlias.GetValueOrDefault(...) ?? tables.FirstOrDefault()`).
@@ -83,7 +92,13 @@ parametresi ister (+ `limit`/`offset`). Ayrıntı: `platform_guide('microservice
83
92
  ## Bakım
84
93
 
85
94
  - `view_rebuild` projeksiyonu sıfırdan kurar; toplu satır yüklemesinden ya da alan listesi
86
- değişikliğinden sonra çağır.
95
+ değişikliğinden sonra çağır. `analyzer.tokenFilters` değişikliği de (özellikle `asciifolding`)
96
+ rebuild ister — analyzer yalnız yeni yazılan satırlara uygulanır.
87
97
  - View'in `slug`'ı projeksiyon tablosunun/indekslerinin adıdır — slug değiştirmek onları yeniden
88
98
  adlandırır; elle oluşturulmuş bir tabloyla çakışmasın diye entity slug'larıyla aynı isim uzayında
89
99
  düşün (`platform_guide('entity')` §"Slug seçimi").
100
+
101
+ ## Backend doğrulama sınırı (VIEW-001)
102
+
103
+ `name` zorunlu; `providerType` yalnız `postgres` (tek değer). Gateway bunu istek gitmeden `block`
104
+ olarak uygular; tam katalog `ngapps://rules/backend-constraints`.
@@ -23,7 +23,7 @@ taşır; gateway açıklamasız gövdeyi platforma göndermez.
23
23
  okunuyor ve hiçbir şey eklemiyor. `"kullanıcının asal pozisyonunu seçer; birden çok aktif atama
24
24
  varsa is_primary olanı tercih eder, o da yoksa ilkini alır"` — yazılmadıkça hiçbir yerde
25
25
  bulunmayan bilgi budur.
26
- - **`start` ve `end` muaftır** (kural onları hiç sormaz): ikisi de yapısal işaretçi, açıklanacak
26
+ - **`start` muaftır** (kural açıklama istemez): yapısal işaretçi, açıklanacak
27
27
  davranışları yok.
28
28
  - **Gateway'in enjekte ettiği `gatewayOnError` adımı kendi açıklamasını taşır** — o adımı sen
29
29
  yazmadığın için kural seni onun yüzünden durdurmaz.
@@ -74,6 +74,9 @@ outline görünümünde geri okunur — sonraki oturumda tuzağı tam gövdeyi a
74
74
  4. **Kaydetmeden önce `workflow_preview`.** Test girdisi **`previewPayload`** anahtarıyla
75
75
  verilir (`parameters`/`input` çalışmaz, ifadeler `undefined` üretir); değişkenler
76
76
  `previewVariables`.
77
+ `type:'workflow'` için `previewPayload` göndereceksen önce `parameterDtoId` ve
78
+ `parameterBehavior` tanımlamalısın; DTO'suz workflow'ta alan gerekçeli doğrulama hatasıyla
79
+ reddedilir. Automation ve middleware preview değişkenleri kendi bağlam kurallarını izler.
77
80
  - **`previewPayload` bir STRING olmalı ve tek kanonik biçimi `"({ … })"`'dir.** Alan
78
81
  JavaScript **kaynak metni** bekliyor (`PreviewWorkflowCommand` gövdeyi `jsEngine.Eval` ile
79
82
  çalıştırır) ve **ifadenin sonucu bir NESNE olmak zorundadır**:
@@ -101,12 +104,40 @@ outline görünümünde geri okunur — sonraki oturumda tuzağı tam gövdeyi a
101
104
  `Sequence contains no elements` alırsın ve hata seni entity'yi suçlamaya iter.
102
105
  Kalıcı satır istiyorsan akışı bağlı ucundan `microservice_execute` ile çağır.
103
106
 
107
+ ## İstek dili: `context.language` ve `Loc()`
108
+
109
+ Aktif dil `context.language`, anahtar çözmek için `Loc(key)`. Motor kültürü de aynı dilden gelir
110
+ (`tr` → `tr-TR`, diğerleri `en-US`). **`context.language` yalnız gerçek istekte doludur** —
111
+ önizleme tetikleyici kaynağını kurmaz; dile bağlı dalı `microservice_execute` +
112
+ `headers: { 'X-Data-Language': 'tr' }` ile doğrula. Ayrıntı: `platform_guide('translation')`.
113
+
104
114
  ## Step'ler nasıl bağlanır
105
115
 
106
116
  Her step'in `branches[]` dizisi vardır; `nextStepId` sonraki step'in id'sini gösterir.
107
117
  Bağlanmamış step çalışmaz. `start` tek branch taşır; normal step'ler `success` + `fail`;
108
- `condition` koşul branch'leri + `else`; terminal step'ler (`response`, `convert`, `revert`,
109
- `end`) **branch taşımaz**.
118
+ `condition` koşul branch'leri + `else`; terminal step'ler (`response`, `convert`, `revert`)
119
+ **branch taşımaz**. Ayrı bir `end` step tipi yoktur; son dalın `nextStepId` değerini
120
+ `null` bırakarak akışı bitirin.
121
+
122
+ ## Preview yazmaz — rollback eder (ölçüm: İK koşusu W-1, 2026-09-01)
123
+
124
+ `workflow_preview` ve `microservice_preview` tek transaction'da koşar ve sonunda **rollback**
125
+ edilir (`PreviewWorkflowCommand`: `BeginTransactionAsync` → … → `RollbackTransactionAsync`).
126
+ Table create/update/upsert step'leri kalıcı satır **yazmaz**; preview'ı istediğin kadar tekrar
127
+ edebilirsin. Tek istisna: code step içinden yapılan **dış HTTP çağrıları** geri alınamaz — dış
128
+ sisteme yazan bir akışı preview'da koşarken bunu bil.
129
+
130
+ **Preview step/branch id'lerini de TÜKETMEZ** (ölçüldü 2026-09-05, `f09-repro`): aynı GUID
131
+ bloğuyla iki kez preview 200 döner, sonra aynı gövde `workflow_create`'e verilince id'ler olduğu
132
+ gibi kaydedilir — önizlemeye ayrı blok ayırmayın. Preview sonrası `RowAlreadyExists` alıyorsanız
133
+ sebebi Kural 3'tür: o id'ler kaydedilmiş başka bir akışta duruyordur.
134
+
135
+ `query_preview` de rollback edilir ve bu daha sinsidir: yazan bir sorgu `affectedRowCount`'ta
136
+ gerçek sayıyı döner, tablo boş kalır (ölçüm: 09-04 Y-19) ve **yanıtta rollback'i söyleyen bir alan
137
+ yoktur** (ölçüm: 2026-09-07). Yazmayı doğrulamak istiyorsan sorguyu kaydet, microservice/workflow
138
+ üzerinden çalıştır ve sonucu `row_list` ile say.
139
+
140
+ Tool açıklamaları bu bölüme işaret eder; iki kaynak çelişirse **bu rehber** doğrudur.
110
141
 
111
142
  ## Hata yolu
112
143
 
@@ -135,13 +166,16 @@ yapıyı dışarı sızdırır.
135
166
 
136
167
  - **Global-error dalı TEK olmalı ve START adımında durmalı.** Başka adıma koyulan dal hiç
137
168
  ç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.
169
+ - **`returnDtoId` süzmesi yalnız 2xx yanıtlarda uygulanır (2026-09-05).** Bir `response` adımının
170
+ `httpStatusCode`'u 2xx dışındaysa (401, 404, 422, 500…) gövde sözleşmeden GEÇMEZ ve
171
+ `bodyExpression` ne ürettiyse o döner. Yani bir koşul dalından `({ success: false, message: '…' })`
172
+ ile 401 dönmek için DTO'ya `message` alanı eklemek gerekmez — muafiyet artık global-error
173
+ dalıyla sınırlı değil. Öncesinde bu gövde sessizce boş `{}` oluyordu.
140
174
  - **Loop içi hatalar global-error dalına GİTMEZ.** Loop çocuk bağlamı bu yönlendirmeyi kapatır;
141
175
  loop adımının kendi `fail` dalını bağla.
142
176
  - **`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.
177
+ yapmaz. Hata yolunu kendin yaz: global-error dalını `revert` (kapsamı `isQueue`'ya
178
+ bağlı — §izin matrisi) ya da `email`/`table` adımına bağla. Bağlı hata yolu yoksa `WF-021` uyarır.
145
179
  - **Hata yolu hiç yoksa** akış patladığında platform 500 + ham exception mesajı döner. Çalışır,
146
180
  ama çıktı denetimsizdir ve lokalize değildir.
147
181
 
@@ -206,6 +240,27 @@ gidilir, hiçbiri tutmazsa `else` dalı yürür:
206
240
  | convert | ✓ | – | – |
207
241
  | revert (Fail and Rollback) | – | ✓ | – |
208
242
 
243
+ > **`revert`'in iki alanı JavaScript ifadesidir, düz metin değil.** `rule.revertType` ve
244
+ > `rule.revertMessage` çalışma anında `Evaluate` edilir; Studio bu alanları kod editörüyle sunar ve
245
+ > varsayılanlarını tırnaklı verir (`"Failed"`, `"Transaction is reverted."`). Tırnaksız metin
246
+ > gönderirsen ifade çözülmez ve adım
247
+ > `Step '<ad>' failed. Please check the workflow. [EvaluateWorkflowStepFailedException]` ile düşer;
248
+ > mesaj hangi alanın sorunlu olduğunu söylemez. Doğru yazım: `revertType: "'all'"`,
249
+ > `revertMessage: "'Is kurali hatasi.'"` (2026-09-02 ölçümü).
250
+ >
251
+ > **Önizlemede `revert` artık 400 değil, dolu bir adım ağacı döndürür (2026-09-05).** Kuru koşuda
252
+ > revert bir hata değil, kapının çalıştığının kanıtıdır: `automation_preview` 200 döner,
253
+ > `executionSuccess: false` olur ve `revert` adımı ağaçta `hasError: true` + `errorType` =
254
+ > `revertType` + `errorMessage` = `revertMessage` ile görünür.
255
+ >
256
+ > **`revert` exception fırlatır ve bulunduğu transaction'ı İKİ yolda da geri alır; ayrım o
257
+ > transaction'ın kapsamıdır (2026-09-07).** `isQueue:false` (async): kapsam yalnız akışın
258
+ > yazdıklarıdır — tetikleyen satır COMMIT olmuş, çağıran 200 almıştır; olay `failed` +
259
+ > `revertMessage` damgalanır ve bu yolda `failed` damgası kapının ÇALIŞTIĞININ izidir, kusur
260
+ > değil. `isQueue:true` (senkron): kapsam satır işlemini de içerir — satır geri alınır, çağırana
261
+ > 400 `{ code: revertType, message: revertMessage }` gider (**AUT-002**); seçim §Bilinen sınırlar'da.
262
+ > `rule.isExpression` bayrağı bu iki alan için **okunmaz**.
263
+
209
264
 
210
265
  ## Loop step — dizinin her öğesi için bir adım koştur
211
266
 
@@ -316,10 +371,26 @@ Tek uçta "başlık + N alt kayıt" yazmanın yolu budur (anket + sorular + seç
316
371
  **`platform_guide('expressions')`** — uydurma fonksiyon adı runtime 500'üdür.
317
372
  - Yazma step'lerinde (create/update): **yazılacak** alanlar `fields`, **okunacak** alanlar
318
373
  `selectFields` — aynı alan ikisinde birden olamaz (`An item with the same key...`).
319
- - **Yazma step'inin çıktısı yalnız primary key'dir** (`<step>.data.id`); sonraki step'lerin ya
320
- da response'un başka alana ihtiyacı varsa bunları **`returnFields`** ile iste
374
+ - **Yazma step'inin çıktısı yalnız primary key'dir**; sonraki step'lerin ya da response'un
375
+ başka alana ihtiyacı varsa bunları **`returnFields`** ile iste
321
376
  (`{ alias, fieldId, fieldSlug }` öğeleriyle) — `selectFields` write operasyonunda sessizce
322
377
  yok sayılır.
378
+ - **Çıktının ŞEKLİ `isSingle`'a bağlıdır** (ölçüm: 09-04 koşusu Y-15). `isSingle: true` ise
379
+ `<step>.data` nesnedir ve `<step>.data.id` çalışır; **`isSingle: false` ise `data` bir
380
+ DİZİDİR** — `<step>.data.id` `undefined` olur, akış `RowValidationFailed` ile düşer ve hata
381
+ hangi adımı işaret ettiğini söylemez. YAZMA adımı da aynı kurala uyar: `isSingle: false` olan
382
+ bir `create`/`update` adımının çıktısı dizidir, ve o id'yi bir FK alanına geçiren sonraki adım
383
+ `23502 null value in column "<fk>"` ile düşer — hata FK'nın adını söyler, sebebin ŞEKİL
384
+ uyuşmazlığı olduğunu değil (09-05 koşusu `S-5`; kuru koşu yakaladı, kaydedilseydi yarım
385
+ kullanıcı yazılmış olurdu). **Üç vakayı da karşılayan tek savunmalı yazım:**
386
+
387
+ ```js
388
+ // dizi (isSingle:false) · nesne (isSingle:true) · düz değer (yalnız pk dönen yazma)
389
+ (Array.isArray(x.data) ? (x.data[0] && x.data[0].id) : (x.data && x.data.id !== undefined ? x.data.id : x.data))
390
+ ```
391
+
392
+ Şekli bilmiyorsan bu yazımı kullan; `workflow_preview` çıktısında `steps[].output`'a bakarak
393
+ hangi vakada olduğunu da görebilirsin.
323
394
 
324
395
  > **Gateway table step'lerinde `path`/`sort`/`limit` eksiklerini otomatik dolduruyor**
325
396
  > (yanıtta `gatewayNormalized`). Dolu değerlere dokunmaz; yanlış path yine WF/MS block'una takılır.
@@ -390,31 +461,45 @@ zorunludur (`CreateWorkflowCommandValidator`: `When(ReturnDtoId.HasValue) → Re
390
461
 
391
462
  ## Automation gerçekleri (hr + qm projelerinde doğrulandı)
392
463
 
393
- - **`returnDtoId`/`parameterDtoId` automation'da YASAKTIR** (validator: "'Return Dto Id' must
394
- be empty"). `returnBehavior: "object"` verilebilir.
464
+ - **`returnDtoId`/`parameterDtoId` ve `returnBehavior`/`parameterBehavior` automation'da
465
+ YASAKTIR** (validator: "'Return Dto Id' must be empty" / "'Return Behavior' must be empty");
466
+ `automation_create` kabul ediyor gibi görünse de `automation_update` reddeder (ölçüm: 09-04
467
+ Y-13). Dördünü de automation gövdesinde hiç yazma.
395
468
  - **`state: 'unready'` automation için NORMALDİR ve tetiklemeyi etkilemez** — qm'deki çalışan
396
469
  automation'lar da unready'dir; "Studio'dan aktifleştirme" diye bir işlem yoktur.
397
- - **Tetikleme kapısı üç alandır**, üçü de `automation` nesnesinde: `entityId` (tetikleyen
398
- entity), `operation` (`create`/`update`/`delete`), **`isQueue` (false=anlık, true=kuyruklu;
399
- null bırakılırsa tetikleme sorgusu HİÇ eşleşmez — automation sessizce ölü kalır)**.
400
- `condition` opsiyonel JavaScript ifadesidir. `isActive` diye bir alan YOKTUR (yok sayılır).
470
+ - **Tetikleme kapısı üç alandır**, üçü de `automation` nesnesinde: `entityId`, `operation`
471
+ (`create`/`update`/`delete`) ve **`isQueue`** — adı yanıltıcıdır (`Entity.cs:735-736`):
472
+ **`true` = isteğin İÇİNDE senkron** koşar (`queueOrder` zorunlu, sıra ondan gelir),
473
+ **`false` = RabbitMQ'ya giden async** ateşle-unut yoldur — satır yazıldıktan hemen sonra
474
+ okursanız automation'ın yazdığını göremezsiniz. `null` bırakılırsa tetikleme sorgusu HİÇ
475
+ eşleşmez — automation sessizce ölü kalır. `condition` opsiyonel JavaScript ifadesidir;
476
+ `isActive` diye bir alan YOKTUR (yok sayılır).
401
477
  - `steps` hem üst seviyede hem `automation` içinde verilir (PLT-007); `response` step'i
402
478
  kullanılamaz, hata yolu için `revert` vardır.
403
479
  - **Tetikleyen satıra `input` ile DEĞİL `row` ile erişilir**; update'te `previousRow`, işlemi
404
480
  yapan kullanıcı `user` altındadır (`row.title`, `previousRow.user`, `user.id`). Step
405
- `fields[]` öğelerinde `source: 'body'` + `value: '<ifade>'` kullanılır.
406
- - **`isQueue: false` senkron demek değildir.** Tetikleme ateşle-unut çalışır; satırı yazdıktan
407
- hemen sonra sonucu okursanız automation'ın yazdığı satırı göremezsiniz. Kısa bir bekleme
408
- sonrası tekrar okuyun.
481
+ `fields[]` öğelerinde `source: 'expression'` + `value: '<ifade>'` kullanılır — table step ile
482
+ aynı yazım. Eski kayıtlarda `source: 'body'` görürsün; davranış AYNIDIR — `TableStep` default
483
+ modda her alanın `value`'sunu `source`'a **bakmadan** JS ile değerlendirir
484
+ (`TableStep.cs:392-412`), yani `expression` automation'da da çalışır.
409
485
  - Hem `row_create` ucundan hem `operation: 'create'` yapan bir microservice'ten tetiklenir —
410
486
  ikisi de aynı hook'a düşer.
411
- - **Bilinen platform sınırı (P-03):** kısa aralıklı ardışık tetiklerde (özellikle `rows[]` ile
412
- TOPLU `row_create`) automation yalnız ilk satır için tetiklenebiliyor — kayıp satırlar
413
- `automation_histories`'te hiç görünmez. Otomasyonlu entity'lere satırları **tekil** yazın
414
- (ya da sonradan `automation_trigger` ile telafi edin); toplu ekleme sonrası tetiklenmeyi
415
- mutlaka sayarak doğrulayın.
416
- - Doğrulama: `automation_trigger` + `automation_histories` (ya da tetikleyen satırı yazıp
417
- sonucu kontrol et).
487
+ - **Tetikleyen satıra geri yazan automation'ın kapısı `automation.condition` OLMALIDIR, akışın
488
+ içindeki bir `condition` ADIMI değil.** Sıra bunu belirler: `automation.condition` eşleşmezse
489
+ mesaj kuyruğa hiç girmez ve sayaç artmaz; akışın ilk adımına konan kapı iş sonucunu doğru tutar
490
+ ama koşumu saydırmaya devam eder. (Ölçüm: `08-kural-envanteri.md`, `B-20`.)
491
+ - **Satır olayının DIŞINDAN değer geçirmek: `rowContext` (2026-09-05).** `row_create`/`row_update`
492
+ (tekil ve toplu) isteğe bağlı bir `rowContext` sözlüğü alır; tetiklenen automation onu
493
+ ifadelerinde **`rowContext.<anahtar>`** ile okur. Tarihsel (geçmiş tarihli) veri üretmenin yolu
494
+ budur: automation damgayı `UtcNow()` yerine `rowContext.occurredAt`'ten alabilir —
495
+ `(typeof rowContext !== 'undefined' && rowContext && rowContext.occurredAt) ? rowContext.occurredAt : UtcNow().toISOString()`.
496
+ - **AUT-003:** `rowContext` yoksa automation sessizce `UtcNow()`a düşer — tarihsel automation'ın
497
+ entity'sine yazan her yol onu verir, duman testi dahil.
498
+ - **`automation_trigger` bir DOĞRULAMA aracıdır, veri üretme aracı DEĞİL:** gerçekten çalıştırır,
499
+ `rowContext` **almaz**, damgası hep `UtcNow()`'dur — tarihsel tohumun üstünde tek çağrı huni
500
+ ortalamasını 5 sa → 226 sa yaptı ve hiçbir uyarı çıkmadı (`B-52`). Tarihsel veri: küçükse
501
+ `rowContext`'li `row_create`, kümeyse
502
+ `platform_guide('entity')` §"Toplu ve türetilmiş satır".
418
503
 
419
504
  ```jsonc
420
505
  {
@@ -431,6 +516,18 @@ zorunludur (`CreateWorkflowCommandValidator`: `When(ReturnDtoId.HasValue) → Re
431
516
  }
432
517
  ```
433
518
 
519
+ ## Bilinen sınırlar ve kabul edilen kanıt
520
+
521
+ Bunlar **platform sınırıdır**, gövdeyi değiştirerek aşılmaz — ama sınır doğrulamayı iptal etmez,
522
+ yolunu değiştirir: kapalı yolu deneyip raporuna "kanıtlayamadım" yazma, kanıt sütunundaki çıktıyı
523
+ üret.
524
+
525
+ | Kapalı yol | Kabul edilen kanıt |
526
+ |---|---|
527
+ | **`AUT-004`** — enum/zorunluluk/benzersizlik denetleyen `revert` dalı `row_create` yolundan HİÇ tetiklenemez: entity doğrulaması automation'dan ÖNCE koşar | `automation_preview`: revert adımı `hasError` + `errorType` ile döner (§izin matrisi) — **tam** kanıt, ikinci koşum aranmaz. Canlı red de şartsa kuralı enum OLMAYAN bir alana kur |
528
+ | **`AUT-002`** — `isQueue:false` automation ÇAĞIRANI reddedemez (§izin matrisi) | Reddin izi `automation_histories`: `failed` + `revertMessage`; çağırana 400 gerekiyorsa kural `isQueue:true` + `queueOrder` yoluna kurulur |
529
+ | **`automation_preview` CANLI davranışı göstermez** — tek transaction, rollback | ŞEKLİN kanıtı preview; CANLI davranışın kanıtı tetikleyen satır + `row_list` + `automation_histories` (`AUT-006`) |
530
+
434
531
  ## Sık hatalar (politika lint'inin yakaladıkları)
435
532
 
436
533
  - Bağlanmamış step grafı (WF-001, block) · workflow'da returnDtoId eksik (WF-002, block)
@@ -442,6 +539,15 @@ zorunludur (`CreateWorkflowCommandValidator`: `When(ReturnDtoId.HasValue) → Re
442
539
  - Adımda ya da akışta `description` yok (WF-024, block — Studio'da adım okunamaz kalır)
443
540
  - İlişki alanının içini okuyan ifade (WF-020, warn — join yoksa alan düz FK'dır)
444
541
  - `set-user` adımı olmadan `user` okuyan middleware (MW-003, warn)
542
+ - Backend zorunlulukları (WF-026, block — istek gitmeden): `name`/`slug`; `returnDtoId` varsa
543
+ `returnBehavior`, `parameterDtoId` varsa `parameterBehavior` (`primitive` | `object` | `array`),
544
+ update/preview'da DTO yokken behavior `null`; `type: 'workflow'` + `parameterDtoId` yokken
545
+ `previewPayload` verilemez; `variable` adımına `user` adı verilemez (set-user'a ayrılmış);
546
+ tekil filtrede `fieldId`; automation `parameterDtoId` almaz, `isQueue: true` ⇒ `queueOrder`
547
+ (0 geçerli). Tam katalog: `ngapps://rules/backend-constraints`.
548
+ - DTO gövdesi (DTO-001, block): `type` sözlükten (`string`, `number`, `guid`, `datetime`, `object`,
549
+ `array`… — `text` YOK, G-23), `children[].slug` zorunlu ve kardeşler arasında tekil, `referenceId`
550
+ yalnız `object`/`array` çocukta.
445
551
 
446
552
  ## Hedefli değişiklik: `workflow_patch` yollarında İNDEKS KULLANMA
447
553
 
@@ -458,7 +564,9 @@ Gateway seçiciyi gövde platforma **gitmeden** indekse çevirir, çözülemezse
458
564
  ve yanıtta `patchTargets` ile her op'un hangi adıma (ad + tip + id) indiğini söyler. Aynı seçici
459
565
  `microservice_patch`'te de çalışır (`/sourceParameters/joins[alias=customers]/path`).
460
566
 
461
-
567
+ Patch tam gövde yazmaz; gateway farkı `{ baseVersion, ops }` olarak gönderir. Araya başka bir
568
+ yazma girdiyse **409** döner ve hiçbir op uygulanmaz — `workflow_get` ile yeniden oku. `version`
569
+ **string**tir, sayıya çevirme.
462
570
 
463
571
  ## Table adımının filtresi: `alias` YOKSA filtre SESSİZCE DÜŞER
464
572
 
@@ -482,3 +590,66 @@ kusur `operation: 'update'` filtresinde **yanlış satırı günceller**.
482
590
 
483
591
  `source` da **`expression`** olmalı: verilmezse `body`'ye düşer ve `value` bir ifade değil
484
592
  **gövde alanı adı** sayılır — yine filtre kaybolur.
593
+
594
+ ## Query adımı: kayıtlı query'nin parametreleri (ölçüm 2026-09-03)
595
+
596
+ **Parametreyi query'yi kaydederken bildir.** `query_create` ve `query_update` gövdesindeki
597
+ `parameters: [{ name, type, value }]` alanı, query'ye bağlı bir parametre DTO'su kurar; `query_get`
598
+ bunu `parameterScheme` olarak geri verir. Bildirmediğin bir parametre sözleşmede yoktur — kaydetme
599
+ başarılı olur, sorgu runtime'da bağlanmaz.
600
+
601
+ ```jsonc
602
+ // query_create gövdesi: "WHERE extract(year from start_date) = @year"
603
+ { "rawQuery": "SELECT count(*) AS total FROM leave_requests WHERE extract(year from start_date) = @year",
604
+ "slug": "izin-raporu", "name": "Izin Raporu", "datasourceId": "<datasource id>",
605
+ "outputType": "object",
606
+ "parameters": [ { "name": "year", "type": "number", "value": 2026 } ] }
607
+ ```
608
+
609
+ `value` burada **önizleme değeridir**: kayıt sırasında sorgu bu değerlerle bir kez çalıştırılır, hem
610
+ SQL doğrulanır hem de yanıt şeması (`returnScheme`) üretilir. Sorgu hatalıysa kayıt 422 ile reddedilir.
611
+
612
+ Kaydedilen sözleşme üç tüketim yolunda da aynı şekilde bağlanır:
613
+
614
+ | Tüketim yolu | Değer nereden gelir |
615
+ |---|---|
616
+ | `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
+ | 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 |
619
+
620
+ > **Boş string parametre `NULL`'a çevrilir.** `Query.NormalizePreviewParameters` değeri normalize
621
+ > ederken boş ya da yalnız boşluktan oluşan değeri `null` yapar. `WHERE status = {{durum}}` yazıp
622
+ > `durum: ''` gönderirsen sorgu `= NULL` olur ve **hiçbir satırla eşleşmez**: 0 satır döner, hata
623
+ > ya da uyarı yoktur. 2026-09-04 ölçümü: aynı sorgu `''` ile 0, `IS NULL` dalı yazıldığında 346
624
+ > satır. "Filtre yok" demek istiyorsan sentinel kullan (`'all'` gibi) ya da sorguyu
625
+ > `({{durum}} IS NULL OR status = {{durum}})` biçiminde yaz. Query kaynaklı uçta da aynı
626
+ > (üç koşuda ölçüldü: 09-02, 09-04·1, 09-04·4).
627
+
628
+ > **`query_preview` yazan sorguyu koşturur, geri alır ve bunu söylemez** (yukarıda Kural 3).
629
+ > Kalıcı yazmanın reçetesi: `platform_guide('entity')` §"Toplu ve türetilmiş satır".
630
+
631
+ ```jsonc
632
+ // library query adımı: query'de "WHERE extract(year from start_date) = @year" yazıyorsa
633
+ { "id": "<GUID-Q>", "name": "izin_raporu", "type": "query",
634
+ "rule": { "stepType": "query", "queryStepSourceType": "library", "isExpression": false,
635
+ "queryId": "<kayıtlı query id>",
636
+ "expression": "{ year: input.year }", // bugün BAĞLAYAN yazım
637
+ "queryParameters": [ { "name": "year", "value": "input.year", "type": "number" } ] },
638
+ "branches": [ /* success + fail */ ] }
639
+ ```
640
+
641
+ > **Bağlamayı `rule.expression`'a yaz.** Kayıtlı sorgunun parametreleri bildirilmişse çalışan
642
+ > motor `rule.queryParameters` dizisine BAKMIYOR: parametreler `null` bağlanıyor ve çağrı
643
+ > **`200` + sıfır satır** dönüyor, hiçbir katman uyarmıyor (ölçüldü 2026-09-07; düzeltme depoda
644
+ > var, canlı dağıtım geride — `B-63`). Gateway bu adımı `WF-025` ile uyarır.
645
+ >
646
+ > **Kimlik/tenant kapsamı da buradan bağlanır** — query kaynaklı ucun yapamadığı şey budur:
647
+ > `"{ company_id: user.company_id, from: input.from }"` (ölçüldü; `middlewareIds` boşsa
648
+ > `500 user is not defined`). Reçete: `platform_guide('microservice')`.
649
+
650
+ - `queryParameters[].name` **`@` ya da `{{ }}` olmadan** yazılır; belirtecin sorgudaki adıyla
651
+ eşleşmelidir.
652
+ - Değer çağrıda bulunamazsa parametre `null` bağlanır — sorgu düşmez, `WHERE` koşulu boşa çıkar.
653
+ - `parameters` bildirilmemiş eski query'lerde belirteçler ham SQL'den okunur; güvenme (string
654
+ literalindeki `@ad`'ı da parametre sanıyor, `query_get` boş `parameterScheme` gösteriyor). Böyle
655
+ bir query'yi `parameters` vererek bir kez `query_update` et.
@@ -10,6 +10,11 @@ export function listKnowledgeFiles(kind) {
10
10
  return [];
11
11
  }
12
12
  }
13
+ /**
14
+ * `schemas` MCP kaynağı olarak YAYINLANMAZ (yalnız `guides` ve `examples` kayıtlıdır) ama
15
+ * dosyalar aynı klasör düzeninde durur ve drift testleri onları da okur — şema kaynağı
16
+ * platformun swagger'ından üretiliyor ve bir alanın oradan düşmesi sessizce olur.
17
+ */
13
18
  export function readKnowledgeFile(kind, file) {
14
19
  return readFileSync(join(resourcesDirectory, kind, file), 'utf8');
15
20
  }
@@ -0,0 +1,27 @@
1
+ import { constraintCatalog } from '../knowledge/backendConstraints.js';
2
+ /** `ngapps://rules/<ad>` kaynakları; docs-lint bu listeyle `ngapps://rules/…` referanslarını denetler. */
3
+ export const ruleResourceNames = ['backend-constraints'];
4
+ /**
5
+ * Backend doğrulayıcı kataloğu tek kaynak olarak koddan üretilir (`backendConstraints.ts`);
6
+ * dosya olarak paketlenmez ki tool açıklaması, lint ve bu kaynak hiçbir zaman ayrışmasın.
7
+ */
8
+ export function registerRuleResources(server) {
9
+ server.registerResource('rules-index', 'ngapps://rules/index', {
10
+ title: 'Kural kaynakları dizini',
11
+ description: "Gateway'in koddan ürettiği kural katalogları; şu an backend doğrulayıcı aynası.",
12
+ mimeType: 'application/json'
13
+ }, async (uri) => ({
14
+ contents: [{
15
+ uri: uri.href,
16
+ mimeType: 'application/json',
17
+ text: JSON.stringify({ rules: ruleResourceNames.map(name => `ngapps://rules/${name}`) })
18
+ }]
19
+ }));
20
+ server.registerResource('rules-backend-constraints', 'ngapps://rules/backend-constraints', {
21
+ title: 'Backend doğrulama sınırları',
22
+ description: "Platformun 422/409 ile reddedeceği gövde kuralları (uzunluk, zorunluluk, tek primaryKey, DTO/behavior eşleşmesi, dil kodları…) — kaynak referanslı; gateway aynı kuralı istek gitmeden block olarak uygular.",
23
+ mimeType: 'application/json'
24
+ }, async (uri) => ({
25
+ contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(constraintCatalog(), null, 2) }]
26
+ }));
27
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "hash": "0e6bb2b45f8c545b50dc6d6169e7d816d9cb9532ecbaed894307995d537ff3b6",
3
+ "generatedAt": "2026-09-09T09:49:52.511Z",
4
+ "types": 103,
5
+ "files": 73,
6
+ "note": "Şemaları üreten backend kaynaklarının parmak izi. Elle düzenlemeyin: 'npm run generate-schemas' yazar, 'npm run schema:check' denetler.",
7
+ "missing": []
8
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "schema": {
3
+ "required": [
4
+ "rows"
5
+ ],
6
+ "type": "object",
7
+ "properties": {
8
+ "entityId": {
9
+ "type": "string",
10
+ "format": "uuid"
11
+ },
12
+ "rows": {
13
+ "type": "array",
14
+ "items": {
15
+ "type": "object",
16
+ "additionalProperties": {}
17
+ },
18
+ "nullable": true
19
+ },
20
+ "rowContext": {
21
+ "type": "object",
22
+ "additionalProperties": {
23
+ "nullable": true
24
+ },
25
+ "nullable": true
26
+ }
27
+ },
28
+ "additionalProperties": false
29
+ },
30
+ "components": {
31
+ "schemas": {}
32
+ }
33
+ }