@adia-ai/a2ui-compose 0.7.25 → 0.7.27
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/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
# Changelog — @adia-ai/a2ui-compose
|
|
2
|
+
## [0.7.27] — 2026-07-12
|
|
3
|
+
|
|
4
|
+
### Fixed
|
|
5
|
+
- **Transpiler — `[span]` is now universally preserved (TKT-0002).** `extractProps()` had a universal preserve for `[slot]` but not `[span]` — the same class of cross-cutting, layout-bearing attribute, consumed purely via `grid.css`'s `& > [span="N"]` child selectors and never declared in any component's own yaml `props:`. Every transpiled asymmetric grid collapsed to equal columns (the admin-dashboard's 3:2 Overview/Recent-Sales split was the operator-reported case). Regression tests cover `span="3"`, `span="full"`, and the absent case.
|
|
6
|
+
- **Transpiler — `Tag` is a leaf type; labels ride the `text` prop (TKT-0004 A).** `Tag` was missing from `LEAF_TYPES`, so `<tag-ui size="sm">label</tag-ui>` transpiled to `Tag > Text variant=body` — a full body-size paragraph inside the pill, inflating every converted chip (581 instances across 233 docs pages measured). Labels now ride tag.yaml's documented `attr(text)` contract, in both the pure-text and element-children extraction paths.
|
|
7
|
+
- **`strategies/zettel/issue-reporter.js` — issue storage repointed to `qa/findings/issues/`.** The previous path would have silently recreated the retired `.brain/` tree at runtime on the first `report_issue` call.
|
|
8
|
+
|
|
9
|
+
### Maintenance
|
|
10
|
+
- Transpiler test suite: `extract-props.test.js` 19 → 24 cases (span preservation ×3, Tag text contract ×2).
|
|
11
|
+
|
|
12
|
+
## [0.7.26] — 2026-07-04
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- **Monolithic engine no longer teaches `variant="outline"` as the encoding for "secondary button."** `strategies/monolithic/_shared.js`'s hardcoded system-prompt text — sent to the LLM on every generation call — read `variant="primary" for main action, "outline" for secondary, "ghost" for tertiary`, actively instructing every generation to reach for `outline` on any non-primary button. `button-ui`'s unstyled default already renders the neutral/secondary look (see `@adia-ai/web-components` changelog); corrected the prompt text to say so explicitly and to leave `variant` unset for secondary/default actions, reserving `variant="primary"` for the one CTA that should stand out and `variant="ghost"` for genuinely lower-emphasis tertiary actions. Also fixed the same anti-pattern hardcoded into the file's fallback-error-UI generator — a "Copy Debug Info" button built with `variant: 'outline'` right next to a `variant: 'primary'` "Try Again" — dropped the attribute there too.
|
|
16
|
+
- Corpus evidence that motivated the investigation: of 446 `<button-ui>` tags across harvested demo-page chunks, only 30 were bare vs. 91 explicit `variant="outline"` + 215 `variant="ghost"` — confirming the prompt text (not incidental author habit) was the dominant driver.
|
|
2
17
|
|
|
3
18
|
## [0.7.25] — 2026-07-03
|
|
4
19
|
|
|
@@ -910,7 +925,7 @@ See root [CHANGELOG.md `[Unreleased]`](../../../CHANGELOG.md) for the v0.4.5 ove
|
|
|
910
925
|
- **`compose/strategies/monolithic/generate-pro.js` + companion `_shared/fragment-expander.js` — fragment machinery removed (§37).** Fragments retired; 85 compositions inlined with previously-referenced fragment content. Expander deleted; its caller's try/catch in `generator-adapter.js` handles the absence gracefully.
|
|
911
926
|
- **MCP server `mcp/server.js` (§37).** Removed `get_fragment` tool, simplified `zettel_stats`, updated imports + boot logs + engine description to reflect the post-fragment world.
|
|
912
927
|
|
|
913
|
-
See root [CHANGELOG.md `[Unreleased]`](../../../CHANGELOG.md) for the cross-cutting arc narrative +
|
|
928
|
+
See root [CHANGELOG.md `[Unreleased]`](../../../CHANGELOG.md) for the cross-cutting arc narrative + `.claude/docs/journal/2026/05/2026-05-12.md` §§ 37 / 38 / 41 / 47 for per-§ details.
|
|
914
929
|
## [0.4.3] - 2026-05-11
|
|
915
930
|
|
|
916
931
|
### Fixed
|
|
@@ -1194,7 +1209,7 @@ packages now share one version, governed by
|
|
|
1194
1209
|
[`.claude/docs/specs/package-architecture.md` § 15 (Versioning Policy)](../../../.claude/docs/specs/package-architecture.md#15-versioning-policy).
|
|
1195
1210
|
This release also lands the `engine/` ↔ `engines/` collision fix from
|
|
1196
1211
|
T3 of the
|
|
1197
|
-
|
|
1212
|
+
`.claude/docs/plans/packages-architecture-fixes-2026-05-02.md`
|
|
1198
1213
|
plan.
|
|
1199
1214
|
|
|
1200
1215
|
### Changed
|
|
@@ -1249,8 +1264,8 @@ multi-turn surface, plus a first-class telemetry surface that auto-fires
|
|
|
1249
1264
|
issue tickets on engine failure paths.
|
|
1250
1265
|
|
|
1251
1266
|
Spec: [`.claude/docs/specs/genui-multiturn-architecture.md`](../../../.claude/docs/specs/genui-multiturn-architecture.md) (Active v0.1.0).
|
|
1252
|
-
Plan:
|
|
1253
|
-
ADR:
|
|
1267
|
+
Plan: `.claude/docs/plans/genui-multiturn-rollout-2026-04-28.md` (Phase A scoped).
|
|
1268
|
+
ADR: `0008-multiturn-genui-architecture.md`.
|
|
1254
1269
|
|
|
1255
1270
|
### Added (engine modules)
|
|
1256
1271
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adia-ai/a2ui-compose",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.27",
|
|
4
4
|
"description": "AdiaUI A2UI compose engine \u2014 framework-agnostic. Takes natural-language intents + a catalog and produces A2UI protocol messages. Pairs with `@adia-ai/a2ui-retrieval` (intent classification, catalog lookup) and `@adia-ai/a2ui-validator` (schema + semantic checks).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -245,7 +245,7 @@ KEY RULES:
|
|
|
245
245
|
- Use Table for tabular data (set data/columns via JS props, not Text children).
|
|
246
246
|
- Use EmptyState for no-content/error states (icon, heading, description, action slot).
|
|
247
247
|
- Use Skeleton for loading placeholders (variant: text|circle|rect).
|
|
248
|
-
- Button: use variant="primary" for main
|
|
248
|
+
- Button: use variant="primary" for the one main CTA that should stand out. Leave variant unset for secondary/default actions — the default (no attribute) already IS the secondary look; do NOT add variant="outline" just to mark something as non-primary. Reserve variant="ghost" for a genuinely lower-emphasis tertiary action.
|
|
249
249
|
- Button: use text prop for button label.
|
|
250
250
|
- NEVER use Text for clickable/interactive actions. "View logs", "Contact support", "Learn more" = Button (variant="ghost" or "link"), NOT Text. Text is for static content only.
|
|
251
251
|
- For error/status dialogs: use Alert for the error message, Button for recovery actions (retry, view logs, contact support). Don't render action labels as Text captions.
|
|
@@ -1391,7 +1391,7 @@ function fallbackMessage(reason, opts = {}) {
|
|
|
1391
1391
|
|
|
1392
1392
|
if (executionId || rawResponse) {
|
|
1393
1393
|
footerChildren.push('copy-btn');
|
|
1394
|
-
components.push({ id: 'copy-btn', component: 'Button', text: 'Copy Debug Info', icon: 'clipboard',
|
|
1394
|
+
components.push({ id: 'copy-btn', component: 'Button', text: 'Copy Debug Info', icon: 'clipboard', 'data-clipboard': debugPayload });
|
|
1395
1395
|
}
|
|
1396
1396
|
|
|
1397
1397
|
components.push(
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 3. Engine auto-fire — internal failure paths trigger `autoReport(reason)`.
|
|
8
8
|
* ctx.reporter = 'auto'. Suppressed when ctx.evalMode is true.
|
|
9
9
|
*
|
|
10
|
-
* Every issue lands as immutable JSON under
|
|
10
|
+
* Every issue lands as immutable JSON under `qa/findings/issues/`.
|
|
11
11
|
* Traces > 200KB spill to a sidecar `.trace.json` next to the issue.
|
|
12
12
|
*
|
|
13
13
|
* Spec: .claude/docs/specs/genui-multiturn-architecture.md §3.5 + §4.6 + §11.
|
|
@@ -23,7 +23,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
23
23
|
|
|
24
24
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
25
25
|
const REPO_ROOT = resolve(__dirname, '../../../../..');
|
|
26
|
-
const DEFAULT_STORAGE_ROOT = resolve(REPO_ROOT, '
|
|
26
|
+
const DEFAULT_STORAGE_ROOT = resolve(REPO_ROOT, 'qa/findings/issues');
|
|
27
27
|
const TRACE_INLINE_THRESHOLD_BYTES = 200 * 1024;
|
|
28
28
|
|
|
29
29
|
const VALID_TYPES = new Set(['bug', 'training-gap', 'protocol-gap', 'ux-feedback']);
|
|
@@ -225,4 +225,39 @@ describe('§163 extractProps — catalog-driven prop fidelity', () => {
|
|
|
225
225
|
expect(props.variant).toBe('outline');
|
|
226
226
|
});
|
|
227
227
|
});
|
|
228
|
+
|
|
229
|
+
describe('TKT-0004 — Tag label via text prop (attr(text) contract)', () => {
|
|
230
|
+
it('falls back to light-DOM textContent when no text= attribute', () => {
|
|
231
|
+
const el = elem({ size: 'sm', variant: 'success' }, { text: '~1 min · no tools' });
|
|
232
|
+
const props = extractProps(el, 'Tag');
|
|
233
|
+
expect(props.text).toBe('~1 min · no tools');
|
|
234
|
+
expect(props.size).toBe('sm');
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('an explicit text= attribute wins over textContent', () => {
|
|
238
|
+
const el = elem({ text: 'explicit' }, { text: 'ignored light-dom' });
|
|
239
|
+
const props = extractProps(el, 'Tag');
|
|
240
|
+
expect(props.text).toBe('explicit');
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
describe('TKT-0002 — [span] universal preservation', () => {
|
|
245
|
+
it('preserves span on a Card (grid-ui child), same as slot', () => {
|
|
246
|
+
const el = elem({ span: '3' });
|
|
247
|
+
const props = extractProps(el, 'Card');
|
|
248
|
+
expect(props.span).toBe('3');
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('preserves span="full" verbatim (not coerced to a number)', () => {
|
|
252
|
+
const el = elem({ span: 'full' });
|
|
253
|
+
const props = extractProps(el, 'Card');
|
|
254
|
+
expect(props.span).toBe('full');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('omits span when absent — no stray empty prop', () => {
|
|
258
|
+
const el = elem({});
|
|
259
|
+
const props = extractProps(el, 'Card');
|
|
260
|
+
expect(props.span).toBeUndefined();
|
|
261
|
+
});
|
|
262
|
+
});
|
|
228
263
|
});
|
|
@@ -317,6 +317,17 @@ export function extractProps(el, a2uiType) {
|
|
|
317
317
|
const slotAttr = attr('slot');
|
|
318
318
|
if (slotAttr) props.slot = slotAttr;
|
|
319
319
|
|
|
320
|
+
// Universal: preserve [span] for every element. Span is a layout-bearing
|
|
321
|
+
// attribute grid-ui's child reads via a pure CSS attribute selector
|
|
322
|
+
// (grid.css: `& > [span="3"] { grid-column: span 3; }`) — it belongs to
|
|
323
|
+
// "whatever this grid child happens to be," never to a specific
|
|
324
|
+
// component's own prop schema, so no .yaml declares it and the
|
|
325
|
+
// catalog-driven pass below silently drops it (TKT-0002: collapsed the
|
|
326
|
+
// admin-dashboard Overview/Recent Sales 3:2 split to two equal, unspanned
|
|
327
|
+
// columns).
|
|
328
|
+
const spanAttr = attr('span');
|
|
329
|
+
if (spanAttr) props.span = spanAttr;
|
|
330
|
+
|
|
320
331
|
// §163 (v0.5.4) — Catalog-driven generic extraction FIRST. Fills in
|
|
321
332
|
// every prop the v0.9 catalog declares for this type (label, icon,
|
|
322
333
|
// variant=, etc.). Legacy per-type blocks below run AFTER and can
|
|
@@ -351,6 +362,15 @@ export function extractProps(el, a2uiType) {
|
|
|
351
362
|
}
|
|
352
363
|
}
|
|
353
364
|
|
|
365
|
+
// ── Tag ── (TKT-0004: tag-ui renders its label via CSS attr(text) —
|
|
366
|
+
// tag.yaml documents textContent as routing to the text attribute. The
|
|
367
|
+
// authored form is light-DOM content (<tag-ui size="sm">label</tag-ui>),
|
|
368
|
+
// so fall back to textContent when no text= attribute is set.)
|
|
369
|
+
if (a2uiType === 'Tag' && props.text === undefined) {
|
|
370
|
+
const text = (el.textContent || '').trim().replace(/\s+/g, ' ');
|
|
371
|
+
if (text) props.text = text;
|
|
372
|
+
}
|
|
373
|
+
|
|
354
374
|
// ── Link ── (§47 — <a> tags map to Link, not Button; carries href, target, rel)
|
|
355
375
|
if (a2uiType === 'Link') {
|
|
356
376
|
const text = (el.textContent || '').trim().replace(/\s+/g, ' ');
|
package/transpiler/transpiler.js
CHANGED
|
@@ -201,9 +201,13 @@ function walkNode(el, context) {
|
|
|
201
201
|
return id;
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
// Leaf types: these render their own text, don't recurse into children
|
|
204
|
+
// Leaf types: these render their own text, don't recurse into children.
|
|
205
|
+
// Tag joined 2026-07-12 (TKT-0004): tag-ui renders its label via CSS
|
|
206
|
+
// attr(text) — as a container, its light-DOM label became a child Text
|
|
207
|
+
// at body size, inflating every converted chip site-wide (581 instances
|
|
208
|
+
// across 233 pages measured).
|
|
205
209
|
const LEAF_TYPES = new Set([
|
|
206
|
-
'Text', 'Button', 'Kbd', 'Code', 'Badge', 'Image', 'Divider',
|
|
210
|
+
'Text', 'Button', 'Kbd', 'Code', 'Badge', 'Tag', 'Image', 'Divider',
|
|
207
211
|
'TextField', 'CheckBox', 'Toggle', 'Radio', 'Slider', 'Search',
|
|
208
212
|
'Upload', 'ColorPicker', 'DateTimeInput', 'TextArea', 'OtpInput',
|
|
209
213
|
]);
|
|
@@ -244,7 +248,7 @@ function walkNode(el, context) {
|
|
|
244
248
|
if (directText) {
|
|
245
249
|
// Preserve the direct text on whichever prop the type uses
|
|
246
250
|
if (a2uiType === 'Text' || a2uiType === 'Kbd' || a2uiType === 'Code') containerProps.textContent = directText;
|
|
247
|
-
else if (a2uiType === 'Button' || a2uiType === 'Badge') {
|
|
251
|
+
else if (a2uiType === 'Button' || a2uiType === 'Badge' || a2uiType === 'Tag') {
|
|
248
252
|
if (containerProps.text === undefined) containerProps.text = directText;
|
|
249
253
|
}
|
|
250
254
|
} else {
|