@adia-ai/a2ui-compose 0.5.5 → 0.5.7

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
@@ -7,11 +7,57 @@ transpiler, evals. Retrieval + validation now ship as sibling
7
7
  packages (`@adia-ai/a2ui-retrieval`, `@adia-ai/a2ui-validator`) so
8
8
  non-compose consumers can depend on them without pulling the
9
9
  generator graph.
10
-
11
10
  ## [Unreleased]
12
11
 
13
12
  _No pending changes._
14
13
 
14
+ ## [0.5.7] - 2026-05-15
15
+
16
+ ### Changed — §210 cross-package sweep — `transpiler/`+`strategies/` text-variant retargeting (v0.5.7)
17
+
18
+ Companion to `@adia-ai/web-components@0.5.7` §210 (`<text-ui>` variant enum cleanup — removed 6 phantom entries `h1`-`h6` + `subheading`).
19
+
20
+ - `transpiler/transpiler-maps.js` — `HTML_TAG_MAP` retargets native `<h1>`-`<h6>` + `<summary>` from removed `h1`-`h6` variants to working role tokens (`display` / `title` / `heading` / `subsection`). Pre-§210 the mapping rendered silent body-default; post-§210 each level renders at its declared token size.
21
+ - `strategies/monolithic/_shared.js` — TEXT TYPES system-prompt block updated to enumerate the actual `<text-ui>` variant enum (display/title/heading/subsection/section for headings; body / caption / label / kicker / code / deck / metric for inline / meta / lede). Generator prompt now matches the runtime contract.
22
+ - `strategies/free-form-composer/free-form-composer.test.js` — fixture variant `'h1'` → `'display'` (test stub uses real variant).
23
+
24
+ ## [0.5.6] - 2026-05-14
25
+
26
+ ### Removed — §195 (v0.5.6) — retired fragment-graph iteration codepath
27
+
28
+ Deleted `packages/a2ui/compose/strategies/zettel/synthesizer.js`. The
29
+ file shipped the `synthesizeComposition` function as a throwing stub
30
+ since §37 (v0.4.7, 2026-05-12, fragment retirement). The dangling
31
+ caller in `generator-adapter.js` (iteration branch, turn ≥ 2 + LLM
32
+ present) caught the throw and auto-fired an `iteration-synthesis-failure`
33
+ issue ticket on every multi-turn refinement — surfaced as an auto-fired
34
+ bug at `.brain/audit-history/issues/2026-05-14-iteration-synthesis-failed-on-turn-3-for-4bfb.json`.
35
+
36
+ ### Changed — §195 (v0.5.6) — weak-retrieval branch bridges to chunk-zettel
37
+
38
+ Replaced the `synthesizeComposition()` call in `generateZettel`'s
39
+ weak-retrieval-with-LLM branch with a bridge to
40
+ `chunk-synthesizer.js::composeFromIntent` (the §37 successor codepath).
41
+ Return shape is preserved (`messages`, `validation`, `strategy:
42
+ 'composition-synthesized'`, `fragments_used: []`, `synthesized_template`,
43
+ `synthesis`). Fragment instances are no longer tracked on this path —
44
+ the chunk-bridge emits a single html-bearing component. Consumers that
45
+ need fragment-level resolution should wire via `engine: 'chunk-zettel'`
46
+ directly.
47
+
48
+ The `composition-iterated` strategy is retired — turn ≥ 2 on
49
+ `engine: 'zettel'` now takes the same path as turn 1. True
50
+ history-aware iteration lives in `chunk-refiner.js::refineFromIntent`
51
+ behind `engine: 'chunk-zettel'`.
52
+
53
+ ### Added — §195 (v0.5.6) — smoke probe
54
+
55
+ NEW `scripts/smoke-iteration-synthesis.mjs` (npm run
56
+ `smoke:iteration-synthesis`) verifies the §195 invariants at three
57
+ layers: filesystem (`synthesizer.js` deleted), source
58
+ (`generator-adapter.js` has no surviving reference to the retired
59
+ identifiers), and runtime (4 sequential turns against the same
60
+ sessionId neither throw nor emit `composition-iterated`).
15
61
  ## [0.5.5] - 2026-05-14
16
62
 
17
63
  ### Changed — §179 (v0.5.5) — principled `deprecated:` yaml field
@@ -27,7 +73,6 @@ Build pipeline (`scripts/build/components.mjs::compileProp`) collects deprecatio
27
73
  Demonstrated on `Avatar.name`. Verified via `buildSystemPrompt`: `name:String[] (deprecated, do not use)` surfaces in CORPUS CONTEXT.
28
74
 
29
75
  Commit `0379dd498`. See root CHANGELOG and journal §179 for full context.
30
-
31
76
  ## [0.5.4] - 2026-05-14
32
77
 
33
78
  ### Fixed — §163 transpiler prop-fidelity; catalog-driven extractor closes the harvester drop class (v0.5.4)
@@ -201,7 +246,6 @@ No compose source change in §173/§175 — both detect future drift in compose'
201
246
  catalog-vs-prompt + registry-vs-catalog invariants.
202
247
 
203
248
  ### Changed — `version`: `0.5.3` → `0.5.4`.
204
-
205
249
  ## [0.5.3] - 2026-05-14
206
250
 
207
251
  ### Fixed — Deterministic chunk-loading order in zettel composition library (§160, v0.5.3)
