@ggui-ai/ui-gen 0.15.0 → 0.17.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.
Files changed (123) hide show
  1. package/dist/adapters/generation-dispatch.d.ts +24 -4
  2. package/dist/adapters/generation-dispatch.js +5188 -3920
  3. package/dist/adapters/generation-dispatch.js.map +1 -1
  4. package/dist/adapters/index.js +33 -26
  5. package/dist/adapters/index.js.map +1 -1
  6. package/dist/advanced/index.d.ts +22 -13
  7. package/dist/advanced/index.js +4889 -3561
  8. package/dist/advanced/index.js.map +1 -1
  9. package/dist/blueprint-validator.js +8 -12
  10. package/dist/blueprint-validator.js.map +1 -1
  11. package/dist/boilerplate.d.ts +19 -1
  12. package/dist/boilerplate.js +671 -254
  13. package/dist/boilerplate.js.map +1 -1
  14. package/dist/check/index.d.ts +17 -3
  15. package/dist/check/index.js +34 -25
  16. package/dist/check/index.js.map +1 -1
  17. package/dist/compose.js +1 -1
  18. package/dist/compose.js.map +1 -1
  19. package/dist/create-ui-generator-C8Ki9Wz1.d.ts +189 -0
  20. package/dist/design-mode-DLufhrNp.d.ts +61 -0
  21. package/dist/{dispatch-BuZfTh9r.d.ts → dispatch-D2htzi9h.d.ts} +4 -1
  22. package/dist/evaluation/axis-checks/index.d.ts +4 -3
  23. package/dist/evaluation/axis-checks/index.js +300 -14
  24. package/dist/evaluation/axis-checks/index.js.map +1 -1
  25. package/dist/evaluation/axis-checks/registry.d.ts +2 -1
  26. package/dist/evaluation/axis-checks/registry.js +253 -1
  27. package/dist/evaluation/axis-checks/registry.js.map +1 -1
  28. package/dist/evaluation/index.d.ts +297 -5
  29. package/dist/evaluation/index.js +4515 -120
  30. package/dist/evaluation/index.js.map +1 -1
  31. package/dist/fragments/index.js +1 -1
  32. package/dist/fragments/index.js.map +1 -1
  33. package/dist/{generate-BTfvELgl.d.ts → generate-9IeNT4ZY.d.ts} +10 -1
  34. package/dist/harness/check/runtime-render/index.d.ts +3 -1
  35. package/dist/harness/check/runtime-render/index.js +8 -12
  36. package/dist/harness/check/runtime-render/index.js.map +1 -1
  37. package/dist/harness/index.d.ts +3 -1
  38. package/dist/harness/index.js +913 -441
  39. package/dist/harness/index.js.map +1 -1
  40. package/dist/harness/llm-trace-sink.d.ts +3 -3
  41. package/dist/harness/llm-trace-sink.js.map +1 -1
  42. package/dist/harness/prompts.d.ts +2 -1
  43. package/dist/harness/prompts.js +6 -3
  44. package/dist/harness/prompts.js.map +1 -1
  45. package/dist/harness/result-types.d.ts +17 -5
  46. package/dist/harness/result-types.js.map +1 -1
  47. package/dist/harness/runtime.d.ts +28 -3
  48. package/dist/harness/runtime.js +702 -274
  49. package/dist/harness/runtime.js.map +1 -1
  50. package/dist/harness/types-public.d.ts +39 -1
  51. package/dist/harness/types-public.js +913 -441
  52. package/dist/harness/types-public.js.map +1 -1
  53. package/dist/harness/validator-trace-sink.d.ts +2 -1
  54. package/dist/index.d.ts +32 -6
  55. package/dist/index.js +4899 -3560
  56. package/dist/index.js.map +1 -1
  57. package/dist/patch.d.ts +3 -1
  58. package/dist/{prompts-BVhtH3M1.d.ts → prompts-YU9piOgs.d.ts} +21 -45
  59. package/dist/providers/index.d.ts +2 -2
  60. package/dist/providers/index.js.map +1 -1
  61. package/dist/styling-profile-CxfvV48g.d.ts +35 -0
  62. package/dist/tools/get-primitives-ts.d.ts +1 -1
  63. package/dist/tools/get-primitives-ts.js +1 -1
  64. package/dist/tools/get-primitives-ts.js.map +1 -1
  65. package/dist/{types-public-CkNWEov9.d.ts → types-public-DVSssdc-.d.ts} +85 -5
  66. package/dist/validation/index.d.ts +1 -1
  67. package/dist/validation/index.js +4 -4
  68. package/dist/validation/index.js.map +1 -1
  69. package/dist/workflows.d.ts +3 -1
  70. package/package.json +12 -12
  71. package/src/adapters/generation-dispatch.ts +62 -4
  72. package/src/adapters/provider-router.ts +7 -6
  73. package/src/adapters/tools.ts +5 -5
  74. package/src/advanced/generator.ts +11 -10
  75. package/src/advanced/index.ts +5 -4
  76. package/src/boilerplate/free-design-prompt.ts +336 -0
  77. package/src/boilerplate/generate.ts +10 -1
  78. package/src/boilerplate/hard-sections.ts +257 -0
  79. package/src/boilerplate/render.ts +32 -8
  80. package/src/boilerplate/styling-profile.ts +166 -0
  81. package/src/boilerplate/system-prompt.ts +101 -237
  82. package/src/boilerplate/templates/base-free.tsx.tmpl +24 -0
  83. package/src/check/run-tier0.ts +82 -20
  84. package/src/coding-agent/tools.ts +8 -2
  85. package/src/contract-context.ts +43 -14
  86. package/src/create-harness.ts +15 -0
  87. package/src/create-ui-generator.ts +112 -27
  88. package/src/design-mode.ts +192 -0
  89. package/src/design-system-docs.ts +24 -17
  90. package/src/effort.ts +48 -0
  91. package/src/evaluation/axis-checks/checks/grid.ts +148 -0
  92. package/src/evaluation/axis-checks/checks/universal.ts +244 -0
  93. package/src/evaluation/axis-checks/dispatch.ts +82 -13
  94. package/src/evaluation/axis-checks/extras.ts +5 -0
  95. package/src/evaluation/axis-checks/helpers.ts +12 -0
  96. package/src/evaluation/axis-checks/registry.ts +2 -0
  97. package/src/evaluation/cost-tracker.ts +2 -3
  98. package/src/evaluation/design-identity.ts +96 -0
  99. package/src/evaluation/index.ts +63 -2
  100. package/src/evaluation/llm-evaluator.ts +89 -21
  101. package/src/evaluation/theme-css.ts +14 -0
  102. package/src/evaluation/types-public.ts +137 -8
  103. package/src/evaluation/visual-evaluator.ts +759 -54
  104. package/src/fragments/render.ts +1 -1
  105. package/src/harness/check/runtime-render/prepare-mockup.ts +18 -11
  106. package/src/harness/coding/dupe-break.ts +0 -0
  107. package/src/harness/coding/init-session.ts +2 -0
  108. package/src/harness/coding/run-coding-turn.ts +9 -14
  109. package/src/harness/coding/run-eval-round.ts +46 -5
  110. package/src/harness/llm-trace-sink.ts +3 -3
  111. package/src/harness/result-types.ts +15 -3
  112. package/src/harness/runtime.ts +28 -0
  113. package/src/harness/types-public.ts +38 -0
  114. package/src/index.ts +28 -0
  115. package/src/internal/extract-call-signatures.ts +2 -2
  116. package/src/pin-fixtures.ts +98 -0
  117. package/src/providers/bedrock.ts +4 -4
  118. package/src/run-check.ts +11 -10
  119. package/src/tools/get-primitives-ts.ts +1 -1
  120. package/src/tools.ts +2 -1
  121. package/src/validation/allowed-imports.ts +8 -2
  122. package/src/validation/primitives.ts +1 -1
  123. package/dist/create-ui-generator-D6TKuv9V.d.ts +0 -106
