@adia-ai/adia-ui-forge 0.8.52 → 0.8.54

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adia-ui-kit-forge",
3
- "version": "0.8.52",
3
+ "version": "0.8.54",
4
4
  "description": "Maintain the adia-ui (@adia-ai) framework itself \u2014 author primitives and shells, run the A2UI generation pipeline and its corpus, review gen-UI quality, sweep QA, cut releases, deploy. The maintainer counterpart to adia-ui-kit-factory (the consumer/app-author plugin).",
5
5
  "author": {
6
6
  "name": "Kim",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adia-ui-kit-forge",
3
- "version": "0.8.52",
3
+ "version": "0.8.54",
4
4
  "description": "Maintain the adia-ui (@adia-ai) framework itself — author primitives and shells, run the A2UI generation pipeline and its corpus, review gen-UI quality, sweep QA, cut releases, deploy. The maintainer counterpart to adia-ui-kit-factory (the consumer/app-author plugin).",
5
5
  "author": {
6
6
  "name": "Kim",
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # Changelog — adia-ui-kit-forge
2
2
 
3
+ ## [0.8.54] — 2026-08-27
4
+
5
+ ### Fixed
6
+ - **`package-release`'s cut pipeline was missing a post-bump regen step for both plugins' Hermes/Pi manifest tree — the same gap gh#1899 already closed for the Codex tree (discovered live during the v0.8.53 release PR #2138: it shipped stale `plugin.yaml`/`__init__.py`, caught by CI's `check:harness-manifests-fresh` and fixed by hand on the release branch).** `release-pack.mjs`'s Step 4 now runs `scripts/build/harness-manifests.mjs` after the version bump and verifies with `npm run check:harness-manifests-fresh` before continuing (`skills/package-release/scripts/release-pack.mjs`); Step 5's staging pathspec now carries both plugins' `plugin.yaml`, `__init__.py`, `prompts/*.md`, and (adia-ui-factory only) `hermes-mcp.yaml`, so the regen actually lands in the release commit instead of riding untracked. `selftest` extended to assert the new step and its staging pathspec.
7
+ - **`patterns-index.md` (both `@adia-ai/mcp`'s `factory/resources/` copy and `adia-ui-factory`'s `skills/pattern-catalog/references/` copy) had no freshness gate at all — it went undetected-stale through the entire v0.8.53 cut, only regenerated as a side effect of an unrelated F-N1 CHANGELOG-coverage fix, never because a gate asserted it.** Added gate 33 (`npm run verify:patterns-index`) to the pre-flight roster, appended after gate 32 with the same numbering discipline as gates 30-32 (`skills/package-release/scripts/gate-roster.mjs`).
8
+
9
+ ### Docs
10
+ - **`a2ui-maintenance/references/pipeline-overview.md`'s Critical Rule #5 stated a stale, now-superseded claim that A2UI never carries CSS — corrected per ADR-0022's 2026-08-24 amendment.** `UpdateStylesMessage`/`RemoveStylesMessage` are ratified protocol scope (a first-class CSS channel, not a carve-out); what still holds is narrower — compose/zettel LLM-synthesis itself never emits `updateStyles`, so the GENERATION-pipeline claim stays correct while the PROTOCOL-scope claim needed the fix. `skills/a2ui-maintenance/references/pipeline-overview.md`.
11
+ - **`gen-ui-review/references/corpus-html-patterns.md` carried the same stale ADR-0022 claim as a secondary citation — corrected alongside the primary fix above.** `skills/gen-ui-review/references/corpus-html-patterns.md`.
12
+ - **`primitive-authoring/references/yaml-contract.md`'s `category:` field documentation was missing ADR-0065's ratified 12-value canonical category vocabulary entirely (`action|agent|container|data|display|feedback|form|input|layout|navigation|shells|utility`) — added as a new passage, including the fold table for the 6 one-off drift values ADR-0065 retired.** `skills/primitive-authoring/references/yaml-contract.md`.
13
+ - **`primitive-authoring/references/lifecycle-patterns.md`'s `slot="caret"` compliant-component list corrected twice this cycle, net result: `drilldown-ui` added.** ADR-0085 first added `drilldown-ui` to the exemption list (kept `slot="chevron"`); ADR-0092 then reversed that ruling (gh#1977 — migrate, don't exempt) and drilldown-ui's row chevron actually migrated to `slot="caret"` (see `@adia-ai/web-components`'s own 0.8.54 entry) — this file now lists `drilldown-ui` as compliant alongside `tree-item-ui`, `pane-ui`, `accordion-item-ui`, `nav-group-ui`, `select-ui`, citing ADR-0036 + ADR-0092. `skills/primitive-authoring/references/lifecycle-patterns.md`.
14
+ - **`primitive-authoring/references/module-promotion.md` was missing a second, distinct ADR-0012 correction: ADR-0015 (ADR-0087) reclassifies 6 nav elements from modules-tier to primitives-tier (consolidated to 3 — `nav-ui`/`nav-group-ui`/`nav-item-ui`) and retires 4 legacy element names — added near the file's existing, unrelated ADR-0012 citation so a future reader classifying nav elements doesn't land them in the wrong tier.** `skills/primitive-authoring/references/module-promotion.md`.
15
+
16
+ ### Maintenance
17
+ - **`.claude-plugin/plugin.json` version bump only** — moves in lockstep with package.json (the `/plugin update` cache key).
18
+ - **`.codex-plugin/` touched in this release window** (1 file(s), e.g. `.codex-plugin/plugin.json`) — carried by the entries above.
19
+
20
+ ## [0.8.53] — 2026-08-26
21
+
22
+ ### Changed
23
+ - **`.claude-plugin/plugin.json` version bump only** — moves in lockstep with package.json (the `/plugin update` cache key). No skill/agent/hook content changed in this package this cut; substantive v0.8.53 work shipped in `@adia-ai/web-components`/`web-modules`/`gen-ui` — see `packages/web-components/CHANGELOG.md#0853--2026-08-26`.
24
+
25
+ ### Maintenance
26
+ - **`.codex-plugin/` touched in this release window** (1 file(s), e.g. `.codex-plugin/plugin.json`) — carried by the entries above.
27
+
3
28
  ## [0.8.52] — 2026-08-25
