@ggui-ai/ui-gen 0.16.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.
- package/dist/adapters/generation-dispatch.d.ts +6 -3
- package/dist/adapters/generation-dispatch.js +4944 -4239
- package/dist/adapters/generation-dispatch.js.map +1 -1
- package/dist/adapters/index.js +8 -5
- package/dist/adapters/index.js.map +1 -1
- package/dist/advanced/index.d.ts +12 -8
- package/dist/advanced/index.js +4462 -3726
- package/dist/advanced/index.js.map +1 -1
- package/dist/blueprint-validator.js +8 -12
- package/dist/blueprint-validator.js.map +1 -1
- package/dist/boilerplate.d.ts +5 -2
- package/dist/boilerplate.js +95 -12
- package/dist/boilerplate.js.map +1 -1
- package/dist/check/index.d.ts +2 -2
- package/dist/check/index.js +2 -0
- package/dist/check/index.js.map +1 -1
- package/dist/compose.js +1 -1
- package/dist/compose.js.map +1 -1
- package/dist/{create-ui-generator-CCIeQaEe.d.ts → create-ui-generator-C8Ki9Wz1.d.ts} +18 -2
- package/dist/{design-mode-BHEE7flj.d.ts → design-mode-DLufhrNp.d.ts} +0 -6
- package/dist/{dispatch-BA5dJcg-.d.ts → dispatch-D2htzi9h.d.ts} +2 -2
- package/dist/evaluation/axis-checks/index.d.ts +4 -4
- package/dist/evaluation/axis-checks/index.js +297 -13
- package/dist/evaluation/axis-checks/index.js.map +1 -1
- package/dist/evaluation/axis-checks/registry.d.ts +2 -2
- package/dist/evaluation/axis-checks/registry.js +252 -1
- package/dist/evaluation/axis-checks/registry.js.map +1 -1
- package/dist/evaluation/index.d.ts +160 -8
- package/dist/evaluation/index.js +702 -81
- package/dist/evaluation/index.js.map +1 -1
- package/dist/fragments/index.js +1 -1
- package/dist/fragments/index.js.map +1 -1
- package/dist/{generate-8L_bc2iP.d.ts → generate-9IeNT4ZY.d.ts} +1 -1
- package/dist/harness/check/runtime-render/index.d.ts +3 -2
- package/dist/harness/check/runtime-render/index.js +8 -12
- package/dist/harness/check/runtime-render/index.js.map +1 -1
- package/dist/harness/index.d.ts +3 -2
- package/dist/harness/index.js +385 -254
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/prompts.d.ts +2 -2
- package/dist/harness/prompts.js +3 -3
- package/dist/harness/prompts.js.map +1 -1
- package/dist/harness/result-types.d.ts +3 -3
- package/dist/harness/runtime.d.ts +13 -4
- package/dist/harness/runtime.js +106 -19
- package/dist/harness/runtime.js.map +1 -1
- package/dist/harness/types-public.d.ts +13 -2
- package/dist/harness/types-public.js +385 -254
- package/dist/harness/types-public.js.map +1 -1
- package/dist/harness/validator-trace-sink.d.ts +2 -2
- package/dist/index.d.ts +29 -7
- package/dist/index.js +4465 -3727
- package/dist/index.js.map +1 -1
- package/dist/patch.d.ts +3 -2
- package/dist/{prompts-k6m6reqe.d.ts → prompts-YU9piOgs.d.ts} +12 -46
- package/dist/styling-profile-CxfvV48g.d.ts +35 -0
- package/dist/tools/get-primitives-ts.d.ts +1 -1
- package/dist/tools/get-primitives-ts.js +1 -1
- package/dist/tools/get-primitives-ts.js.map +1 -1
- package/dist/{types-public-D24JLJgl.d.ts → types-public-DVSssdc-.d.ts} +39 -3
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +1 -1
- package/dist/validation/index.js.map +1 -1
- package/dist/workflows.d.ts +3 -2
- package/package.json +12 -12
- package/src/adapters/generation-dispatch.ts +6 -1
- package/src/boilerplate/free-design-prompt.ts +7 -0
- package/src/boilerplate/hard-sections.ts +16 -1
- package/src/boilerplate/styling-profile.ts +166 -0
- package/src/boilerplate/system-prompt.ts +20 -8
- package/src/check/run-tier0.ts +2 -0
- package/src/contract-context.ts +15 -14
- package/src/create-harness.ts +4 -0
- package/src/create-ui-generator.ts +41 -2
- package/src/design-mode.ts +11 -1
- package/src/design-system-docs.ts +7 -4
- package/src/effort.ts +48 -0
- package/src/evaluation/axis-checks/checks/grid.ts +148 -0
- package/src/evaluation/axis-checks/checks/universal.ts +244 -0
- package/src/evaluation/axis-checks/dispatch.ts +78 -13
- package/src/evaluation/axis-checks/helpers.ts +12 -0
- package/src/evaluation/axis-checks/registry.ts +2 -0
- package/src/evaluation/design-identity.ts +96 -0
- package/src/evaluation/index.ts +17 -0
- package/src/evaluation/llm-evaluator.ts +13 -1
- package/src/evaluation/theme-css.ts +14 -0
- package/src/evaluation/types-public.ts +35 -1
- package/src/evaluation/visual-evaluator.ts +478 -45
- package/src/fragments/render.ts +1 -1
- package/src/harness/check/runtime-render/prepare-mockup.ts +18 -11
- package/src/harness/coding/init-session.ts +1 -0
- package/src/harness/coding/run-coding-turn.ts +6 -14
- package/src/harness/coding/run-eval-round.ts +31 -3
- package/src/harness/runtime.ts +10 -0
- package/src/harness/types-public.ts +11 -0
- package/src/index.ts +2 -0
- package/src/pin-fixtures.ts +98 -0
- package/src/run-check.ts +10 -10
- package/src/tools/get-primitives-ts.ts +1 -1
- package/src/tools.ts +2 -1
- package/src/validation/primitives.ts +1 -1
package/src/fragments/render.ts
CHANGED
|
@@ -27,7 +27,7 @@ export const renderFragments: Record<string, HarnessFragment> = {
|
|
|
27
27
|
value: "grid",
|
|
28
28
|
cacheTier: "axisDelta",
|
|
29
29
|
promptText:
|
|
30
|
-
"## Render: grid\
|
|
30
|
+
"## Render: grid\nTwo shapes share this axis — read the contract:\n- A TILE grid (a gallery, a stat grid, items that may carry row/col fields): `<Grid columns={N} gap=\"md\">` (or `<CardGrid>`); if items carry row/col fields, position each tile at (row, col).\n- A BOARD (a columns / lanes / stages array whose items are cards — a kanban): the columns sit SIDE BY SIDE at md and wider — `<Grid columns={{ base: 1, md: columns.length }} gap=\"md\">` wrapping the columns map, each column its own `<Stack>` of `<Card>`s with its header and count; NEVER a `<Stack>` of column sections (that is a list — tier-0 fails it).\nNever hand-roll `display: grid`. Do not scroll horizontally.",
|
|
31
31
|
},
|
|
32
32
|
spatial: {
|
|
33
33
|
axis: "render",
|
|
@@ -129,6 +129,16 @@ function synthesizeFromSchema(
|
|
|
129
129
|
hint: string,
|
|
130
130
|
warnings: string[],
|
|
131
131
|
depth: number,
|
|
132
|
+
/**
|
|
133
|
+
* 1-based position when the value belongs to an array element (inherited
|
|
134
|
+
* by the element's own fields). Every synthesized string inside an element
|
|
135
|
+
* carries it, so two elements never share an id or a visible label — the
|
|
136
|
+
* runtime probe's selection-identity rule ("clicking each fired the action
|
|
137
|
+
* with the same id") is undecidable on identical items and read a correct
|
|
138
|
+
* component as broken (2026-09-10, the 1.6 hello: both quick replies were
|
|
139
|
+
* `Sample Id` / `Sample Label`).
|
|
140
|
+
*/
|
|
141
|
+
ordinal?: number,
|
|
132
142
|
): SynthOk | SynthFail {
|
|
133
143
|
if (!schema) return { kind: "fail", reason: "no schema" };
|
|
134
144
|
if (depth > MAX_DEPTH) return { kind: "fail", reason: "schema too deep" };
|
|
@@ -146,10 +156,10 @@ function synthesizeFromSchema(
|
|
|
146
156
|
|
|
147
157
|
// Union schemas — try first branch only
|
|
148
158
|
if (schema.oneOf && schema.oneOf.length > 0) {
|
|
149
|
-
return synthesizeFromSchema(schema.oneOf[0], hint, warnings, depth + 1);
|
|
159
|
+
return synthesizeFromSchema(schema.oneOf[0], hint, warnings, depth + 1, ordinal);
|
|
150
160
|
}
|
|
151
161
|
if (schema.anyOf && schema.anyOf.length > 0) {
|
|
152
|
-
return synthesizeFromSchema(schema.anyOf[0], hint, warnings, depth + 1);
|
|
162
|
+
return synthesizeFromSchema(schema.anyOf[0], hint, warnings, depth + 1, ordinal);
|
|
153
163
|
}
|
|
154
164
|
|
|
155
165
|
// Draft-07 type array (`['string','null']` — the canonical nullable
|
|
@@ -158,12 +168,7 @@ function synthesizeFromSchema(
|
|
|
158
168
|
// above bounds the recursion.
|
|
159
169
|
if (Array.isArray(schema.type)) {
|
|
160
170
|
const primary = schema.type.find((t) => t !== "null") ?? "null";
|
|
161
|
-
return synthesizeFromSchema(
|
|
162
|
-
{ ...schema, type: primary },
|
|
163
|
-
hint,
|
|
164
|
-
warnings,
|
|
165
|
-
depth + 1,
|
|
166
|
-
);
|
|
171
|
+
return synthesizeFromSchema({ ...schema, type: primary }, hint, warnings, depth + 1, ordinal);
|
|
167
172
|
}
|
|
168
173
|
|
|
169
174
|
switch (schema.type) {
|
|
@@ -184,7 +189,9 @@ function synthesizeFromSchema(
|
|
|
184
189
|
// Use field name as a hint so it appears in DOM and prop-coverage check sees it.
|
|
185
190
|
// Capitalize first letter for natural-looking placeholder.
|
|
186
191
|
const cap = hint.charAt(0).toUpperCase() + hint.slice(1);
|
|
187
|
-
|
|
192
|
+
// Inside an array element every string is distinct per element.
|
|
193
|
+
const value = ordinal === undefined ? `Sample ${cap}` : `Sample ${cap} ${ordinal}`;
|
|
194
|
+
return { kind: "ok", value, source: "schema-synth" };
|
|
188
195
|
}
|
|
189
196
|
}
|
|
190
197
|
}
|
|
@@ -211,7 +218,7 @@ function synthesizeFromSchema(
|
|
|
211
218
|
const items: JsonValue[] = [];
|
|
212
219
|
for (let i = 0; i < 2; i++) {
|
|
213
220
|
const itemHint = `${hint}Item${i + 1}`;
|
|
214
|
-
const itemSynth = synthesizeFromSchema(schema.items, itemHint, warnings, depth + 1);
|
|
221
|
+
const itemSynth = synthesizeFromSchema(schema.items, itemHint, warnings, depth + 1, i + 1);
|
|
215
222
|
if (itemSynth.kind === "ok") {
|
|
216
223
|
// Inject an `id` for list-key purposes if the item is an object lacking one.
|
|
217
224
|
if (
|
|
@@ -233,7 +240,7 @@ function synthesizeFromSchema(
|
|
|
233
240
|
const propsMap = schema.properties ?? {};
|
|
234
241
|
const required = new Set(schema.required ?? []);
|
|
235
242
|
for (const [k, sub] of Object.entries(propsMap)) {
|
|
236
|
-
const subSynth = synthesizeFromSchema(sub, k, warnings, depth + 1);
|
|
243
|
+
const subSynth = synthesizeFromSchema(sub, k, warnings, depth + 1, ordinal);
|
|
237
244
|
if (subSynth.kind === "ok") {
|
|
238
245
|
obj[k] = subSynth.value;
|
|
239
246
|
} else if (required.has(k)) {
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
WRITE_PLAN_TOOL,
|
|
34
34
|
type LLMToolDef,
|
|
35
35
|
} from "../../tools.js";
|
|
36
|
+
import { LUCIDE_ICON_NAMES } from "@ggui-ai/design";
|
|
36
37
|
import { formatWithHashlines } from "../hashline.js";
|
|
37
38
|
import {
|
|
38
39
|
recordToolExchange,
|
|
@@ -592,20 +593,11 @@ ${closingInstruction}`;
|
|
|
592
593
|
// Handle get_available_icons — return icon list without consuming a turn
|
|
593
594
|
if (call.name === "get_available_icons") {
|
|
594
595
|
if (!iconNamesCache) {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
).pathname;
|
|
601
|
-
const { LUCIDE_ICONS } = await import(iconDataPath);
|
|
602
|
-
iconNamesCache = Object.keys(LUCIDE_ICONS)
|
|
603
|
-
.map((n) => n.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase())
|
|
604
|
-
.join(", ");
|
|
605
|
-
} catch {
|
|
606
|
-
iconNamesCache =
|
|
607
|
-
"sun, moon, cloud, cloud-rain, heart, star, search, home, plus, minus, check, x, arrow-left, arrow-right, chevron-down, chevron-up, menu, user, settings, bell, mail, phone, shopping-cart, download, upload, edit, trash-2, save, eye, lock, globe, map-pin, calendar, clock, zap, alert-circle, info, help-circle";
|
|
608
|
-
}
|
|
596
|
+
// The one list the Icon primitive renders (ggui#1015) — the same
|
|
597
|
+
// export the check leg reads, so the tool never advertises a name
|
|
598
|
+
// the primitive does not know (the former dist-relative dynamic
|
|
599
|
+
// import fell back to a 40-name string whenever the path missed).
|
|
600
|
+
iconNamesCache = LUCIDE_ICON_NAMES.join(", ");
|
|
609
601
|
}
|
|
610
602
|
console.log(`[simple] turn ${turnsUsed}: get_available_icons`);
|
|
611
603
|
let iconResult = `Available Lucide icon names (use with <Icon name="...">):\n${iconNamesCache}`;
|
|
@@ -128,6 +128,9 @@ const MAX_FEEDBACK_ISSUES = 3;
|
|
|
128
128
|
* criterion is `not-applicable` (excluded from coverage denominators),
|
|
129
129
|
* not `skipped` (which would count as silent absence).
|
|
130
130
|
*/
|
|
131
|
+
/** Why an eval result carries no criteria: the round itself threw (ggui#1110). */
|
|
132
|
+
const EVAL_ROUND_THREW_REASON = "eval round threw";
|
|
133
|
+
|
|
131
134
|
const LOW_RISK_BYPASS_REASON =
|
|
132
135
|
"same-image low-risk bypass: axis checks clean, tier-1/2 evaluation not invoked";
|
|
133
136
|
|
|
@@ -457,6 +460,7 @@ export async function runEvalRound(
|
|
|
457
460
|
// the source (free mode: color-only tokens, arm-neutral
|
|
458
461
|
// visual judging).
|
|
459
462
|
designMode: harness.designMode,
|
|
463
|
+
profile: harness.profile,
|
|
460
464
|
},
|
|
461
465
|
{
|
|
462
466
|
provider: mapProviderForEvaluator(evaluationAgent.provider),
|
|
@@ -480,12 +484,20 @@ export async function runEvalRound(
|
|
|
480
484
|
: null,
|
|
481
485
|
visualMod
|
|
482
486
|
? visualMod.runVisualEval(
|
|
483
|
-
{
|
|
487
|
+
{
|
|
488
|
+
compiledCode,
|
|
489
|
+
originalPrompt: userPrompt,
|
|
490
|
+
profile: harness.profile,
|
|
491
|
+
// The caller's theme, when named — the round paints what the app paints.
|
|
492
|
+
...(visualEvaluation?.cssTokens !== undefined ? { cssTokens: visualEvaluation.cssTokens } : {}),
|
|
493
|
+
},
|
|
484
494
|
{
|
|
485
495
|
provider: mapProviderForEvaluator(visualEvalAgent.provider) as "claude" | "google",
|
|
486
496
|
model: visualEvalAgent.model,
|
|
487
497
|
passThreshold: visualThreshold,
|
|
488
|
-
|
|
498
|
+
// The in-loop visual round renders the same sample the runtime probe
|
|
499
|
+
// does (the caller's fixture) unless the config names its own.
|
|
500
|
+
sampleProps: visualEvaluation?.sampleProps ?? fixtureProps,
|
|
489
501
|
viewport: visualEvaluation?.viewport,
|
|
490
502
|
// Per-canvas judging (arm-neutral) — absent = single shot.
|
|
491
503
|
...(visualEvaluation?.canvases !== undefined
|
|
@@ -881,7 +893,23 @@ export async function runEvalRound(
|
|
|
881
893
|
lastDiffFailed: false,
|
|
882
894
|
};
|
|
883
895
|
} catch (e) {
|
|
884
|
-
|
|
896
|
+
// The round threw. Until ggui#1110 this returned `evalResult` UNDEFINED and
|
|
897
|
+
// said so only in a console line: downstream the generation carried no
|
|
898
|
+
// evaluation at all, an offline mint exported no `eval.json`, and the bar
|
|
899
|
+
// read the missing file as "probe skipped" — a thrown evaluation and an
|
|
900
|
+
// evaluation that never ran became the same fact, with the reason lost.
|
|
901
|
+
// Now the failure IS the result: the round reports what threw, on the
|
|
902
|
+
// channel a judge reads, and can never be mistaken for a pass (`pass` is
|
|
903
|
+
// empty, the probe status is `infra-skipped`, and a bar that requires the
|
|
904
|
+
// probe still refuses — with a cause instead of a silence).
|
|
905
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
906
|
+
console.error("[simple] eval failed:", message);
|
|
907
|
+
evalResult = evalResult ?? {
|
|
908
|
+
issues: [],
|
|
909
|
+
pass: [],
|
|
910
|
+
criteriaCoverage: notApplicableCoverage(`${EVAL_ROUND_THREW_REASON}: ${message}`),
|
|
911
|
+
runtimeProbe: { status: "infra-skipped", reason: `${EVAL_ROUND_THREW_REASON}: ${message}` },
|
|
912
|
+
};
|
|
885
913
|
return {
|
|
886
914
|
control: "break",
|
|
887
915
|
evalDone: false,
|
package/src/harness/runtime.ts
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
// directly to the coding agent in the system prompt — there is no
|
|
25
25
|
// separate planner stage.
|
|
26
26
|
|
|
27
|
+
import type { GenerationProfileInput } from "../boilerplate/styling-profile.js";
|
|
27
28
|
import type { QualityConfig } from "../evaluation/types-public.js";
|
|
28
29
|
import { DEFAULT_DESIGN_SYSTEM_DOCS } from "../design-system-docs.js";
|
|
29
30
|
import { PRIMITIVES_DOCUMENTATION } from "../validation/index.js";
|
|
@@ -90,6 +91,12 @@ export interface SingleComponentParams {
|
|
|
90
91
|
* today's single screenshot at `viewport`.
|
|
91
92
|
*/
|
|
92
93
|
canvases?: readonly CanvasClass[];
|
|
94
|
+
/**
|
|
95
|
+
* The stylesheet the in-loop visual round renders under (a caller's
|
|
96
|
+
* theme composed by `cssTokensForAppTheme`); absent = the design's
|
|
97
|
+
* default tokens. Never part of the prompt or the identity.
|
|
98
|
+
*/
|
|
99
|
+
cssTokens?: string;
|
|
93
100
|
};
|
|
94
101
|
onProgress?: (event: unknown) => void;
|
|
95
102
|
onInitialResult?: (result: {
|
|
@@ -149,6 +156,8 @@ export function buildSystemPrompt(
|
|
|
149
156
|
designMode?: DesignMode,
|
|
150
157
|
/** Rendering canvas class (`free` mode) — see `SystemPromptInputs.canvas`. */
|
|
151
158
|
canvas?: CanvasClass,
|
|
159
|
+
/** The app's generation profile (#991). */
|
|
160
|
+
profile?: GenerationProfileInput,
|
|
152
161
|
): string {
|
|
153
162
|
return buildSystemPromptSkeleton({
|
|
154
163
|
userRequest,
|
|
@@ -163,6 +172,7 @@ export function buildSystemPrompt(
|
|
|
163
172
|
gadgetTypes,
|
|
164
173
|
designMode,
|
|
165
174
|
canvas,
|
|
175
|
+
profile,
|
|
166
176
|
// criteriaBlock left undefined — ui-gen fills default from open CRITERIA.
|
|
167
177
|
});
|
|
168
178
|
}
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
// types hub for the harness module. `AxisCheck` + `AxisCheckInput` live
|
|
19
19
|
// in `@ggui-ai/ui-gen/evaluation`.
|
|
20
20
|
|
|
21
|
+
import type { GenerationProfileInput } from "../boilerplate/styling-profile.js";
|
|
21
22
|
import type { GadgetDescriptor, DataContract, JsonValue } from "@ggui-ai/protocol";
|
|
22
23
|
import type { Classification } from "../classifier/axes.js";
|
|
23
24
|
import type { EvalIssue, EvalTier, AxisCheck, RuntimeProbeStatus } from "../evaluation/types-public.js";
|
|
@@ -302,6 +303,8 @@ export interface Harness {
|
|
|
302
303
|
* into `free`.
|
|
303
304
|
*/
|
|
304
305
|
readonly designMode: DesignMode;
|
|
306
|
+
/** The app's generation profile in force for this harness (#991), if any. */
|
|
307
|
+
readonly profile?: GenerationProfileInput;
|
|
305
308
|
|
|
306
309
|
readonly how: HowLeg;
|
|
307
310
|
readonly what: WhatLeg;
|
|
@@ -396,6 +399,8 @@ export type SystemPromptBuilder = (input: {
|
|
|
396
399
|
readonly designMode?: DesignMode;
|
|
397
400
|
/** Rendering canvas class (`free` mode) — see `CreateHarnessInput.canvas`. */
|
|
398
401
|
readonly canvas?: CanvasClass;
|
|
402
|
+
/** The app's generation profile (#991) — see `CreateHarnessInput.profile`. */
|
|
403
|
+
readonly profile?: GenerationProfileInput;
|
|
399
404
|
}) => string;
|
|
400
405
|
|
|
401
406
|
export interface CreateHarnessInput {
|
|
@@ -419,6 +424,12 @@ export interface CreateHarnessInput {
|
|
|
419
424
|
* by the `constrained` prompt.
|
|
420
425
|
*/
|
|
421
426
|
readonly canvas?: CanvasClass;
|
|
427
|
+
/**
|
|
428
|
+
* The app's generation profile (#991): operator-declared free text the
|
|
429
|
+
* HOW leg renders as one bounded section and every judge reads under its
|
|
430
|
+
* own frame. Absent = byte-identical prompts.
|
|
431
|
+
*/
|
|
432
|
+
readonly profile?: GenerationProfileInput;
|
|
422
433
|
/**
|
|
423
434
|
* Pre-resolved static harness policy. When omitted, `createHarness`
|
|
424
435
|
* falls back to `DEFAULT_HARNESS_POLICY` from `@ggui-ai/ui-gen/policy`.
|
package/src/index.ts
CHANGED
|
@@ -82,3 +82,5 @@ export type {
|
|
|
82
82
|
// go through this helper instead of `new Anthropic(...)` so future
|
|
83
83
|
// header / baseURL tweaks land in one place.
|
|
84
84
|
export { createAnthropicClient } from './adapters/claude/client.js';
|
|
85
|
+
export { AESTHETIC_BRIEF_MAX_CHARS, type GenerationProfileInput } from './boilerplate/styling-profile.js';
|
|
86
|
+
export { EFFORT_DIALS, EFFORT_TABLE_VERSION, effortDials, type EffortDials } from './effort.js';
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two fixed fixtures every prompt pin renders — one action-bearing
|
|
3
|
+
* `chat`×`mobile` shell, one `fullscreen`×`desktop` board with a
|
|
4
|
+
* `contextSpec` — shared by `design-mode.pin.test.ts` (the stable prefix)
|
|
5
|
+
* and `shape-guidance.pin.test.ts` (the axis-conditioned Shape Guidance).
|
|
6
|
+
*/
|
|
7
|
+
import type { DataContract } from '@ggui-ai/protocol';
|
|
8
|
+
|
|
9
|
+
export interface PinFixture {
|
|
10
|
+
readonly userRequest: string;
|
|
11
|
+
readonly shellType: 'chat' | 'fullscreen' | 'spatial';
|
|
12
|
+
readonly screen: 'mobile' | 'tablet' | 'desktop' | 'universal';
|
|
13
|
+
readonly contract: DataContract;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const PIN_FIXTURES: readonly PinFixture[] = [
|
|
17
|
+
{
|
|
18
|
+
userRequest: 'A todo list where each item can be toggled done',
|
|
19
|
+
shellType: 'chat',
|
|
20
|
+
screen: 'mobile',
|
|
21
|
+
contract: {
|
|
22
|
+
propsSpec: {
|
|
23
|
+
properties: {
|
|
24
|
+
todos: {
|
|
25
|
+
required: true,
|
|
26
|
+
description: 'The todos to show',
|
|
27
|
+
schema: {
|
|
28
|
+
type: 'array',
|
|
29
|
+
items: {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
id: { type: 'string' },
|
|
33
|
+
title: { type: 'string' },
|
|
34
|
+
done: { type: 'boolean' },
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
actionSpec: {
|
|
42
|
+
toggleTodo: {
|
|
43
|
+
label: 'Toggle',
|
|
44
|
+
description: 'Flip the done state of one todo',
|
|
45
|
+
schema: {
|
|
46
|
+
type: 'object',
|
|
47
|
+
properties: { id: { type: 'string' } },
|
|
48
|
+
required: ['id'],
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
userRequest: 'A kanban board with live task updates and a selected card',
|
|
56
|
+
shellType: 'fullscreen',
|
|
57
|
+
screen: 'desktop',
|
|
58
|
+
contract: {
|
|
59
|
+
propsSpec: {
|
|
60
|
+
properties: {
|
|
61
|
+
columns: {
|
|
62
|
+
required: true,
|
|
63
|
+
schema: { type: 'array', items: { type: 'string' } },
|
|
64
|
+
},
|
|
65
|
+
tasks: {
|
|
66
|
+
required: false,
|
|
67
|
+
schema: {
|
|
68
|
+
type: 'array',
|
|
69
|
+
items: {
|
|
70
|
+
type: 'object',
|
|
71
|
+
properties: {
|
|
72
|
+
id: { type: 'string' },
|
|
73
|
+
column: { type: 'string' },
|
|
74
|
+
title: { type: 'string' },
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
streamSpec: {
|
|
82
|
+
taskUpdates: {
|
|
83
|
+
description: 'Task CRUD feed',
|
|
84
|
+
schema: {
|
|
85
|
+
type: 'object',
|
|
86
|
+
properties: {
|
|
87
|
+
action: { type: 'string', enum: ['create', 'move', 'edit', 'delete'] },
|
|
88
|
+
id: { type: 'string' },
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
contextSpec: {
|
|
94
|
+
selectedId: { schema: { type: 'string', nullable: true } },
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
];
|
package/src/run-check.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
newValidatorTraceId,
|
|
23
23
|
truncateSourceForTrace,
|
|
24
24
|
} from "./harness/validator-trace-sink.js";
|
|
25
|
+
import { runGatedAxisChecks, traceAxisChecksSkipped } from "./evaluation/axis-checks/dispatch.js";
|
|
25
26
|
|
|
26
27
|
export interface RunCheckInput {
|
|
27
28
|
readonly sourceCode: string;
|
|
@@ -66,6 +67,10 @@ export async function runCheck(input: RunCheckInput): Promise<CheckResult> {
|
|
|
66
67
|
const startedAt = Date.now();
|
|
67
68
|
|
|
68
69
|
if (compiledCode === null) {
|
|
70
|
+
traceAxisChecksSkipped(
|
|
71
|
+
{ sourceCode, contract, originalPrompt: prompt, classification: harness.classification, designMode: harness.designMode },
|
|
72
|
+
"compiledCode null — no check ran",
|
|
73
|
+
);
|
|
69
74
|
const result: CheckResult = {
|
|
70
75
|
issues: [],
|
|
71
76
|
axisIssueCount: 0,
|
|
@@ -115,16 +120,11 @@ export async function runCheck(input: RunCheckInput): Promise<CheckResult> {
|
|
|
115
120
|
classification: harness.classification,
|
|
116
121
|
designMode: harness.designMode,
|
|
117
122
|
};
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
firedIds.push(axisCheck.id);
|
|
124
|
-
const axisIssues = axisCheck.run(axisInput);
|
|
125
|
-
issues.push(...axisIssues);
|
|
126
|
-
axisIssueCount += axisIssues.length;
|
|
127
|
-
}
|
|
123
|
+
// The one runner `runAxisChecks` shares (ggui#1046 trace prints here, on the served path).
|
|
124
|
+
const axisRun = runGatedAxisChecks(check.axisChecks, axisInput);
|
|
125
|
+
issues.push(...axisRun.issues);
|
|
126
|
+
firedIds.push(...axisRun.firedIds);
|
|
127
|
+
const axisIssueCount = axisRun.issues.length;
|
|
128
128
|
|
|
129
129
|
// ── Tier checks (deterministic, harness-owned) ────────────────────────
|
|
130
130
|
let tierIssueCount = 0;
|