@adia-ai/adia-ui-forge 0.1.2

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.
Files changed (136) hide show
  1. package/.claude-plugin/plugin.json +23 -0
  2. package/CHANGELOG.md +26 -0
  3. package/README.md +54 -0
  4. package/bin/forge-lint +263 -0
  5. package/bin/lib/audit-axes.mjs +555 -0
  6. package/bin/lib/dry-run-irreversible.mjs +236 -0
  7. package/bin/lib/run-skill-evals.mjs +487 -0
  8. package/bin/lib/teach-router.mjs +250 -0
  9. package/commands/adia-forge-a2ui.md +10 -0
  10. package/commands/adia-forge-author.md +10 -0
  11. package/commands/adia-forge-dogfood.md +8 -0
  12. package/commands/adia-forge-llm.md +8 -0
  13. package/commands/adia-forge-orient.md +10 -0
  14. package/commands/adia-forge-release.md +8 -0
  15. package/commands/adia-forge-review.md +10 -0
  16. package/hooks/hooks.json +15 -0
  17. package/package.json +41 -0
  18. package/references/shared/content-trust.md +76 -0
  19. package/references/shared/pev-rationale.md +64 -0
  20. package/references/shared/skill-conventions.md +133 -0
  21. package/skills/adia-ui-a2ui/CHANGELOG.md +32 -0
  22. package/skills/adia-ui-a2ui/SKILL.md +243 -0
  23. package/skills/adia-ui-a2ui/evals/adversarial-corpus.json +75 -0
  24. package/skills/adia-ui-a2ui/evals/routing-corpus.json +31 -0
  25. package/skills/adia-ui-a2ui/evals/teach-routing-cases.json +100 -0
  26. package/skills/adia-ui-a2ui/references/anti-patterns.md +24 -0
  27. package/skills/adia-ui-a2ui/references/chunk-authoring.md +88 -0
  28. package/skills/adia-ui-a2ui/references/corpus-discipline.md +56 -0
  29. package/skills/adia-ui-a2ui/references/eval-diagnostics.md +127 -0
  30. package/skills/adia-ui-a2ui/references/fragment-graph.md +91 -0
  31. package/skills/adia-ui-a2ui/references/mcp-pipeline-ops.md +106 -0
  32. package/skills/adia-ui-a2ui/references/mcp-tool-reference.md +398 -0
  33. package/skills/adia-ui-a2ui/references/pipeline-overview.md +175 -0
  34. package/skills/adia-ui-a2ui/references/semantic-fail-lifting.md +120 -0
  35. package/skills/adia-ui-a2ui/references/strategy-engines.md +111 -0
  36. package/skills/adia-ui-a2ui/references/teach-protocol.md +220 -0
  37. package/skills/adia-ui-a2ui/references/zettel-calibration.md +93 -0
  38. package/skills/adia-ui-a2ui/scripts/audit-a2ui-roster.mjs +96 -0
  39. package/skills/adia-ui-a2ui/scripts/teach-route.mjs +157 -0
  40. package/skills/adia-ui-a2ui/skill.json +38 -0
  41. package/skills/adia-ui-authoring/CHANGELOG.md +32 -0
  42. package/skills/adia-ui-authoring/SKILL.md +256 -0
  43. package/skills/adia-ui-authoring/assets/case-studies/admin-shell-decomposition.md +101 -0
  44. package/skills/adia-ui-authoring/assets/case-studies/maxtokens-32768-discovery.md +109 -0
  45. package/skills/adia-ui-authoring/assets/case-studies/theme-panel-promotion.md +113 -0
  46. package/skills/adia-ui-authoring/evals/adversarial-design-plan-gates.json +138 -0
  47. package/skills/adia-ui-authoring/evals/routing-corpus.json +245 -0
  48. package/skills/adia-ui-authoring/references/anti-patterns.md +606 -0
  49. package/skills/adia-ui-authoring/references/api-contract.md +205 -0
  50. package/skills/adia-ui-authoring/references/authoring-cycle.md +211 -0
  51. package/skills/adia-ui-authoring/references/canonical-pattern-index.md +179 -0
  52. package/skills/adia-ui-authoring/references/code-style.md +329 -0
  53. package/skills/adia-ui-authoring/references/common-gotchas.md +93 -0
  54. package/skills/adia-ui-authoring/references/composite-demo-protocol.md +1084 -0
  55. package/skills/adia-ui-authoring/references/css-patterns.md +364 -0
  56. package/skills/adia-ui-authoring/references/lifecycle-patterns.md +302 -0
  57. package/skills/adia-ui-authoring/references/llm-bridge.md +254 -0
  58. package/skills/adia-ui-authoring/references/module-promotion.md +289 -0
  59. package/skills/adia-ui-authoring/references/primitive-audit.md +123 -0
  60. package/skills/adia-ui-authoring/references/shell-patterns.md +561 -0
  61. package/skills/adia-ui-authoring/references/teach-protocol.md +428 -0
  62. package/skills/adia-ui-authoring/references/token-contract.md +120 -0
  63. package/skills/adia-ui-authoring/references/worked-example.md +351 -0
  64. package/skills/adia-ui-authoring/references/yaml-contract.md +224 -0
  65. package/skills/adia-ui-authoring/scripts/audit-authoring-roster.mjs +148 -0
  66. package/skills/adia-ui-authoring/scripts/build-canonical-pattern-index.mjs +199 -0
  67. package/skills/adia-ui-authoring/skill.json +45 -0
  68. package/skills/adia-ui-dogfood/CHANGELOG.md +17 -0
  69. package/skills/adia-ui-dogfood/README.md +62 -0
  70. package/skills/adia-ui-dogfood/SKILL.md +866 -0
  71. package/skills/adia-ui-dogfood/scripts/analyze.mjs +603 -0
  72. package/skills/adia-ui-dogfood/skill.json +40 -0
  73. package/skills/adia-ui-forge/SKILL.md +88 -0
  74. package/skills/adia-ui-forge/evals/routing-corpus.json +30 -0
  75. package/skills/adia-ui-gen-review/CHANGELOG.md +108 -0
  76. package/skills/adia-ui-gen-review/SKILL.md +266 -0
  77. package/skills/adia-ui-gen-review/references/loop-protocol.md +712 -0
  78. package/skills/adia-ui-gen-review/references/rubric-cosmetic.md +144 -0
  79. package/skills/adia-ui-gen-review/references/rubric-decompose.md +117 -0
  80. package/skills/adia-ui-gen-review/references/rubric-score.md +249 -0
  81. package/skills/adia-ui-gen-review/references/scores.schema.json +125 -0
  82. package/skills/adia-ui-gen-review/references/teach-protocol.md +214 -0
  83. package/skills/adia-ui-gen-review/scripts/gen-review-coverage-audit.mjs +127 -0
  84. package/skills/adia-ui-gen-review/scripts/gen-review-decompose.mjs +569 -0
  85. package/skills/adia-ui-gen-review/scripts/gen-review-status.mjs +176 -0
  86. package/skills/adia-ui-gen-review/scripts/validate-cycle-scores.mjs +198 -0
  87. package/skills/adia-ui-gen-review/skill.json +22 -0
  88. package/skills/adia-ui-llm/CHANGELOG.md +25 -0
  89. package/skills/adia-ui-llm/SKILL.md +165 -0
  90. package/skills/adia-ui-llm/evals/adversarial-corpus.json +75 -0
  91. package/skills/adia-ui-llm/evals/routing-corpus.json +30 -0
  92. package/skills/adia-ui-llm/evals/teach-routing-cases.json +73 -0
  93. package/skills/adia-ui-llm/references/adapter-contract.md +99 -0
  94. package/skills/adia-ui-llm/references/add-a-provider.md +90 -0
  95. package/skills/adia-ui-llm/references/bridge-facade.md +94 -0
  96. package/skills/adia-ui-llm/references/browser-proxy-boundary.md +89 -0
  97. package/skills/adia-ui-llm/references/model-registry.md +75 -0
  98. package/skills/adia-ui-llm/references/streaming-sse.md +95 -0
  99. package/skills/adia-ui-llm/references/teach-protocol.md +78 -0
  100. package/skills/adia-ui-llm/scripts/audit-llm-roster.mjs +93 -0
  101. package/skills/adia-ui-llm/scripts/teach-route.mjs +119 -0
  102. package/skills/adia-ui-llm/skill.json +33 -0
  103. package/skills/adia-ui-release/CHANGELOG.md +23 -0
  104. package/skills/adia-ui-release/SKILL.md +295 -0
  105. package/skills/adia-ui-release/assets/case-studies/2026-05-20-batch-push-v0.6.14-v0.6.15.md +144 -0
  106. package/skills/adia-ui-release/assets/case-studies/2026-05-20-corpus-drift-remediation-v0.6.15.md +155 -0
  107. package/skills/adia-ui-release/assets/case-studies/2026-05-20-version-skip-correction-v0.6.12.md +114 -0
  108. package/skills/adia-ui-release/assets/case-studies/2026-05-21-author-from-scratch-v0.6.18.md +139 -0
  109. package/skills/adia-ui-release/assets/case-studies/2026-05-21-feedback37-retraction-v0.6.21.md +124 -0
  110. package/skills/adia-ui-release/assets/case-studies/2026-05-21-fn1-enrichment-pass-v0.6.19.md +125 -0
  111. package/skills/adia-ui-release/assets/case-studies/2026-05-21-stale-test-detection-v0.6.20.md +142 -0
  112. package/skills/adia-ui-release/assets/case-studies/2026-05-23-freshness-gate-recovery-v0.6.32.md +97 -0
  113. package/skills/adia-ui-release/assets/case-studies/2026-05-26-catalog-drift-recurring-v0.6.40.md +147 -0
  114. package/skills/adia-ui-release/assets/templates/stub-changelog.template.md +22 -0
  115. package/skills/adia-ui-release/evals/evals.json +164 -0
  116. package/skills/adia-ui-release/references/changelog-discipline.md +250 -0
  117. package/skills/adia-ui-release/references/cycle-happy-path.md +520 -0
  118. package/skills/adia-ui-release/references/exe-deploy.md +149 -0
  119. package/skills/adia-ui-release/references/gates-catalog.md +778 -0
  120. package/skills/adia-ui-release/references/ledger-discipline.md +232 -0
  121. package/skills/adia-ui-release/references/migration-guide-authoring.md +174 -0
  122. package/skills/adia-ui-release/references/multi-agent-baseline.md +207 -0
  123. package/skills/adia-ui-release/references/notes-authoring.md +212 -0
  124. package/skills/adia-ui-release/references/recovery-paths.md +215 -0
  125. package/skills/adia-ui-release/references/rollup-notes.md +208 -0
  126. package/skills/adia-ui-release/references/teach-protocol.md +468 -0
  127. package/skills/adia-ui-release/scripts/assert-monorepo-root.mjs +49 -0
  128. package/skills/adia-ui-release/scripts/audit-gate-roster.mjs +196 -0
  129. package/skills/adia-ui-release/scripts/bump.mjs +118 -0
  130. package/skills/adia-ui-release/scripts/dispatch-publish.mjs +151 -0
  131. package/skills/adia-ui-release/scripts/insert-stub.mjs +127 -0
  132. package/skills/adia-ui-release/scripts/make-ledger.mjs +179 -0
  133. package/skills/adia-ui-release/scripts/promote-unreleased.mjs +88 -0
  134. package/skills/adia-ui-release/scripts/release-pack.mjs +350 -0
  135. package/skills/adia-ui-release/scripts/tag-lockstep.mjs +110 -0
  136. package/skills/adia-ui-release/skill.json +75 -0