4
29
 
5
30
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/adia-ui-forge",
3
- "version": "0.8.52",
3
+ "version": "0.8.54",
4
4
  "description": "Maintain the adia-ui (@adia-ai) framework itself — author primitives and shells, run the A2UI generation pipeline and its corpus, review gen-UI quality, sweep QA, cut releases, deploy. The maintainer counterpart to adia-factory (the consumer/app-author plugin).",
5
5
  "keywords": [
6
6
  "adia-ui",
package/plugin.yaml CHANGED
@@ -1,4 +1,4 @@
1
1
  name: "adia-ui-kit-forge"
2
- version: "0.8.52"
2
+ version: "0.8.54"
3
3
  description: "Maintain the adia-ui (@adia-ai) framework itself — author primitives and shells, run the A2UI generation pipeline and its corpus, review gen-UI quality, sweep QA, cut releases, deploy. The maintainer counterpart to adia-ui-kit-factory (the consumer/app-author plugin)."
4
4
  manifest_version: 1
@@ -105,6 +105,17 @@ for any constant or decision lives in git and PR descriptions
105
105
  5. **A2UI describes LAYOUT, not behavior.** Generation emits component trees +
106
106
  props + slot bindings; never JS or per-canvas CSS. Behavior delegates to
107
107
  traits or pre-built apps. "Make the generator emit JS/CSS" is a won't-fix.
108
+ **This is a GENERATION-pipeline claim, not the protocol's outer bound**
109
+ (ADR-0022 amendment, 2026-08-24): the protocol itself, as consumed by the
110
+ renderer and wire bridge, now carries a ratified CSS channel —
111
+ `UpdateStylesMessage`/`RemoveStylesMessage`
112
+ (`packages/gen-ui/a2ui/a2ui.schema.json:279-305`, renderer
113
+ `#updateStyles`/`#removeStyles` at `renderer.js:113-114,821-879`) — a
114
+ first-class part of the protocol, not a carve-out. What stays true
115
+ verbatim: compose/zettel synthesis itself still never emits
116
+ `updateStyles`, so this rule's generation claim is unchanged; only the
117
+ closed "the protocol never carries CSS at all" claim was falsified. JS
118
+ remains fully out of scope for both the protocol and generation.
108
119
  6. **Renderer guards `textContent` against container wipe** —
109
120
  `packages/gen-ui/a2ui/renderer.js` whitelists pure-text leaves
110
121
  (`TEXT_TAG_OK`); everything else routes through the `text=` attribute so
@@ -70,7 +70,14 @@ HTML pages (apps/, catalog/, packages/web-modules/) ← SoT
70
70
  primitives" fix plan.
71
71
  - A2UI describes **layout, not behavior** (ADR-0022): trees carry components +
72
72
  props + slot bindings only. Fix plans must never ask the generator to emit JS
73
- or per-canvas CSS — that class of plan is won't-fix.
73
+ or per-canvas CSS — that class of plan is won't-fix. This is a
74
+ generation-pipeline claim, not a statement about the protocol's outer
75
+ bound: per ADR-0022's 2026-08-24 amendment, the protocol itself now
76
+ carries a ratified CSS channel (`UpdateStylesMessage`/`RemoveStylesMessage`,
77
+ consumed by the renderer and wire bridge) — compose/zettel synthesis still
78
+ never emits it, so a review fix plan asking the generator to produce CSS
79
+ is still won't-fix, but "the protocol never carries CSS" is no longer
80
+ accurate framing if a fix plan touches the renderer or wire-bridge layer.
74
81
 
75
82
  ---
76
83
 
@@ -142,6 +142,16 @@ export const GATE_ROSTER = [
142
142
  // gate 31's Codex tree is — a stale copy is the same real install hazard.
143
143
  // Appended, not inserted — same numbering discipline as gate 30/31.
144
144
  { n: 32, cmd: 'npm run check:harness-manifests-fresh', what: 'Hermes/Pi plugin.yaml + __init__.py + prompts vs .claude-plugin/plugin.json + commands SoT' },
145
+ // v0.8.53 retro: patterns-index.md (both @adia-ai/mcp's factory/resources/
146
+ // copy and adia-ui-factory's skills/pattern-catalog/references/ copy,
147
+ // consumed by the pattern-catalog skill and ui-architect) went undetected-
148
+ // stale through the whole v0.8.53 cut — it happened to be regenerated as
149
+ // a side effect of an unrelated F-N1 CHANGELOG-coverage fix, not because
150
+ // any gate asserted it. A stale index here is the same class of
151
+ // install/consumption hazard gates 31/32 close for the Codex/Hermes/Pi
152
+ // manifest trees. Appended, not inserted — same numbering discipline as
153
+ // gate 30/31/32.
154
+ { n: 33, cmd: 'npm run verify:patterns-index', what: 'pattern-index.md (mcp + adia-ui-factory) vs corpus source' },
145
155
  ];
146
156
 
147
157
  // -- CLI ------------------------------------------------------------------
@@ -440,11 +440,11 @@ function step1ReBaseline(args) {
440
440
 
441
441
  // ── Step 3 — Pre-flight gates (gh#2006) ───────────────────────────
442
442
  //
443
- // gh#1987's audit found the real "feels slow" lever isn't roster size (all
444
- // 32 gates are justified defense-in-depth — `main`'s branch protection only
443
+ // gh#1987's audit found the real "feels slow" lever isn't roster size (every
444
+ // gate is justified defense-in-depth — `main`'s branch protection only
445
445
  // hard-requires the browser-scheme probe, so the `npm run check` name-
446
446
  // duplicates in this roster are real re-checks, not dead weight) — it's
447
- // that step3PreFlight ran every gate one at a time. Most of the 32 are
447
+ // that step3PreFlight ran every gate one at a time. Most gates are
448
448
  // independent, read-only checks with no shared mutable state; this splits
449
449
  // them into three phases instead of one flat serial for-loop. Two real
450
450
  // sequencing requirements survived the audit — everything else runs
@@ -852,6 +852,32 @@ function step4PromoteAndBump(args) {
852
852
  console.log(' ✓ Codex plugin manifests fresh after regeneration');
853
853
  }
854
854
 
855
+ // Step 4d.7 — regenerate the Hermes/Pi plugin manifests (gh#1954): both
856
+ // plugins' plugin.yaml + __init__.py embed `version` from
857
+ // .claude-plugin/plugin.json, which the bump above just moved — the exact
858
+ // same "derived artifact embeds the lockstep version" class as 4d.6's
859
+ // Codex tree. Gate 32 (check:harness-manifests-fresh) runs pre-bump in
860
+ // Step 3, so it only proves freshness against the PREVIOUS version;
861
+ // without this step nothing re-ran the generator after the bump — the
862
+ // v0.8.53 release PR (#2138) shipped stale manifests as a result (Static
863
+ // checks + MCP smoke failed on check:harness-manifests-fresh, fixed by
864
+ // hand on the release branch — the doc at cut-procedure.md's step 4d.7
865
+ // described this step for two cuts before the script actually ran it).
866
+ // Step 5 stages both plugins' plugin.yaml + __init__.py, factory's
867
+ // hermes-mcp.yaml, and prompts/*.md.
868
+ console.log('\n Step 4d.7 — regenerate Hermes/Pi plugin manifests (version embeds the lockstep bump):');
869
+ sh('node scripts/build/harness-manifests.mjs', args);
870
+ if (args.dry) {
871
+ console.log(' [dry] npm run check:harness-manifests-fresh (post-regen proof)');
872
+ } else if (!shOk('npm run check:harness-manifests-fresh')) {
873
+ console.error('\nERROR: Hermes/Pi plugin manifests still stale after regeneration — harness-manifests.mjs');
874
+ console.error(' did not produce output matching check:harness-manifests-fresh\'s expectations.');
875
+ console.error(' Run both by hand to diagnose before re-running the cut.');
876
+ process.exit(1);
877
+ } else {
878
+ console.log(' ✓ Hermes/Pi plugin manifests fresh after regeneration');
879
+ }
880
+
855
881
  // Step 4e — generate docs/ops/releases/vX.Y.Z.md (gh#399: neither this step nor
856
882
  // its staging into the release commit existed before — check-cut-hygiene's
857
883
  // "notes present" claim (Step 4g, below) went unverified until
@@ -982,8 +1008,14 @@ function step5Commit(args) {
982
1008
  // and the per-skill count isn't statically knowable (same shape as
983
1009
  // the chunk-corpus directory pathspec above).
984
1010
  files.push(`${dir}/.codex-plugin/plugin.json`, `${dir}/skills/*/agents/openai.yaml`);
1011
+ // gh#1954: Step 4d.7 regenerates both plugins' Hermes/Pi manifest
1012
+ // tree post-bump, the same class of drift 4d.6's Codex tree fixes
1013
+ // above — stage it here or CI catches it the way v0.8.53's release
1014
+ // PR (#2138) did. `prompts/*.md` is a glob for the same reason the
1015
+ // Codex `agents/openai.yaml` glob above is.
1016
+ files.push(`${dir}/plugin.yaml`, `${dir}/__init__.py`, `${dir}/prompts/*.md`);
985
1017
  }
986
- if (name === 'adia-ui-factory') files.push(`${dir}/.mcp.json`);
1018
+ if (name === 'adia-ui-factory') files.push(`${dir}/.mcp.json`, `${dir}/hermes-mcp.yaml`);
987
1019
  return files;
988
1020
  }),
