@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 + [.claude/docs/journal/2026/05/2026-05-12.md](../../../.claude/docs/journal/2026/05/2026-05-12.md) §§ 37 / 38 / 41 / 47 for per-§ details.
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
- [`.claude/docs/plans/packages-architecture-fixes-2026-05-02.md`](../../../.brain/archive/2026-Q2/PLAN-packages-architecture-fixes-2026-05-02.md)
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: [`.claude/docs/plans/genui-multiturn-rollout-2026-04-28.md`](../../../.claude/docs/plans/genui-multiturn-rollout-2026-04-28.md) (Phase A scoped).
1253
- ADR: [`0008-multiturn-genui-architecture.md`](../../../.brain/adrs/0008-multiturn-genui-architecture.md).
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.25",
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 action, "outline" for secondary, "ghost" for tertiary.
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', variant: 'outline', 'data-clipboard': debugPayload });
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 `.brain/audit-history/issues/`.
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, '.brain/audit-history/issues');
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, ' ');
@@ -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 {