@@ -0,0 +1,329 @@
1
+ # AdiaUI code style — best practices
2
+
3
+ Modern AdiaUI is small, declarative, and token-driven. Most of the "bugs" agents write are bugs _against the conventions_ — bare `<div>`s where `col-ui` belongs, raw `<input>` where `input-ui` belongs, hex colors where tokens belong. The conventions are not stylistic preferences; each one corresponds to a working feature (theme switching, density modes, form association, focus rings) that breaks silently when the convention is violated.
4
+
5
+ When in doubt: look up the catalog (use the `lookup_component` MCP tool, or the consumer-side composition skill in the adia-ui-factory plugin), pick the existing primitive, wire through tokens.
6
+
7
+ Absorbed from the legacy code-bestpractices skill (now a redirect; this file replaces the daily-driver content).
8
+
9
+ ---
10
+
11
+ ## `<*-ui>` custom elements
12
+
13
+ Every interactive surface, form control, layout container, and content block ships as a custom element with a `-ui` suffix.
14
+
15
+ **Don't author raw HTML primitives** for anything the catalog covers. Raw `<button>` skips focus rings, density modes, theming, and the trait factory; raw `<input>` skips form association and validation propagation; raw `<div>` skips the layout primitive's spacing scale.
16
+
17
+ ```html
18
+ <!-- right -->
19
+ <button-ui text="Save" variant="primary"></button-ui>
20
+ <input-ui type="email" label="Email"></input-ui>
21
+
22
+ <!-- wrong -->
23
+ <button class="btn-primary">Save</button>
24
+ <input type="email" />
25
+ ```
26
+
27
+ The catalog has 90+ `*-ui` elements. Before composing, confirm via `get_component_map` (the a2ui MCP) that the one you want exists — and what its real prop names are.
28
+
29
+ ## Layout primitives
30
+
31
+ Use `<col-ui>`, `<row-ui>`, `<grid-ui>`, `<stack-ui>` for layout — never a bare `<div>`. The primitives carry the project's spacing scale (`gap`, padding tokens) and respond to density / theme providers; a plain `<div>` strands content outside that system.
32
+
33
+ Pick the primitive by the axis the content flows along:
34
+
35
+ - Vertical stack of fields → `<col-ui gap="N">`
36
+ - Horizontal action cluster or info row → `<row-ui gap="N" justify="end">`
37
+ - Responsive grid → `<grid-ui cols="1 | 2 | 3 | 4">`
38
+ - Loose wrapping items → `<stack-ui gap="N">`
39
+
40
+ ```html
41
+ <!-- right -->
42
+ <col-ui gap="2">
43
+ <input-ui label="Email" type="email"></input-ui>
44
+ <input-ui label="Password" type="password"></input-ui>
45
+ </col-ui>
46
+
47
+ <!-- wrong — manual spacing fights the density provider -->
48
+ <div style="display: flex; flex-direction: column; gap: 8px;">
49
+ <input-ui label="Email" type="email"></input-ui>
50
+ <input-ui label="Password" type="password"></input-ui>
51
+ </div>
52
+ ```
53
+
54
+ The validator (`check_anti_patterns` → `noBareDivs`) catches this on rendered HTML. If a `<section>` has multiple direct children, wrap them in one layout primitive — `cardContentModel` flags the alternative.
55
+
56
+ ### `@bp` responsive prop annotations
57
+
58
+ Layout primitives accept `@bp` (breakpoint) annotations on numeric or keyword props. Mobile-first parsing: unannotated value = base; `@bp` applies at that breakpoint and wider.
59
+
60
+ ```html
61
+ <grid-ui cols="2 4@md"><!-- 2 cols base, 4 at md+ --></grid-ui>
62
+ <col-ui gap="2 4@lg"><!-- 8px gap base, 16px at lg+ --></col-ui>
63
+ <text-ui size="sm md@lg"><!-- sm at base, md at lg+ --></text-ui>
64
+ ```
65
+
66
+ Subscription is lazy: a primitive only listens to the breakpoint signal when at least one prop carries `@`. Wired primitives: `grid-ui`, `col-ui`, `row-ui`, `text-ui`, `block-ui`. Source: `packages/web-components/core/responsive.js`.
67
+
68
+ ## `<card-ui>` content model
69
+
70
+ The card content model is mandatory for anything resembling a block. Header carries icon / heading / description / action slots; section wraps content in a layout primitive; footer holds action rows.
71
+
72
+ ```html
73
+ <card-ui>
74
+ <header>
75
+ <span slot="icon"><icon-ui name="users"></icon-ui></span>
76
+ <span slot="heading" variant="section">Members</span>
77
+ </header>
78
+ <section>
79
+ <col-ui gap="2"><!-- one layout primitive per section --></col-ui>
80
+ </section>
81
+ <footer divider>
82
+ <button-ui slot="action" text="View All" variant="outline"></button-ui>
83
+ </footer>
84
+ </card-ui>
85
+ ```
86
+
87
+ Headings live in `<header>`, never in `<section>`. Sections contain exactly one layout primitive. Multiple direct children in `<section>` fail `cardContentModel` validation.
88
+
89
+ `<card-ui>` and `<drawer-ui>` body content must wrap in `<section>` — direct `<col-ui>` / `<row-ui>` / `<stack-ui>` / `<div>` / `<text-ui>` / `<h*>` bypass the canonical body slot, lose `--card-inset` margin, and corrupt gen-UI corpus. Use `<section bleed>` to preserve existing padding when needed. Gated by `npm run audit:card-structure` + `audit:drawer-structure`.
90
+
91
+ ## `<field-ui>` for form composition
92
+
93
+ Wrap form controls in `<field-ui>`. Don't pass `[label]` as an attribute on new form controls — it's the legacy form, kept for back-compat but not extended.
94
+
95
+ ```html
96
+ <!-- right -->
97
+ <field-ui label="Email" hint="We'll never share it">
98
+ <input-ui type="email" required></input-ui>
99
+ </field-ui>
100
+
101
+ <!-- wrong (deprecated, console.warn) -->
102
+ <input-ui label="Email" type="email" required></input-ui>
103
+ ```
104
+
105
+ `<field-ui>` owns the real `<label for="…">` and binds to the slotted control's `id` for proper click-to-focus — a pattern the embedded attribute can't provide. It also carries `[slot="trailing"]` / `[slot="action"]` for composition (clear button, helper actions) and an `inline` mode for stacked vs single-row layout.
106
+
107
+ For a single `check-ui` (which is its own labeled affordance), just use `<check-ui name label>` directly — wrapping it in `<field-ui inline>` produces ghost rows.
108
+
109
+ ## Generated UIs through `<a2ui-root>`
110
+
111
+ Static markup written by hand uses the components directly. UIs generated from intent (LLM, agent) render through `<a2ui-root>` consuming a stream of A2UI messages or a static `doc` array.
112
+
113
+ ```html
114
+ <a2ui-root data-stream-src="/api/genui-stream"></a2ui-root>
115
+ ```
116
+
117
+ The runtime handles `createSurface` / `updateComponents` / `wireComponents` messages and binds `FormController` / `DataStreamController` controllers automatically. Don't roll your own message handler — the wiring lives in `@adia-ai/a2ui-runtime`.
118
+
119
+ For multi-turn refinement, pass back `state_id` from prior responses so the engine can chain through `parent_state_id`. See the **adia-ui-a2ui** skill for the operator playbook.
120
+
121
+ ## Two-block `@scope` CSS
122
+
123
+ Component CSS uses a mandatory two-block `@scope` structure. Tokens go in `:where(:scope)` (zero-specificity); base styles go in `:scope`; variants and states override TOKENS only.
124
+
125
+ ```css
126
+ @scope (badge-ui) {
127
+ :where(:scope) {
128
+ --badge-bg: var(--a-bg-muted);
129
+ --badge-fg: var(--a-fg);
130
+ --badge-radius: var(--a-radius-sm);
131
+ }
132
+
133
+ :scope {
134
+ background: var(--badge-bg);
135
+ color: var(--badge-fg);
136
+ border-radius: var(--badge-radius);
137
+ }
138
+
139
+ :scope[variant="accent"] {
140
+ --badge-bg: var(--a-accent-bg); /* token override only */
141
+ --badge-fg: var(--a-accent-fg);
142
+ }
143
+ }
144
+ ```
145
+
146
+ The `:where()` wrapper around the token block is what keeps token overrides from a parent provider winning over child component defaults. Drop it and theme switching breaks. A Safari `@scope` sweep found that hover / state-bearing selectors inside the `@scope` block can fail to match in some Safari versions; if that bites, lift the rule out of the block as a plain `tagname:hover` selector.
147
+
148
+ Detailed pattern in [css-patterns.md](css-patterns.md).
149
+
150
+ ## Tokens, never raw colors
151
+
152
+ Every color comes through a token. Zero `#hex` / `rgb()` / `rgba()` / `oklch()` in component CSS — the only places raw colors live are `packages/web-components/styles/colors/semantics.css` and `packages/web-components/styles/tokens.css`.
153
+
154
+ Component CSS aliases from L3 (the role × state matrix), not L2 (the family base). The L3 matrix is where state wiring lives; bypassing it strands the component outside the theme system and breaks dark mode / high-contrast modes silently.
155
+
156
+ ```css
157
+ /* right — L3 alias keeps the state cascade working */
158
+ :scope[variant="accent"]:hover {
159
+ --button-bg: var(--a-accent-bg-hover);
160
+ --button-fg: var(--a-accent-fg-hover);
161
+ }
162
+
163
+ /* wrong — L2 stops the cascade */
164
+ :scope[variant="accent"]:hover {
165
+ --button-bg: var(--a-accent); /* loses theme + contrast wiring */
166
+ }
167
+ ```
168
+
169
+ For text on filled accent discs (radio dots, step numerals), use `--a-chrome-light`, not `--a-accent-fg` — `accent-fg` resolves near-black in the current OKLCH derivation and disappears on saturated accent backgrounds.
170
+
171
+ Audit procedure + chrome palette in [token-contract.md](token-contract.md).
172
+
173
+ ## Variants change tokens, modes change layout
174
+
175
+ A **variant** is cosmetic — color, border, shadow depth. A **mode** restructures the box — direction, grid template, display type.
176
+
177
+ Variant bodies may only contain `--component-*: var(...)` lines. They must NEVER touch `padding`, `display`, `position`, `width`, `height`, `gap`, `flex`, `grid`, `overflow`, `border-radius`. Layout-changing attributes are modes, and modes require an entry in the Sanctioned Mode Attributes table at `docs/specs/component-token-contract.md` `Modes` section.
178
+
179
+ ```css
180
+ /* right — variant overrides tokens only */
181
+ :scope[variant="outlined"] {
182
+ --button-bg: transparent;
183
+ --button-border: var(--a-border);
184
+ }
185
+
186
+ /* wrong — variant changes layout; this should be a mode */
187
+ :scope[variant="outlined"] {
188
+ padding: var(--a-space-3) var(--a-space-4);
189
+ display: flex;
190
+ }
191
+ ```
192
+
193
+ ## Boolean attributes default to `false`
194
+
195
+ Every Boolean prop on a component must default to `false`. If the expected default behavior is "on," the prop name is wrong — flip it.
196
+
197
+ | Right (default off, opt in) | Wrong (default on, opt out) |
198
+ | --------------------------------- | ------------------------------ |
199
+ | `<drawer-ui permanent>` | `<drawer-ui closable>` |
200
+ | `<chart-ui static>` | `<chart-ui animate>` |
201
+ | `<cursor-ui hide-cursor>` | `<cursor-ui cursor>` |
202
+ | `<carousel-ui no-pause-on-hover>` | `<carousel-ui pause-on-hover>` |
203
+
204
+ Default behavior is the absent attribute. Attributes exist to opt OUT or carry a value — never to opt INTO the expected default.
205
+
206
+ State-bearing Booleans must `reflect: true` so CSS can match `:scope[disabled]`, `:scope[selected]`, etc. Without reflection, hover / active / selected styles break silently.
207
+
208
+ Native DOM accessors (`textContent`, `innerHTML`) get clobbered if declared in `static properties` — `installProps` overrides the native setter and `el.textContent = ''` becomes a signal write, not a child-wipe. Don't declare those names in `static properties`.
209
+
210
+ ## Symmetric lifecycle
211
+
212
+ Every `addEventListener` in `connected()` must be paired with a `removeEventListener` in `disconnected()`. Every timer cleared, every observer disconnected, every popover removed. Handlers must be stable `#field` arrows so `removeEventListener` finds them via reference equality.
213
+
214
+ ```js
215
+ class AdiaBadge extends AdiaElement {
216
+ #onRemove = (e) => { /* ... */ }; // stable handler
217
+
218
+ connected() {
219
+ this.#removeBtn?.addEventListener('click', this.#onRemove);
220
+ }
221
+
222
+ disconnected() {
223
+ this.#removeBtn?.removeEventListener('click', this.#onRemove);
224
+ this.#removeBtn = null; // null cached refs to release GC pin
225
+ }
226
+ }
227
+ ```
228
+
229
+ Inline arrows passed to `addEventListener` look fine and leak quietly — `removeEventListener` no-ops because the new arrow has different identity. Three components in one audit cycle bit on this exact bug.
230
+
231
+ If the class extends `AdiaFormElement`, `connected()` and `disconnected()` MUST call `super.*` — `ElementInternals` registration depends on it. And declare `disconnected()` exactly once per class; the second silently overrides the first.
232
+
233
+ Full lifecycle patterns in [lifecycle-patterns.md](lifecycle-patterns.md).
234
+
235
+ ## Reactivity through signals + effects
236
+
237
+ New runtime data flows use the project's `signal()` / `effect()` primitives — never parallel `CustomEvent`-only paths. The convention keeps reactivity coherent: a single read-write surface, observable by any number of consumers, integrated with the framework's effect graph.
238
+
239
+ ```js
240
+ import { signal, effect } from '@adia-ai/web-components/core/reactivity';
241
+
242
+ const count = signal(0);
243
+
244
+ effect(() => {
245
+ console.log('count is', count.value);
246
+ });
247
+
248
+ count.value = 1; // effect re-runs
249
+ ```
250
+
251
+ `CustomEvent` is fine for one-shot lifecycle notifications (a button click, a form submit). It's wrong for ongoing state that other components want to read — that wants a signal.
252
+
253
+ Custom setter `untracked()` discipline: reactive-property reads inside `set options/data/columns/...` setters that read `this.value` (via installProps getter) subscribe the caller's effect. Next `el.value = X` re-triggers it → drain loop. Wrap setter body in `untracked()`.
254
+
255
+ ## `data-stream-*` for data ingestion
256
+
257
+ Universal data ingestion across the library is the `data-stream-*` attribute trait (`packages/web-components/core/data-stream.js`). Signal-backed, refcounted shared transports, supports HTTP / SSE / WS, formats JSON / CSV / TSV / JSONL / text. Use it instead of bespoke `fetch` / `EventSource` per component.
258
+
259
+ ```html
260
+ <table-ui data-stream-src="/api/members"></table-ui>
261
+ <chart-ui data-stream-src="/api/metrics" data-stream-format="csv"></chart-ui>
262
+ <a2ui-root data-stream-src="/api/genui"></a2ui-root>
263
+ ```
264
+
265
+ The trait dedupes connections — two components pointing at the same URL share one transport — and propagates updates through the signal graph automatically.
266
+
267
+ ## Modern browser baseline
268
+
269
+ The project targets Chromium 125+, Safari 18.0+, Firefox 129+ (raised from 17.4 to 18.0 in ADR-0007 to clear the OKLCH-with-transparent red-shift bug and `:scope:state` `@scope` selector failures).
270
+
271
+ Don't propose polyfilling native APIs already at the baseline. The runtime expects native `:has()`, `@scope`, `@property`, OKLCH, `light-dark()`, `popover`, `dialog.showModal()`, `ResizeObserver`, `color-mix()`. Authoring against an older implicit floor is the source of most "but it works in Chrome" surprises.
272
+
273
+ When transparency-mixing colors, use `oklab` not `oklch` for the interpolation space — Safari < 18 had an OKLCH-with-transparent red-shift bug, and `oklab` is perceptually equivalent here:
274
+
275
+ ```css
276
+ --code-active-line-bg: color-mix(in oklab, var(--a-accent-muted) 40%, transparent);
277
+ ```
278
+
279
+ ## Legacy avoidance
280
+
281
+ The library was renamed from `@agent-ui-kit/*` to `@adia-ai/*`; tokens went from `--n-*` to `--a-*`; the base class went from `NanoElement` to `AdiaElement`. Don't use the legacy forms in new code. Don't reference `nano-ui` in notes, commit messages, CHANGELOG entries, or any AdiaUI artifact.
282
+
283
+ ```js
284
+ // right
285
+ import { AdiaElement } from '@adia-ai/web-components';
286
+ :where(:scope) { --button-bg: var(--a-accent-bg); }
287
+
288
+ // wrong
289
+ import { NanoElement } from '@agent-ui-kit/web-components';
290
+ :where(:scope) { --button-bg: var(--n-accent-bg); }
291
+ ```
292
+
293
+ Other deprecated forms swept in an early cut:
294
+
295
+ - `<button-ui variant="danger">` → `color="danger"`
296
+ - `<timeline-item-ui completed>` / `<stepper-item-ui completed>` → `status="completed"`
297
+ - `<table-toolbar-ui filterable>` (default-true Boolean) → `<table-toolbar-ui no-filter>` (default-false opt-out)
298
+ - `<chat-input-ui busy>` → `loading`
299
+ - `<toast-ui variant="error">` → `variant="danger"`
300
+ - `'chat-submit'` event → `'submit'`
301
+ - `<field-ui error="…">` → message moves to the slotted control
302
+
303
+ For a wholesale migration, use the dedicated migration skill — it reads the MIGRATION GUIDE and runs the mechanical sweeps.
304
+
305
+ ## Validate, always
306
+
307
+ Before declaring AdiaUI work done, walk the rule list above against the file you touched, then run the gate.
308
+
309
+ For consumer markup (HTML pages, exemplars, training data), the checks are manual + visual:
310
+
311
+ - Walk the rule list above (`*-ui` over raw HTML, layout primitives over `<div>`, card content model, no inline `style=`, no raw colors, L3 tokens, etc.).
312
+ - Run the project's lint if applicable: `npx eslint <path>`.
313
+ - Smoke the rendered output via `npm run dev` and load the touched page.
314
+
315
+ For component source under `packages/web-components/components/**`, the gate is heavier — see [authoring-cycle.md](authoring-cycle.md) Step 5 + the full release-side gate roster in the **adia-ui-release** skill's gate catalog.
316
+
317
+ ## Cross-references
318
+
319
+ - [authoring-cycle.md](authoring-cycle.md) — the full 5-step authoring procedure
320
+ - [api-contract.md](api-contract.md) — prop naming, type choices, reflection policy
321
+ - [css-patterns.md](css-patterns.md) — full @scope + variant/mode CSS architecture
322
+ - [lifecycle-patterns.md](lifecycle-patterns.md) — timers, observers, popovers, listeners
323
+ - [token-contract.md](token-contract.md) — token audit procedure
324
+ - [llm-bridge.md](llm-bridge.md) — mode 6 (extend @adia-ai/llm)
325
+ - [module-promotion.md](module-promotion.md) — mode 4 (promote inline → module)
326
+ - [anti-patterns.md](anti-patterns.md) — failure-mode catalogue with file:line refs
327
+ - **adia-ui-a2ui** (sibling skill) — generator / corpus / MCP pipeline
328
+ - **adia-ui-dogfood** (sibling skill) — cross-surface visual / static QA
329
+ - the **adia-ui-factory** plugin — composition playbook for consumers building apps on the framework
@@ -0,0 +1,93 @@
1
+ # Common gotchas — composite authoring traps
2
+
3
+ Five concrete failure modes encountered across a billing-overview rebuild + multi-demo grandfather-elimination cycle. Each is the kind of bug that:
4
+
5
+ - Renders visually broken without console errors
6
+ - Passes existing audits silently
7
+ - Is fixed in one place but recurs in others until pattern-corrected
8
+
9
+ Read this BEFORE Phase 3 sketch. Each entry includes the pattern, the detector (if any), and the fix.
10
+
11
+ ---
12
+
13
+ ## 1. Component used without reading its CSS — and especially without reading its composition grammar
14
+
15
+ **Pattern**: Stamping `<X-ui>` and relying on attributes/slots without opening `X-ui.css`. The primitive's `@scope` rules ARE part of its API contract, not implementation details. Particularly load-bearing: composition grammars (which children the primitive expects + how it lays them out).
16
+
17
+ **Example**: `payment-method-list-ui` stamped three sibling `<div data-brand>/<div data-meta>/<div data-actions>` inside `<card-ui>` and re-implemented the 3-column grid via custom `@scope` rules. Bypassed card-ui's canonical `<header>` + `[slot=icon|heading|description|action]` grammar entirely. Result: visual debt downstream (50% icon-to-frame ratio, off-rhythm tag placement, fragile chrome).
18
+
19
+ **Detector**: [§Phase 2.5a Component Literacy](composite-demo-protocol.md) suggests `composition_grammar` extraction from each used primitive's `.css`. `audit:card-structure` catches the specific card-ui bypass; analogous audits don't exist yet for avatar-ui / drawer-ui / aside-ui (forward work).
20
+
21
+ **Fix**: Read the primitive's `.css` end-to-end. Identify its expected child structure (slot grammar). USE it; never invent a parallel layer.
22
+
23
+ ---
24
+
25
+ ## 2. Parent CSS overriding child component's intrinsic display
26
+
27
+ **Pattern**: A parent composite hides/shows an embedded child via `display: none` ↔ `display: block` toggling. The `display: block` override beats the child's `:scope { display: flex }` from its own `@scope` (specificity 0,2,0 vs 0,1,0). Child's intrinsic layout silently collapses.
28
+
29
+ **Example**: 4 billing composites all had:
30
+
31
+ ```css
32
+ /* WRONG — clobbers empty-state-ui's flex column */
33
+ :scope > [data-empty] { display: none; }
34
+ :scope[empty] > [data-empty] { display: block; }
35
+ ```
36
+
37
+ `empty-state-ui` declares its own `:scope { display: flex; flex-direction: column; align-items: center }`. The parent's `display: block` removed that, making icon + heading + description flow inline: **`⊡ No payment methodsAdd a method to get started.`**
38
+
39
+ **Detector**: None today. Caught by user visual review.
40
+
41
+ **Fix** — invert visibility toggle so no display value is set when shown:
42
+
43
+ ```css
44
+ /* RIGHT — child's :scope display remains intact */
45
+ :scope:not([empty]) > [data-empty] { display: none; }
46
+ ```
47
+
48
+ ---
49
+
50
+ ## 3. Mixed sizes across form/control groups
51
+
52
+ **Pattern**: A composite stamps multiple form/control primitives in the same visual row (toolbar, button cluster, filter strip) without coordinating `size` attributes. Defaults differ — buttons might default `sm`, inputs default to a larger size, search-ui doesn't forward `size` to its inner input.
53
+
54
+ **Example**: an invoice-history toolbar had buttons at `size='sm'` (24px), filter chips at `size='sm'` (24px), search input at default (~36px). Same row, mismatched baseline.
55
+
56
+ **Detector**: None today. Caught by user visual review.
57
+
58
+ **Fix**:
59
+
60
+ - When stamping a control group, set the SAME `size` attribute on every control explicitly.
61
+ - Wrapper primitives (search-ui wraps input-ui; select-ui wraps native select) MUST forward `[size]` to their inner control. If a wrapper doesn't forward, file a fix in the wrapper rather than working around it in the consumer.
62
+
63
+ ---
64
+
65
+ ## 4. minmax(min, 1fr) inside repeat() fighting container queries
66
+
67
+ **Pattern**: A grid uses `repeat(N, minmax(<min>, 1fr))` with a hardcoded minimum, BUT the container also has `@container` queries that collapse columns at breakpoints. The minmax fights the breakpoints — when the container narrows, columns hit the floor and overflow before the breakpoint reduces column count.
68
+
69
+ **Example**: a dashboard-layout KPI grid was `repeat(4, minmax(16em, 1fr))` plus `@container ≤48em → 2 cols` and `≤32em → 1 col`. Redundant + conflicted. Removed the minmax; container queries own the responsive collapse cleanly.
70
+
71
+ **Detector**: None today.
72
+
73
+ **Fix**: When a grid has container-query breakpoints, use plain `repeat(N, 1fr)`. The breakpoints handle responsive behavior; minmax is for grids WITHOUT container queries.
74
+
75
+ ---
76
+
77
+ ## 5. Nested `<!-- ... -->` inside design-plan canonical-sketch fenced blocks
78
+
79
+ **Pattern**: The `<!-- design-plan: ... -->` block contains a fenced ` ```canonical-sketch ... ``` ` body. Authors sometimes paste HTML examples with inner `<!-- ... -->` comments into the sketch. HTML comments DON'T NEST — the inner `-->` closes the OUTER `<!-- design-plan: -->`. Trailing ` ``` --> ` then leaks as visible text on the page.
80
+
81
+ **Example**: a billing-overview.examples.html had two inner comments inside its canonical-sketch (annotations + a drawer composition example). Stray ` ``` --> ` rendered above the page header.
82
+
83
+ **Detector**: ✓ caught by `check:demo-pattern-source` — emits `phase_3_sketch contains an inner <!-- ... --> comment` finding.
84
+
85
+ **Fix**: Remove inner HTML comments from the canonical-sketch. Use plain text annotations or remove the doc-noise entirely.
86
+
87
+ ---
88
+
89
+ ## Meta-pattern across all 5
90
+
91
+ **Composites and primitives have layered contracts. The parent's CSS shouldn't reach into the child's layout territory. The child's CSS shouldn't fight its parent's container queries. The audit should detect the rendering hazard, not just the parsing structure.**
92
+
93
+ The structural defense for #1 (composition-grammar bypass) is `npm run audit:card-structure[:strict]` / `npm run audit:avatar-structure` / `npm run audit:alert-structure` (HTML + JS `createElement` scan) plus `npm run audit:sketch-grammar` at Phase 3. Phase 2.5a Component Literacy is a literacy hint, not a gate — the mechanical defenses above are the proximate fix; pre-flight is optional enrichment. See [composite-demo-protocol.md](composite-demo-protocol.md) §Phase 2.5a for the reframing. The other four gotchas are caught only by visual review until corresponding audits are added.