@@ -224,7 +268,6 @@ Finalizes the v0.6.0 deprecation schedule for the last two `_debug.*` fields tha
224
268
  **Scheduled removal**: v0.6.0 drops the `_debug` block entirely from the free-form-composed result shape. The dialog-recorder will read first-class fields directly. v0.5.3 is the **migration window** — any external consumers reading `_debug.attempts` / `_debug.warnings` should switch to the first-class fields before v0.6.0 ships.
225
269
 
226
270
  **Internal verification**: zero live in-repo consumers (`grep -rn '_debug\?\.attempts\|_debug\.attempts\|_debug\?\.warnings\|_debug\.warnings' apps/ playgrounds/ catalog/ packages/` returns only the deprecation comment itself).
227
-
228
271
  ## [0.5.2] - 2026-05-13
229
272
 
230
273
  ### Changed — `plan` graduates from `_debug.*` to first-class on free-form-composed result (§107a infra, v0.5.2)
@@ -259,7 +302,6 @@ Expected impact: lifts substitution ratio 27.4% → ~35-40% (target ≥30%); F1
259
302
  `strategies/registry.js` reads model priority chain at call-time (not module-load): `ctx.model` > `process.env.FREE_FORM_MODEL_OVERRIDE` > `FREE_FORM_MODEL_DEFAULT` (Haiku 4.5). Enables `eval-diff.mjs --model <id>` to run the Haiku-vs-Opus A/B for §127 without env-dance or static-import-ordering hazards. `FREE_FORM_MODEL` constant renamed to `FREE_FORM_MODEL_DEFAULT` to reflect the new priority chain.
260
303
 
261
304
  Default behavior unchanged when no override set — the v0.5.1 §108 Haiku pin holds for every consumer that doesn't explicitly override.
262
-
263
305
  ## [0.5.1] - 2026-05-13
264
306
 
265
307
  ### Added — Free-form composer INTENT-PARAPHRASE block + paraphrase-retry (§106, v0.5.1)
@@ -281,7 +323,6 @@ Consumers passing `ctx.llmAdapter` keep getting their adapter via the mint-failu
281
323
  ### Coverage at v0.5.1 cut
282
324
 
