@decantr/core 1.0.0-beta.8 → 1.0.0
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 +55 -0
- package/dist/index.d.ts +285 -17
- package/dist/index.js +930 -71
- package/dist/index.js.map +1 -1
- package/package.json +21 -7
- package/schema/execution-pack-bundle.v1.json +58 -0
- package/schema/execution-pack.common.v1.json +189 -0
- package/schema/mutation-pack.v1.json +94 -0
- package/schema/pack-manifest.v1.json +110 -0
- package/schema/page-pack.v1.json +101 -0
- package/schema/review-pack.v1.json +97 -0
- package/schema/scaffold-pack.v1.json +87 -0
- package/schema/section-pack.v1.json +92 -0
- package/schema/selected-execution-pack.v1.json +75 -0
package/dist/index.js
CHANGED
|
@@ -89,15 +89,19 @@ function routePath(pageId, index) {
|
|
|
89
89
|
function capitalize(str) {
|
|
90
90
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
91
91
|
}
|
|
92
|
-
function buildNavItems(pages) {
|
|
92
|
+
function buildNavItems(pages, routes) {
|
|
93
|
+
const routeLookup = new Map(
|
|
94
|
+
(routes ?? []).map((route) => [route.pageId, route.path])
|
|
95
|
+
);
|
|
93
96
|
return pages.map((page, i) => ({
|
|
94
|
-
href: routePath(page.id, i),
|
|
97
|
+
href: routeLookup.get(page.id) ?? routePath(page.id, i),
|
|
95
98
|
icon: NAV_ICONS[page.id] || "circle",
|
|
96
99
|
label: capitalize(page.id.replace(/-/g, " "))
|
|
97
100
|
}));
|
|
98
101
|
}
|
|
99
|
-
function
|
|
100
|
-
const shell =
|
|
102
|
+
function buildThemeDecoration(theme) {
|
|
103
|
+
const shell = theme.shell;
|
|
104
|
+
if (!shell) return null;
|
|
101
105
|
const shellAny = shell;
|
|
102
106
|
return {
|
|
103
107
|
root: shell.root || "",
|
|
@@ -105,7 +109,7 @@ function buildRecipeDecoration(recipe) {
|
|
|
105
109
|
header: shell.header || "",
|
|
106
110
|
brand: shellAny["brand"] || "",
|
|
107
111
|
navLabel: shellAny["navLabel"] || "",
|
|
108
|
-
// AUTO: default nav style is 'pill'
|
|
112
|
+
// AUTO: default nav style is 'pill' when the theme does not declare one
|
|
109
113
|
navStyle: shell.nav_style || "pill",
|
|
110
114
|
defaultNavState: shellAny["default_nav_state"] || "expanded",
|
|
111
115
|
dimensions: shell.dimensions || null
|
|
@@ -113,20 +117,18 @@ function buildRecipeDecoration(recipe) {
|
|
|
113
117
|
}
|
|
114
118
|
function buildTheme(essence, isAddon) {
|
|
115
119
|
return {
|
|
116
|
-
|
|
120
|
+
id: essence.theme.id,
|
|
117
121
|
mode: essence.theme.mode,
|
|
118
122
|
shape: essence.theme.shape || null,
|
|
119
|
-
recipe: essence.theme.recipe,
|
|
120
123
|
isAddon
|
|
121
124
|
};
|
|
122
125
|
}
|
|
123
126
|
function buildThemeFromV3(essence, isAddon) {
|
|
124
127
|
const dna = essence.dna;
|
|
125
128
|
return {
|
|
126
|
-
|
|
129
|
+
id: dna.theme.id,
|
|
127
130
|
mode: dna.theme.mode,
|
|
128
131
|
shape: dna.radius.philosophy || dna.theme.shape || null,
|
|
129
|
-
recipe: dna.theme.recipe,
|
|
130
132
|
isAddon
|
|
131
133
|
};
|
|
132
134
|
}
|
|
@@ -138,6 +140,23 @@ function blueprintPageToStructurePage(page, defaultShell) {
|
|
|
138
140
|
...page.surface ? { surface: page.surface } : {}
|
|
139
141
|
};
|
|
140
142
|
}
|
|
143
|
+
function buildV3Routes(essence, blueprintPages, structurePages) {
|
|
144
|
+
const explicitRoutes = /* @__PURE__ */ new Map();
|
|
145
|
+
for (const [path, entry] of Object.entries(essence.blueprint.routes ?? {})) {
|
|
146
|
+
if (entry?.page && !explicitRoutes.has(entry.page)) {
|
|
147
|
+
explicitRoutes.set(entry.page, path);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
for (const page of blueprintPages) {
|
|
151
|
+
if (page.route && !explicitRoutes.has(page.id)) {
|
|
152
|
+
explicitRoutes.set(page.id, page.route);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return structurePages.map((page, i) => ({
|
|
156
|
+
path: explicitRoutes.get(page.id) ?? routePath(page.id, i),
|
|
157
|
+
pageId: page.id
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
141
160
|
function convertWiring(wiringResults) {
|
|
142
161
|
if (wiringResults.length === 0) return null;
|
|
143
162
|
const signals = [];
|
|
@@ -166,12 +185,13 @@ function convertWiring(wiringResults) {
|
|
|
166
185
|
}
|
|
167
186
|
return { signals, props, hooks: [...hookSet], hookProps };
|
|
168
187
|
}
|
|
169
|
-
function resolveVisualEffects(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const
|
|
173
|
-
const
|
|
174
|
-
const
|
|
188
|
+
function resolveVisualEffects(theme, pattern, _slot) {
|
|
189
|
+
const effects = theme.effects;
|
|
190
|
+
if (!effects?.enabled) return null;
|
|
191
|
+
const typeMapping = effects.type_mapping || {};
|
|
192
|
+
const componentFallback = effects.component_fallback || {};
|
|
193
|
+
const intensity = effects.intensity || "medium";
|
|
194
|
+
const intensityValues = effects.intensity_values || {};
|
|
175
195
|
let decorators = [];
|
|
176
196
|
for (const comp of pattern.components || []) {
|
|
177
197
|
const effectType = componentFallback[comp];
|
|
@@ -216,30 +236,30 @@ async function resolveEssence(essence, resolver) {
|
|
|
216
236
|
} else {
|
|
217
237
|
throw new Error("Invalid essence format");
|
|
218
238
|
}
|
|
219
|
-
let
|
|
220
|
-
const
|
|
221
|
-
if (
|
|
222
|
-
|
|
239
|
+
let registryTheme = null;
|
|
240
|
+
const themeResult = await resolver.resolve("theme", simpleEssence.theme.id);
|
|
241
|
+
if (themeResult) {
|
|
242
|
+
registryTheme = themeResult.item;
|
|
223
243
|
}
|
|
224
|
-
const
|
|
225
|
-
const density = computeDensity(simpleEssence.personality,
|
|
226
|
-
density_bias:
|
|
227
|
-
content_gap_shift:
|
|
244
|
+
const themeSpatial = registryTheme?.spatial;
|
|
245
|
+
const density = computeDensity(simpleEssence.personality, themeSpatial ? {
|
|
246
|
+
density_bias: themeSpatial.density_bias,
|
|
247
|
+
content_gap_shift: themeSpatial.content_gap_shift
|
|
228
248
|
} : void 0);
|
|
229
|
-
const
|
|
230
|
-
const isAddon =
|
|
249
|
+
const themeId = simpleEssence.theme.id;
|
|
250
|
+
const isAddon = themeId.startsWith("custom:") || !CORE_STYLES.has(themeId);
|
|
231
251
|
const theme = buildTheme(simpleEssence, isAddon);
|
|
232
|
-
const resolvedPages = await resolvePages(simpleEssence.structure, resolver,
|
|
252
|
+
const resolvedPages = await resolvePages(simpleEssence.structure, resolver, registryTheme);
|
|
233
253
|
const shellType = simpleEssence.structure[0]?.shell || "sidebar-main";
|
|
234
254
|
const brand = pascalCase(simpleEssence.archetype);
|
|
235
255
|
const nav = buildNavItems(simpleEssence.structure);
|
|
236
|
-
const
|
|
256
|
+
const decoration = registryTheme ? buildThemeDecoration(registryTheme) : null;
|
|
237
257
|
const shell = {
|
|
238
258
|
type: shellType,
|
|
239
259
|
brand,
|
|
240
260
|
nav,
|
|
241
261
|
inset: false,
|
|
242
|
-
|
|
262
|
+
decoration
|
|
243
263
|
};
|
|
244
264
|
const routes = simpleEssence.structure.map((page, i) => ({
|
|
245
265
|
path: routePath(page.id, i),
|
|
@@ -248,7 +268,7 @@ async function resolveEssence(essence, resolver) {
|
|
|
248
268
|
return {
|
|
249
269
|
essence,
|
|
250
270
|
pages: resolvedPages,
|
|
251
|
-
|
|
271
|
+
registryTheme,
|
|
252
272
|
density: { gap: density.content_gap, level: density.level },
|
|
253
273
|
theme,
|
|
254
274
|
shell,
|
|
@@ -259,48 +279,45 @@ async function resolveEssence(essence, resolver) {
|
|
|
259
279
|
}
|
|
260
280
|
async function resolveV3Essence(essence, resolver) {
|
|
261
281
|
const { dna, blueprint, meta } = essence;
|
|
262
|
-
let
|
|
263
|
-
const
|
|
264
|
-
if (
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
const
|
|
268
|
-
const density = computeDensity(dna.personality,
|
|
269
|
-
density_bias:
|
|
270
|
-
content_gap_shift:
|
|
282
|
+
let registryTheme = null;
|
|
283
|
+
const themeResult = await resolver.resolve("theme", dna.theme.id);
|
|
284
|
+
if (themeResult) {
|
|
285
|
+
registryTheme = themeResult.item;
|
|
286
|
+
}
|
|
287
|
+
const themeSpatial = registryTheme?.spatial;
|
|
288
|
+
const density = computeDensity(dna.personality, themeSpatial ? {
|
|
289
|
+
density_bias: themeSpatial.density_bias,
|
|
290
|
+
content_gap_shift: themeSpatial.content_gap_shift
|
|
271
291
|
} : void 0);
|
|
272
292
|
const densityResult = {
|
|
273
293
|
gap: dna.spacing.content_gap || density.content_gap,
|
|
274
294
|
level: dna.spacing.density || density.level
|
|
275
295
|
};
|
|
276
|
-
const
|
|
277
|
-
const isAddon =
|
|
296
|
+
const themeId = dna.theme.id;
|
|
297
|
+
const isAddon = themeId.startsWith("custom:") || !CORE_STYLES.has(themeId);
|
|
278
298
|
const theme = buildThemeFromV3(essence, isAddon);
|
|
279
299
|
const blueprintPages = blueprint.pages ?? (blueprint.sections ? blueprint.sections.flatMap((s) => s.pages) : [{ id: "home", layout: ["hero"] }]);
|
|
280
300
|
const defaultShell = blueprint.shell ?? (blueprint.sections?.[0]?.shell ?? "sidebar-main");
|
|
281
301
|
const structurePages = blueprintPages.map(
|
|
282
302
|
(p) => blueprintPageToStructurePage(p, defaultShell)
|
|
283
303
|
);
|
|
284
|
-
const resolvedPages = await resolvePages(structurePages, resolver,
|
|
304
|
+
const resolvedPages = await resolvePages(structurePages, resolver, registryTheme);
|
|
285
305
|
const shellType = defaultShell;
|
|
286
306
|
const brand = pascalCase(meta.archetype);
|
|
287
|
-
const
|
|
288
|
-
const
|
|
307
|
+
const routes = buildV3Routes(essence, blueprintPages, structurePages);
|
|
308
|
+
const nav = buildNavItems(structurePages, routes);
|
|
309
|
+
const decoration = registryTheme ? buildThemeDecoration(registryTheme) : null;
|
|
289
310
|
const shell = {
|
|
290
311
|
type: shellType,
|
|
291
312
|
brand,
|
|
292
313
|
nav,
|
|
293
314
|
inset: false,
|
|
294
|
-
|
|
315
|
+
decoration
|
|
295
316
|
};
|
|
296
|
-
const routes = structurePages.map((page, i) => ({
|
|
297
|
-
path: routePath(page.id, i),
|
|
298
|
-
pageId: page.id
|
|
299
|
-
}));
|
|
300
317
|
return {
|
|
301
318
|
essence,
|
|
302
319
|
pages: resolvedPages,
|
|
303
|
-
|
|
320
|
+
registryTheme,
|
|
304
321
|
density: densityResult,
|
|
305
322
|
theme,
|
|
306
323
|
shell,
|
|
@@ -309,7 +326,7 @@ async function resolveV3Essence(essence, resolver) {
|
|
|
309
326
|
isV3Source: true
|
|
310
327
|
};
|
|
311
328
|
}
|
|
312
|
-
async function resolvePages(pages, resolver,
|
|
329
|
+
async function resolvePages(pages, resolver, registryTheme) {
|
|
313
330
|
const resolvedPages = [];
|
|
314
331
|
for (const page of pages) {
|
|
315
332
|
const refs = extractLayoutRefs(page.layout);
|
|
@@ -320,7 +337,7 @@ async function resolvePages(pages, resolver, recipe) {
|
|
|
320
337
|
const preset = resolvePatternPreset(
|
|
321
338
|
patternResult.item,
|
|
322
339
|
ref.explicitPreset,
|
|
323
|
-
|
|
340
|
+
registryTheme?.pattern_preferences?.default_presets
|
|
324
341
|
);
|
|
325
342
|
const key = ref.alias || ref.id;
|
|
326
343
|
patterns.set(key, { pattern: patternResult.item, preset });
|
|
@@ -340,32 +357,30 @@ function isPatternRef2(item) {
|
|
|
340
357
|
function isColumnLayout2(item) {
|
|
341
358
|
return typeof item === "object" && "cols" in item;
|
|
342
359
|
}
|
|
343
|
-
function shouldWrapInCard(pattern, preset,
|
|
360
|
+
function shouldWrapInCard(pattern, preset, theme) {
|
|
344
361
|
const layout = preset.layout.layout;
|
|
345
362
|
if (layout === "hero" || layout === "row") return false;
|
|
346
363
|
if (pattern.contained === false) return false;
|
|
347
|
-
const cardWrapping =
|
|
364
|
+
const cardWrapping = theme?.spatial?.card_wrapping;
|
|
348
365
|
if (cardWrapping === "none") return false;
|
|
349
366
|
if (cardWrapping === "minimal") {
|
|
350
367
|
return Object.keys(pattern.presets).length > 0;
|
|
351
368
|
}
|
|
352
369
|
return true;
|
|
353
370
|
}
|
|
354
|
-
function buildCardWrapping(pattern,
|
|
355
|
-
const mode =
|
|
356
|
-
const overrides = recipe?.pattern_overrides?.[pattern.id];
|
|
371
|
+
function buildCardWrapping(pattern, theme) {
|
|
372
|
+
const mode = theme?.spatial?.card_wrapping || "always";
|
|
357
373
|
return {
|
|
358
374
|
mode,
|
|
359
|
-
headerLabel: pattern.name
|
|
360
|
-
background: overrides?.background?.join(" ")
|
|
375
|
+
headerLabel: pattern.name
|
|
361
376
|
};
|
|
362
377
|
}
|
|
363
|
-
function buildPatternNode(patternId, alias, resolved, wiring,
|
|
378
|
+
function buildPatternNode(patternId, alias, resolved, wiring, theme, density, layer) {
|
|
364
379
|
const pattern = resolved?.pattern;
|
|
365
380
|
const preset = resolved?.preset;
|
|
366
381
|
const layout = preset?.layout.layout || "column";
|
|
367
382
|
const isStandalone = layout === "hero" || layout === "row";
|
|
368
|
-
const contained = pattern && preset ? shouldWrapInCard(pattern, preset,
|
|
383
|
+
const contained = pattern && preset ? shouldWrapInCard(pattern, preset, theme) : false;
|
|
369
384
|
const components = pattern?.components || [];
|
|
370
385
|
const patternMeta = {
|
|
371
386
|
patternId,
|
|
@@ -377,9 +392,9 @@ function buildPatternNode(patternId, alias, resolved, wiring, recipe, density, l
|
|
|
377
392
|
code: preset?.code ? { imports: preset.code.imports, example: preset.code.example } : null,
|
|
378
393
|
components
|
|
379
394
|
};
|
|
380
|
-
const card = contained && !isStandalone && pattern ? buildCardWrapping(pattern,
|
|
395
|
+
const card = contained && !isStandalone && pattern ? buildCardWrapping(pattern, theme) : null;
|
|
381
396
|
const wireProps = wiring?.props[alias] || wiring?.props[patternId] || null;
|
|
382
|
-
const visualEffects =
|
|
397
|
+
const visualEffects = theme && pattern ? resolveVisualEffects(theme, pattern) : null;
|
|
383
398
|
return {
|
|
384
399
|
type: "pattern",
|
|
385
400
|
id: alias,
|
|
@@ -392,16 +407,16 @@ function buildPatternNode(patternId, alias, resolved, wiring, recipe, density, l
|
|
|
392
407
|
...layer ? { layer } : {}
|
|
393
408
|
};
|
|
394
409
|
}
|
|
395
|
-
function buildPageIR(page, resolvedPatterns, wiring,
|
|
410
|
+
function buildPageIR(page, resolvedPatterns, wiring, theme, density, layer) {
|
|
396
411
|
const children = [];
|
|
397
412
|
for (const item of page.layout) {
|
|
398
413
|
if (typeof item === "string") {
|
|
399
414
|
const resolved = resolvedPatterns.get(item);
|
|
400
|
-
children.push(buildPatternNode(item, item, resolved, wiring,
|
|
415
|
+
children.push(buildPatternNode(item, item, resolved, wiring, theme, density, layer));
|
|
401
416
|
} else if (isPatternRef2(item)) {
|
|
402
417
|
const alias = item.as || item.pattern;
|
|
403
418
|
const resolved = resolvedPatterns.get(alias) || resolvedPatterns.get(item.pattern);
|
|
404
|
-
children.push(buildPatternNode(item.pattern, alias, resolved, wiring,
|
|
419
|
+
children.push(buildPatternNode(item.pattern, alias, resolved, wiring, theme, density, layer));
|
|
405
420
|
} else if (isColumnLayout2(item)) {
|
|
406
421
|
const cols = item.cols;
|
|
407
422
|
const breakpoint = item.at || null;
|
|
@@ -416,7 +431,7 @@ function buildPageIR(page, resolvedPatterns, wiring, recipe, density, layer) {
|
|
|
416
431
|
const gridChildren = [];
|
|
417
432
|
for (const col of cols) {
|
|
418
433
|
const resolved = resolvedPatterns.get(col);
|
|
419
|
-
gridChildren.push(buildPatternNode(col, col, resolved, wiring,
|
|
434
|
+
gridChildren.push(buildPatternNode(col, col, resolved, wiring, theme, density, layer));
|
|
420
435
|
}
|
|
421
436
|
const totalCols = normalizedSpans ? Object.values(normalizedSpans).reduce((a, b) => a + b, 0) : cols.length;
|
|
422
437
|
const breakpoints = item.breakpoints?.map((bp) => ({ at: bp.at, cols: bp.cols })) || null;
|
|
@@ -461,10 +476,15 @@ async function runPipeline(essence, options) {
|
|
|
461
476
|
throw new Error(`Invalid essence: ${validation.errors.join(", ")}`);
|
|
462
477
|
}
|
|
463
478
|
const effectiveEssence = isV32(essence) ? essence : migrateV2ToV3(essence);
|
|
464
|
-
const resolver =
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
479
|
+
const resolver = options.resolver ?? (() => {
|
|
480
|
+
if (!options.contentRoot) {
|
|
481
|
+
throw new Error("Pipeline options must include either a contentRoot or a resolver.");
|
|
482
|
+
}
|
|
483
|
+
return createResolver({
|
|
484
|
+
contentRoot: options.contentRoot,
|
|
485
|
+
overridePaths: options.overridePaths
|
|
486
|
+
});
|
|
487
|
+
})();
|
|
468
488
|
const resolved = await resolveEssence(effectiveEssence, resolver);
|
|
469
489
|
const layer = resolved.isV3Source ? "blueprint" : void 0;
|
|
470
490
|
const pageNodes = [];
|
|
@@ -473,7 +493,7 @@ async function runPipeline(essence, options) {
|
|
|
473
493
|
rp.page,
|
|
474
494
|
rp.patterns,
|
|
475
495
|
rp.wiring,
|
|
476
|
-
resolved.
|
|
496
|
+
resolved.registryTheme,
|
|
477
497
|
{ gap: resolved.density.gap },
|
|
478
498
|
layer
|
|
479
499
|
);
|
|
@@ -557,14 +577,853 @@ function validateIR(root) {
|
|
|
557
577
|
});
|
|
558
578
|
return errors;
|
|
559
579
|
}
|
|
580
|
+
|
|
581
|
+
// src/packs.ts
|
|
582
|
+
import { isV3 as isV33, migrateV2ToV3 as migrateV2ToV32 } from "@decantr/essence-spec";
|
|
583
|
+
var EXECUTION_PACK_SCHEMA_URLS = {
|
|
584
|
+
scaffold: "https://decantr.ai/schemas/scaffold-pack.v1.json",
|
|
585
|
+
section: "https://decantr.ai/schemas/section-pack.v1.json",
|
|
586
|
+
page: "https://decantr.ai/schemas/page-pack.v1.json",
|
|
587
|
+
mutation: "https://decantr.ai/schemas/mutation-pack.v1.json",
|
|
588
|
+
review: "https://decantr.ai/schemas/review-pack.v1.json"
|
|
589
|
+
};
|
|
590
|
+
var PACK_MANIFEST_SCHEMA_URL = "https://decantr.ai/schemas/pack-manifest.v1.json";
|
|
591
|
+
var EXECUTION_PACK_BUNDLE_SCHEMA_URL = "https://decantr.ai/schemas/execution-pack-bundle.v1.json";
|
|
592
|
+
var SELECTED_EXECUTION_PACK_SCHEMA_URL = "https://decantr.ai/schemas/selected-execution-pack.v1.json";
|
|
593
|
+
var DEFAULT_TARGET = {
|
|
594
|
+
platform: "web",
|
|
595
|
+
framework: null,
|
|
596
|
+
runtime: null,
|
|
597
|
+
adapter: "generic-web"
|
|
598
|
+
};
|
|
599
|
+
var DEFAULT_TOKEN_BUDGET = {
|
|
600
|
+
target: 1400,
|
|
601
|
+
max: 2200,
|
|
602
|
+
strategy: [
|
|
603
|
+
"Prefer route summaries over repeated prose.",
|
|
604
|
+
"Use compact vocabulary lists instead of large reference tables.",
|
|
605
|
+
"Include only task-relevant examples and checks."
|
|
606
|
+
]
|
|
607
|
+
};
|
|
608
|
+
var DEFAULT_SUCCESS_CHECKS = [
|
|
609
|
+
{
|
|
610
|
+
id: "route-topology",
|
|
611
|
+
label: "Routes and page IDs match the compiled topology.",
|
|
612
|
+
severity: "error"
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
id: "shell-consistency",
|
|
616
|
+
label: "The declared shell contract is preserved unless the task explicitly mutates it.",
|
|
617
|
+
severity: "error"
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
id: "theme-consistency",
|
|
621
|
+
label: "Theme identity and mode remain consistent across scaffolded routes.",
|
|
622
|
+
severity: "warn"
|
|
623
|
+
}
|
|
624
|
+
];
|
|
625
|
+
var DEFAULT_SECTION_SUCCESS_CHECKS = [
|
|
626
|
+
{
|
|
627
|
+
id: "section-route-coherence",
|
|
628
|
+
label: "Section pages and routes remain coherent with the compiled topology.",
|
|
629
|
+
severity: "error"
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
id: "section-shell-consistency",
|
|
633
|
+
label: "The section shell contract stays consistent across its routes.",
|
|
634
|
+
severity: "error"
|
|
635
|
+
},
|
|
636
|
+
{
|
|
637
|
+
id: "section-pattern-coverage",
|
|
638
|
+
label: "Primary section patterns are represented without adding off-contract filler sections.",
|
|
639
|
+
severity: "warn"
|
|
640
|
+
}
|
|
641
|
+
];
|
|
642
|
+
var DEFAULT_PAGE_SUCCESS_CHECKS = [
|
|
643
|
+
{
|
|
644
|
+
id: "page-route-contract",
|
|
645
|
+
label: "The page keeps the compiled route, shell, and section contract intact.",
|
|
646
|
+
severity: "error"
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
id: "page-pattern-contract",
|
|
650
|
+
label: "The page preserves its primary compiled patterns instead of drifting into unrelated layouts.",
|
|
651
|
+
severity: "error"
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
id: "page-state-contract",
|
|
655
|
+
label: "Any declared wiring signals remain coherent with the rendered page structure.",
|
|
656
|
+
severity: "warn"
|
|
657
|
+
}
|
|
658
|
+
];
|
|
659
|
+
var DEFAULT_MUTATION_SUCCESS_CHECKS = {
|
|
660
|
+
"add-page": [
|
|
661
|
+
{
|
|
662
|
+
id: "mutation-essence-first",
|
|
663
|
+
label: "New pages are declared in the essence before any code generation begins.",
|
|
664
|
+
severity: "error"
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
id: "mutation-shell-contract",
|
|
668
|
+
label: "New routes inherit an existing shell and section contract unless the essence changes first.",
|
|
669
|
+
severity: "error"
|
|
670
|
+
},
|
|
671
|
+
{
|
|
672
|
+
id: "mutation-refresh",
|
|
673
|
+
label: "Refresh compiled packs after the mutation so downstream tasks read current topology.",
|
|
674
|
+
severity: "warn"
|
|
675
|
+
}
|
|
676
|
+
],
|
|
677
|
+
modify: [
|
|
678
|
+
{
|
|
679
|
+
id: "mutation-existing-topology",
|
|
680
|
+
label: "Modified routes remain coherent with the compiled topology unless the essence changes first.",
|
|
681
|
+
severity: "error"
|
|
682
|
+
},
|
|
683
|
+
{
|
|
684
|
+
id: "mutation-theme-contract",
|
|
685
|
+
label: "Theme, shell, and page identity stay aligned with the current contract during edits.",
|
|
686
|
+
severity: "error"
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
id: "mutation-page-pack-first",
|
|
690
|
+
label: "Route-local edits should start from the compiled page pack rather than improvised structure.",
|
|
691
|
+
severity: "warn"
|
|
692
|
+
}
|
|
693
|
+
]
|
|
694
|
+
};
|
|
695
|
+
var DEFAULT_REVIEW_SUCCESS_CHECKS = [
|
|
696
|
+
{
|
|
697
|
+
id: "review-contract-baseline",
|
|
698
|
+
label: "Review findings should use the compiled route, shell, and theme contract as the baseline.",
|
|
699
|
+
severity: "error"
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
id: "review-evidence",
|
|
703
|
+
label: "Each critique finding should cite concrete evidence from the generated workspace.",
|
|
704
|
+
severity: "error"
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
id: "review-remediation",
|
|
708
|
+
label: "Suggested fixes should point back to code changes or essence updates when contract drift exists.",
|
|
709
|
+
severity: "warn"
|
|
710
|
+
}
|
|
711
|
+
];
|
|
712
|
+
var DEFAULT_REVIEW_ANTI_PATTERNS = [
|
|
713
|
+
{
|
|
714
|
+
id: "inline-styles",
|
|
715
|
+
summary: "Avoid inline style literals as the primary styling path.",
|
|
716
|
+
guidance: "Move visual styling into tokens.css and treatments.css instead of component-local style objects."
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
id: "hardcoded-colors",
|
|
720
|
+
summary: "Avoid hardcoded color literals.",
|
|
721
|
+
guidance: "Use CSS variables and theme decorators instead of hex, rgb, or hsl values."
|
|
722
|
+
},
|
|
723
|
+
{
|
|
724
|
+
id: "utility-framework-leakage",
|
|
725
|
+
summary: "Avoid utility-framework leakage as the primary design language.",
|
|
726
|
+
guidance: "Prefer compiled Decantr treatments and contract vocabulary over ad hoc utility class stacks."
|
|
727
|
+
}
|
|
728
|
+
];
|
|
729
|
+
function collectPatternIds(page) {
|
|
730
|
+
const patternIds = [];
|
|
731
|
+
walkIR(page, (node) => {
|
|
732
|
+
if (node.type !== "pattern") return;
|
|
733
|
+
const patternNode = node;
|
|
734
|
+
patternIds.push(patternNode.pattern.patternId);
|
|
735
|
+
});
|
|
736
|
+
return [...new Set(patternIds)];
|
|
737
|
+
}
|
|
738
|
+
function summarizeRoutes(appNode) {
|
|
739
|
+
return appNode.children.map((page) => {
|
|
740
|
+
const pageNode = page;
|
|
741
|
+
const route = appNode.routes.find((entry) => entry.pageId === pageNode.pageId);
|
|
742
|
+
return {
|
|
743
|
+
pageId: pageNode.pageId,
|
|
744
|
+
path: route?.path || `/${pageNode.pageId}`,
|
|
745
|
+
patternIds: collectPatternIds(pageNode)
|
|
746
|
+
};
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
function summarizeSectionRoutes(appNode, input) {
|
|
750
|
+
return summarizeRoutes(appNode).filter((route) => input.pageIds.includes(route.pageId));
|
|
751
|
+
}
|
|
752
|
+
function summarizePageRoute(appNode, pageId) {
|
|
753
|
+
return summarizeRoutes(appNode).find((route) => route.pageId === pageId) ?? null;
|
|
754
|
+
}
|
|
755
|
+
function findPageNode(appNode, pageId) {
|
|
756
|
+
const page = appNode.children.find((node) => node.pageId === pageId);
|
|
757
|
+
return page ? page : null;
|
|
758
|
+
}
|
|
759
|
+
function collectPagePatterns(page) {
|
|
760
|
+
const patterns = [];
|
|
761
|
+
walkIR(page, (node) => {
|
|
762
|
+
if (node.type !== "pattern") return;
|
|
763
|
+
const patternNode = node;
|
|
764
|
+
patterns.push({
|
|
765
|
+
id: patternNode.pattern.patternId,
|
|
766
|
+
alias: patternNode.pattern.alias,
|
|
767
|
+
preset: patternNode.pattern.preset,
|
|
768
|
+
layout: patternNode.pattern.layout
|
|
769
|
+
});
|
|
770
|
+
});
|
|
771
|
+
return patterns;
|
|
772
|
+
}
|
|
773
|
+
function mergeTokenBudget(overrides) {
|
|
774
|
+
return {
|
|
775
|
+
target: overrides?.target ?? DEFAULT_TOKEN_BUDGET.target,
|
|
776
|
+
max: overrides?.max ?? DEFAULT_TOKEN_BUDGET.max,
|
|
777
|
+
strategy: overrides?.strategy ?? DEFAULT_TOKEN_BUDGET.strategy
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
function renderList(title, entries) {
|
|
781
|
+
if (entries.length === 0) return [];
|
|
782
|
+
return [title, ...entries.map((entry) => `- ${entry}`), ""];
|
|
783
|
+
}
|
|
784
|
+
function renderExecutionPackMarkdown(pack) {
|
|
785
|
+
const lines = [];
|
|
786
|
+
lines.push(`# ${pack.packType.charAt(0).toUpperCase()}${pack.packType.slice(1)} Pack`);
|
|
787
|
+
lines.push("");
|
|
788
|
+
lines.push(`**Objective:** ${pack.objective}`);
|
|
789
|
+
lines.push(`**Target:** ${pack.target.adapter}${pack.target.framework ? ` (${pack.target.framework})` : ""}`);
|
|
790
|
+
lines.push(`**Scope:** pages=${pack.scope.pageIds.join(", ") || "none"} | patterns=${pack.scope.patternIds.join(", ") || "none"}`);
|
|
791
|
+
lines.push("");
|
|
792
|
+
if (pack.packType === "scaffold") {
|
|
793
|
+
const scaffoldPack = pack;
|
|
794
|
+
lines.push("## Scaffold Contract");
|
|
795
|
+
lines.push(`- Shell: ${scaffoldPack.data.shell}`);
|
|
796
|
+
lines.push(`- Theme: ${scaffoldPack.data.theme.id} (${scaffoldPack.data.theme.mode})`);
|
|
797
|
+
lines.push(`- Routing: ${scaffoldPack.data.routing}`);
|
|
798
|
+
if (scaffoldPack.data.features.length > 0) {
|
|
799
|
+
lines.push(`- Features: ${scaffoldPack.data.features.join(", ")}`);
|
|
800
|
+
}
|
|
801
|
+
lines.push("");
|
|
802
|
+
lines.push("## Route Plan");
|
|
803
|
+
for (const route of scaffoldPack.data.routes) {
|
|
804
|
+
const patterns = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
805
|
+
lines.push(`- ${route.path} -> ${route.pageId} [${patterns}]`);
|
|
806
|
+
}
|
|
807
|
+
lines.push("");
|
|
808
|
+
}
|
|
809
|
+
if (pack.packType === "section") {
|
|
810
|
+
const sectionPack = pack;
|
|
811
|
+
lines.push("## Section Contract");
|
|
812
|
+
lines.push(`- Section: ${sectionPack.data.sectionId}`);
|
|
813
|
+
lines.push(`- Role: ${sectionPack.data.role}`);
|
|
814
|
+
lines.push(`- Shell: ${sectionPack.data.shell}`);
|
|
815
|
+
lines.push(`- Theme: ${sectionPack.data.theme.id} (${sectionPack.data.theme.mode})`);
|
|
816
|
+
if (sectionPack.data.features.length > 0) {
|
|
817
|
+
lines.push(`- Features: ${sectionPack.data.features.join(", ")}`);
|
|
818
|
+
}
|
|
819
|
+
if (sectionPack.data.description) {
|
|
820
|
+
lines.push(`- Description: ${sectionPack.data.description}`);
|
|
821
|
+
}
|
|
822
|
+
lines.push("");
|
|
823
|
+
lines.push("## Section Routes");
|
|
824
|
+
for (const route of sectionPack.data.routes) {
|
|
825
|
+
const patterns = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
826
|
+
lines.push(`- ${route.path} -> ${route.pageId} [${patterns}]`);
|
|
827
|
+
}
|
|
828
|
+
lines.push("");
|
|
829
|
+
}
|
|
830
|
+
if (pack.packType === "page") {
|
|
831
|
+
const pagePack = pack;
|
|
832
|
+
lines.push("## Page Contract");
|
|
833
|
+
lines.push(`- Page: ${pagePack.data.pageId}`);
|
|
834
|
+
lines.push(`- Path: ${pagePack.data.path}`);
|
|
835
|
+
lines.push(`- Shell: ${pagePack.data.shell}`);
|
|
836
|
+
if (pagePack.data.sectionId) {
|
|
837
|
+
const role = pagePack.data.sectionRole ? ` (${pagePack.data.sectionRole})` : "";
|
|
838
|
+
lines.push(`- Section: ${pagePack.data.sectionId}${role}`);
|
|
839
|
+
}
|
|
840
|
+
lines.push(`- Theme: ${pagePack.data.theme.id} (${pagePack.data.theme.mode})`);
|
|
841
|
+
if (pagePack.data.features.length > 0) {
|
|
842
|
+
lines.push(`- Features: ${pagePack.data.features.join(", ")}`);
|
|
843
|
+
}
|
|
844
|
+
if (pagePack.data.surface) {
|
|
845
|
+
lines.push(`- Surface: ${pagePack.data.surface}`);
|
|
846
|
+
}
|
|
847
|
+
lines.push("");
|
|
848
|
+
lines.push("## Page Patterns");
|
|
849
|
+
for (const pattern of pagePack.data.patterns) {
|
|
850
|
+
lines.push(`- ${pattern.alias} -> ${pattern.id} [${pattern.layout}${pattern.preset ? ` | ${pattern.preset}` : ""}]`);
|
|
851
|
+
}
|
|
852
|
+
lines.push("");
|
|
853
|
+
if (pagePack.data.wiringSignals.length > 0) {
|
|
854
|
+
lines.push("## Wiring Signals");
|
|
855
|
+
for (const signal of pagePack.data.wiringSignals) {
|
|
856
|
+
lines.push(`- ${signal}`);
|
|
857
|
+
}
|
|
858
|
+
lines.push("");
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
if (pack.packType === "mutation") {
|
|
862
|
+
const mutationPack = pack;
|
|
863
|
+
lines.push("## Mutation Contract");
|
|
864
|
+
lines.push(`- Operation: ${mutationPack.data.mutationType}`);
|
|
865
|
+
lines.push(`- Shell: ${mutationPack.data.shell}`);
|
|
866
|
+
lines.push(`- Theme: ${mutationPack.data.theme.id} (${mutationPack.data.theme.mode})`);
|
|
867
|
+
lines.push(`- Routing: ${mutationPack.data.routing}`);
|
|
868
|
+
if (mutationPack.data.features.length > 0) {
|
|
869
|
+
lines.push(`- Features: ${mutationPack.data.features.join(", ")}`);
|
|
870
|
+
}
|
|
871
|
+
lines.push("");
|
|
872
|
+
lines.push("## Route Topology");
|
|
873
|
+
for (const route of mutationPack.data.routes) {
|
|
874
|
+
const patterns = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
875
|
+
lines.push(`- ${route.path} -> ${route.pageId} [${patterns}]`);
|
|
876
|
+
}
|
|
877
|
+
lines.push("");
|
|
878
|
+
if (mutationPack.data.workflow.length > 0) {
|
|
879
|
+
lines.push("## Workflow");
|
|
880
|
+
for (const step of mutationPack.data.workflow) {
|
|
881
|
+
lines.push(`- ${step}`);
|
|
882
|
+
}
|
|
883
|
+
lines.push("");
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
if (pack.packType === "review") {
|
|
887
|
+
const reviewPack = pack;
|
|
888
|
+
lines.push("## Review Contract");
|
|
889
|
+
lines.push(`- Review Type: ${reviewPack.data.reviewType}`);
|
|
890
|
+
lines.push(`- Shell: ${reviewPack.data.shell}`);
|
|
891
|
+
lines.push(`- Theme: ${reviewPack.data.theme.id} (${reviewPack.data.theme.mode})`);
|
|
892
|
+
lines.push(`- Routing: ${reviewPack.data.routing}`);
|
|
893
|
+
if (reviewPack.data.features.length > 0) {
|
|
894
|
+
lines.push(`- Features: ${reviewPack.data.features.join(", ")}`);
|
|
895
|
+
}
|
|
896
|
+
lines.push("");
|
|
897
|
+
lines.push("## Review Topology");
|
|
898
|
+
for (const route of reviewPack.data.routes) {
|
|
899
|
+
const patterns = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
900
|
+
lines.push(`- ${route.path} -> ${route.pageId} [${patterns}]`);
|
|
901
|
+
}
|
|
902
|
+
lines.push("");
|
|
903
|
+
if (reviewPack.data.focusAreas.length > 0) {
|
|
904
|
+
lines.push("## Focus Areas");
|
|
905
|
+
for (const focusArea of reviewPack.data.focusAreas) {
|
|
906
|
+
lines.push(`- ${focusArea}`);
|
|
907
|
+
}
|
|
908
|
+
lines.push("");
|
|
909
|
+
}
|
|
910
|
+
if (reviewPack.data.workflow.length > 0) {
|
|
911
|
+
lines.push("## Review Workflow");
|
|
912
|
+
for (const step of reviewPack.data.workflow) {
|
|
913
|
+
lines.push(`- ${step}`);
|
|
914
|
+
}
|
|
915
|
+
lines.push("");
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
lines.push("## Required Setup");
|
|
919
|
+
if (pack.requiredSetup.length === 0) {
|
|
920
|
+
lines.push("- None declared.");
|
|
921
|
+
} else {
|
|
922
|
+
lines.push(...pack.requiredSetup.map((entry) => `- ${entry}`));
|
|
923
|
+
}
|
|
924
|
+
lines.push("");
|
|
925
|
+
lines.push("## Allowed Vocabulary");
|
|
926
|
+
if (pack.allowedVocabulary.length === 0) {
|
|
927
|
+
lines.push("- None declared.");
|
|
928
|
+
} else {
|
|
929
|
+
lines.push(...pack.allowedVocabulary.map((entry) => `- ${entry}`));
|
|
930
|
+
}
|
|
931
|
+
lines.push("");
|
|
932
|
+
lines.push(...renderList("## Success Checks", pack.successChecks.map((entry) => `${entry.label} [${entry.severity}]`)));
|
|
933
|
+
lines.push(...renderList("## Anti-Patterns", pack.antiPatterns.map((entry) => `${entry.summary}: ${entry.guidance}`)));
|
|
934
|
+
lines.push(...renderList("## Examples", pack.examples.map((entry) => `${entry.label} (${entry.language})`)));
|
|
935
|
+
lines.push("## Token Budget");
|
|
936
|
+
lines.push(`- Target: ${pack.tokenBudget.target}`);
|
|
937
|
+
lines.push(`- Max: ${pack.tokenBudget.max}`);
|
|
938
|
+
lines.push(...pack.tokenBudget.strategy.map((entry) => `- ${entry}`));
|
|
939
|
+
lines.push("");
|
|
940
|
+
return lines.join("\n").trimEnd() + "\n";
|
|
941
|
+
}
|
|
942
|
+
function resolvePackAdapter(target, platformType) {
|
|
943
|
+
if (target === "react" && platformType === "spa") return "react-vite";
|
|
944
|
+
if (target === "react") return "react-web";
|
|
945
|
+
if (target === "vue" && platformType === "spa") return "vue-vite";
|
|
946
|
+
if (target === "svelte" && platformType === "spa") return "sveltekit";
|
|
947
|
+
if (target) return target;
|
|
948
|
+
return "generic-web";
|
|
949
|
+
}
|
|
950
|
+
function listPackSections(essence) {
|
|
951
|
+
const declaredSections = essence.blueprint.sections;
|
|
952
|
+
if (declaredSections && declaredSections.length > 0) {
|
|
953
|
+
return declaredSections.map((section) => ({
|
|
954
|
+
id: section.id,
|
|
955
|
+
role: section.role,
|
|
956
|
+
shell: section.shell,
|
|
957
|
+
description: section.description,
|
|
958
|
+
features: section.features,
|
|
959
|
+
pageIds: section.pages.map((page) => page.id)
|
|
960
|
+
}));
|
|
961
|
+
}
|
|
962
|
+
const pages = essence.blueprint.pages ?? [{ id: "home", layout: ["hero"] }];
|
|
963
|
+
return [{
|
|
964
|
+
id: essence.meta.archetype || "default",
|
|
965
|
+
role: "primary",
|
|
966
|
+
shell: essence.blueprint.shell ?? "sidebar-main",
|
|
967
|
+
description: `${essence.meta.archetype || "Application"} section`,
|
|
968
|
+
features: essence.blueprint.features || [],
|
|
969
|
+
pageIds: pages.map((page) => page.id)
|
|
970
|
+
}];
|
|
971
|
+
}
|
|
972
|
+
function listPackPages(essence) {
|
|
973
|
+
const declaredSections = essence.blueprint.sections;
|
|
974
|
+
if (declaredSections && declaredSections.length > 0) {
|
|
975
|
+
return declaredSections.flatMap(
|
|
976
|
+
(section) => section.pages.map((page) => ({
|
|
977
|
+
pageId: page.id,
|
|
978
|
+
shell: page.shell_override ?? section.shell,
|
|
979
|
+
sectionId: section.id,
|
|
980
|
+
sectionRole: section.role,
|
|
981
|
+
features: section.features
|
|
982
|
+
}))
|
|
983
|
+
);
|
|
984
|
+
}
|
|
985
|
+
const pages = essence.blueprint.pages ?? [{ id: "home", layout: ["hero"] }];
|
|
986
|
+
const defaultShell = essence.blueprint.shell ?? "sidebar-main";
|
|
987
|
+
return pages.map((page) => ({
|
|
988
|
+
pageId: page.id,
|
|
989
|
+
shell: page.shell_override ?? defaultShell,
|
|
990
|
+
sectionId: essence.meta.archetype || "default",
|
|
991
|
+
sectionRole: "primary",
|
|
992
|
+
features: essence.blueprint.features || []
|
|
993
|
+
}));
|
|
994
|
+
}
|
|
995
|
+
function buildScaffoldPack(appNode, options = {}) {
|
|
996
|
+
const routes = summarizeRoutes(appNode);
|
|
997
|
+
const scopePatternIds = [...new Set(routes.flatMap((route) => route.patternIds))];
|
|
998
|
+
const pack = {
|
|
999
|
+
$schema: EXECUTION_PACK_SCHEMA_URLS.scaffold,
|
|
1000
|
+
packVersion: "1.0.0",
|
|
1001
|
+
packType: "scaffold",
|
|
1002
|
+
objective: options.objective ?? `Scaffold the ${appNode.theme.id} app shell and declared routes.`,
|
|
1003
|
+
target: {
|
|
1004
|
+
...DEFAULT_TARGET,
|
|
1005
|
+
...options.target
|
|
1006
|
+
},
|
|
1007
|
+
preset: options.preset ?? null,
|
|
1008
|
+
scope: {
|
|
1009
|
+
appId: appNode.id,
|
|
1010
|
+
pageIds: routes.map((route) => route.pageId),
|
|
1011
|
+
patternIds: scopePatternIds
|
|
1012
|
+
},
|
|
1013
|
+
requiredSetup: options.requiredSetup ?? [
|
|
1014
|
+
"Treat the declared routes as the topology source of truth.",
|
|
1015
|
+
"Preserve the resolved theme and shell contract unless the task explicitly mutates them."
|
|
1016
|
+
],
|
|
1017
|
+
allowedVocabulary: [.../* @__PURE__ */ new Set([
|
|
1018
|
+
appNode.shell.config.type,
|
|
1019
|
+
appNode.theme.id,
|
|
1020
|
+
appNode.theme.mode,
|
|
1021
|
+
...appNode.features,
|
|
1022
|
+
...scopePatternIds
|
|
1023
|
+
])],
|
|
1024
|
+
examples: options.examples ?? [],
|
|
1025
|
+
antiPatterns: options.antiPatterns ?? [],
|
|
1026
|
+
successChecks: options.successChecks ?? DEFAULT_SUCCESS_CHECKS,
|
|
1027
|
+
tokenBudget: mergeTokenBudget(options.tokenBudget),
|
|
1028
|
+
data: {
|
|
1029
|
+
shell: appNode.shell.config.type,
|
|
1030
|
+
theme: {
|
|
1031
|
+
id: appNode.theme.id,
|
|
1032
|
+
mode: appNode.theme.mode,
|
|
1033
|
+
shape: appNode.theme.shape
|
|
1034
|
+
},
|
|
1035
|
+
routing: appNode.routing,
|
|
1036
|
+
features: appNode.features,
|
|
1037
|
+
routes
|
|
1038
|
+
},
|
|
1039
|
+
renderedMarkdown: ""
|
|
1040
|
+
};
|
|
1041
|
+
pack.renderedMarkdown = renderExecutionPackMarkdown(pack);
|
|
1042
|
+
return pack;
|
|
1043
|
+
}
|
|
1044
|
+
function buildSectionPack(appNode, input, options = {}) {
|
|
1045
|
+
const routes = summarizeSectionRoutes(appNode, input);
|
|
1046
|
+
const scopePatternIds = [...new Set(routes.flatMap((route) => route.patternIds))];
|
|
1047
|
+
const pack = {
|
|
1048
|
+
$schema: EXECUTION_PACK_SCHEMA_URLS.section,
|
|
1049
|
+
packVersion: "1.0.0",
|
|
1050
|
+
packType: "section",
|
|
1051
|
+
objective: options.objective ?? `Implement the ${input.id} section using the compiled ${input.shell} shell contract.`,
|
|
1052
|
+
target: {
|
|
1053
|
+
...DEFAULT_TARGET,
|
|
1054
|
+
...options.target
|
|
1055
|
+
},
|
|
1056
|
+
preset: options.preset ?? null,
|
|
1057
|
+
scope: {
|
|
1058
|
+
appId: appNode.id,
|
|
1059
|
+
pageIds: routes.map((route) => route.pageId),
|
|
1060
|
+
patternIds: scopePatternIds
|
|
1061
|
+
},
|
|
1062
|
+
requiredSetup: options.requiredSetup ?? [
|
|
1063
|
+
"Use the declared section routes as the source of truth for this slice of the app.",
|
|
1064
|
+
"Keep the section shell consistent unless the task explicitly changes the shell contract."
|
|
1065
|
+
],
|
|
1066
|
+
allowedVocabulary: [.../* @__PURE__ */ new Set([
|
|
1067
|
+
input.id,
|
|
1068
|
+
input.role,
|
|
1069
|
+
input.shell,
|
|
1070
|
+
appNode.theme.id,
|
|
1071
|
+
appNode.theme.mode,
|
|
1072
|
+
...input.features,
|
|
1073
|
+
...scopePatternIds
|
|
1074
|
+
])],
|
|
1075
|
+
examples: options.examples ?? [],
|
|
1076
|
+
antiPatterns: options.antiPatterns ?? [],
|
|
1077
|
+
successChecks: options.successChecks ?? DEFAULT_SECTION_SUCCESS_CHECKS,
|
|
1078
|
+
tokenBudget: mergeTokenBudget(options.tokenBudget),
|
|
1079
|
+
data: {
|
|
1080
|
+
sectionId: input.id,
|
|
1081
|
+
role: input.role,
|
|
1082
|
+
shell: input.shell,
|
|
1083
|
+
description: input.description,
|
|
1084
|
+
features: input.features,
|
|
1085
|
+
theme: {
|
|
1086
|
+
id: appNode.theme.id,
|
|
1087
|
+
mode: appNode.theme.mode,
|
|
1088
|
+
shape: appNode.theme.shape
|
|
1089
|
+
},
|
|
1090
|
+
routes
|
|
1091
|
+
},
|
|
1092
|
+
renderedMarkdown: ""
|
|
1093
|
+
};
|
|
1094
|
+
pack.renderedMarkdown = renderExecutionPackMarkdown(pack);
|
|
1095
|
+
return pack;
|
|
1096
|
+
}
|
|
1097
|
+
function buildPagePack(appNode, input, options = {}) {
|
|
1098
|
+
const pageNode = findPageNode(appNode, input.pageId);
|
|
1099
|
+
const route = summarizePageRoute(appNode, input.pageId);
|
|
1100
|
+
if (!pageNode || !route) {
|
|
1101
|
+
throw new Error(`Unknown page for page pack: ${input.pageId}`);
|
|
1102
|
+
}
|
|
1103
|
+
const patterns = collectPagePatterns(pageNode);
|
|
1104
|
+
const pack = {
|
|
1105
|
+
$schema: EXECUTION_PACK_SCHEMA_URLS.page,
|
|
1106
|
+
packVersion: "1.0.0",
|
|
1107
|
+
packType: "page",
|
|
1108
|
+
objective: options.objective ?? `Implement the ${input.pageId} route using the compiled page contract.`,
|
|
1109
|
+
target: {
|
|
1110
|
+
...DEFAULT_TARGET,
|
|
1111
|
+
...options.target
|
|
1112
|
+
},
|
|
1113
|
+
preset: options.preset ?? null,
|
|
1114
|
+
scope: {
|
|
1115
|
+
appId: appNode.id,
|
|
1116
|
+
pageIds: [route.pageId],
|
|
1117
|
+
patternIds: [...new Set(patterns.map((pattern) => pattern.id))]
|
|
1118
|
+
},
|
|
1119
|
+
requiredSetup: options.requiredSetup ?? [
|
|
1120
|
+
"Keep the compiled route and shell contract stable for this page.",
|
|
1121
|
+
"Treat the listed page patterns as the primary structure for this route."
|
|
1122
|
+
],
|
|
1123
|
+
allowedVocabulary: [...new Set([
|
|
1124
|
+
input.pageId,
|
|
1125
|
+
input.shell,
|
|
1126
|
+
input.sectionId ?? "",
|
|
1127
|
+
input.sectionRole ?? "",
|
|
1128
|
+
appNode.theme.id,
|
|
1129
|
+
appNode.theme.mode,
|
|
1130
|
+
...input.features,
|
|
1131
|
+
...patterns.flatMap((pattern) => [pattern.id, pattern.alias, pattern.layout])
|
|
1132
|
+
].filter(Boolean))],
|
|
1133
|
+
examples: options.examples ?? [],
|
|
1134
|
+
antiPatterns: options.antiPatterns ?? [],
|
|
1135
|
+
successChecks: options.successChecks ?? DEFAULT_PAGE_SUCCESS_CHECKS,
|
|
1136
|
+
tokenBudget: mergeTokenBudget(options.tokenBudget),
|
|
1137
|
+
data: {
|
|
1138
|
+
pageId: route.pageId,
|
|
1139
|
+
path: route.path,
|
|
1140
|
+
shell: input.shell,
|
|
1141
|
+
sectionId: input.sectionId,
|
|
1142
|
+
sectionRole: input.sectionRole,
|
|
1143
|
+
features: input.features,
|
|
1144
|
+
surface: pageNode.surface,
|
|
1145
|
+
theme: {
|
|
1146
|
+
id: appNode.theme.id,
|
|
1147
|
+
mode: appNode.theme.mode,
|
|
1148
|
+
shape: appNode.theme.shape
|
|
1149
|
+
},
|
|
1150
|
+
wiringSignals: pageNode.wiring?.signals.map((signal) => signal.name) ?? [],
|
|
1151
|
+
patterns
|
|
1152
|
+
},
|
|
1153
|
+
renderedMarkdown: ""
|
|
1154
|
+
};
|
|
1155
|
+
pack.renderedMarkdown = renderExecutionPackMarkdown(pack);
|
|
1156
|
+
return pack;
|
|
1157
|
+
}
|
|
1158
|
+
function buildMutationPack(appNode, options) {
|
|
1159
|
+
const routes = summarizeRoutes(appNode);
|
|
1160
|
+
const scopePatternIds = [...new Set(routes.flatMap((route) => route.patternIds))];
|
|
1161
|
+
const defaultWorkflow = options.mutationType === "add-page" ? [
|
|
1162
|
+
"Declare the new page in the essence before generating code.",
|
|
1163
|
+
"Refresh Decantr context so section and page packs include the new route.",
|
|
1164
|
+
"Read the relevant section pack and new page pack before implementation."
|
|
1165
|
+
] : [
|
|
1166
|
+
"Read the page pack for the route you are modifying first.",
|
|
1167
|
+
"Stop and update the essence before changing route, shell, or pattern contracts.",
|
|
1168
|
+
"Validate and check drift after code changes complete."
|
|
1169
|
+
];
|
|
1170
|
+
const pack = {
|
|
1171
|
+
$schema: EXECUTION_PACK_SCHEMA_URLS.mutation,
|
|
1172
|
+
packVersion: "1.0.0",
|
|
1173
|
+
packType: "mutation",
|
|
1174
|
+
objective: options.objective ?? `Execute the ${options.mutationType} workflow against the compiled app contract.`,
|
|
1175
|
+
target: {
|
|
1176
|
+
...DEFAULT_TARGET,
|
|
1177
|
+
...options.target
|
|
1178
|
+
},
|
|
1179
|
+
preset: options.preset ?? null,
|
|
1180
|
+
scope: {
|
|
1181
|
+
appId: appNode.id,
|
|
1182
|
+
pageIds: routes.map((route) => route.pageId),
|
|
1183
|
+
patternIds: scopePatternIds
|
|
1184
|
+
},
|
|
1185
|
+
requiredSetup: options.requiredSetup ?? [
|
|
1186
|
+
"Treat the compiled topology as the source of truth until the essence changes.",
|
|
1187
|
+
"Refresh Decantr context after structural mutations so downstream tasks read current packs."
|
|
1188
|
+
],
|
|
1189
|
+
allowedVocabulary: [.../* @__PURE__ */ new Set([
|
|
1190
|
+
options.mutationType,
|
|
1191
|
+
appNode.shell.config.type,
|
|
1192
|
+
appNode.theme.id,
|
|
1193
|
+
appNode.theme.mode,
|
|
1194
|
+
...appNode.features,
|
|
1195
|
+
...scopePatternIds
|
|
1196
|
+
])],
|
|
1197
|
+
examples: options.examples ?? [],
|
|
1198
|
+
antiPatterns: options.antiPatterns ?? [],
|
|
1199
|
+
successChecks: options.successChecks ?? DEFAULT_MUTATION_SUCCESS_CHECKS[options.mutationType],
|
|
1200
|
+
tokenBudget: mergeTokenBudget(options.tokenBudget),
|
|
1201
|
+
data: {
|
|
1202
|
+
mutationType: options.mutationType,
|
|
1203
|
+
shell: appNode.shell.config.type,
|
|
1204
|
+
theme: {
|
|
1205
|
+
id: appNode.theme.id,
|
|
1206
|
+
mode: appNode.theme.mode,
|
|
1207
|
+
shape: appNode.theme.shape
|
|
1208
|
+
},
|
|
1209
|
+
routing: appNode.routing,
|
|
1210
|
+
features: appNode.features,
|
|
1211
|
+
routes,
|
|
1212
|
+
workflow: options.workflow ?? defaultWorkflow
|
|
1213
|
+
},
|
|
1214
|
+
renderedMarkdown: ""
|
|
1215
|
+
};
|
|
1216
|
+
pack.renderedMarkdown = renderExecutionPackMarkdown(pack);
|
|
1217
|
+
return pack;
|
|
1218
|
+
}
|
|
1219
|
+
function buildReviewPack(appNode, options = {}) {
|
|
1220
|
+
const routes = summarizeRoutes(appNode);
|
|
1221
|
+
const scopePatternIds = [...new Set(routes.flatMap((route) => route.patternIds))];
|
|
1222
|
+
const reviewType = options.reviewType ?? "app";
|
|
1223
|
+
const focusAreas = options.focusAreas ?? [
|
|
1224
|
+
"route-topology",
|
|
1225
|
+
"theme-consistency",
|
|
1226
|
+
"treatment-usage",
|
|
1227
|
+
"accessibility",
|
|
1228
|
+
"responsive-design"
|
|
1229
|
+
];
|
|
1230
|
+
const workflow = options.workflow ?? [
|
|
1231
|
+
"Read the scaffold pack and page packs before evaluating generated code.",
|
|
1232
|
+
"Compare findings against the compiled route, shell, and theme contract first.",
|
|
1233
|
+
"Escalate contract drift into essence updates when the requested output intentionally changes topology or theme identity."
|
|
1234
|
+
];
|
|
1235
|
+
const pack = {
|
|
1236
|
+
$schema: EXECUTION_PACK_SCHEMA_URLS.review,
|
|
1237
|
+
packVersion: "1.0.0",
|
|
1238
|
+
packType: "review",
|
|
1239
|
+
objective: options.objective ?? "Review generated output against the compiled Decantr contract.",
|
|
1240
|
+
target: {
|
|
1241
|
+
...DEFAULT_TARGET,
|
|
1242
|
+
...options.target
|
|
1243
|
+
},
|
|
1244
|
+
preset: options.preset ?? null,
|
|
1245
|
+
scope: {
|
|
1246
|
+
appId: appNode.id,
|
|
1247
|
+
pageIds: routes.map((route) => route.pageId),
|
|
1248
|
+
patternIds: scopePatternIds
|
|
1249
|
+
},
|
|
1250
|
+
requiredSetup: options.requiredSetup ?? [
|
|
1251
|
+
"Read the compiled scaffold and route packs before reviewing code.",
|
|
1252
|
+
"Use concrete evidence from the workspace instead of purely stylistic intuition."
|
|
1253
|
+
],
|
|
1254
|
+
allowedVocabulary: [.../* @__PURE__ */ new Set([
|
|
1255
|
+
reviewType,
|
|
1256
|
+
appNode.shell.config.type,
|
|
1257
|
+
appNode.theme.id,
|
|
1258
|
+
appNode.theme.mode,
|
|
1259
|
+
...appNode.features,
|
|
1260
|
+
...scopePatternIds,
|
|
1261
|
+
...focusAreas
|
|
1262
|
+
])],
|
|
1263
|
+
examples: options.examples ?? [],
|
|
1264
|
+
antiPatterns: options.antiPatterns ?? DEFAULT_REVIEW_ANTI_PATTERNS,
|
|
1265
|
+
successChecks: options.successChecks ?? DEFAULT_REVIEW_SUCCESS_CHECKS,
|
|
1266
|
+
tokenBudget: mergeTokenBudget(options.tokenBudget),
|
|
1267
|
+
data: {
|
|
1268
|
+
reviewType,
|
|
1269
|
+
shell: appNode.shell.config.type,
|
|
1270
|
+
theme: {
|
|
1271
|
+
id: appNode.theme.id,
|
|
1272
|
+
mode: appNode.theme.mode,
|
|
1273
|
+
shape: appNode.theme.shape
|
|
1274
|
+
},
|
|
1275
|
+
routing: appNode.routing,
|
|
1276
|
+
features: appNode.features,
|
|
1277
|
+
routes,
|
|
1278
|
+
focusAreas,
|
|
1279
|
+
workflow
|
|
1280
|
+
},
|
|
1281
|
+
renderedMarkdown: ""
|
|
1282
|
+
};
|
|
1283
|
+
pack.renderedMarkdown = renderExecutionPackMarkdown(pack);
|
|
1284
|
+
return pack;
|
|
1285
|
+
}
|
|
1286
|
+
async function compileExecutionPackBundle(essence, options = {}) {
|
|
1287
|
+
const effectiveEssence = isV33(essence) ? essence : migrateV2ToV32(essence);
|
|
1288
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1289
|
+
const sharedTarget = {
|
|
1290
|
+
framework: effectiveEssence.meta.target || null,
|
|
1291
|
+
runtime: effectiveEssence.meta.platform.type || null,
|
|
1292
|
+
adapter: resolvePackAdapter(effectiveEssence.meta.target, effectiveEssence.meta.platform.type)
|
|
1293
|
+
};
|
|
1294
|
+
const pipeline = await runPipeline(essence, {
|
|
1295
|
+
contentRoot: options.contentRoot,
|
|
1296
|
+
overridePaths: options.overridePaths,
|
|
1297
|
+
resolver: options.resolver
|
|
1298
|
+
});
|
|
1299
|
+
const scaffold = buildScaffoldPack(pipeline.ir, {
|
|
1300
|
+
target: sharedTarget
|
|
1301
|
+
});
|
|
1302
|
+
const review = buildReviewPack(pipeline.ir, {
|
|
1303
|
+
target: sharedTarget
|
|
1304
|
+
});
|
|
1305
|
+
const sections = listPackSections(effectiveEssence).map((section) => buildSectionPack(pipeline.ir, section, {
|
|
1306
|
+
target: sharedTarget
|
|
1307
|
+
}));
|
|
1308
|
+
const pages = listPackPages(effectiveEssence).map((page) => buildPagePack(pipeline.ir, page, {
|
|
1309
|
+
target: sharedTarget
|
|
1310
|
+
}));
|
|
1311
|
+
const mutations = ["add-page", "modify"].map((mutationType) => buildMutationPack(pipeline.ir, {
|
|
1312
|
+
mutationType,
|
|
1313
|
+
target: sharedTarget
|
|
1314
|
+
}));
|
|
1315
|
+
const manifest = {
|
|
1316
|
+
$schema: PACK_MANIFEST_SCHEMA_URL,
|
|
1317
|
+
version: "1.0.0",
|
|
1318
|
+
generatedAt,
|
|
1319
|
+
scaffold: {
|
|
1320
|
+
id: "scaffold",
|
|
1321
|
+
markdown: "scaffold-pack.md",
|
|
1322
|
+
json: "scaffold-pack.json"
|
|
1323
|
+
},
|
|
1324
|
+
review: {
|
|
1325
|
+
id: "review",
|
|
1326
|
+
markdown: "review-pack.md",
|
|
1327
|
+
json: "review-pack.json"
|
|
1328
|
+
},
|
|
1329
|
+
sections: listPackSections(effectiveEssence).map((section) => ({
|
|
1330
|
+
id: section.id,
|
|
1331
|
+
markdown: `section-${section.id}-pack.md`,
|
|
1332
|
+
json: `section-${section.id}-pack.json`,
|
|
1333
|
+
pageIds: section.pageIds
|
|
1334
|
+
})),
|
|
1335
|
+
pages: listPackPages(effectiveEssence).map((page) => ({
|
|
1336
|
+
id: page.pageId,
|
|
1337
|
+
markdown: `page-${page.pageId}-pack.md`,
|
|
1338
|
+
json: `page-${page.pageId}-pack.json`,
|
|
1339
|
+
sectionId: page.sectionId,
|
|
1340
|
+
sectionRole: page.sectionRole
|
|
1341
|
+
})),
|
|
1342
|
+
mutations: ["add-page", "modify"].map((mutationType) => ({
|
|
1343
|
+
id: mutationType,
|
|
1344
|
+
markdown: `mutation-${mutationType}-pack.md`,
|
|
1345
|
+
json: `mutation-${mutationType}-pack.json`,
|
|
1346
|
+
mutationType
|
|
1347
|
+
}))
|
|
1348
|
+
};
|
|
1349
|
+
return {
|
|
1350
|
+
$schema: EXECUTION_PACK_BUNDLE_SCHEMA_URL,
|
|
1351
|
+
generatedAt,
|
|
1352
|
+
sourceEssenceVersion: essence.version,
|
|
1353
|
+
manifest,
|
|
1354
|
+
scaffold,
|
|
1355
|
+
review,
|
|
1356
|
+
sections,
|
|
1357
|
+
pages,
|
|
1358
|
+
mutations
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
function selectExecutionPackFromBundle(bundle, selector) {
|
|
1362
|
+
const id = selector.id ?? null;
|
|
1363
|
+
let pack = null;
|
|
1364
|
+
switch (selector.packType) {
|
|
1365
|
+
case "scaffold":
|
|
1366
|
+
pack = bundle.scaffold;
|
|
1367
|
+
break;
|
|
1368
|
+
case "review":
|
|
1369
|
+
pack = bundle.review;
|
|
1370
|
+
break;
|
|
1371
|
+
case "section":
|
|
1372
|
+
pack = id ? bundle.sections.find((entry) => entry.data.sectionId === id) ?? null : null;
|
|
1373
|
+
break;
|
|
1374
|
+
case "page":
|
|
1375
|
+
pack = id ? bundle.pages.find((entry) => entry.data.pageId === id) ?? null : null;
|
|
1376
|
+
break;
|
|
1377
|
+
case "mutation":
|
|
1378
|
+
pack = id ? bundle.mutations.find((entry) => entry.data.mutationType === id) ?? null : null;
|
|
1379
|
+
break;
|
|
1380
|
+
default:
|
|
1381
|
+
pack = null;
|
|
1382
|
+
break;
|
|
1383
|
+
}
|
|
1384
|
+
if (!pack) {
|
|
1385
|
+
return null;
|
|
1386
|
+
}
|
|
1387
|
+
return {
|
|
1388
|
+
$schema: SELECTED_EXECUTION_PACK_SCHEMA_URL,
|
|
1389
|
+
generatedAt: bundle.generatedAt,
|
|
1390
|
+
sourceEssenceVersion: bundle.sourceEssenceVersion,
|
|
1391
|
+
manifest: bundle.manifest,
|
|
1392
|
+
selector: {
|
|
1393
|
+
packType: selector.packType,
|
|
1394
|
+
id
|
|
1395
|
+
},
|
|
1396
|
+
pack
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
async function compileSelectedExecutionPack(essence, selector, options = {}) {
|
|
1400
|
+
const bundle = await compileExecutionPackBundle(essence, options);
|
|
1401
|
+
return selectExecutionPackFromBundle(bundle, selector);
|
|
1402
|
+
}
|
|
560
1403
|
export {
|
|
1404
|
+
EXECUTION_PACK_BUNDLE_SCHEMA_URL,
|
|
1405
|
+
EXECUTION_PACK_SCHEMA_URLS,
|
|
1406
|
+
PACK_MANIFEST_SCHEMA_URL,
|
|
1407
|
+
SELECTED_EXECUTION_PACK_SCHEMA_URL,
|
|
1408
|
+
buildMutationPack,
|
|
561
1409
|
buildPageIR,
|
|
1410
|
+
buildPagePack,
|
|
1411
|
+
buildReviewPack,
|
|
1412
|
+
buildScaffoldPack,
|
|
1413
|
+
buildSectionPack,
|
|
1414
|
+
compileExecutionPackBundle,
|
|
1415
|
+
compileSelectedExecutionPack,
|
|
562
1416
|
countPatterns,
|
|
563
1417
|
findNodes,
|
|
1418
|
+
listPackPages,
|
|
1419
|
+
listPackSections,
|
|
564
1420
|
pascalCase,
|
|
1421
|
+
renderExecutionPackMarkdown,
|
|
565
1422
|
resolveEssence,
|
|
1423
|
+
resolvePackAdapter,
|
|
566
1424
|
resolveVisualEffects,
|
|
567
1425
|
runPipeline,
|
|
1426
|
+
selectExecutionPackFromBundle,
|
|
568
1427
|
validateIR,
|
|
569
1428
|
walkIR
|
|
570
1429
|
};
|