989
1021
  ];
@@ -1415,6 +1447,21 @@ function selftest() {
1415
1447
  'packages/plugins/adia-ui-forge/skills/*/agents/openai.yaml',
1416
1448
  'packages/plugins/adia-ui-factory/.codex-plugin/plugin.json',
1417
1449
  'packages/plugins/adia-ui-factory/skills/*/agents/openai.yaml',
1450
+ // gh#2138 — Step 4 must regenerate the Hermes/Pi plugin manifests
1451
+ // post-bump too, the same class of gap as gh#1899 above (the v0.8.53
1452
+ // release PR #2138 shipped stale ones without this step).
1453
+ 'Step 4d.7 — regenerate Hermes/Pi plugin manifests',
1454
+ '[dry] node scripts/build/harness-manifests.mjs',
1455
+ // gh#2138 — Step 5's staging pathspec must carry both plugins'
1456
+ // Hermes/Pi manifest tree, or it rides untracked/uncommitted past the
1457
+ // release commit the same way the Codex tree did before gh#1899.
1458
+ 'packages/plugins/adia-ui-forge/plugin.yaml',
1459
+ 'packages/plugins/adia-ui-forge/__init__.py',
1460
+ 'packages/plugins/adia-ui-forge/prompts/*.md',
1461
+ 'packages/plugins/adia-ui-factory/plugin.yaml',
1462
+ 'packages/plugins/adia-ui-factory/__init__.py',
1463
+ 'packages/plugins/adia-ui-factory/hermes-mcp.yaml',
1464
+ 'packages/plugins/adia-ui-factory/prompts/*.md',
1418
1465
  ];