283
325
  Post-§106 prompt-tuning + §108 picker pin + §109 first-class graduation, free-form composer coverage measured at **~96-97%** on the 100-intent held-out set (up from §104's 92%). New AGENTS.md regression threshold floor: `cov≥96%, avg≥85, F1≥0.60` (§115 trip-wire baseline).
284
-
285
326
  ## [0.5.0] - 2026-05-13
286
327
 
287
328
  ### Added — Free-form composer auto-grouping (§103, v0.5.0)
@@ -299,17 +340,14 @@ Schema validation rejects unknown target keys; downgrade warnings fire when a ta
299
340
  ### Coverage at v0.5.0 cut
300
341
 
301
342
  After §93 (layout regrowth) + §94 (forms regrowth) + §103 (auto-grouping) + §104 (structural substitutions + v0.5.1 deferred regrowth fold-in), free-form composer coverage measured at **92% on the 100-intent held-out set** (up from 21% at §92 baseline). New AGENTS.md regression threshold floor: `cov≥80%, avg≥85, F1≥0.45` (§97 rebaseline).
302
-
303
343
  ## [0.4.9] - 2026-05-13
304
344
 
305
345
  _No pending changes._
306
-
307
346
  ## [0.4.8] - 2026-05-12
308
347
 
309
348
  ### Fixed — `strategies/zettel/generator-adapter.js` `ensureBooted()` race (§87a, v0.4.8)
310
349
 
311
350
  The zettel composition library's lazy boot had a race condition where concurrent `ensureBooted()` calls could each kick off a separate boot promise. Under contention (multiple requests landing simultaneously at server cold-start), the composition map could be partially populated when consumers reached the synchronous getters. Fix: memoize the boot promise on first call so all subsequent callers await the same promise. Pairs with the v0.4.8 §87 honest-floor eval threshold rebaseline — without the boot race fix, the 1% rebaseline was actually undercounting due to occasional empty-map reads.
312
-
313
351
  ## [0.4.7] - 2026-05-12
314
352
 
315
353
  ### Changed — `strategies/monolithic/_shared.js` `getComponentCatalog()` reads canonical catalog (§72)
@@ -317,7 +355,6 @@ The zettel composition library's lazy boot had a race condition where concurrent
317
355
  The legacy reader of `@adia-ai/a2ui-corpus/patterns/_components.json` has been migrated to read `@adia-ai/a2ui-corpus/catalog-a2ui_0_9.json` (the canonical v0.9 catalog, already the package root export). Per-component aliases are now lifted from `components[name].x-adiaui.synonyms.tags`. Same legacy output shape (`{ <Name>: { aliases: string[] } }`); zero behavior change for downstream callers.
318
356
 
319
357
  Closes the §65 carry-over from v0.4.6 — `@adia-ai/a2ui-corpus` `patterns/` + `compositions/` are now deleted from disk + tarball.
320
-
321
358
  ## [0.4.6] - 2026-05-12
322
359
 
323
360
  ### Changed — `core/` retirement follow-through (§64, v0.4.6)
@@ -353,7 +390,6 @@ Closes the catalog-drift surface that §56 explicitly deferred ("different schem
353
390
  5. `const` fields (e.g. `{const: 'Button'}`) are discriminator-only. Skip.
354
391
 
355
392
  Source: `packages/a2ui/compose/strategies/monolithic/_shared.js` (+91, -2 lines, commit `afda98f5`).
356
-
357
393
  ## [0.4.5] - 2026-05-12
358
394
 
359
395
  ### Changed — GenUI overhaul prompt-engineering (§56, v0.4.5)
@@ -369,7 +405,6 @@ Source: `packages/a2ui/compose/strategies/monolithic/_shared.js` (+91, -2 lines,
369
405
  - **`strategies/monolithic/generate-pro.js` — STRUCTURAL REFERENCE prose enriched.** Prior copy: "a real production block from the codebase matched this intent." New copy: "this chunk was retrieved from the AdiaUI training corpus (annotated production HTML, harvested from real app pages). It matched your intent on keyword/domain ranking." Threads chunk `metadata.domain`, `metadata.description`, `metadata.keywords` into the prompt when present. Reframes "do not copy the HTML" as "the chunk represents the SHAPE the user wants; instantiate it with their content" — more actionable framing.
370
406
 
371
407
  See root [CHANGELOG.md `[Unreleased]`](../../../CHANGELOG.md) for the v0.4.5 overhaul arc + apps/genui/CHANGELOG.md `[Unreleased]` for the per-§ rollup.
372
-
373
408
  ## [0.4.4] - 2026-05-12
374
409
 
375
410
  ### Changed
@@ -386,7 +421,6 @@ See root [CHANGELOG.md `[Unreleased]`](../../../CHANGELOG.md) for the v0.4.5 ove
386
421
  - **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.
387
422
 
388
423
  See root [CHANGELOG.md `[Unreleased]`](../../../CHANGELOG.md) for the cross-cutting arc narrative + [docs/journal/2026/05/2026-05-12.md](../../../docs/journal/2026/05/2026-05-12.md) §§ 37 / 38 / 41 / 47 for per-§ details.
389
-
390
424
  ## [0.4.3] - 2026-05-11
391
425
 
392
426
  ### Fixed
@@ -396,7 +430,6 @@ See root [CHANGELOG.md `[Unreleased]`](../../../CHANGELOG.md) for the cross-cutt
396
430
  ### Lockstep
397
431
 
398
432
  9-package coordinated PATCH cut to v0.4.3 (per [`docs/specs/package-architecture.md` § 15](../../../docs/specs/package-architecture.md#15-versioning-policy)). Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4.0` covers `0.4.x` under semver). Source change scoped to `core/generator.js` + `strategies/zettel/state-cache.js`. Rides alongside `@adia-ai/web-components` v0.4.3 (input-ui locale + thousands grouping + hold-to-repeat). See root [CHANGELOG.md `## [0.4.3]`](../../../CHANGELOG.md) for the cut narrative.
399
-
400
433
  ## [0.4.2] - 2026-05-11
401
434
 
402
435
  ### Ride-along (no source changes)
@@ -404,7 +437,6 @@ See root [CHANGELOG.md `[Unreleased]`](../../../CHANGELOG.md) for the cross-cutt
404
437
  Lockstep PATCH cut alongside `@adia-ai/web-components@0.4.2` (`<input-ui type="number">` rewrite drops native `<input type=number>` wrapping) + `@adia-ai/web-modules@0.4.2` (`<editor-sidebar>` grid-track width-mirror fix). Source byte-identical to v0.4.1.
405
438
 
406
439
  Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4.0` covers `0.4.x` under semver). See root [CHANGELOG.md `## [0.4.2]`](../../../CHANGELOG.md) for the cut narrative.
407
-
408
440
  ## [0.4.1] - 2026-05-10
409
441
 
410
442
  ### Ride-along (no source changes)
@@ -412,7 +444,6 @@ Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4
412
444
  Lockstep PATCH cut alongside `@adia-ai/web-modules@0.4.1` (simple cluster) + `@adia-ai/a2ui-validator@0.4.1` (Phase 3 foundation) + `@adia-ai/a2ui-corpus@0.4.1` (fragment metrics reconciliation). Source byte-identical to v0.4.0.
413
445
 
414
446
  Internal `@adia-ai/*` dep ranges bumped from `^0.4.0` to `^0.4.1`. See root [CHANGELOG.md `## [0.4.1]`](../../../CHANGELOG.md) for the cut narrative.
415
-
416
447
  ## [0.4.0] - 2026-05-10
417
448
 
418
449
  ### Ride-along (no source changes)
@@ -420,25 +451,21 @@ Internal `@adia-ai/*` dep ranges bumped from `^0.4.0` to `^0.4.1`. See root [CHA
420
451
  Lockstep MINOR cut alongside `@adia-ai/web-modules@0.4.0` (ADR-0024 legacy shell shapes retired). Source byte-identical to v0.3.6.
421
452
 
422
453
  Internal `@adia-ai/*` dep ranges bumped from `^0.3.0` to `^0.4.0`. See root [CHANGELOG.md `## [0.4.0]`](../../../CHANGELOG.md) for the cut narrative.
423
-
424
454
  ## [0.3.6] - 2026-05-10
425
455
 
426
456
  ### Ride-along (no source changes)
427
457
 
428
458
  Lockstep version bump only — source byte-identical to v0.3.5. Internal `@adia-ai/*` dep ranges remain at `^0.3.0`. See root [CHANGELOG.md `## [0.3.6]`](../../../CHANGELOG.md) for the cut narrative.
429
-
430
459
  ## [0.3.5] - 2026-05-07
431
460
 
432
461
  ### Ride-along (no source changes)
433
462
 
434
463
  Lockstep version bump only — source byte-identical to v0.3.4. Internal `@adia-ai/*` dep ranges remain at `^0.3.0`. See root [CHANGELOG.md `## [0.3.5]`](../../../CHANGELOG.md) for the cut narrative.
435
-
436
464
  ## [0.3.4] - 2026-05-07
437
465
 
438
466
  ### Ride-along (no source changes)
439
467
 
440
468
  Lockstep version bump only — source byte-identical to v0.3.3. Internal `@adia-ai/*` dep ranges remain at `^0.3.0`. See root [CHANGELOG.md `## [0.3.4]`](../../../CHANGELOG.md) for the cut narrative.
441
-
442
469
  ## [0.3.3] - 2026-05-07
443
470
 
444
471
  **Lockstep cut.** All 9 published `@adia-ai/*` packages now share version `0.3.3`, governed by [`docs/specs/package-architecture.md` § 15](../../../docs/specs/package-architecture.md#15-versioning-policy). Internal `@adia-ai/*` ranges stay at `^0.3.0` (patch-cut asymmetry — caret floats `0.3.x`).
@@ -483,7 +510,6 @@ Lockstep version bump only — source byte-identical to v0.3.3. Internal `@adia-
483
510
  for the lifetime of the process. Trade-offs: good for long-running
484
511
  MCP, bad for tests/hot-reload. To force a reload, call `loadAll()`
485
512
  directly. (closes backlog #100)
486
-
487
513
  ## [0.3.2] - 2026-05-06
488
514
 
489
515
  **9-package lockstep patch cut to v0.3.2.** All lockstep members share
@@ -510,7 +536,6 @@ Internal `@adia-ai/*` dep ranges unchanged at `^0.3.0`.
510
536
  ### Changed
511
537
 
512
538
  - `version`: `0.3.1` → `0.3.2`.
513
-
514
539
  ## [0.3.1] - 2026-05-06
515
540
 
516
541
  **9-package lockstep patch cut.** All 9 published `@adia-ai/*` packages bump 0.3.0 → 0.3.1 per [`docs/specs/package-architecture.md` § 15](../../../docs/specs/package-architecture.md#15-versioning-policy). Internal `@adia-ai/*` dep ranges remain at `^0.3.0` (covers `0.3.1` under semver — patch-cut asymmetry).
@@ -521,7 +546,6 @@ This package itself ships **no source changes** in v0.3.1. The cut bumps version
521
546
 
522
547
  - `version`: `0.3.0` → `0.3.1`.
523
548
  - Internal `@adia-ai/*` dep ranges: unchanged at `^0.3.0` (covers `0.3.1` under semver — patch-cut asymmetry).
524
-
525
549
  ## [0.3.0] - 2026-05-05
526
550
 
527
551
  **9-package lockstep cut + LLM subpath dropped.** All 9 published `@adia-ai/*` packages bump 0.2.5 → 0.3.0 per [`docs/specs/package-architecture.md` § 15](../../../docs/specs/package-architecture.md#15-versioning-policy). Internal `@adia-ai/*` dep ranges bump `^0.2.0` → `^0.3.0`.
@@ -567,7 +591,6 @@ This is a **minor cut on top of v0.2.5 with one BREAKING change**: the `./llm` s
567
591
  ```
568
592
 
569
593
  Plus rewrite imports as shown above. The change is mechanical.
570
-
571
594
  ## [0.2.5] - 2026-05-04
572
595
 
573
596
  **8-package lockstep cut.** All 8 published `@adia-ai/*` packages bump 0.2.4 → 0.2.5 per [`docs/specs/package-architecture.md` § 15](../../../docs/specs/package-architecture.md#15-versioning-policy). Internal `@adia-ai/*` dep ranges remain at `^0.2.0` (covers 0.2.5 under semver — patch-cut asymmetry).
@@ -578,7 +601,6 @@ This is a **patch cut on top of v0.2.4, no BREAKING changes.** Substantive conte
578
601
 
579
602
  - `version`: `0.2.4` → `0.2.5`.
580
603
  - Internal `@adia-ai/*` dep ranges: unchanged at `^0.2.0` (covers `0.2.5` under semver — patch-cut asymmetry).
581
-
582
604
  ## [0.2.4] - 2026-05-04
583
605
 
584
606
  **8-package lockstep cut.** All 8 published `@adia-ai/*` packages bump 0.2.3 → 0.2.4 per [`docs/specs/package-architecture.md` § 15](../../../docs/specs/package-architecture.md#15-versioning-policy). Internal `@adia-ai/*` dep ranges remain at `^0.2.0` (covers 0.2.4 under semver — patch-cut asymmetry).
@@ -597,7 +619,6 @@ _Nothing yet._
597
619
  ---
598
620
 
599
621
  ---
600
-
601
622
  ## [0.2.3] - 2026-05-04
602
623
 
603
624
  **Lockstep cut.** All 8 published `@adia-ai/*` packages bump
@@ -615,7 +636,6 @@ Patch cut — no breaking changes.
615
636
  ### No source changes
616
637
 
617
638
  `@adia-ai/a2ui-compose` source is byte-identical to `0.2.2`. The cut bumps version + the internal dep range entries only.
618
-
619
639
  ## [0.2.2] - 2026-05-02
620
640
 
621
641
  **Lockstep cut + reasoning-panel emission fixes.** All 8 published `@adia-ai/*` packages bump 0.2.1 → 0.2.2 per [`docs/specs/package-architecture.md` § 15](../../../../docs/specs/package-architecture.md#15-versioning-policy). Patch cut — no breaking changes.
@@ -639,7 +659,6 @@ Patch cut — no breaking changes.
639
659
  Companion fix in `@adia-ai/a2ui-retrieval` Unreleased (web-research.js EXPLICIT/IMPLICIT pattern split). Both surfaces feed the same reasoning-panel deception class — five distinct bugs caught + fixed in commit `9986c71e`.
640
660
 
641
661
  ---
642
-
643
662
  ## [0.2.1] - 2026-05-02
644
663
 
645
664
  **Lockstep cut + scope-drift gate at composer time + skeleton harvest + Tier-1 block filter + high-resolution session ticket trace.** All 8 published `@adia-ai/*` packages bump 0.2.0 → 0.2.1 per [`docs/specs/package-architecture.md` § 15](../../../../docs/specs/package-architecture.md#15-versioning-policy). Patch cut — no breaking changes.
@@ -678,7 +697,6 @@ The synthesizer now captures a `retrievalTrace` per attempt: Tier-1 hits with sc
678
697
  `issue-reporter.js` renders this as a sibling Markdown ticket alongside the JSON; sections cover header, description, reproduction, component count, retrieval log table, LLM attempts (raw responses), user prompt, composer plan, generated HTML preview, warnings, ops history, environment. A maintainer can replay any flagged session from the ticket alone.
679
698
 
680
699
  ---
681
-
682
700
  ## [0.2.0] - 2026-05-02
683
701
 
684
702
  **Lockstep cut + boundary cleanup.** All 8 published `@adia-ai/*`
@@ -733,7 +751,6 @@ cycle, but should update on next touch:
733
751
  ```
734
752
 
735
753
  ---
736
-
737
754
  ## [0.1.0] - 2026-04-28
738
755
 
739
756
  **Multi-turn refinement engine (Phase A).** Adds three new modules to the
@@ -820,7 +837,6 @@ Additive surface; no breaking changes. Existing consumers calling
820
837
  `composeFromIntent` continue to work unchanged.
821
838
 
822
839
  ---
823
-
824
840
  ## [0.0.1] - 2026-04-24
825
841
 
826
842
  First public release. Framework-agnostic compose engine for the
@@ -879,7 +895,6 @@ behind a plug-in engine registry.
879
895
  - Stale internal-identifier references in comments + doc strings swept to the current naming.
880
896
 
881
897
  ---
882
-
883
898
  ## [0.1.0] — internal baseline (unreleased)
884
899
 
885
900
  Initial version at the time the monorepo was established. Contains:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@adia-ai/a2ui-compose",
3
- "version": "0.5.5",
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).",
3
+ "version": "0.5.7",
4
+ "description": "AdiaUI A2UI compose engine 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": {
7
7
  ".": "./index.js",
@@ -22,7 +22,7 @@ const FIXTURE_VOCAB = [
22
22
  keywords: ['login', 'auth'],
23
23
  template: [
24
24
  { id: 'card', component: 'Card', children: ['title', 'submit'] },
25
- { id: 'title', component: 'Text', variant: 'h1', textContent: 'Sign in' },
25
+ { id: 'title', component: 'Text', variant: 'display', textContent: 'Sign in' },
26
26
  { id: 'submit', component: 'Button', text: 'Continue', variant: 'primary' },
27
27
  ],
28
28
  },
@@ -33,7 +33,7 @@ const FIXTURE_VOCAB = [
33
33
  keywords: ['signup', 'register'],
34
34
  template: [
35
35
  { id: 'card', component: 'Card', children: ['title'] },
36
- { id: 'title', component: 'Text', variant: 'h1', textContent: 'Create account' },
36
+ { id: 'title', component: 'Text', variant: 'display', textContent: 'Create account' },
37
37
  ],
38
38
  },
39
39
  ];
@@ -195,9 +195,10 @@ LAYOUT PRIMITIVES:
195
195
  - Grid (grid-ui): CSS grid. Use columns="2"|"3"|"4", gap="4".
196
196
 
197
197
  TEXT TYPES (component="Text"):
198
- - variant="h1" through "h6" for headings (renders native heading tags)
199
- - variant="body" for paragraphs
200
- - for small text
198
+ - variant="display" / "title" / "heading" / "subsection" / "section" for headings (largest smallest)
199
+ - variant="body" for paragraphs (default)
200
+ - variant="caption" / "label" / "kicker" / "code" for small / meta text
201
+ - variant="deck" / "metric" for lede / KPI text
201
202
  - Use textContent prop for the display text
202
203
 
203
204
  TABS (component="Tabs"):
@@ -903,6 +904,15 @@ export function mergeCanvasDiff(priorComponents, diffComponents) {
903
904
  const modifiedIds = new Set();
904
905
  const newComponents = [];
905
906
 
907
+ // §190b (v0.5.6) — Aggregate blocked-layout-type-change warnings.
908
+ // The for-loop below can fire `console.warn` once per blocked component,
909
+ // producing 5-10 line bursts when the LLM tries to restructure a form's
910
+ // fields (typical case: 5 Field→Column blocks for a 5-field form).
911
+ // Collect blocks into an array; emit a single aggregated warning after
912
+ // the loop. Closes the 2026-05-14 user-reported "buglabels" intent
913
+ // emitting 6 consecutive blocks.
914
+ const blockedLayoutChanges = [];
915
+
906
916
  for (const dc of diffComponents) {
907
917
  if (!dc || !dc.id) continue;
908
918
 
@@ -920,7 +930,7 @@ export function mergeCanvasDiff(priorComponents, diffComponents) {
920
930
  const LAYOUT_CONTAINERS = new Set(['Grid', 'Row', 'Column', 'Stack']);
921
931
  if (dc.component && dc.component !== existing.component &&
922
932
  (LAYOUT_CONTAINERS.has(existing.component) || LAYOUT_CONTAINERS.has(dc.component))) {
923
- console.warn(`[mergeCanvasDiff] Blocked layout type change: ${existing.component}→${dc.component} on id="${dc.id}". Preserving original type.`);
933
+ blockedLayoutChanges.push({ id: dc.id, from: existing.component, to: dc.component });
924
934
  delete dc.component;
925
935
  }
926
936
  const merged = { ...existing, ...dc };
@@ -934,6 +944,14 @@ export function mergeCanvasDiff(priorComponents, diffComponents) {
934
944
  }
935
945
  }
936
946
 
947
+ // §190b — single aggregated warning for blocked layout changes.
948
+ if (blockedLayoutChanges.length > 0) {
949
+ const summary = blockedLayoutChanges
950
+ .map(b => `${b.from}→${b.to} on id="${b.id}"`)
951
+ .join(', ');
952
+ console.warn(`[mergeCanvasDiff] Blocked ${blockedLayoutChanges.length} layout type change${blockedLayoutChanges.length === 1 ? '' : 's'}: ${summary}. Preserving original types.`);
953
+ }
954
+
937
955
  // Build result: prior components (minus deleted) + new components
938
956
  const result = [];
939
957
  for (const c of priorComponents) {
@@ -197,8 +197,12 @@ describe('§168 mergeCanvasDiff — iteration handoff invariants', () => {
197
197
  const result = mergeCanvasDiff(prior, llmOutput);
198
198
  // Grid type preserved despite LLM's attempted change
199
199
  expect(result.find(c => c.id === 'root').component).toBe('Grid');
200
+ // §190b — warning is aggregated: "Blocked N layout type change(s): …"
200
201
  expect(warn).toHaveBeenCalledWith(
201
- expect.stringContaining('Blocked layout type change')
202
+ expect.stringContaining('Blocked 1 layout type change')
203
+ );
204
+ expect(warn).toHaveBeenCalledWith(
205
+ expect.stringContaining('Grid→Column on id="root"')
202
206
  );
203
207
  warn.mockRestore();
204
208
  });
@@ -3,35 +3,66 @@
3
3
  *
4
4
  * generate({ intent, mode, llmAdapter, sessionId }) -> { messages, validation, strategy, ...extra }
5
5
  *
6
- * Three reasoning layers:
6
+ * Two reasoning layers (post-§195, v0.5.6):
7
7
  * 1. Retrieval (always available) — keyword-rank the corpus, resolve top composition.
8
- * 2. LLM synthesis (when llmAdapter provided AND retrieval weak) — compose a new
9
- * composition from fragments, have the composer resolve it into A2UI messages.
10
- * 3. Session-aware iteration (when sessionId provided AND prior turns exist) —
11
- * pass prior-turn history to the LLM so follow-ups ("add a button", "hydrate
12
- * with real images") modify the existing canvas instead of regenerating.
8
+ * 2. LLM synthesis (when llmAdapter provided AND retrieval weak) — bridge to
9
+ * chunk-zettel's `composeFromIntent` (the §37 successor codepath) which
10
+ * produces a single-shot html composition from the chunk corpus.
11
+ *
12
+ * Session iteration is currently NOT history-aware in this engine. Prior turns
13
+ * are recorded for analytics + drift tracking, but turn≥2 takes the same code
14
+ * path as turn 1 (fresh retrieval → fresh synthesis). Full history-aware
15
+ * iteration (multi-turn refinement modifying an existing canvas) lives in the
16
+ * `chunk-zettel` engine via `chunk-refiner.js::refineFromIntent` — wire it via
17
+ * `engine: 'chunk-zettel'` rather than `engine: 'zettel'`.
18
+ *
19
+ * The prior fragment-graph iteration codepath (`synthesizeComposition` with
20
+ * `historySummary`) was retired in §37 (2026-05-12) when fragments retired.
21
+ * §195 (v0.5.6) cleaned up the dangling caller — turn≥2 no longer throws +
22
+ * auto-fires a `iteration-synthesis-failure` issue on every multi-turn turn.
13
23
  *
14
24
  * Strategy labels in the return:
15
25
  * - composition-match — fresh retrieval, strong match, emitted verbatim
16
- * - composition-synthesized — fresh LLM composition (no prior turns)
17
- * - composition-iterated — LLM modified prior turn's template
26
+ * - composition-synthesized — chunk-zettel single-shot synthesis
18
27
  * - fragment-candidates — retrieval weak + no LLM, returning atoms only
19
- * - synthesis-failed — LLM tried and failed validation
28
+ * - synthesis-failed — chunk-zettel tried and failed validation
20
29
  */
21
30
  import {
22
31
  getComposition,
23
32
  searchAll,
24
33
  } from './composition-library.js';
25
34
  import { resolveComposition, templateToMessages } from './composer.js';
26
- import { synthesizeComposition } from './synthesizer.js';
27
35
  import {
28
36
  recordTurn,
29
37
  getTurns,
30
- buildHistorySummary,
31
38
  } from './session-store.js';
32
- import { autoReport } from './issue-reporter.js';
33
39
  import { validateSchema } from '../../../validator/validator.js';
34
40
 
41
+ // Lazy-load the chunk-synthesizer bridge. It imports chunk-corpus data
42
+ // via composition-library's dual-mode loader, which is already top-level
43
+ // awaited; importing it here statically would not break anything but the
44
+ // async-import keeps the cold-start surface tight for the strong-match
45
+ // (no-LLM) path that doesn't need synthesis at all.
46
+ async function bridgeToChunkSynthesis({ intent, llmAdapter }) {
47
+ const { composeFromIntent } = await import('./chunk-synthesizer.js');
48
+ const result = await composeFromIntent({ intent, llmAdapter, maxAttempts: 2 });
49
+ // Shape-adapt chunk-synthesizer's `{ html, plan, source, ... }` to the
50
+ // synthesis-branch's prior `{ messages, template, synthesis }` contract.
51
+ const messages = result.html
52
+ ? [{ type: 'updateComponents', components: [{ id: 'chunk-root', component: 'article', html: result.html }] }]
53
+ : [];
54
+ return {
55
+ messages,
56
+ template: result.plan ? [{ $chunk: 'composeFromIntent', plan: result.plan }] : [],
57
+ synthesis: {
58
+ source: result.source,
59
+ score: result.score,
60
+ warnings: result.warnings || [],
61
+ scopeDrift: result.scopeDrift || null,
62
+ },
63
+ };
64
+ }
65
+
35
66
  // Composition library auto-loads at module import time via top-level `await
36
67
  // loadAll()` in composition-library.js (§72 dual-mode loader, commit
37
68
  // `76dbcff2`). The previous `ensureBooted()` here called `loadAll()` WITHOUT
@@ -72,62 +103,17 @@ function toUpdateComponentsMessages(template) {
72
103
  }
73
104
 
74
105
  export async function generateZettel({ intent, mode = 'instant', llmAdapter = null, sessionId = null } = {}) {
75
- // ── Session-aware iteration (turn > 1) ──
76
- // If we have prior turns AND an LLM, the user is almost certainly modifying
77
- // the existing canvas NEVER pick a fresh retrieved composition. Go straight
78
- // to synthesis with history context. This is what makes follow-ups work.
106
+ // ── Session iteration note (post-§195, v0.5.6) ──
107
+ // Fragment-graph history-aware iteration (`synthesizeComposition` with
108
+ // `historySummary`) was retired in §37 when fragments retired. Turn≥2 now
109
+ // takes the same path as turn 1 fresh retrieval → fresh chunk-synthesis.
110
+ // For true history-aware iteration (modify-an-existing-canvas), wire the
111
+ // request via `engine: 'chunk-zettel'` which uses `chunk-refiner.js`.
112
+ //
113
+ // Prior turns are still recorded (analytics + drift tracking via `getDrift`)
114
+ // but the iteration BRANCH is gone — no more spurious
115
+ // `iteration-synthesis-failure` auto-fires on every multi-turn request.
79
116
  const priorTurns = sessionId ? getTurns(sessionId) : [];
80
- const hasHistory = priorTurns.length > 0;
81
-
82
- if (hasHistory && llmAdapter) {
83
- try {
84
- const historySummary = buildHistorySummary(sessionId, 3);
85
- const synth = await synthesizeComposition({ intent, llmAdapter, historySummary });
86
- const validation = validateSchema(synth.messages, { intent });
87
- const fragments = (synth.template || []).filter((n) => n.$fragment).map((n) => n.$fragment);
88
- recordTurn(sessionId, {
89
- intent,
90
- messages: synth.messages,
91
- template: synth.template,
92
- composition: null,
93
- strategy: 'composition-iterated',
94
- fragments,
95
- validation,
96
- });
97
- return {
98
- messages: synth.messages,
99
- validation,
100
- strategy: 'composition-iterated',
101
- retrieval: { hit: false, rank: null, candidate: null, reason: `iteration on turn ${priorTurns.length + 1}` },
102
- composition: null,
103
- fragments_used: fragments,
104
- synthesized_template: synth.template,
105
- synthesis: synth.synthesis,
106
- sessionTurns: priorTurns.length + 1,
107
- };
108
- } catch (err) {
109
- // If iteration synthesis fails, fall through to the normal path. Record
110
- // the failure so the next turn can see we tried, and auto-fire an issue
111
- // ticket so synthesis-owners can investigate the failure post-hoc.
112
- console.error('[zettel] iteration synthesis failed:', err.message);
113
- try {
114
- await autoReport(
115
- 'iteration-synthesis-failure',
116
- {
117
- intent,
118
- turn: priorTurns.length + 1,
119
- state_id: sessionId,
120
- body: `Auto-fired by generator-adapter. Iteration synthesis threw on turn ${priorTurns.length + 1}.\n\nError: \`${err.message}\``,
121
- tags: ['generator-adapter', `turn-${priorTurns.length + 1}`],
122
- },
123
- { evalMode: mode === 'eval' }
124
- );
125
- } catch (reportErr) {
126
- // Never let issue-reporting crash the request path.
127
- console.error('[zettel] autoReport failed:', reportErr.message);
128
- }
129
- }
130
- }
131
117
 
132
118
  const hits = searchAll(intent, { limit: 5 });
133
119
  const composition = hits.find((h) => h.type === 'composition');
@@ -163,19 +149,22 @@ export async function generateZettel({ intent, mode = 'instant', llmAdapter = nu
163
149
  };
164
150
  }
165
151
 
166
- // ── Weak/no retrieval: try LLM synthesis if available (fresh, no history) ──
152
+ // ── Weak/no retrieval: bridge to chunk-zettel synthesis (the §37 successor) ──
167
153
  if (llmAdapter && mode !== 'instant-only') {
168
154
  try {
169
- const synth = await synthesizeComposition({ intent, llmAdapter });
155
+ const synth = await bridgeToChunkSynthesis({ intent, llmAdapter });
170
156
  const validation = validateSchema(synth.messages, { intent });
171
- const fragments = (synth.template || []).filter((n) => n.$fragment).map((n) => n.$fragment);
157
+ // Chunk-bridge produces an html-bearing single component, not
158
+ // resolved fragment instances. `fragments_used` is empty by design;
159
+ // consumers tracking fragment usage should switch to the
160
+ // chunk-zettel engine which surfaces `_debug.plan` directly.
172
161
  recordTurn(sessionId, {
173
162
  intent,
174
163
  messages: synth.messages,
175
164
  template: synth.template,
176
165
  composition: null,
177
166
  strategy: 'composition-synthesized',
178
- fragments,
167
+ fragments: [],
179
168
  validation,
180
169
  });
181
170
  return {
@@ -189,7 +178,7 @@ export async function generateZettel({ intent, mode = 'instant', llmAdapter = nu
189
178
  reason: composition ? `top score ${composition.score} below threshold ${STRONG_MATCH_THRESHOLD}` : 'no composition retrieved',
190
179
  },
191
180
  composition: null,
192
- fragments_used: fragments,
181
+ fragments_used: [],
193
182
  synthesized_template: synth.template,
194
183
  synthesis: synth.synthesis,
195
184
  candidates: hits,
@@ -186,12 +186,26 @@ for (const [type, tag] of registry) {
186
186
 
187
187
  /** @type {Record<string, { type: string, props?: Record<string, unknown> }>} */
188
188
  export const HTML_TAG_MAP = {
189
- h1: { type: 'Text', props: { variant: 'h1' } },
190
- h2: { type: 'Text', props: { variant: 'h2' } },
191
- h3: { type: 'Text', props: { variant: 'h3' } },
192
- h4: { type: 'Text', props: { variant: 'h4' } },
193
- h5: { type: 'Text', props: { variant: 'h5' } },
194
- h6: { type: 'Text', props: { variant: 'h6' } },
189
+ // §210 (v0.5.7, FEEDBACK-17 §1): map native heading levels to working
190
+ // text-ui variants. Pre-§210 these mapped to `h1`-`h6` strings that
191
+ // text.css had no `:scope[variant=…]` rule for — silent body-default
192
+ // rendering. The retired variants (`h1`-`h6` + `subheading`) were
193
+ // removed from text.yaml/text.d.ts/text.a2ui.json in §210 because no
194
+ // matching tokens shipped. Replacement mapping prefers role tokens
195
+ // that actually render:
196
+ // <h1> → 'display' (largest type — marketing hero)
197
+ // <h2> → 'title' (page title)
198
+ // <h3> → 'heading' (section division)
199
+ // <h4> → 'subsection' (sub-grouping inside a heading section)
200
+ // <h5>, <h6> → 'subsection' (no smaller subsection token ships;
201
+ // consistent fallback over silent
202
+ // body-default)
203
+ h1: { type: 'Text', props: { variant: 'display' } },
204
+ h2: { type: 'Text', props: { variant: 'title' } },
205
+ h3: { type: 'Text', props: { variant: 'heading' } },
206
+ h4: { type: 'Text', props: { variant: 'subsection' } },
207
+ h5: { type: 'Text', props: { variant: 'subsection' } },
208
+ h6: { type: 'Text', props: { variant: 'subsection' } },
195
209
  p: { type: 'Text', props: { variant: 'body' } },
196
210
  small: { type: 'Text', props: { variant: 'caption' } },
197
211
  strong: { type: 'Text', props: { variant: 'body' } },
@@ -217,7 +231,7 @@ export const HTML_TAG_MAP = {
217
231
  main: { type: 'Column' },
218
232
  article: { type: 'Card' },
219
233
  details: { type: 'Accordion' },
220
- summary: { type: 'Text', props: { variant: 'h5' } },
234
+ summary: { type: 'Text', props: { variant: 'subsection' } },
221
235
  dialog: { type: 'Modal' },
222
236
  hr: { type: 'Divider' },
223
237
  a: { type: 'Link' }, // §47 — was: Button + variant=ghost (button.yaml line 60: "for inline navigation use `<link-ui>` instead")
@@ -1,22 +0,0 @@
1
- /**
2
- * Composition synthesizer — RETIRED (§37, 2026-05-12).
3
- *
4
- * Previously: when zettel retrieval was weak, this called the LLM to
5
- * assemble a NEW composition from the fragment catalog (technique B/C
6
- * fragment-graph synthesis). Fragments are retired and there's no
7
- * fragment catalog to draw from anymore. The deterministic path
8
- * (`resolveComposition` on a retrieved composition) still works.
9
- *
10
- * Until a chunk-based synthesis fallback lands, this export throws on
11
- * call. Callers should catch and fall through to monolithic-pro's
12
- * generation path. generator-adapter.js handles this gracefully.
13
- */
14
-
15
- export async function synthesizeComposition() {
16
- const err = new Error(
17
- 'synthesizeComposition is retired (§37 fragment retirement). ' +
18
- 'Use chunk-zettel or monolithic-pro for LLM-driven composition.'
19
- );
20
- err.code = 'SYNTHESIZER_RETIRED';
21
- throw err;
22
- }