@@ -1,6 +1,8 @@
1
+ import { LUCIDE_ICON_NAMES } from '@ggui-ai/design';
1
2
  import { listContractGadgets, HOOK_NAME_RE, STDLIB_GADGETS_PACKAGE } from '@ggui-ai/protocol';
3
+ import { createHash } from 'crypto';
2
4
 
3
- // src/evaluation/axis-checks/helpers.ts
5
+ // src/evaluation/axis-checks/checks/universal.ts
4
6
  var ID_FIELD_CANDIDATES = ["id", "uuid", "symbol", "key", "slug", "code"];
5
7
  function getItemsProperties(p) {
6
8
  return p.items?.properties ?? p.schema?.items?.properties;
@@ -157,6 +159,9 @@ function mkIssue(subcategory, description, fix, result = "fail") {
157
159
  function cap(s) {
158
160
  return s.charAt(0).toUpperCase() + s.slice(1);
159
161
  }
162
+ function axisCheckTraceEnabled() {
163
+ return process.env["GGUI_AXIS_CHECK_TRACE"] === "1";
164
+ }
160
165
 
161
166
  // src/evaluation/axis-checks/checks/universal.ts
162
167
  var ALL_RENDER_VALUES = [
@@ -260,7 +265,172 @@ function runPropSeedNoResync(input) {
260
265
  }
261
266
  return issues;
262
267
  }
268
+ var KNOWN_ICON_KEYS = new Set(
269
+ LUCIDE_ICON_NAMES.map((n) => n.replace(/[-_]/g, "").toLowerCase())
270
+ );
271
+ function isKnownIconName(name) {
272
+ return KNOWN_ICON_KEYS.has(name.replace(/[-_]/g, "").toLowerCase());
273
+ }
274
+ var ICON_NAME_LITERAL_RX = /<Icon\b[^>]*?\bname\s*=\s*(?:"([^"]*)"|'([^']*)'|\{\s*"([^"]*)"\s*\}|\{\s*'([^']*)'\s*\})/g;
275
+ function findUnknownIconNames(sourceCode) {
276
+ const unknown = /* @__PURE__ */ new Set();
277
+ for (const m of sourceCode.matchAll(ICON_NAME_LITERAL_RX)) {
278
+ const name = m[1] ?? m[2] ?? m[3] ?? m[4] ?? "";
279
+ if (name.length === 0) continue;
280
+ if (/[^\x20-\x7e]/.test(name)) continue;
281
+ if (!isKnownIconName(name)) unknown.add(name);
282
+ }
283
+ return [...unknown];
284
+ }
285
+ function runIconNameKnown(input) {
286
+ if (input.compiledCode === null) return [];
287
+ const unknown = findUnknownIconNames(input.sourceCode);
288
+ if (unknown.length === 0) return [];
289
+ const list = unknown.map((n) => `"${n}"`).join(", ");
290
+ return [
291
+ mkIssue(
292
+ "universal.icon_name_known",
293
+ `<Icon name=\u2026> uses ${unknown.length} name(s) outside the design system's Lucide subset: ${list} \u2014 an unknown name renders an empty box.`,
294
+ "Call get_available_icons and pick a listed name (kebab-case), or use an emoji directly."
295
+ )
296
+ ];
297
+ }
298
+ var LADDER_STOP_AS_TEXT_RX = /(?<![\w-])color\s*(?:=\s*\{?\s*|:\s*)["'`]?\s*var\(--ggui-color-primary-(\d{2,3})\s*[,)]/g;
299
+ var TEXT_INK_STOP_MIN = 300;
300
+ var TEXT_INK_STOP_MAX = 600;
301
+ function findLadderStopsAsText(sourceCode) {
302
+ const found = /* @__PURE__ */ new Set();
303
+ for (const m of sourceCode.matchAll(LADDER_STOP_AS_TEXT_RX)) {
304
+ const digits = m[1] ?? "";
305
+ const stop = Number(digits);
306
+ if (stop >= TEXT_INK_STOP_MIN && stop <= TEXT_INK_STOP_MAX) found.add(`primary-${digits}`);
307
+ }
308
+ return [...found];
309
+ }
310
+ function runAccentTextInk(input) {
311
+ if (input.compiledCode === null) return [];
312
+ const stops = findLadderStopsAsText(input.sourceCode);
313
+ if (stops.length === 0) return [];
314
+ const list = stops.map((s) => `\`${s}\``).join(", ");
315
+ return [
316
+ mkIssue(
317
+ "universal.accent_text_ink",
318
+ `Text is painted with a brand-ladder fill stop as its color (${list}) \u2014 on a low-saturation brand these stops are mid-grey on the surface and the text fails readability.`,
319
+ 'Accent text (eyebrows, taglines, links, labels, inline arrows) takes var(--ggui-color-link) or <Text tone="emphasized">; body text takes var(--ggui-color-onContainer); keep primary-* stops on fills and borders.'
320
+ )
321
+ ];
322
+ }
323
+ var SCOPED_SURFACE_TAG_RX = /<(Card|Box)\b([^<>]*?)surface\s*=\s*["'](inverted|hero)["']([^<>]*?)>/g;
324
+ var STYLE_BACKGROUND_RX = /\bstyle\s*=\s*\{\{[^}]*\b(background|backgroundColor|backgroundImage)\s*:/;
325
+ function findScopedSurfaceBackgrounds(sourceCode) {
326
+ const hits = [];
327
+ for (const m of sourceCode.matchAll(SCOPED_SURFACE_TAG_RX)) {
328
+ const attrs = `${m[2] ?? ""} ${m[4] ?? ""}`;
329
+ if (STYLE_BACKGROUND_RX.test(attrs)) hits.push([m[1] ?? "", m[3] ?? ""]);
330
+ }
331
+ return hits;
332
+ }
333
+ function runScopedSurfaceOwnsGround(input) {
334
+ if (input.compiledCode === null) return [];
335
+ if (input.designMode === "free") return [];
336
+ const hits = findScopedSurfaceBackgrounds(input.sourceCode);
337
+ if (hits.length === 0) return [];
338
+ const [tag, surface] = hits[0];
339
+ return [
340
+ mkIssue(
341
+ "universal.scoped_surface_owns_ground",
342
+ `<${tag} surface="${surface}"> sets its own background in style \u2014 a scoped surface owns its ground; repainting it puts the scope's swapped inks over the wrong ground (ink on ink).`,
343
+ `Remove the background from that ${tag}'s style \u2014 surface="${surface}" paints the ground; pick another surface if a different ground is wanted.`
344
+ )
345
+ ];
346
+ }
347
+ var CAPS_TEXT_RX = /<Text\b(?=[^>]*\bcaps\b)[^>]*>([\s\S]*?)<\/Text>/g;
348
+ function findInventedCapsLabels(sourceCode) {
349
+ const out = [];
350
+ for (const m of sourceCode.matchAll(CAPS_TEXT_RX)) {
351
+ const inner = (m[1] ?? "").trim();
352
+ if (inner.length === 0) continue;
353
+ if (inner.includes("{")) continue;
354
+ out.push(inner.replace(/\s+/g, " ").slice(0, 60));
355
+ }
356
+ return out;
357
+ }
358
+ function runCapsLabelInvented(input) {
359
+ if (input.compiledCode === null) return [];
360
+ const invented = findInventedCapsLabels(input.sourceCode);
361
+ if (invented.length === 0) return [];
362
+ const list = invented.map((t) => `"${t}"`).join(", ");
363
+ return [
364
+ mkIssue(
365
+ "universal.caps_label_invented",
366
+ `${invented.length} caps label(s) carry invented copy the props do not supply: ${list} \u2014 an eyebrow, kicker or overline the agent cannot change is chatter on every render.`,
367
+ "Remove the label, or bind its text to a prop (`<Text caps>{props.section}</Text>`); copy comes from the contract, never from the component."
368
+ )
369
+ ];
370
+ }
371
+ var VIEWPORT_UNIT = String.raw`(?:calc\(\s*)?100(?:vh|dvh|svh|lvh)\b(?:[^)"'\x60;\n]*\))?`;
372
+ var VIEWPORT_STYLE_OBJECT_RX = new RegExp(
373
+ String.raw`\b(?:minHeight|height|minBlockSize|blockSize)\s*:\s*["'\x60]\s*${VIEWPORT_UNIT}["'\x60]?`,
374
+ "g"
375
+ );
376
+ var VIEWPORT_CSS_RX = new RegExp(
377
+ String.raw`(?<![\w-])(?:min-)?(?:height|block-size)\s*:\s*${VIEWPORT_UNIT}`,
378
+ "g"
379
+ );
380
+ var VIEWPORT_CLASS_RX = /(?<![\w-])(?:min-)?h-(?:screen|dvh|svh|lvh)\b/g;
381
+ function findViewportSizing(sourceCode) {
382
+ const hits = [];
383
+ for (const rx of [VIEWPORT_STYLE_OBJECT_RX, VIEWPORT_CSS_RX, VIEWPORT_CLASS_RX]) {
384
+ for (const m of sourceCode.matchAll(rx)) {
385
+ hits.push({ at: m.index ?? 0, text: m[0].replace(/\s+/g, " ").trim().slice(0, 60) });
386
+ }
387
+ }
388
+ return hits.sort((a, b) => a.at - b.at).map((h) => h.text);
389
+ }
390
+ function runViewportSized(input) {
391
+ if (input.compiledCode === null) return [];
392
+ const sized = findViewportSizing(input.sourceCode);
393
+ if (sized.length === 0) return [];
394
+ const list = sized.map((t) => "`" + t + "`").join(", ");
395
+ return [
396
+ mkIssue(
397
+ "universal.viewport_sized",
398
+ `${sized.length} element(s) are sized to the viewport: ${list} \u2014 the frame owns the height; a content-sized frame is measured from the content, so a viewport-sized root never shrinks and grows the frame on every re-measure.`,
399
+ "Remove the viewport height \u2014 size to content, never to the viewport. Under fullscreen the frame stretches your root already (centre inside it with `display: flex; flex-direction: column; justify-content: center`); to fill a parent that has a height use `height: 100%` or `flex: 1`."
400
+ )
401
+ ];
402
+ }
263
403
  var UNIVERSAL_CHECKS = [
404
+ {
405
+ id: "universal.icon_name_known",
406
+ axis: "render",
407
+ values: ALL_RENDER_VALUES,
408
+ run: runIconNameKnown
409
+ },
410
+ {
411
+ id: "universal.caps_label_invented",
412
+ axis: "render",
413
+ values: ALL_RENDER_VALUES,
414
+ run: runCapsLabelInvented
415
+ },
416
+ {
417
+ id: "universal.viewport_sized",
418
+ axis: "render",
419
+ values: ALL_RENDER_VALUES,
420
+ run: runViewportSized
421
+ },
422
+ {
423
+ id: "universal.accent_text_ink",
424
+ axis: "render",
425
+ values: ALL_RENDER_VALUES,
426
+ run: runAccentTextInk
427
+ },
428
+ {
429
+ id: "universal.scoped_surface_owns_ground",
430
+ axis: "render",
431
+ values: ALL_RENDER_VALUES,
432
+ run: runScopedSurfaceOwnsGround
433
+ },
264
434
  {
265
435
  id: "universal.prop_coverage",
266
436
  axis: "render",
@@ -1063,6 +1233,87 @@ var TOOLING_CHECKS = [
1063
1233
  }
1064
1234
  ];
1065
1235
 
1236
+ // src/evaluation/axis-checks/checks/grid.ts
1237
+ var BOARD_PROP_RX = /^(columns?|lanes?|stages?|lists?|buckets?|swimlanes?)$/i;
1238
+ var BOARD_PROMPT_RX = /\b(kanban|board)\b/i;
1239
+ var BOARD_NAMES = ["columns", "lanes", "stages", "lists", "buckets", "swimlanes"];
1240
+ var LAYOUT_TAG_RX = /<(\/?)(Grid|Row|Stack|Card|Box|Container|ScrollArea|div|section)\b([^<>]*?)>/g;
1241
+ var SIDE_BY_SIDE = /* @__PURE__ */ new Set(["Grid", "Row"]);
1242
+ function boardPropNames(contract) {
1243
+ const propsField = contract?.propsSpec;
1244
+ const properties = propsField?.properties ?? {};
1245
+ return Object.entries(properties).filter(([name, p]) => BOARD_PROP_RX.test(name) && (p?.type === "array" || p?.schema?.type === "array")).map(([name]) => name);
1246
+ }
1247
+ function jsxMapPositions(sourceCode, name) {
1248
+ const rx = new RegExp(
1249
+ `\\b${name}(?:\\?\\.)?\\.map\\(\\s*\\(?\\s*\\w+(?:\\s*,\\s*\\w+)?\\s*\\)?\\s*=>\\s*(?:\\(?\\s*<|\\{[^{}]{0,600}?\\breturn\\s*\\(?\\s*<)`,
1250
+ "g"
1251
+ );
1252
+ return [...sourceCode.matchAll(rx)].map((m) => m.index ?? 0);
1253
+ }
1254
+ function enclosingLayoutTag(sourceCode, pos) {
1255
+ const stack = [];
1256
+ for (const m of sourceCode.slice(0, pos).matchAll(LAYOUT_TAG_RX)) {
1257
+ const closing = m[1] === "/";
1258
+ const name = m[2] ?? "";
1259
+ const attrs = m[3] ?? "";
1260
+ if (closing) {
1261
+ if (stack[stack.length - 1] === name) stack.pop();
1262
+ continue;
1263
+ }
1264
+ if (attrs.trimEnd().endsWith("/")) continue;
1265
+ stack.push(name);
1266
+ }
1267
+ return stack[stack.length - 1];
1268
+ }
1269
+ function describeBoardRead(input) {
1270
+ const declared = boardPropNames(input.contract);
1271
+ const names = declared.length > 0 ? declared : BOARD_PROMPT_RX.test(input.originalPrompt) ? [...BOARD_NAMES] : BOARD_NAMES.filter((n) => jsxMapPositions(input.sourceCode, n).length > 0);
1272
+ const perName = names.map((name) => {
1273
+ const positions = jsxMapPositions(input.sourceCode, name);
1274
+ return { name, maps: positions.length, enclosing: positions.map((pos) => enclosingLayoutTag(input.sourceCode, pos) ?? "nothing") };
1275
+ });
1276
+ return { declared, names, perName };
1277
+ }
1278
+ function findStackedBoardColumns(input) {
1279
+ const stacked = [];
1280
+ for (const { name, enclosing } of describeBoardRead(input).perName) {
1281
+ for (const tag of enclosing) if (!SIDE_BY_SIDE.has(tag)) stacked.push([name, tag]);
1282
+ }
1283
+ return stacked;
1284
+ }
1285
+ function runBoardColumnsSideBySide(input) {
1286
+ if (input.compiledCode === null) return [];
1287
+ const standDown = input.designMode === "free";
1288
+ const stacked = standDown ? [] : findStackedBoardColumns(input);
1289
+ if (axisCheckTraceEnabled()) {
1290
+ const line = {
1291
+ ...describeBoardRead(input),
1292
+ designMode: input.designMode ?? "constrained",
1293
+ standDown,
1294
+ result: standDown ? "stand-down" : stacked.length > 0 ? "fail" : "pass"
1295
+ };
1296
+ console.log(JSON.stringify({ boardColumnsTrace: line }));
1297
+ }
1298
+ if (stacked.length === 0) return [];
1299
+ const [name, tag] = stacked[0];
1300
+ return [
1301
+ mkIssue(
1302
+ "grid.board_columns_side_by_side",
1303
+ `The board's \`${name}\` are composed under <${tag}> \u2014 a vertical stack of column sections; at md and wider the columns must sit side by side.`,
1304
+ `Wrap the ${name} map in <Grid columns={{ base: 1, md: ${name}.length }} gap="md"> (each column its own <Stack> of cards); never a <Stack> of column sections.`
1305
+ )
1306
+ ];
1307
+ }
1308
+ var GRID_CHECKS = [
1309
+ {
1310
+ id: "grid.board_columns_side_by_side",
1311
+ axis: "render",
1312
+ values: ["grid"],
1313
+ run: runBoardColumnsSideBySide
1314
+ }
1315
+ ];
1316
+
1066
1317
  // src/evaluation/axis-checks/extras.ts
1067
1318
  function mkIssue2(subcategory, description, fix, result = "warn") {
1068
1319
  return { tier: 0, result, category: "mode", subcategory, description, fix };
@@ -1152,6 +1403,7 @@ var stepperAdopted = {
1152
1403
  values: ["multi-step"],
1153
1404
  run(input) {
1154
1405
  if (input.compiledCode === null) return [];
1406
+ if (input.designMode === "free") return [];
1155
1407
  if (/<Stepper[\s/>]/.test(input.sourceCode)) return [];
1156
1408
  return [
1157
1409
  mkIssue2(
@@ -1201,6 +1453,7 @@ var REGISTRY = [
1201
1453
  ...WRITES_CHECKS,
1202
1454
  ...STATE_PAYLOAD_CHECKS,
1203
1455
  ...TOOLING_CHECKS,
1456
+ ...GRID_CHECKS,
1204
1457
  ...EXTRA_CHECKS
1205
1458
  ];
1206
1459
 
@@ -1214,26 +1467,59 @@ function matches(vector, check) {
1214
1467
  }
1215
1468
  return true;
1216
1469
  }
1217
-
1218
- // src/evaluation/axis-checks/dispatch.ts
1219
1470
  function runAxisChecks(classification, input) {
1220
- if (input.compiledCode === null) return [];
1221
- const axisInput = {
1471
+ const facts = {
1222
1472
  sourceCode: input.sourceCode,
1223
- compiledCode: input.compiledCode,
1224
1473
  ...input.contract !== void 0 ? { contract: input.contract } : {},
1225
1474
  originalPrompt: input.originalPrompt,
1226
- classification
1475
+ classification,
1476
+ ...input.designMode !== void 0 ? { designMode: input.designMode } : {}
1227
1477
  };
1478
+ if (input.compiledCode === null) {
1479
+ traceAxisChecksSkipped(facts, "compiledCode null \u2014 no check ran");
1480
+ return [];
1481
+ }
1482
+ const axisInput = { ...facts, compiledCode: input.compiledCode };
1483
+ return runGatedAxisChecks(
1484
+ REGISTRY.filter((check) => matches(classification.vector, check)),
1485
+ axisInput
1486
+ ).issues;
1487
+ }
1488
+ function runGatedAxisChecks(checks, input) {
1228
1489
  const issues = [];
1229
- const firedIds = /* @__PURE__ */ new Set();
1230
- for (const check of REGISTRY) {
1231
- if (!matches(classification.vector, check)) continue;
1232
- if (firedIds.has(check.id)) continue;
1233
- firedIds.add(check.id);
1234
- issues.push(...check.run(axisInput));
1490
+ const firedIds = [];
1491
+ const seen = /* @__PURE__ */ new Set();
1492
+ for (const check of checks) {
1493
+ if (seen.has(check.id)) continue;
1494
+ seen.add(check.id);
1495
+ firedIds.push(check.id);
1496
+ issues.push(...check.run(input));
1235
1497
  }
1236
- return issues;
1498
+ if (axisCheckTraceEnabled()) {
1499
+ const line = {
1500
+ ...traceFacts(input),
1501
+ matched: [...firedIds],
1502
+ issues: issues.map((i) => ({ id: i.subcategory ?? i.category, result: i.result }))
1503
+ };
1504
+ console.log(JSON.stringify({ axisCheckTrace: line }));
1505
+ }
1506
+ return { issues, firedIds };
1507
+ }
1508
+ function traceAxisChecksSkipped(facts, note) {
1509
+ if (!axisCheckTraceEnabled()) return;
1510
+ const line = { ...traceFacts(facts), matched: [], issues: [], note };
1511
+ console.log(JSON.stringify({ axisCheckTrace: line }));
1512
+ }
1513
+ function traceFacts(input) {
1514
+ const propsSpec = input.contract?.propsSpec;
1515
+ return {
1516
+ sourceSha256: createHash("sha256").update(input.sourceCode, "utf8").digest("hex"),
1517
+ originalPrompt: input.originalPrompt,
1518
+ designMode: input.designMode ?? "constrained",
1519
+ classification: input.classification.vector,
1520
+ propsSpecKeys: Object.keys(propsSpec ?? {}),
1521
+ propsSpecPropertyKeys: Object.keys(propsSpec?.properties ?? {})
1522
+ };
1237
1523
  }
1238
1524
 
1239
1525
  export { EXTRA_CHECKS, REGISTRY, matches, runAxisChecks };