@appaflytech/zeyrek-ai-mcp 0.0.10 → 0.0.12
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.
- package/README.md +11 -6
- package/dist/knowledge/designSnapshot.js +77 -0
- package/dist/knowledge/errorPath.js +48 -5
- package/dist/knowledge/normalize.js +47 -0
- package/dist/knowledge/pageDiscover.js +151 -0
- package/dist/knowledge/rules.js +163 -0
- package/dist/knowledge/themeDiscover.js +884 -0
- package/dist/knowledge/themeResolve.js +4 -2
- package/dist/platform/errors.js +76 -1
- package/dist/platform/responses.js +12 -1
- package/dist/resources/examples/design-snapshot.json +157 -0
- package/dist/resources/guides/theme.md +84 -1
- package/dist/resources/guides/workflow.md +120 -9
- package/dist/tools/automations.js +7 -4
- package/dist/tools/flows.js +119 -17
- package/dist/tools/pages.js +16 -0
- package/dist/tools/themes.js +52 -0
- package/dist/tools/workflows.js +6 -2
- package/package.json +2 -1
|
@@ -252,7 +252,9 @@ const BASE_TOKENS = {
|
|
|
252
252
|
};
|
|
253
253
|
export const TOKENS = BASE_TOKENS;
|
|
254
254
|
/** Palet gruplarının anahtarları — paletin KENDİSİNDEN türer ki listeyi ikinci kez yazmayalım. */
|
|
255
|
-
|
|
255
|
+
/** Tabanda BULUNMAYAN, yalnız override olarak yazılabilen anahtarlar (f-08: `accent.onPrimary` → `--ng-text-on-accent`). */
|
|
256
|
+
export const OPTIONAL_PALETTE_KEYS = { accent: ['onPrimary'] };
|
|
257
|
+
export const PALETTE_GROUPS = Object.fromEntries(Object.entries(WHITE_PALETTE).map(([group, values]) => [group, [...Object.keys(values), ...(OPTIONAL_PALETTE_KEYS[group] ?? [])]]));
|
|
256
258
|
/* ---- Birleştiriciler ve ölçekler (`utils.ts` kopyası) ---- */
|
|
257
259
|
export function mergePalette(base, overrides) {
|
|
258
260
|
const result = { ...base };
|
|
@@ -685,7 +687,7 @@ export function buildThemeStyle(palette, form, t, shadow, formDefaultSize) {
|
|
|
685
687
|
--ng-action-yellow: ${actions.yellow || colors.yellow.r600};
|
|
686
688
|
--ng-action-white: ${actions.white || palette.background.surface};
|
|
687
689
|
--ng-action-black: ${actions.black || colors.grey.r900};
|
|
688
|
-
--ng-text-on-accent: ${getContrastColor(palette.accent.primary)};
|
|
690
|
+
--ng-text-on-accent: ${palette.accent.onPrimary || getContrastColor(palette.accent.primary)};
|
|
689
691
|
`;
|
|
690
692
|
}
|
|
691
693
|
/** Temanın yaydığı değişken adlarının tam listesi — elle tutulmaz, gerçek şablondan türer. */
|
package/dist/platform/errors.js
CHANGED
|
@@ -177,6 +177,79 @@ function diagnoseKnownFailure(status, message, code, context) {
|
|
|
177
177
|
}
|
|
178
178
|
return '';
|
|
179
179
|
}
|
|
180
|
+
const MaxErrorCauseWalk = 32;
|
|
181
|
+
const MaxErrorPathShown = 6;
|
|
182
|
+
const MaxErrorTextLength = 400;
|
|
183
|
+
function nonEmptyText(value) {
|
|
184
|
+
if (typeof value !== 'string' || value.trim() === '')
|
|
185
|
+
return undefined;
|
|
186
|
+
return value.length > MaxErrorTextLength ? `${value.slice(0, MaxErrorTextLength)}…` : value;
|
|
187
|
+
}
|
|
188
|
+
function asRecord(value) {
|
|
189
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
|
|
190
|
+
}
|
|
191
|
+
function errorLevels(body) {
|
|
192
|
+
const levels = [];
|
|
193
|
+
let current = asRecord(body);
|
|
194
|
+
while (current) {
|
|
195
|
+
if (levels.length === MaxErrorCauseWalk)
|
|
196
|
+
return { levels, truncated: true };
|
|
197
|
+
const step = asRecord(current.step);
|
|
198
|
+
const name = nonEmptyText(step?.name);
|
|
199
|
+
const stepType = nonEmptyText(step?.type);
|
|
200
|
+
levels.push({
|
|
201
|
+
step: name ? `'${name}'${stepType ? ` (${stepType})` : ''}` : undefined,
|
|
202
|
+
code: nonEmptyText(current.code),
|
|
203
|
+
message: nonEmptyText(current.message),
|
|
204
|
+
detail: nonEmptyText(current.detail),
|
|
205
|
+
type: nonEmptyText(current.type)
|
|
206
|
+
});
|
|
207
|
+
current = asRecord(current.cause);
|
|
208
|
+
}
|
|
209
|
+
return { levels, truncated: false };
|
|
210
|
+
}
|
|
211
|
+
function seenKey(...parts) {
|
|
212
|
+
return JSON.stringify(parts);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Workflow 5xx gövdesinden, başlıkta zaten yazılmış `code`/`message` dışında kalan bilgiyi çıkarır.
|
|
216
|
+
* İki gövde şekli okunur: platformun `WorkflowStepErrorResponse`'u `{ code, message, detail, type }`
|
|
217
|
+
* (hata yolu olmayan akış, `ExceptionHandler.cs`) ve gateway hata adımının gövdesi
|
|
218
|
+
* `{ …, step: { name, type }, cause }` (`errorPath.ts`). `cause` zinciri alt akış seviyelerini
|
|
219
|
+
* taşır; aynı kod+mesaj her seviyede tekrarlandığı için mesaj EN DERİNDEN başlanarak bir kez, hatanın
|
|
220
|
+
* doğduğu adıma atfedilerek yazılır. Kodla aynı `type` ve mesajla aynı `detail` gürültüdür. Uzun
|
|
221
|
+
* zincirde yolun başı ve SONU gösterilir (hatanın doğduğu adım kaybolmasın); metinler kırpılır.
|
|
222
|
+
*/
|
|
223
|
+
export function describeErrorBody(body, shown) {
|
|
224
|
+
const { levels, truncated } = errorLevels(body);
|
|
225
|
+
const fragments = [];
|
|
226
|
+
const path = levels.map(level => level.step).filter((step) => step !== undefined);
|
|
227
|
+
const shownPath = path.length > MaxErrorPathShown ? [...path.slice(0, 2), '…', ...path.slice(-3)] : path;
|
|
228
|
+
if (shownPath.length > 0)
|
|
229
|
+
fragments.push(`hata yolu: ${shownPath.join(' → ')}${truncated ? ' → …' : ''}`);
|
|
230
|
+
const seen = new Set();
|
|
231
|
+
if (shown) {
|
|
232
|
+
seen.add(seenKey('head', shown.code, shown.message));
|
|
233
|
+
if (levels[0])
|
|
234
|
+
seen.add(seenKey('head', levels[0].code, levels[0].message));
|
|
235
|
+
}
|
|
236
|
+
for (const level of [...levels].reverse()) {
|
|
237
|
+
const key = seenKey('head', level.code, level.message);
|
|
238
|
+
if ((level.code || level.message) && !seen.has(key)) {
|
|
239
|
+
seen.add(key);
|
|
240
|
+
fragments.push(`${level.step ? `${level.step} ` : ''}${[level.code, level.message].filter(Boolean).join(': ')}`);
|
|
241
|
+
}
|
|
242
|
+
if (level.detail && level.detail !== level.message && !seen.has(seenKey('detail', level.detail))) {
|
|
243
|
+
seen.add(seenKey('detail', level.detail));
|
|
244
|
+
fragments.push(`detail: ${level.detail}`);
|
|
245
|
+
}
|
|
246
|
+
if (level.type && level.type !== level.code && !seen.has(seenKey('type', level.type))) {
|
|
247
|
+
seen.add(seenKey('type', level.type));
|
|
248
|
+
fragments.push(`type: ${level.type}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return fragments;
|
|
252
|
+
}
|
|
180
253
|
export function mapPlatformError(status, payload, context) {
|
|
181
254
|
const body = payload && typeof payload === 'object' ? payload : {};
|
|
182
255
|
const code = typeof body.code === 'string' ? body.code : undefined;
|
|
@@ -222,7 +295,9 @@ export function mapPlatformError(status, payload, context) {
|
|
|
222
295
|
}
|
|
223
296
|
const fallbackValidationMessages = collectValidationMessages(body);
|
|
224
297
|
const fallbackDetail = fallbackValidationMessages.length > 0 ? ` → ${fallbackValidationMessages.join(' ; ')}` : '';
|
|
225
|
-
|
|
298
|
+
const errorBody = status >= 500 ? describeErrorBody(body, { code, message: apiMessage }) : [];
|
|
299
|
+
const errorBodyDetail = errorBody.length > 0 ? ` — ${errorBody.join(' · ')}` : '';
|
|
300
|
+
return new PlatformApiError(`Platform hatası ${status} (${location}): ${apiMessage ?? 'beklenmeyen hata'}${code ? ` [${code}]` : ''}${errorBodyDetail}${fallbackDetail}${diagnoseKnownFailure(status, apiMessage ?? '', code, context)}`, status, code);
|
|
226
301
|
}
|
|
227
302
|
export function mapNetworkError(error, context) {
|
|
228
303
|
const cause = error instanceof Error ? error : new Error(String(error));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { logError, logStep } from '../observability/trace.js';
|
|
2
|
-
import { PlatformApiError } from './errors.js';
|
|
2
|
+
import { PlatformApiError, describeErrorBody } from './errors.js';
|
|
3
3
|
import { PolicyBlockError } from '../knowledge/policy.js';
|
|
4
4
|
import { maskSecrets } from './secrets.js';
|
|
5
5
|
export function prune(value) {
|
|
@@ -76,6 +76,17 @@ export function runtimeErrorSummary(payload) {
|
|
|
76
76
|
const errors = [];
|
|
77
77
|
if (typeof record.httpStatus === 'number' && record.httpStatus >= 400) {
|
|
78
78
|
errors.push(`httpStatus: ${record.httpStatus} — çağrı ${record.httpStatus} ile bitti; gövde başarı çıktısı değil hata gövdesidir. Adım adım bakmak için microservice_debug kullanın.`);
|
|
79
|
+
// Yalnız 5xx: 4xx gövdeleri (422 doğrulama, 401) bilinçli iş yanıtıdır ve zaten gövdede okunur.
|
|
80
|
+
// `/debug/` ucu gövdeyi her zaman, `/m/` ve servis önizlemesi ise uiActions kurulmuşsa
|
|
81
|
+
// `{ data, uiActions[, executionItems] }` ile sarar (MicroservicesController.Debug / HandleResult).
|
|
82
|
+
const raw = record.body && typeof record.body === 'object' && !Array.isArray(record.body) ? record.body : undefined;
|
|
83
|
+
const wrapped = raw !== undefined && ('executionItems' in raw || 'uiActions' in raw) && raw.data && typeof raw.data === 'object' && !Array.isArray(raw.data);
|
|
84
|
+
const errorBody = wrapped ? raw.data : raw;
|
|
85
|
+
const describable = record.httpStatus >= 500 && errorBody !== undefined &&
|
|
86
|
+
(typeof errorBody.code === 'string' || Boolean(errorBody.step && typeof errorBody.step === 'object') || Boolean(errorBody.cause && typeof errorBody.cause === 'object'));
|
|
87
|
+
const fragments = describable ? describeErrorBody(errorBody) : [];
|
|
88
|
+
if (fragments.length > 0)
|
|
89
|
+
errors.push(`hata gövdesi — ${fragments.join(' · ')}`);
|
|
79
90
|
}
|
|
80
91
|
const stepErrors = [];
|
|
81
92
|
collectStepErrors(body.steps, stepErrors);
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_meta": {
|
|
3
|
+
"synthetic": false,
|
|
4
|
+
"note": "Figma dosyasından 2026-09-16 tek geçiş protokolüyle okunan gerçek anlık görüntü: 8 çağrı (get_metadata sayfa listesi · get_variable_defs iki frame · get_metadata Deals frame ağacı · get_screenshot Deals · get_design_context buton ve durum rozeti, excludeScreenshot) ≈ 29 bin karakter. Kit iki token dili taşır (Fonts/*, Light/Basic/*); hints ajanın kararıdır ve dosyada kalır — ikinci oturum Figma'ya dönmez. Gölge (effects) okunmadı, alan yok.",
|
|
5
|
+
"usage": "Gövde <themeDiscoverArgs.snapshot> theme_discover'a verilir; öneri theme_update({ theme, dryRun: true }) ile önizlenir, sonra yazılır. Dosyanın yeri: zeyrek-mcp/docs/<koşu>/design/<fileKey>.snapshot.json."
|
|
6
|
+
},
|
|
7
|
+
"_rules": [
|
|
8
|
+
"Bütçe: tasarım dosyası başına ≤ 8 Figma çağrısı, bağlama giren ≤ 150 bin karakter; get_design_context yalnız atomik bileşene (excludeScreenshot: true), ekran frame'ine asla.",
|
|
9
|
+
"variables get_variable_defs çıktısının kendisidir — ad yeniden yazılmaz, değer olduğu gibi taşınır (Font(...) değerleri metin stili olarak okunur); eşleme adı okur.",
|
|
10
|
+
"Eşlenmeyen token için Figma'ya dönülmez: karar hints'e yazılır (tema yolu ya da ignore). Sayfa düzeyinde get_variable_defs seçim ister — frame id verin.",
|
|
11
|
+
"Koyu palet aydınlıktan türetilmez: variablesDark yoksa paletteDark boşluk kalır (THEME-009); isteğe bağlı deriveDark: true başlangıç noktası üretir."
|
|
12
|
+
],
|
|
13
|
+
"themeDiscoverArgs": {
|
|
14
|
+
"snapshot": {
|
|
15
|
+
"source": {
|
|
16
|
+
"kind": "figma",
|
|
17
|
+
"fileKey": "9NnUskJSeNkMy1gAU2fAaA",
|
|
18
|
+
"fileName": "Recurring | Saas Company Management System (Community) (Copy)",
|
|
19
|
+
"nodeIds": [
|
|
20
|
+
"0:1",
|
|
21
|
+
"92:4339",
|
|
22
|
+
"79:8715",
|
|
23
|
+
"92:4343",
|
|
24
|
+
"92:5596"
|
|
25
|
+
],
|
|
26
|
+
"fetchedAt": "2026-09-16",
|
|
27
|
+
"calls": 8,
|
|
28
|
+
"characters": 29000
|
|
29
|
+
},
|
|
30
|
+
"variables": {
|
|
31
|
+
"Primary": "#4787FA",
|
|
32
|
+
"Fonts/Primary": "#25213B",
|
|
33
|
+
"Fonts/Primary Variant": "#6E6893",
|
|
34
|
+
"Fonts/Primary Icon": "#8B83BA",
|
|
35
|
+
"Fonts/Positive": "#007F00",
|
|
36
|
+
"Backgrounds/Stripe": "#F4F2FF",
|
|
37
|
+
"White Primary": "#F3F4F8",
|
|
38
|
+
"Table/Footer": "Font(family: \"Inter\", style: Semi Bold, size: 12, weight: 600, lineHeight: 100, letterSpacing: 5)",
|
|
39
|
+
"Light/Basic/Primary": "#242731",
|
|
40
|
+
"Light/Basic/Secondary": "#575F6E",
|
|
41
|
+
"Light/Basic/Thriary": "#79808F",
|
|
42
|
+
"Light/Basic/Strokes": "#D4D4D4",
|
|
43
|
+
"Light/Basic/White": "#FFFFFF",
|
|
44
|
+
"Light/Accent/Primary": "#4277FF",
|
|
45
|
+
"Light/Background/Secondary": "#E3EBFF",
|
|
46
|
+
"Light/Background/Contrast": "#242731",
|
|
47
|
+
"Light/Invertion/Primary": "#FFFFFF"
|
|
48
|
+
},
|
|
49
|
+
"textStyles": [
|
|
50
|
+
{
|
|
51
|
+
"name": "Button label",
|
|
52
|
+
"fontFamily": "DM Sans",
|
|
53
|
+
"fontSize": 14,
|
|
54
|
+
"fontWeight": 700
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "Status pill",
|
|
58
|
+
"fontFamily": "Inter",
|
|
59
|
+
"fontSize": 12,
|
|
60
|
+
"fontWeight": 500
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
"layout": {
|
|
64
|
+
"desktopWidth": 1440,
|
|
65
|
+
"mobileWidth": 375,
|
|
66
|
+
"sidebarWidth": 94,
|
|
67
|
+
"notes": [
|
|
68
|
+
"Kabuk (get_metadata 92:4339): solda 94 px 'Admin Nav' rayı, üstte 76 px 'Admin Top Nav'; içerik x=94'ten başlar, 1345 px genişlik",
|
|
69
|
+
"Liste (Deals): 'Headers' şeridi 76 px — solda 39 px yüksekliğinde başlık metni, sağda hap biçimli 202×46 px birincil buton; altında 48 px sekme şeridi (All · Paid · Unpaid · Overdue, aktif sekmenin altında 2 px çizgi) ve sağda toplam tutar; 45 px tablo başlığı (büyük harf); 61 px satırlar; satırda durum pill'i (19 px, önünde 6 px nokta), 'View More' ikincil buton 71×25, satır sonu 'Shape/More'; altta 53 px Pagination",
|
|
70
|
+
"Form (Create Client Modal 79:8715) iki token dili taşıyor: 'Light/Basic/*' metin tonları, 'Light/Accent/Primary' #4277FF (Deals'in #4787FA'sından farklı ikinci bir accent) — kit iki tasarım sisteminin karışımı",
|
|
71
|
+
"Masaüstü ızgarası iki nesil: eski ekranlar 1440×1024, yeni İK ekranları 1728×1117 — 1440 taban alındı"
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
"components": {
|
|
75
|
+
"button/primary": {
|
|
76
|
+
"background": "#4787FA",
|
|
77
|
+
"color": "#FFFFFF",
|
|
78
|
+
"borderRadius": 23,
|
|
79
|
+
"height": 46,
|
|
80
|
+
"paddingX": 29,
|
|
81
|
+
"paddingY": 14,
|
|
82
|
+
"fontSize": 14,
|
|
83
|
+
"fontWeight": 700
|
|
84
|
+
},
|
|
85
|
+
"badge/status": {
|
|
86
|
+
"background": "#E6E6F2",
|
|
87
|
+
"color": "#4A4AFF",
|
|
88
|
+
"borderRadius": 10,
|
|
89
|
+
"height": 19,
|
|
90
|
+
"paddingX": 18,
|
|
91
|
+
"paddingY": 2,
|
|
92
|
+
"fontSize": 12,
|
|
93
|
+
"fontWeight": 500
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"screens": [
|
|
97
|
+
{
|
|
98
|
+
"nodeId": "92:4339",
|
|
99
|
+
"name": "Deals",
|
|
100
|
+
"role": "list",
|
|
101
|
+
"notes": [
|
|
102
|
+
"sekme filtreleri + toplam tutar vurgusu + pill rozetler; 11 satır + Pagination"
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"nodeId": "74:4098",
|
|
107
|
+
"name": "Clients",
|
|
108
|
+
"role": "list"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"nodeId": "79:8715",
|
|
112
|
+
"name": "Create Client Modal",
|
|
113
|
+
"role": "form"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"nodeId": "310:5445",
|
|
117
|
+
"name": "grant leave",
|
|
118
|
+
"role": "form"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"nodeId": "310:3809",
|
|
122
|
+
"name": "employee profile",
|
|
123
|
+
"role": "detail"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"nodeId": "230:3757",
|
|
127
|
+
"name": "Login",
|
|
128
|
+
"role": "auth",
|
|
129
|
+
"notes": [
|
|
130
|
+
"767 px mobil varyantı var"
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"nodeId": "8:3389",
|
|
135
|
+
"name": "settings",
|
|
136
|
+
"role": "other"
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
"hints": {
|
|
140
|
+
"Light/Accent/Primary": "ignore",
|
|
141
|
+
"Light/Basic/Primary": "ignore",
|
|
142
|
+
"Light/Basic/Secondary": "ignore",
|
|
143
|
+
"Fonts/Primary Variant": "text.secondary",
|
|
144
|
+
"Fonts/Primary Icon": "ignore",
|
|
145
|
+
"Fonts/Positive": "status.success",
|
|
146
|
+
"Backgrounds/Stripe": "background.surfaceAlt",
|
|
147
|
+
"White Primary": "background.canvas",
|
|
148
|
+
"Light/Basic/Thriary": "text.disabled",
|
|
149
|
+
"Light/Basic/Strokes": "border.primary",
|
|
150
|
+
"Light/Basic/White": "background.surface",
|
|
151
|
+
"Light/Background/Secondary": "accent.subtle",
|
|
152
|
+
"Light/Background/Contrast": "background.inverse",
|
|
153
|
+
"Light/Invertion/Primary": "accent.onPrimary"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -18,7 +18,7 @@ yazmalarının aksine burada "önce development'ta dene" diye bir ayrım YOKTUR.
|
|
|
18
18
|
| Alan | Ne yapar | Ürettiği değişken(ler) |
|
|
19
19
|
|---|---|---|
|
|
20
20
|
| `mode` | `'light'` (varsayılan) \| `'dark'` — yayınlanan uygulamanın modu | palet tabanını seçer |
|
|
21
|
-
| `palette` | AYDINLIK moddaki renk override'
|
|
21
|
+
| `palette` | AYDINLIK moddaki renk override'ları; `accent.onPrimary` isteğe bağlıdır — accent ÜSTÜNDEKİ metin, verilmezse kontrasttan türer (`#4787FA` gibi açık bir mavi siyah metin üretir, tasarımda beyazsa buraya yazın) | `--ng-accent-*` · `--ng-background-*` · `--ng-text-*` · `--ng-border-*` · `--ng-status-*` · `--ng-text-on-accent` |
|
|
22
22
|
| `paletteDark` | KARANLIK moddaki renk override'ları | aynı küme |
|
|
23
23
|
| `fontFamily` · `headingFontFamily` | Gövde ve başlık yazı tipi (Google Fonts adı) | `--ng-font` · `--ng-font-heading` |
|
|
24
24
|
| `fontSize` · `fontWeight` | Taban punto ve ağırlık | `--ng-font-size` · `--ng-font-weight` |
|
|
@@ -208,3 +208,86 @@ kareyi de alın: `page_screenshot(…)` ve `page_screenshot(…, colorScheme: 'd
|
|
|
208
208
|
**Renkler hex olmalı.** CSS `rebeccapurple`'ı çizer ama studio'nun renk alanları hex bekler
|
|
209
209
|
ve vurgu üstündeki metin rengi (`--ng-text-on-accent`) accent'i karakter karakter okuyup
|
|
210
210
|
kontrast hesapladığı için tanımadığı değerde beyaza düşer (THEME-005).
|
|
211
|
+
|
|
212
|
+
## Tasarım dosyasından tema (Figma) — tek geçiş
|
|
213
|
+
|
|
214
|
+
Bir Figma dosyası **bir kez** okunur, makine-okur bir **anlık görüntüye** alınır ve tema o
|
|
215
|
+
dosyadan türetilir. Sonrası Figma'sızdır: sayfa kurma, preset bağlama ve koyu mod anlık görüntü +
|
|
216
|
+
`theme_get` ile yürür. Ölçüm (2026-09-16): ekran frame'lerine atılan altı `get_design_context`
|
|
217
|
+
çağrısı yaklaşık **bir milyon karakter** React kodu getirdi; kullanılan yalnız renk, boşluk, yazı
|
|
218
|
+
tipi ve düzendi. O bilgi Figma'nın ucuz tool'larından çıkar.
|
|
219
|
+
|
|
220
|
+
**Bütçe: tasarım dosyası başına ≤ 8 Figma çağrısı, bağlama giren ≤ 150 bin karakter.**
|
|
221
|
+
İş tanımına frame URL'lerini yazdırın — sayfa düzeyinde ağaç dökümü tek başına 1 MB'tır.
|
|
222
|
+
|
|
223
|
+
| Adım | Figma çağrısı | Bütçe | Anlık görüntüye ne girer |
|
|
224
|
+
|---|---|---|---|
|
|
225
|
+
| 1 | `get_metadata(fileKey)` — `nodeId` YOK | 1 çağrı, < 2 K | `source.fileName`, sayfa id'si |
|
|
226
|
+
| 2 | **Koşullu:** insan frame URL'si vermediyse `get_metadata(fileKey, nodeId: <sayfa>)`; yalnız üst seviye frame'lerin ad + id'si okunur | 1 çağrı; büyükse harness dosyaya alır | `screens[]` (`role`'ü siz atarsınız: shell · list · form · detail · auth · other) |
|
|
227
|
+
| 3 | `get_variable_defs(fileKey, nodeId: <kütüphane/Components frame'i, yoksa ana ekran>)`; koyu mod frame'i varsa ona da. **Sayfa id'si (`0:1`) çalışmaz** — "select a layer" hatası; her zaman frame id verin. Farklı frame'ler farklı token dili taşıyabilir (ölçüldü: `Fonts/*` ve `Light/Basic/*` aynı dosyada) — iki temsilci frame yeter | 1–2 çağrı, 0,5–10 K | `variables`, `variablesDark` — **olduğu gibi**, ad yeniden yazılmaz; `Font(family: …, size: …, weight: …)` değerleri metin stili olarak okunur |
|
|
228
|
+
| 4 | `get_screenshot(fileKey, nodeId, maxDimension: 800)` — liste, form, detay için birer | ≤ 3 çağrı; varsayılan URL kipi, `curl -o` ile diske | `screens[].screenshot` (yerel yol), `layout.notes` (gözlem: ray genişliği, ızgara, kart/pill dili) |
|
|
229
|
+
| 5 | `get_design_context(fileKey, nodeId: <ATOMİK bileşen>, excludeScreenshot: true)` — button, badge, input; **ekran frame'ine ASLA** | ≤ 2 çağrı | `components` (ölçüler), `textStyles`, `effects` |
|
|
230
|
+
|
|
231
|
+
Tip ölçeği için sıra: (a) `variables` içinde `font/size/h1` gibi adlar, (b) kütüphanede bir
|
|
232
|
+
"Typography" bileşeni varsa 5. adımın bir çağrısı ona, (c) yoksa alan boş kalır ve tema önerisi
|
|
233
|
+
bunu `gaps` ile söyler — ekran frame'i bunun için de açılmaz.
|
|
234
|
+
|
|
235
|
+
**Anlık görüntü sözleşmesi** (`design-snapshot` altın örneği; her seviyede strict, tanınmayan
|
|
236
|
+
anahtar reddedilir):
|
|
237
|
+
|
|
238
|
+
| Alan | Ne |
|
|
239
|
+
|---|---|
|
|
240
|
+
| `source` | `kind` (`figma` \| `manual`), `fileKey`, `fileName`, `nodeIds`, `fetchedAt`, ölçüm için `calls` ve `characters` |
|
|
241
|
+
| `variables` · `variablesDark` | `get_variable_defs` çıktısı: `{ 'Primary': '#4787FA', 'spacing/md': 16 }`; koyu küme ayrı |
|
|
242
|
+
| `textStyles[]` | `name`, isteğe bağlı `role` (h1…small), `fontFamily`, `fontSize` (px), `fontWeight`, `lineHeight` |
|
|
243
|
+
| `effects[]` | gölge: `name`, offset/blur/spread, `color`, `opacity` (Figma'nın 0–1'i) |
|
|
244
|
+
| `layout` | `desktopWidth`, `mobileWidth`, `gutter`, `sidebarWidth`, `notes[]` — temaya gitmez, sayfa fazı için saklanır |
|
|
245
|
+
| `components` | `'button/primary'` → `background`, `color`, `border`, `borderRadius`, `paddingX/Y`, `height`, `fontSize`, `fontWeight`, `shadow` |
|
|
246
|
+
| `screens[]` | `nodeId`, `name`, `role`, `screenshot` (yerel yol), `notes[]` |
|
|
247
|
+
| `hints` | Figma adı → tema yolu (`accent.muted`, `colors.blue.500`) ya da `ignore` — **sizin kararınız, kalıcı** |
|
|
248
|
+
|
|
249
|
+
Dosya koşu klasöründe yaşar: `zeyrek-mcp/docs/<koşu>/design/<fileKey>.snapshot.json`. Ekran
|
|
250
|
+
görüntüleri repoya girmez. Gateway bu dosyayı hatırlamaz (HTTP taşımasında her çağrı yeni
|
|
251
|
+
sunucudur); ikinci oturum dosyayı okur, Figma'ya dönmez.
|
|
252
|
+
|
|
253
|
+
**Eksik değer için Figma'ya dönülmez.** Öneri bir token'ı eşleyemediyse sebebini `unmapped`'da
|
|
254
|
+
söyler; kararı `hints`'e yazıp yeniden çevirirsiniz. Değer dosyada hiç yoksa (`gaps`) ekran
|
|
255
|
+
görüntüsünden ya da insandan alıp anlık görüntüye elle eklersiniz (`source.kind` `figma` kalır,
|
|
256
|
+
`notes` söyler). Koyu palet aydınlıktan türetilmez: `variablesDark` yoksa `paletteDark` boşluktur
|
|
257
|
+
(THEME-009 uyarır).
|
|
258
|
+
|
|
259
|
+
Akış: anlık görüntüyü yaz → `theme_discover({ snapshot })` → `unmapped`/`gaps`'e bak, `hints` yaz →
|
|
260
|
+
`theme_discover` yeniden (`project` verirseniz mevcut temaya göre `cssVarsChanged` önizlemesi de
|
|
261
|
+
gelir) → `theme_update({ theme, dryRun: true })` → `theme_update({ theme })` →
|
|
262
|
+
`theme_get(view:'resolved')` → `page_screenshot` (aydınlık + `colorScheme:'dark'`). Bu rehberin
|
|
263
|
+
§Doğrulama sırası aynen geçerlidir.
|
|
264
|
+
|
|
265
|
+
`theme_discover` yazmaz ve tahmin etmez: öneri `theme_update` şemasındadır, `mapping` her Figma
|
|
266
|
+
adının hangi yola hangi kuralla (`role` · `ramp` · `hint` · `textStyle` · `variable` · `effect` ·
|
|
267
|
+
`component` · `component→var`) gittiğini söyler; yalnız SAPAN değer yazılır (platform varsayılanına
|
|
268
|
+
eşit taban ve ±0,05 içindeki oran öneriye girmez). Tek istisna `typeScale.p: 1` — platformda p
|
|
269
|
+
tabanın 0,925'idir, Figma gövdesi tabanın kendisi olduğu için yazılmazsa 15 px gövde 13,9 px
|
|
270
|
+
çizilir. Grup kelimesinin yanında tanınmayan bir niteleyici (`bg/error`, `text/on-primary`,
|
|
271
|
+
`button/primary`) varsayılana DÜŞMEZ, `unmapped` olur — kararı `hints` ile verirsiniz. Bileşen
|
|
272
|
+
ölçüleri adlı preset olur ve eşlenmiş renkler `var(--ng-*)` ile bağlanır (preset temayı izler);
|
|
273
|
+
`componentPresets` üretilmez. Gateway'in düzelttiği değerler (hex büyük harf, `rgba` → hex, gölge
|
|
274
|
+
`opacity` ×100, yazı tipi yazımı) yanıtta `gatewayNormalized` ile listelenir.
|
|
275
|
+
|
|
276
|
+
Bileşen adının ilk kelimesi katalogdaki bir TİP ise (`button/primary` → `button`) aynı ölçüler o
|
|
277
|
+
tipin **bileşen preseti** (`componentPresets.button.versions.V1.props.style`) olarak da önerilir:
|
|
278
|
+
adlı preset mevcut düğümlere bağlanır, bileşen preseti yeni düğümlere başlangıç olur. `input` katalog
|
|
279
|
+
tipi değildir ve `textinput` diye düzeltilmez — adı katalog tipiyle başlatın (`component_list`).
|
|
280
|
+
Koyu palet varsayılan olarak türetilmez; `theme_discover({ snapshot, deriveDark: true })` Figma'da
|
|
281
|
+
koyu küme YOKSA accent/status değerlerinden deterministik bir başlangıç noktası üretir ve her değeri
|
|
282
|
+
`via: 'derived'` ile işaretler — marka kararı değil, koyu kareyi görüp düzeltmek sizin işiniz.
|
|
283
|
+
|
|
284
|
+
**Ekranlar için `page_discover`.** Anlık görüntüdeki bir ekranın rolünden (list · form · detail ·
|
|
285
|
+
auth · shell) altın örnek arketipini seçip `page_create` + `page_schema_patch` iskeletini döner;
|
|
286
|
+
`theme_discover`'ın ürettiği adlı preset tek adaysa bileşene `props.style.$preset` olarak bağlanır.
|
|
287
|
+
Piksel kopyası değildir: `caveats` neyin doldurulacağını, `next` çağrı sırasını söyler.
|
|
288
|
+
|
|
289
|
+
`hints` biçimleri: çıplak `accent.hover` **iki kümeye de** uygulanır (Figma modları aynı adı
|
|
290
|
+
aydınlık ve koyu değerle verir — `variables`'taki `palette`'e, `variablesDark`'taki
|
|
291
|
+
`paletteDark`'a gider); `palette.accent.hover` / `paletteDark.accent.hover` yalnız kendi kümesinde;
|
|
292
|
+
`colors.blue.500` rampaya (yalnız aydınlık); `ignore` hiç eşleme. Anlık görüntüde olmayan bir ada
|
|
293
|
+
hint yazmak hatadır — Figma'da ad değiştiyse hint de değişir.
|
|
@@ -227,19 +227,68 @@ tanımı kendi global-error dalınla gönder — dal **bağlıysa** (`nextStepId
|
|
|
227
227
|
dokunmaz. `nextStepId: null` ile bağlanmamış gönderirsen gateway ikinci bir dal eklemez, mevcut
|
|
228
228
|
dalını enjekte ettiği hata adımına bağlar.
|
|
229
229
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
230
|
+
### `gatewayOnError`'un 500 gövdesi: patlayan adım ve alt akış zinciri (2026-09-16)
|
|
231
|
+
|
|
232
|
+
```jsonc
|
|
233
|
+
// POST /m/web/ticket-ac → 500 (ebeveyn → callChild → çocuk akış → childBoom patladı)
|
|
234
|
+
{ "code": "JavaScriptException", "message": "Error: x is not defined\n at <anonymous>:1:8",
|
|
235
|
+
"detail": "…", "type": "JavaScriptException",
|
|
236
|
+
"step": { "name": "callChild", "type": "workflow" }, // bu akışta patlayan adım
|
|
237
|
+
"cause": { "code": "JavaScriptException", "message": "…", // alt akışın KENDİ hata gövdesi
|
|
238
|
+
"step": { "name": "childBoom", "type": "code" } } } // çocukta patlayan adım
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
- **`step` yalnız `{ name, type }`'tır.** `error.step.input` ifade değerlerini (parola dahil),
|
|
242
|
+
`error.step.data` adımın çıktısını taşır; ikisi de dışarı yazılmaz. `error.step`'i bütün olarak
|
|
243
|
+
gövdeye koyarsan platform onu **siler** (`ResponseStep` global-error kipinde `error.step` ile
|
|
244
|
+
birebir aynı `step` alanını kaldırır) — alt küme yaz.
|
|
245
|
+
- **`cause` bir seviye aşağıyı taşır** ve her seviyede yalnız `code`, `message`, `detail`, `type`,
|
|
246
|
+
`step { name, type }` ve `cause` geçer (gateway beyaz listeye süzer, en fazla dokuz seviye). Backend
|
|
247
|
+
2026-09-17'den beri zinciri `error.cause`'ta kendisi kurar; üç durum kapsanır:
|
|
248
|
+
- **Alt akış patladı:** `cause.step` çocukta patlayan adım, çocuk da alt akış çağırıyorsa zincir
|
|
249
|
+
seviye seviye iç içe iner. Çocuk Studio'da hata yolsuz kurulduysa da adım adı gelir.
|
|
250
|
+
- **Loop iterasyonu patladı:** `step` loop adımı, `cause.step.name` `loopRows[0]` gibi
|
|
251
|
+
`<loop adı>[<sıra>]`, iterasyondaki alt akışın zinciri onun altında.
|
|
252
|
+
- **Çocuk istisnasız 2xx dışı döndü** (`response` 422): `code` `SubWorkflowFailedException`,
|
|
253
|
+
`message` `Sub-workflow '<ad>' returned 422 (<gövde code>): <gövde message>`.
|
|
254
|
+
|
|
255
|
+
Eski backend'de `error.cause` yoktur; gateway o zaman patlayan `workflow` adımının `step.data`'sına
|
|
256
|
+
(çocuğun kendi gövdesi) düşer — loop ve istisnasız çocuk orada kaybolur. Çocuk kendi 4xx/5xx yanıtını
|
|
257
|
+
elle yazdıysa (`({ success: false, token })`, `({ err: error })`) o gövdenin yalnız bu alanları geçer.
|
|
258
|
+
Canlı ölçüldü (2026-09-16/17): torunun `token` ve `step.input`'u üst gövdeye ulaşmadı; üç seviyeli
|
|
259
|
+
zincir ve `loopRows` → `loopRows[0]` → `boomStep` hem hata yolsuz hem gateway hata yollu uçta döndü.
|
|
260
|
+
- **Gateway zinciri okur (yalnız 5xx):** `microservice_execute`/`debug`/`preview` `runtimeErrors`'a
|
|
261
|
+
`hata gövdesi — hata yolu: 'callChild' (workflow) → 'childBoom' (code) · 'childBoom' (code)
|
|
262
|
+
JavaScriptException: …` satırını ekler (debug ucunun `{ data, executionItems }` sarmalı açılır);
|
|
263
|
+
fırlatan çağrıların (senkron automation'lı `row_create`, `workflow_preview`…) araç hatası da
|
|
264
|
+
gövdede bulunan `detail`'i, koddan farklı `type`'ı ve varsa zinciri yazar. 4xx gövdeleri
|
|
265
|
+
süslenmez — onlar bilinçli iş yanıtıdır.
|
|
266
|
+
- **Eski kayıtlar:** 2026-09-16 öncesi gateway `({ code: error.code, message: error.message })`
|
|
267
|
+
yazıyordu. Global-error dalının hedefi olan, adı `gatewayOnError[n]`, ifadesi bu metinle AYNEN
|
|
268
|
+
aynı ve açıklaması gateway'in eski metni (ya da boş) olan adım bir sonraki
|
|
269
|
+
`workflow_create`/`update`/`patch`/`preview`'da yeni gövdeye yükseltilir (`gatewayNormalized`:
|
|
270
|
+
"hata gövdesi genişletildi"; önizleme kaydetmez). Yanıta alan eklenir, `code`/`message` değişmez.
|
|
271
|
+
Açıklaması ya da ifadesi elle değiştirilmiş adıma dokunulmaz; yükseltme açıklamayı da yenilediği
|
|
272
|
+
için sonradan bilerek eski gövdeye dönen `bodyExpression` bir daha ezilmez.
|
|
273
|
+
- **Dışarı ne verdiğine sen karar verirsin.** Adım adları iç yapıyı, `detail` iç istisna mesajını
|
|
274
|
+
(kısıt adı, host) gösterebilir. **Middleware'in 500 gövdesi korumalı uçların anonim çağıranına da
|
|
275
|
+
gider** (`ExecuteMicroserviceBehavior`). Herkese açık yüzeyde istemiyorsan hata adımının
|
|
276
|
+
`bodyExpression`'ını daralt, örn. `({ code: error.code, message: error.message })`.
|
|
233
277
|
|
|
234
278
|
Global-error dalında `error` değişkeni tanımlıdır:
|
|
235
279
|
|
|
236
280
|
```jsonc
|
|
237
281
|
{ "code": "RowAlreadyExists", "message": "…", "detail": "…", "type": "…",
|
|
238
|
-
"step": { "id": "…", "name": "writeRow", "type": "table", "data": …, "input": [ … ] }
|
|
282
|
+
"step": { "id": "…", "name": "writeRow", "type": "table", "data": …, "input": [ … ] },
|
|
283
|
+
"cause": null } // alt akış / loop iterasyonunda { code, message, detail, type, step: { name, type }, cause }
|
|
239
284
|
```
|
|
240
285
|
|
|
241
|
-
|
|
242
|
-
|
|
286
|
+
Fail dalında aynı zincir `<adım adı>.error.cause`'tadır (`<adım adı>.error` = `{ code, message, detail,
|
|
287
|
+
type, cause }`).
|
|
288
|
+
|
|
289
|
+
`error.step` hangi adımın patladığını söyler. `input`/`data` alanları ifade değerlerini ve adım
|
|
290
|
+
çıktısını taşır — gövdeye yalnız ihtiyaç duyduğun alt kümeyi yaz (gateway'in varsayılanı
|
|
291
|
+
`{ name, type }`'tır, yukarıdaki §`gatewayOnError`'un 500 gövdesi).
|
|
243
292
|
|
|
244
293
|
**Kurallar ve sınırlar:**
|
|
245
294
|
|
|
@@ -257,9 +306,14 @@ yapıyı dışarı sızdırır.
|
|
|
257
306
|
bağlı — §izin matrisi) ya da `email`/`table` adımına bağla. Bağlı hata yolu yoksa `WF-021` uyarır.
|
|
258
307
|
- **Hata yolu hiç yoksa** (fail dalı ve global-error dalı bağlı değil) adım patladığında motor
|
|
259
308
|
canlıda `WorkflowStepFailedException` fırlatır (2026-09-16): HTTP yanıt 500 +
|
|
260
|
-
`{ code, message, detail, type
|
|
261
|
-
|
|
262
|
-
|
|
309
|
+
`{ code, message, detail, type, step: { name, type }, cause }` — `step` ve `cause` 2026-09-17'den
|
|
310
|
+
beri, gateway hata adımıyla aynı şekil (yerel ortamda ayrıca `stackTrace`). Zamanlanmış iş ve
|
|
311
|
+
automation geçmişi `failed` + `Step 'x' failed: <sebep>`, senkron automation tetikleyen satır
|
|
312
|
+
yazımını düşürür. Önizlemede fırlatmaz, ağaç döner. Çalışır, ama çıktı lokalize değildir.
|
|
313
|
+
- **Adım hata yolunun DIŞINDA patlarsa** (ör. `revert`'in `revertType` ifadesi tırnaksız) kod
|
|
314
|
+
`EvaluateWorkflowStepFailedException`, mesaj `Step '<ad>' threw outside its error path: <sebep>`
|
|
315
|
+
olur: fail dalı ve global-error dalı ÇALIŞMAZ, canlı koşu düşer. 2026-09-17'den beri önizleme
|
|
316
|
+
ağacı kaybolmaz; adım `hasError: true` ile işaretlenir.
|
|
263
317
|
|
|
264
318
|
> **Preview HTTP 200 dönerken bile akış patlamış olabilir.** `executionSuccess` yalnız SON adıma
|
|
265
319
|
> bakar; ortada patlayıp hata dalından dönen akış `true` gösterir. Gateway tüm adımları (iç içe
|
|
@@ -347,6 +401,63 @@ gidilir, hiçbiri tutmazsa `else` dalı yürür:
|
|
|
347
401
|
> `rule.isExpression` bayrağı bu iki alan için **okunmaz**.
|
|
348
402
|
|
|
349
403
|
|
|
404
|
+
## Workflow adımı: başka bir akışı parametreyle çağırmak (WF-032)
|
|
405
|
+
|
|
406
|
+
Ortak mantığı (kayıt açma, bildirim, numaralandırma) **bir kez** `type:'workflow'` akışı olarak
|
|
407
|
+
yazıp diğer akışlardan çağırmanın yolu budur — 30 adımı kopyalamak değil. Adım tipi `workflow`;
|
|
408
|
+
üç akış tipinde de kullanılabilir. Kuralı **iki alan** taşır ve girdi eşlemesi ikincisidir:
|
|
409
|
+
|
|
410
|
+
```jsonc
|
|
411
|
+
{ "id": "<GUID>", "name": "createTicket", "type": "workflow",
|
|
412
|
+
"description": "Telemetri olayından create-ticket akışıyla kayıt açar.",
|
|
413
|
+
"rule": {
|
|
414
|
+
"workflowId": "<create-ticket akışının id'si>",
|
|
415
|
+
"expression": "({ title: input.title, source: 'telemetry', payload: parseEvent.data })"
|
|
416
|
+
},
|
|
417
|
+
"branches": [
|
|
418
|
+
{ "id": "<GUID>", "name": "success", "type": "success", "nextStepId": "<sonraki>" },
|
|
419
|
+
{ "id": "<GUID>", "name": "fail", "type": "fail", "nextStepId": "<hata adımı>" }
|
|
420
|
+
] }
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
- **Eşleme `rule.expression`'daki nesne literalidir: her anahtar çağrılan akışın parametre
|
|
424
|
+
DTO'sundaki bir alan, her değer bir JS ifadesi** (`input.x`, önceki adımın `<ad>.data.x`'i,
|
|
425
|
+
sabit). Studio'nun adım editörü de aynı şeyi yapar — akış seçilince ifadeyi `parameterScheme`'den
|
|
426
|
+
`{ "alan": <örnek> }` olarak doldurur (`WorkflowTrigger.svelte`). Table adımının `fields[]`
|
|
427
|
+
listesinin buradaki karşılığı budur; ayrı bir liste alanı **yoktur** ve gerekmez.
|
|
428
|
+
- **Çağrılan akışın `parameterDtoId` + `parameterBehavior`'ı OLMAK ZORUNDA.** `WorkflowStep`
|
|
429
|
+
ifadeyi yalnız `workflow.HasParameter` iken değerlendirir (`WorkflowStep.cs`); DTO'suz alt
|
|
430
|
+
akışa yazılan ifade hiç koşmaz, alt akış boş `input` ile çalışır ve **200 döner**. Gateway
|
|
431
|
+
çağrılan akışı okuyup bunu **block** eder (WF-032). Reçete: `dto_create` → `workflow_patch(id:
|
|
432
|
+
<alt akış>, ops: [{ op: 'add', path: '/parameterDtoId', value: <dtoId> }, { op: 'add', path:
|
|
433
|
+
'/parameterBehavior', value: 'object' }])`.
|
|
434
|
+
- **Başka hiçbir alan girdi taşımaz.** Şema üyeleri (`payloadExpression`, `customParameters`,
|
|
435
|
+
`queryParameters`, `bodyExpression`, `fields`) kaydedilir ama `WorkflowStep` okumaz; şema dışı
|
|
436
|
+
adlar (`parameters`, `input`, `payload`, `args`…) platformda hiç kaydedilmez (`unmappedMembers`).
|
|
437
|
+
İki durumda da alt akış boş girdi alır (RFQ koşusu W-7, 2026-09-14: beş biçim denendi, beşinde
|
|
438
|
+
de boş girdi). Gateway DOLU değer görürse block eder; `workflow_get`'in geri okuttuğu
|
|
439
|
+
`fields: []` / `customParameters: null` gibi boş varsayılanlar kuralı tetiklemez.
|
|
440
|
+
- **Ad tuzağı:** `rule.expression` `code` adımında GÖVDE (çalıştırılan kod), `workflow` ve
|
|
441
|
+
`microfunction` adımında GİRDİ (argüman nesnesi) demektir. Aynı ad, iki anlam.
|
|
442
|
+
- **Alt akış girdiyi `input.<alan>` ile okur ve onun `input`'u bu nesnedir** — çağıranın
|
|
443
|
+
`input`'unu görmez; çağıranın bir alanı gerekiyorsa eşlemeye koy. `parameterBehavior: 'array'`
|
|
444
|
+
ise ifade dizi üretmelidir (gateway nesne literali görürse uyarır).
|
|
445
|
+
- **Çıktı alt akışın yanıt gövdesidir** (`response` adımının `returnDtoId` süzgecinden geçmiş
|
|
446
|
+
hâli) ve sonraki adımlara `<adım adı>.data` ile akar. Alt akış 2xx dışı dönerse ya da bir
|
|
447
|
+
adımı düşerse çağıran adım **fail** dalına gider; `<adım adı>.data` o zaman alt akışın hata
|
|
448
|
+
gövdesidir.
|
|
449
|
+
- **Şekli tahmin etme, oku:** `workflow_step_schema(step)` çağrılan akışın `parameterScheme` ve
|
|
450
|
+
`returnScheme`'ini döner; ya da `workflow_get(<workflowId>)` → `parameterDtoId` → `dto_get`.
|
|
451
|
+
- **Loop içinde:** `rule.stepType: 'workflow'`, `rule.arrayExpression`, çocuk kural
|
|
452
|
+
`rule.stepRule: { workflowId, expression: "({ title: loopItem.title })" }` — WF-032 çocuğu da
|
|
453
|
+
denetler.
|
|
454
|
+
- **Önizlemede** alt akışın kendi adım izi `steps[].children[]` altında `kind: 'subWorkflow'` ile
|
|
455
|
+
gelir; çağıran adımın `input[]` listesinde `member: 'parameter'` satırı ifadenin ürettiği nesneyi
|
|
456
|
+
gösterir — **satır yoksa ya da değeri boşsa girdi bağlanmamıştır.** Alt akışı tek başına
|
|
457
|
+
sınamak için kendi `previewPayload`'ı ile `workflow_preview` çağır.
|
|
458
|
+
- Aynı alt akışı birden çok adımdan çağırmak serbesttir; özyineleme runner'ın koşum sayacına
|
|
459
|
+
takılır (kural envanteri WF-028 — gateway kuralı değil, backend davranışı).
|
|
460
|
+
|
|
350
461
|
## Loop step — dizinin her öğesi için bir adım koştur
|
|
351
462
|
|
|
352
463
|
Tek uçta "başlık + N alt kayıt" yazmanın yolu budur (anket + sorular + seçenekler tek çağrıda;
|
|
@@ -2,7 +2,7 @@ import { guid, entityRef, resolveEntityId, withResolved, projectParam, environme
|
|
|
2
2
|
import { RefResolver } from '../platform/refs.js';
|
|
3
3
|
import { normalizeWorkflowDefinition, withNormalization } from '../knowledge/normalize.js';
|
|
4
4
|
import { evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/policy.js';
|
|
5
|
-
import { policyRules, microfunctionStepFindings } from '../knowledge/rules.js';
|
|
5
|
+
import { policyRules, microfunctionStepFindings, workflowStepFindings } from '../knowledge/rules.js';
|
|
6
6
|
import { languageHeader } from '../knowledge/languageCodes.js';
|
|
7
7
|
import { handle } from '../platform/responses.js';
|
|
8
8
|
import { z } from 'zod';
|
|
@@ -40,7 +40,8 @@ export function registerAutomationTools(server, platform) {
|
|
|
40
40
|
const normalized = normalizeWorkflowDefinition(definition);
|
|
41
41
|
const findings = enforcePolicy([
|
|
42
42
|
...evaluatePolicy(config, policyRules, 'workflow', definition, { operation: 'create', workflowKind: 'automation' }, policyOverride),
|
|
43
|
-
...await microfunctionStepFindings(platform, project, environment, definition, 'automation', policyOverride)
|
|
43
|
+
...await microfunctionStepFindings(platform, project, environment, definition, 'automation', policyOverride),
|
|
44
|
+
...await workflowStepFindings(platform, project, environment, undefined, definition, 'automation', policyOverride)
|
|
44
45
|
]);
|
|
45
46
|
const resolver = new RefResolver(platform, project, environment);
|
|
46
47
|
const resolvedEntityId = await resolveEntityId(resolver, entityId);
|
|
@@ -56,7 +57,8 @@ export function registerAutomationTools(server, platform) {
|
|
|
56
57
|
const normalized = normalizeWorkflowDefinition(definition);
|
|
57
58
|
const findings = enforcePolicy([
|
|
58
59
|
...evaluatePolicy(config, policyRules, 'workflow', definition, { operation: 'update', workflowKind: 'automation' }, policyOverride),
|
|
59
|
-
...await microfunctionStepFindings(platform, project, environment, definition, 'automation', policyOverride)
|
|
60
|
+
...await microfunctionStepFindings(platform, project, environment, definition, 'automation', policyOverride),
|
|
61
|
+
...await workflowStepFindings(platform, project, environment, undefined, definition, 'automation', policyOverride)
|
|
60
62
|
]);
|
|
61
63
|
const resolver = new RefResolver(platform, project, environment);
|
|
62
64
|
const resolvedEntityId = await resolveEntityId(resolver, entityId);
|
|
@@ -135,7 +137,8 @@ export function registerAutomationTools(server, platform) {
|
|
|
135
137
|
const normalized = normalizeWorkflowDefinition(definition);
|
|
136
138
|
const findings = enforcePolicy([
|
|
137
139
|
...evaluatePolicy(config, policyRules, 'workflow', definition, { operation: 'preview', workflowKind: 'automation' }, policyOverride),
|
|
138
|
-
...await microfunctionStepFindings(platform, project, environment, definition, 'automation', policyOverride)
|
|
140
|
+
...await microfunctionStepFindings(platform, project, environment, definition, 'automation', policyOverride),
|
|
141
|
+
...await workflowStepFindings(platform, project, environment, undefined, definition, 'automation', policyOverride)
|
|
139
142
|
]);
|
|
140
143
|
const resolver = new RefResolver(platform, project, environment);
|
|
141
144
|
const resolvedEntityId = await resolveEntityId(resolver, entityId);
|