1419
1466
  for (const marker of need) {
1420
1467
  if (!out.includes(marker)) {
@@ -320,13 +320,14 @@ The failure this prevents: a `render()` that overwrites `textContent` from the a
320
320
 
321
321
  ### `slot="caret"` — the one disclosure-indicator slot
322
322
 
323
- The expand/collapse (and dropdown) directional-indicator slot is named `caret` on every primitive that exposes composition slots (`tree-item-ui`, `pane-ui`, `accordion-item-ui`, `nav-group-ui`, `select-ui`). `chevron` is retired — a hard rename with no compat alias, and `--*-chevron-*` tokens became `--*-caret-*` (Phosphor names the glyphs `caret-right` / `caret-down`; there is no `chevron-*` icon).
323
+ The expand/collapse (and dropdown) directional-indicator slot is named `caret` on every primitive that exposes composition slots (`tree-item-ui`, `pane-ui`, `accordion-item-ui`, `nav-group-ui`, `select-ui`, `drilldown-ui`). `chevron` is retired — a hard rename with no compat alias, and `--*-chevron-*` tokens became `--*-caret-*` (Phosphor names the glyphs `caret-right` / `caret-down`; there is no `chevron-*` icon).
324
324
 
325
325
  - The mechanism is adopt-or-stamp (above): honor a declarative `<… slot="caret">` child if present, else stamp `<icon-ui slot="caret" name="caret-right">`. The same adopt-or-stamp rule applies to **every** composition slot the stamp builds (e.g. `slot="actions"`).
326
326
  - Rotation is driven by host open-state CSS (`:scope[open]` / `[collapsed]` / `[expanded]`) — never by swapping the icon name.
327
327
  - Internal-render components with no composition slots (`agent-trace`, `agent-reasoning`) keep `[data-*-caret]` styling hooks — the convention governs slots, not internal hooks.
328
328
 
329
- Source: ADR-0036.
329
+ Source: ADR-0036, ADR-0092 (`drilldown-ui`'s row-level caret migrated from `slot="chevron"`,
330
+ retained under ADR-0085, to `slot="caret"`, reversing that exemption).
330
331
 
331
332
  ## Anti-patterns to watch for
332
333
 
@@ -178,6 +178,8 @@ Either ship as a standalone lockstep cut (per the sibling **package-release** sk
178
178
 
179
179
  **ADR prose drift — don't trust an ADR's "Today's work" narrative for live paths.** ADR-0012 is the source for the primitives-vs-modules-vs-themes decision above, but its own "Today's work"/outcome-notes prose has drifted out from under its ratified Decision: that prose describes `packages/web-components/patterns/` as the shell/chat/editor/runtime source-clustering directory, but present-day `patterns/` holds 45+ usage-example dirs instead — source moved on to `packages/web-modules/{chat,editor,runtime,shell}/` per the later ADR-0015. The same drift claims `@adia-ai/web-themes` as a sibling workspace package; it never shipped that way — themes live under `packages/web-modules/theme/` as a subpath instead. An ADR's ratified **Decision** stays append-only-safe; its **"Today's work" / outcome-notes prose is not** — later reorganizations can strand it, and a reader following the literal directory claims today would git-mv into the wrong (docs-example) directory. Before executing a cluster-placement call cited from an ADR, verify the directory it names still matches the live tree (see "Path-rebase awareness" below) rather than trusting the ADR's narrative section verbatim.
180
180
 
181
+ **A second, distinct ADR-0012 correction — the nav family's tier, and 4 retired element names (ADR-0087, partial supersession).** ADR-0012's "Today's work" cluster table and "What lives where today" table classified 6 nav elements (`<app-nav-ui>`, `<app-nav-group-ui>`, `<app-nav-item-ui>`, `<section-nav-ui>`, `<section-nav-group-ui>`, `<section-nav-item-ui>`) as modules-tier, living in `web-modules/shell/`. ADR-0015 corrected this same-day as "a misclassification ADR-0012 inherited from the patterns/ era": the nav family is primitives-tier, consolidated to 3 elements (`<nav-ui>`, `<nav-group-ui>`, `<nav-item-ui>`) in `web-components/components/`. Using ADR-0012's tables to classify a new nav-family element via the OD-001 decision table above would land it in modules-tier — wrong; nav elements are primitives per ADR-0015. ADR-0012's tables also named 4 element names since retired per ADR-0015's rename map: `<adia-chat-ui>` → `<chat-shell>`, `<adia-editor-ui>` → `<editor-shell>`, `<app-shell-ui>` → `<admin-shell>`, `<gen-ui>` → `<gen-root>`. ADR-0087 records this partial supersession formally (`supersedes: adr-0012`, scoped to these two tables only — the three-tier package model, the lateral-dependency rule, and the cluster boundaries all stand unchanged). Live authority: ADR-0015 itself, not ADR-0012's tables.
182
+
181
183
  ## Two rules from observed bugs
182
184
 
183
185
  ### OD-002 — Absorb the standalone-button-next-door
@@ -171,6 +171,19 @@ have no v1.0 sidecar.
171
171
 
172
172
  **Ratified, closed enum — compiler-enforced at build time, mirrored in the schema (ADR-0065).** These twelve values are the whole vocabulary; the census that ratified them found 18 free-form values in live use (typos like `forms`/`data-display`, one-off singletons, and three named misclassifications) — all folded or corrected onto this set as part of the same change. Enforcement: `scripts/build/components.mjs` holds `CATEGORY_VALUES` and `compileComponent()` throws on a MISSING `category:` field (unlike `status:`, `category:` is required, not defaulted) as well as on any out-of-enum value, the same place and severity as the `status:` check above — so `npm run verify:components` hard-fails an invalid OR absent category. `scripts/schemas/component.yaml.schema.json`'s `category` enum mirrors this list for the `$schema:` IDE contract; the hand-synced constant in `components.mjs` is the live gate, same relationship as `status`. Source: ADR-0065.
173
173
 
174
+ **The six one-off drift values ADR-0065 folded**, each independently justified by what the component does, not a blanket rule:
175
+
176
+ | Drift value | Folds to | Example |
177
+ | --- | --- | --- |
178
+ | `forms` | `form` | `toggle-option.yaml` — spelling drift against `form`'s existing members. |
179
+ | `data-display` | `data` | `heatmap.yaml` — joins `tree.yaml`/`tree-item.yaml`, same spelling drift shape. |
180
+ | `content` | `display` | `link.yaml` — inline content rendering, same role as `text-ui`/`code-ui`. |
181
+ | `typography` | `display` | `mark.yaml` — a text-highlight element, same role as `text-ui`. |
182
+ | `control` | `action` | `toggle-scheme.yaml` — a click-to-fire toggle, same shape as `button-ui`. |
183
+ | `interaction` | `container` | `admin-command.yaml` — a command-palette surface, same role as `command-ui`. |
184
+
185
+ Three named misclassifications were also fixed, not folded: `check.yaml`/`switch.yaml`/`textarea.yaml` (`layout` → `input` — bindable form controls, not structural primitives), `tabs.yaml` (`container` → `navigation`, unifying with `tab.yaml`), and `feed.yaml` (`container` → `feedback`, unifying with `feed-item.yaml`). Full rationale and the progress-family partial unification: ADR-0065 Decision §2–§4.
186
+
174
187
  **A sibling family (a wrapper + its child items, e.g. `tabs-ui`/`tab-ui`) is not required to share one category by default** — `menu-ui` (`container`) + `menu-item-ui` (`navigation`) is a deliberate, working split. Where a family's sibling values disagreed with no evident rationale, ADR-0065 unified them; new families should pick per-component, not assume unification is required.
175
188
 
176
189
  **Sidecar emission**: `x-adiaui.category` field in `<name>.a2ui.json`. `packages/gen-ui/engine/retrieval/catalog.js`'s `buildCatalog()` reads this directly per entry — no separate registration step.