@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,205 @@
1
+ # API Contract — Props, Attributes, Reflection
2
+
3
+ Deep dive on declaring component APIs in AdiaUI. Read this when adding a new prop whose shape doesn't match any obvious pattern in the good-citizen references.
4
+
5
+ ## The `static properties` block
6
+
7
+ Every `AdiaElement` subclass declares its public API as a `static properties` object. Keys are camelCase JS names; values describe type, default, reflection, and attribute mapping.
8
+
9
+ ```javascript
10
+ static properties = {
11
+ disabled: { type: Boolean, default: false, reflect: true },
12
+ placeholder: { type: String, default: '', reflect: true },
13
+ value: { type: String, default: '' },
14
+ maxLength: { type: Number, default: null, reflect: true, attribute: 'max-length' },
15
+ };
16
+ ```
17
+
18
+ **Field rules:**
19
+
20
+ - `type` — one of `String`, `Number`, `Boolean`, `Object`, `Array`. The runtime uses this to coerce attribute strings into typed values.
21
+ - `default` — the value the prop takes when no attribute is present AND no JS value has been assigned. For `Boolean` props, default is ALWAYS `false` (see rule 1). For numeric props, default is `0`, a real value, or `null` for indeterminate — NEVER a sentinel like `-1`.
22
+ - `reflect` — when `true`, JS property changes write back to the HTML attribute so CSS can match it. Required for every state-bearing Boolean. Usually safe to omit for large value props (long strings, big objects).
23
+ - `attribute` — explicit kebab-case mapping when the JS name doesn't auto-convert cleanly. `camelCase` → `camel-case` automatic; override via `attribute: 'max-length'` when you want non-default behavior.
24
+
25
+ ## The `attr:` silent-typo trap
26
+
27
+ A real bug caught in iteration 4:
28
+
29
+ ```javascript
30
+ // BROKEN — silent
31
+ allowHalf: { type: Boolean, default: false, reflect: true, attr: 'allow-half' },
32
+
33
+ // FIXED
34
+ allowHalf: { type: Boolean, default: false, reflect: true, attribute: 'allow-half' },
35
+ ```
36
+
37
+ `attr:` is not a recognized key. The mapper ignores it. The default auto-conversion (`allowHalf` → `allow-half`) happens to produce the same result, so the bug hides — until someone changes the prop name and notices the attribute never wired.
38
+
39
+ Use `attribute:` verbatim. If you think you're writing `attr:`, stop and correct it before saving.
40
+
41
+ ## Boolean prop naming — the flip rule
42
+
43
+ AdiaUI conventions require `default: false` on all Booleans. The naming follows:
44
+
45
+ | Intended default behavior | Wrong name (default:true) | Right name (default:false) |
46
+ | --- | --- | --- |
47
+ | Modal can be dismissed | `closable` | `permanent` |
48
+ | Skeleton animates | `animate` | `static` |
49
+ | Stream shows blinking cursor | `cursor` | `hideCursor` |
50
+ | Chart shows average line | `average` | `hideAverage` |
51
+ | Toggle group allows multi-select | `multiple` | `single` |
52
+ | Swiper pauses on hover | `pause-on-hover` | `noPauseOnHover` |
53
+
54
+ **Naming patterns:**
55
+
56
+ - `permanent` / `static` / `readonly` — describes the non-default state positively.
57
+ - `no*` / `hide*` / `disable*` — prefixes for "opt-out of a default."
58
+
59
+ **Don't write:**
60
+
61
+ - `enabled` (invert to `disabled`), `visible` (invert to `hidden`) — these clash with standard HTML attribute vocabulary.
62
+ - Double-negatives like `unhide` or `dontSkip`.
63
+
64
+ ## Numeric props — `null` over sentinels
65
+
66
+ Indeterminate, unknown, or "not yet set" numeric state uses `null`, not `-1` or `Infinity`:
67
+
68
+ ```javascript
69
+ // BROKEN — `-1` is a magic sentinel
70
+ value: { type: Number, default: -1, reflect: true }
71
+ // Consumer: if (this.value !== -1) { ... }
72
+
73
+ // FIXED
74
+ value: { type: Number, default: null, reflect: true }
75
+ // Consumer: if (this.value != null) { ... }
76
+ ```
77
+
78
+ The `progress-ui` component carried `-1` for "indeterminate" for a long time. Branching on a specific number is fragile — someone assigns `-1` meaningfully later and the indeterminate check breaks silently. `null` is unambiguous.
79
+
80
+ **Back-compat coercion is allowed:**
81
+
82
+ ```javascript
83
+ set value(v) {
84
+ if (v === -1) v = null; // legacy input coercion, remove in a future cycle
85
+ this._value = v;
86
+ }
87
+ ```
88
+
89
+ ## Reserved-name anti-patterns
90
+
91
+ These prop names collide with HTML semantics, framework-wide conventions, or create ambiguity in audit. Pick alternatives:
92
+
93
+ ### `title`
94
+
95
+ HTML's global `title` attribute is the browser tooltip. Using `title` on a custom element makes any custom-element heading a tooltip by default, which is almost never intended. Use `heading`.
96
+
97
+ ### `active` on a parent component
98
+
99
+ Children can have per-item `active` (a Boolean describing that item's state). Parents use `value` (for a selection) or `step` (for an index into a series).
100
+
101
+ - `<timeline-item-ui active>` — OK, per-item Boolean state.
102
+ - `<timeline-ui step="3">` — correct: parent holds the index.
103
+ - `<timeline-ui active="3">` — wrong: `active` shouldn't carry a non-boolean payload.
104
+
105
+ ### `error` as a variant
106
+
107
+ Reserve `error` for validation state (`[error]` on form inputs matches ARIA patterns). For visual emphasis meaning "destructive/negative," use `danger`, matching the semantic family (`--a-danger-*` tokens).
108
+
109
+ ```html
110
+ <!-- wrong -->
111
+ <tag-ui variant="error">Failed</tag-ui>
112
+
113
+ <!-- right -->
114
+ <tag-ui variant="danger">Failed</tag-ui>
115
+
116
+ <!-- separate use -->
117
+ <input-ui error>Please enter a valid email</input-ui>
118
+ ```
119
+
120
+ ### `disabled` on a non-form component
121
+
122
+ `disabled` has form-participating semantics — it removes the element from the tab order, blocks submission, etc. On a non-form component (a diagram, a toolbar, a noodle editor), use `readonly`:
123
+
124
+ - `<input-ui disabled>` — correct: input is form-participating.
125
+ - `<noodles-ui readonly>` — correct: diagram is read-only, not form-disabled.
126
+
127
+ ### `multiple` with exclusion semantics
128
+
129
+ `<select multiple>` in HTML means "allow multiple selections." A prop named `multiple` means "multi-select is the default." If you want single-select as the default behavior:
130
+
131
+ ```html
132
+ <!-- wrong: implies multiple was default, which violates Boolean-false rule -->
133
+ <toggle-group-ui multiple>...</toggle-group-ui>
134
+
135
+ <!-- right: single-select is the opt-out, matches Boolean-false -->
136
+ <toggle-group-ui single>...</toggle-group-ui>
137
+ ```
138
+
139
+ ## Three-way name consistency
140
+
141
+ The component has three names that must agree:
142
+
143
+ - **File path:** `packages/web-components/components/foo/foo.js`
144
+ - **Class name:** `class AdiaFoo extends AdiaElement`
145
+ - **Custom element tag:** `customElements.define('foo-ui', AdiaFoo)`
146
+
147
+ A fourth consistency requirement: the CSS file at `packages/web-components/components/foo/foo.css` uses `@scope (foo-ui)`.
148
+
149
+ ## Extending `AdiaFormElement`
150
+
151
+ Form-participating components extend `AdiaFormElement` (which extends `AdiaElement`) and get `ElementInternals` wiring, form-reset handling, and `.form` / `.labels` / `.validity` accessors for free.
152
+
153
+ ```javascript
154
+ import { AdiaFormElement } from '../../core/form.js';
155
+
156
+ class AdiaInput extends AdiaFormElement {
157
+ static properties = {
158
+ ...AdiaFormElement.properties, // inherit name, value, disabled, required, etc.
159
+ placeholder: { type: String, default: '', reflect: true },
160
+ };
161
+
162
+ connected() {
163
+ super.connected(); // MUST call — registers ElementInternals
164
+ // ...
165
+ }
166
+
167
+ disconnected() {
168
+ super.disconnected(); // MUST call
169
+ // ...
170
+ }
171
+
172
+ // Override `value` getter/setter if the form-submitted value differs
173
+ // from the stored one
174
+ get value() { return this._value ?? ''; }
175
+ set value(v) { this._value = v; this.syncValue(String(v)); }
176
+ }
177
+ ```
178
+
179
+ Key details:
180
+
181
+ - **Always `super.connected()` and `super.disconnected()`** — without them, form-association doesn't register.
182
+ - **`this.syncValue(str)`** — call this whenever the value changes to update the form-submitted string. Accepts a string.
183
+ - **Inheriting properties** — spread `AdiaFormElement.properties` into your own `static properties` so you don't re-declare `name`, `disabled`, `required`.
184
+
185
+ ## Event conventions
186
+
187
+ - Bubble custom events: `new CustomEvent('foo', { bubbles: true, detail: {...} })`.
188
+ - Reuse standard events where possible: `input`, `change`, `submit`, `focus`.
189
+ - Custom event names are kebab-case: `cot-toggle`, `noodle-connected`.
190
+ - When dispatching state changes, fire `input` during interaction and `change` on commit. Matches native form semantics.
191
+
192
+ ## When to add the `render()` method
193
+
194
+ `render()` runs when reflected attributes change. Use it to update internal DOM that depends on props:
195
+
196
+ ```javascript
197
+ render() {
198
+ if (this.#textareaEl) {
199
+ this.#textareaEl.disabled = this.disabled;
200
+ this.#textareaEl.placeholder = this.placeholder;
201
+ }
202
+ }
203
+ ```
204
+
205
+ Rule of thumb: if CSS can do the work via an attribute selector (`:scope[disabled] { ... }`), prefer CSS. Reserve `render()` for propagating state into child inputs, recalculating positions, or reflecting data changes that attribute selectors can't express.
@@ -0,0 +1,211 @@
1
+ # Authoring cycle — modes 1 & 2 (NEW or MODIFY primitive)
2
+
3
+ The 5-step procedure run AFTER [primitive-audit.md](primitive-audit.md) clears (mode 1) or jumping straight to Step 2 (mode 2: modify existing).
4
+
5
+ Absorbed from the legacy author skill's Workflow section.
6
+
7
+ ---
8
+
9
+ ## Step 1 — Read the contract and the reference components
10
+
11
+ Before writing or editing, load these four files. They are the ground truth:
12
+
13
+ - `docs/specs/component-token-contract.md` — the authoritative invariants. Skim the whole thing if you haven't read it this session; focus on "Variants vs Modes" and "Sanctioned Mode Attributes" if you're adding a new layout-affecting attribute.
14
+ - `packages/web-components/core/element.js` — `AdiaElement` base class. The `static properties` schema, `connected()`/`disconnected()`/`render()` lifecycle, and attribute-mapping conventions are all defined here.
15
+ - `packages/web-components/core/form.js` — `AdiaFormElement`. Only needed if the new component participates in forms (inputs, selects, checkboxes, etc.).
16
+ - At least one good-citizen reference that matches the shape of what you're building. Pick from: `button-ui`, `card-ui`, `input-ui`, `textarea-ui`, `check-ui`. Read both the `.js` and the `.css`.
17
+
18
+ ## Step 2 — Classify the work before you write
19
+
20
+ Before typing, answer:
21
+
22
+ 1. **Is this cosmetic or structural?** If the change affects `display`, `flex-direction`, `grid-template`, `padding`, layout geometry — it's a mode, not a variant. Modes require an entry in the Sanctioned Mode Attributes table (`docs/specs/component-token-contract.md` `Modes` section). Do not introduce undocumented layout-changing variants.
23
+
24
+ 2. **Does the new prop fit the Boolean-default-false rule?** If the default behavior is "on," negate the prop name before writing (`closable` is wrong if closable is the default; `permanent` is right).
25
+
26
+ 3. **Does the component hold state that CSS needs to read?** If yes, every state-bearing Boolean declares `{ type: Boolean, default: false, reflect: true }`.
27
+
28
+ 4. **Does the component add listeners, timers, or observers?** If yes, plan the `disconnected()` method at the same time as `connected()` — do not defer. The symmetric pair is one unit of work, never two.
29
+
30
+ ## Step 3 — Apply the non-negotiable rules
31
+
32
+ These rules are the distilled lessons from a 5-iteration audit. Each one corresponds to a real bug that was fixed in the codebase. Full rationale and the bug histories are in [anti-patterns.md](anti-patterns.md).
33
+
34
+ ### API / Attributes
35
+
36
+ 1. **Boolean defaults are `false`.** If the expected default is "on," rename: `closable` → `permanent`, `animate` → `static`, `cursor` → `hideCursor`, `average` → `hideAverage`, `pause-on-hover` → `noPauseOnHover`.
37
+
38
+ 2. **No magic-value sentinels in numeric props.** Indeterminate = `null`, not `-1`. Consumers branch on `value == null`, which is explicit.
39
+
40
+ 3. **Use `attribute:` not `attr:`.** `attr:` is a silent typo — the mapper ignores it and the kebab-case HTML attribute never wires. This cost a real bug in `rating-ui`.
41
+
42
+ 4. **State-bearing Booleans reflect.** `{ type: Boolean, default: false, reflect: true }`. Without `reflect`, CSS can't match `:scope[disabled]`, hover/active/selected states break silently.
43
+
44
+ 5. **Reserved-name anti-patterns.** Avoid: `title` (collides with HTML tooltip attribute), `active` on parent components (use `value` for a selection or `step` for an index — per-item `active` on children is fine), `error` in variant names (use `danger`; reserve `error` for validation state), `disabled` on non-form-participating components (use `readonly`), `multiple` with exclusion semantics (use a negated positive like `single`).
45
+
46
+ 6. **Element tag ends in `-ui`; JS class is `Adia<Component>`.** `<foo-ui>` ↔ `class AdiaFoo extends AdiaElement`. Three-way consistency: filename, class name, custom-element tag. The sanctioned `-n` carve-out is `cot-ui` (the chain-of-thought streaming component); `nav-ui` was deprecated in favor of the `-ui` replacements. New `-n` tags require a contract-doc update — see `docs/specs/component-token-contract.md`.
47
+
48
+ ### CSS
49
+
50
+ 1. **Two-block `@scope` structure** is mandatory:
51
+
52
+ ```css
53
+ @scope (component-ui) {
54
+ :where(:scope) {
55
+ /* ── Tokens ── */
56
+ --component-bg: var(--a-bg);
57
+ --component-fg: var(--a-fg);
58
+ /* ...all component tokens here, zero-specificity */
59
+ }
60
+
61
+ :scope {
62
+ /* ── Base styles — consume only component tokens ── */
63
+ background: var(--component-bg);
64
+ color: var(--component-fg);
65
+ }
66
+
67
+ /* ── Variants / states third — override TOKENS only ── */
68
+ :scope[variant="outlined"] {
69
+ --component-bg: transparent;
70
+ --component-border: var(--a-border);
71
+ }
72
+ }
73
+ ```
74
+
75
+ 2. **Variants override tokens only.** A variant body may only contain `--component-*: var(...)` lines. Never `padding`, `display`, `position`, `width`, `height`, `margin`, `gap`, `flex`, `grid`, `overflow`, `border-radius`. Layout changes are modes, see rule 3 in Step 2.
76
+
77
+ 3. **Zero raw colors in component CSS.** No `#hex`, `rgb()`, `rgba()`, `oklch()` outside `packages/web-components/styles/colors/semantics.css` and `packages/web-components/styles/tokens.css`. Every color goes through a token.
78
+
79
+ 4. **Raw px ≥ 3 is forbidden.** Use `var(--a-space-*)`. Stroke/border widths (1–2px) and documented component-intrinsic constants are carve-outs, and each carve-out needs a one-line code comment explaining why the literal.
80
+
81
+ 5. **Component tokens follow `--<tag-stem>-<prop>`.** `--button-bg`, not `--btn-bg`. Files hosting multiple `@scope` blocks (e.g. `layout.css` with `col-ui`, `row-ui`, `stack-ui`) use each scope's own stem (`--col-gap`, `--row-gap`, `--stack-gap`).
82
+
83
+ 6. **Consume L3, not L2.** In a variant/state body, alias from the role×state matrix, not the family base. Right: `--button-fg-hover: var(--a-accent-fg-hover)`. Wrong: `--button-fg: var(--a-accent)`.
84
+
85
+ 7. **No BEM, no `::part()`, no `::slotted()`.** AdiaUI is light-DOM; the shadow-DOM escape hatches don't apply. Slots are styled through slotted attribute selectors (`:scope > [slot="foo"]`), not `::slotted()`.
86
+
87
+ ### JS Lifecycle
88
+
89
+ 1. **Every `addEventListener` in `connected()` has a matching `removeEventListener` in `disconnected()`.** Handler must be a stable `#field` arrow (`#onClick = (e) => { ... }`), never an inline arrow passed to `addEventListener`. Inline arrows can't be removed — `removeEventListener` needs reference equality.
90
+
91
+ 2. **`AdiaFormElement` subclasses call `super.connected()` and `super.disconnected()`.** `ElementInternals` registration depends on it. Omitting `super` strands the form-association.
92
+
93
+ 3. **Timers and observers are torn down.** `clearInterval`, `clearTimeout`, `ResizeObserver.disconnect()`, `MutationObserver.disconnect()`, `IntersectionObserver.disconnect()` all in `disconnected()`.
94
+
95
+ 4. **Null cached DOM refs in `disconnected()`.** `this.#fooEl = null` after removing its listeners. Prevents stale-tree GC pinning when the component is re-attached.
96
+
97
+ 5. **Never declare `disconnected()` twice in one class.** The second silently overrides the first — this exact bug lost `ResizeObserver` cleanup in `chart.js` for several commits. If you find yourself needing a "second disconnected," merge it into the existing one.
98
+
99
+ 6. **Popover/tooltip overlays created in `connected()` are removed in `disconnected()`.** Anything appended to `document.body` or `<body>` via the Popover API needs explicit cleanup; they don't GC with the host.
100
+
101
+ 7. **Inline arrows on dynamically created ephemeral DOM are tolerated only when the container is guaranteed fully-detached before re-render.** If a row is built fresh per render and the parent's `innerHTML` replacement detaches the old subtree, GC collects the listeners with their nodes. If the container persists, use stable handlers or event delegation on the parent.
102
+
103
+ ### Field composition
104
+
105
+ 1. **Do not add a `label` attribute to a new form-associated control.** `<field-ui label="…">` is the canonical labeled-field wrapper. It owns the real `<label for="…">` and binds to the slotted control's id for proper click-to-focus — a pattern the embedded per-control `label` attribute can't provide (no `[for]`, just a shadow slot). Existing controls (input-ui, select-ui, textarea-ui, switch-ui, check-ui, radio-ui, slider-ui, calendar-picker-ui, upload-ui, range-ui) still accept the legacy `label` attr but log a one-shot console.warn; **no new control should declare one**. Wrap instead:
106
+
107
+ ```html
108
+ <!-- right -->
109
+ <field-ui label="Email">
110
+ <input-ui type="email" value="…"></input-ui>
111
+ </field-ui>
112
+
113
+ <!-- wrong (deprecated) -->
114
+ <input-ui label="Email" type="email" value="…"></input-ui>
115
+ ```
116
+
117
+ `field-ui` also carries `[slot="trailing"]` and `[slot="action"]` composition slots + an `inline` mode attribute (stacked vs. single-row). See the Field component at `packages/web-components/components/field/`.
118
+
119
+ ### Nested-control composition
120
+
121
+ 1. **Composite hosts own the focus ring; nested form controls suppress theirs.** When a composite wraps a form control as an internal implementation detail (textarea-ui inside chat-input-ui is the canonical example), the composite IS the primary surface from a user's perspective. Focus should wrap the whole composite, not just the inner control. Pattern:
122
+
123
+ ```css
124
+ @scope (my-composite-ui) {
125
+ /* 1. Composite paints the ring via :focus-within —
126
+ wraps both the control and any siblings inside the shell. */
127
+ :scope:focus-within {
128
+ box-shadow: var(--my-composite-focus-ring);
129
+ }
130
+ :scope[aria-invalid="true"]:focus-within,
131
+ :scope[error]:focus-within {
132
+ box-shadow: var(--my-composite-focus-ring-invalid);
133
+ }
134
+
135
+ /* 2. Suppress the inner control's own focus affordance.
136
+ The @scope block's containment IS the signal — no data
137
+ attribute or explicit opt-in needed; selectors targeting
138
+ inner elements only apply when they're inside this host. */
139
+ textarea-ui [slot="text"]:focus {
140
+ box-shadow: none;
141
+ }
142
+ }
143
+ ```
144
+
145
+ Tokens follow the L3 pattern from rule 12:
146
+
147
+ ```css
148
+ --my-composite-focus-ring: var(--a-focus-ring);
149
+ --my-composite-focus-ring-invalid: var(--a-focus-ring-invalid);
150
+ ```
151
+
152
+ **When to use this (not field-ui):**
153
+ - **field-ui** is a _wrapper composite_ — it adds chrome (label / hint / error / required) around a control, but the control is still the primary focus target. Control owns the ring.
154
+ - **chat-input-ui** (and future equivalents) are _shell composites_ — the composite IS the control; the inner textarea is an implementation detail. Host owns the ring.
155
+
156
+ A user's mental model is the discriminator: do they think of the composite as a single control, or as a labeled/wrapped version of an inner control? The former is a shell; the latter is a wrapper.
157
+
158
+ ## Step 4 — Run the 30-second self-check
159
+
160
+ Before declaring the work done, run through this checklist. If anything fails, fix before committing.
161
+
162
+ ### Attributes
163
+
164
+ - [ ] No Boolean prop has `default: true`.
165
+ - [ ] No numeric prop uses `-1` or other sentinels (indeterminate = `null`).
166
+ - [ ] Every `static properties` entry uses `attribute:` (not `attr:`).
167
+ - [ ] Every state-bearing Boolean has `reflect: true`.
168
+ - [ ] No reserved-name anti-patterns (`title`, parent-level `active`, `error` variant, `disabled` on non-form, exclusion-`multiple`).
169
+ - [ ] Element tag ends in `-ui`; class is `Adia<Component>`.
170
+
171
+ ### CSS
172
+
173
+ - [ ] File opens with `@scope (component-ui) {` and has both `:where(:scope)` (tokens) and `:scope` (base styles) blocks.
174
+ - [ ] Variants in the file contain ONLY `--component-*: var(...)` lines.
175
+ - [ ] Zero `#hex` / `rgb()` / `rgba()` / `oklch()` in the file.
176
+ - [ ] Zero raw `px` values ≥ 3 (or each has a one-line justification comment).
177
+ - [ ] Component tokens prefixed with the scope's tag-stem.
178
+ - [ ] Variant/state bodies alias from L3 (`--a-<family>-<role>-<state>`), not L2 (`--a-<family>`).
179
+
180
+ ### Lifecycle
181
+
182
+ - [ ] Every `addEventListener` in `connected()` has a paired `removeEventListener` in `disconnected()`.
183
+ - [ ] All handlers passed to `addEventListener` are stable `#field` arrows (or bound method refs), not inline arrows.
184
+ - [ ] If the class extends `AdiaFormElement`, both `connected()` and `disconnected()` call `super`.
185
+ - [ ] Every timer/observer created in `connected()` is disposed in `disconnected()`.
186
+ - [ ] Cached DOM refs (`this.#fooEl`) are nulled in `disconnected()`.
187
+ - [ ] Class declares `disconnected()` exactly once.
188
+
189
+ ## Step 5 — Run the project's verification gates
190
+
191
+ Before committing, run the project's verify scripts. These catch the drift-shaped bugs the checklist can miss:
192
+
193
+ ```bash
194
+ npm run verify:components # component schema integrity (catalog)
195
+ npm run verify:palette # CVD thresholds across theme × scheme
196
+ node -c path/to/new/file.js # JS syntax check
197
+ ```
198
+
199
+ The full release-side gate roster lives in the **adia-ui-release** skill's gate catalog. Invoke `adia-ui-release` mode "Just verify" for the comprehensive sweep after any structural change.
200
+
201
+ If a gate fails, fix before declaring done.
202
+
203
+ ## Cross-references
204
+
205
+ - [primitive-audit.md](primitive-audit.md) — mode 1 §0 gate (run BEFORE this)
206
+ - [api-contract.md](api-contract.md) — deep dive on prop naming, type choices, reflection policy
207
+ - [css-patterns.md](css-patterns.md) — exhaustive CSS architecture (@scope, variants, modes)
208
+ - [lifecycle-patterns.md](lifecycle-patterns.md) — timers, observers, popovers, listener patterns
209
+ - [anti-patterns.md](anti-patterns.md) — full failure-mode catalogue, file:line refs
210
+ - [worked-example.md](worked-example.md) — badge-ui + counter-ui walkthroughs
211
+ - [token-contract.md](token-contract.md) — mode 5 audit (post-implementation token check)
@@ -0,0 +1,179 @@
1
+ # Canonical Pattern Index — survey targets for Mode 8 (composite-demo-protocol.md)
2
+
3
+ **Auto-generated** by `scripts/build-canonical-pattern-index.mjs`. Do not edit by hand — re-run the build script after adding new canonicals to `apps/`, `catalog/`, or `playgrounds/`.
4
+
5
+ This file is the checked-in snapshot the build script produces; the entries below are **representative** of a typical monorepo state (file lists + line counts will differ on your tree — re-run the build to refresh). The durable content is the **UI-type taxonomy** (the section headers + the lift-guidance under each): that taxonomy is the Phase 2 survey-target schema, and `ui_type` in Phase 1 must resolve to one of these section slugs.
6
+
7
+ ## How to use this index
8
+
9
+ 1. From Phase 1 of [composite-demo-protocol.md](composite-demo-protocol.md), name the UI type.
10
+ 2. Find the matching section below.
11
+ 3. Read every `.contents.html` listed (or the closest 2-3 if the section has many).
12
+ 4. Extract primitive composition per Phase 3 of the protocol.
13
+ 5. Cite the path in your demo's `<!-- Pattern source: ... -->` comment.
14
+
15
+ ---
16
+
17
+ ## billing
18
+
19
+ > Billing dashboards (current plan, invoices, payment methods, usage). Lift card-ui + section + col-ui + field-ui chain from billing.contents.html.
20
+
21
+ - `apps/saas/app/billing/billing.contents.html` (from SaaS app dashboards)
22
+ - `apps/user-flow/app/registration/billing/billing.contents.html` (from User-flow templates)
23
+
24
+ ## dashboard
25
+
26
+ > Admin dashboards (KPI grids, overview cards). Lift grid-ui responsive columns from admin-dashboard.contents.html.
27
+
28
+ - `apps/saas/app/admin-dashboard/admin-dashboard.contents.html` (from SaaS app dashboards)
29
+
30
+ ## settings
31
+
32
+ > Settings pages (preferences, security, appearance). Lift card-ui per setting group + col-ui spacing.
33
+
34
+ - `apps/saas/app/settings-page/settings-page.contents.html` (from SaaS app dashboards)
35
+ - `apps/user-flow/app/onboarding/notification-prefs/notification-prefs.contents.html` (from User-flow templates)
36
+ - `catalog/ui-patterns/app/settings-appearance/settings-appearance.contents.html` (from UI-pattern atomics)
37
+ - `catalog/ui-patterns/app/settings-notifications/settings-notifications.contents.html` (from UI-pattern atomics)
38
+ - `catalog/page-shells/app/settings-page/settings-page.contents.html` (from Page-shell templates)
39
+
40
+ ## auth-flow
41
+
42
+ > Authentication flows (sign-in, MFA, OAuth, password reset). Lift single-column form layout from sign-in.contents.html siblings.
43
+
44
+ - `apps/user-flow/app/auth/forgot-password/forgot-password.contents.html` (from User-flow templates)
45
+ - `apps/user-flow/app/auth/reset-password/reset-password.contents.html` (from User-flow templates)
46
+ - `apps/user-flow/app/auth/sign-in/mfa/mfa.contents.html` (from User-flow templates)
47
+ - `apps/user-flow/app/auth/sign-in/sign-in.contents.html` (from User-flow templates)
48
+ - `apps/user-flow/app/auth/sign-up/mfa-setup/mfa-setup.contents.html` (from User-flow templates)
49
+
50
+ ## onboarding-wizard
51
+
52
+ > Onboarding multi-step wizards. Lift step-progress + section + cta-row pattern.
53
+
54
+ - `apps/user-flow/app/onboarding/first-action/first-action.contents.html` (from User-flow templates)
55
+ - `apps/user-flow/app/onboarding/welcome/welcome.contents.html` (from User-flow templates)
56
+
57
+ ## registration-wizard
58
+
59
+ > Registration multi-step flows. Lift wizard step + form composition.
60
+
61
+ - `apps/saas/app/profile-security/profile-security.contents.html` (from SaaS app dashboards)
62
+ - `apps/user-flow/app/auth/sign-up/profile/profile.contents.html` (from User-flow templates)
63
+ - `catalog/ui-patterns/app/user-profile-card/user-profile-card.contents.html` (from UI-pattern atomics)
64
+
65
+ ## list-with-detail
66
+
67
+ > List + detail compositions (entity rows + drawer/modal detail). Lift entity-item + drawer pattern.
68
+
69
+ - `apps/saas/app/members/members.contents.html` (from SaaS app dashboards)
70
+ - `catalog/ui-patterns/app/users-table-badge/users-table-badge.contents.html` (from UI-pattern atomics)
71
+
72
+ ## integrations-list
73
+
74
+ > Searchable integration/connector grids. Lift grid-ui + card-ui + empty-state-inside-card pattern.
75
+
76
+ - `apps/saas/app/integrations/integrations.contents.html` (from SaaS app dashboards)
77
+ - `apps/user-flow/app/registration/integrations/integrations.contents.html` (from User-flow templates)
78
+
79
+ ## agent-activity
80
+
81
+ > Agent activity / reasoning feeds. Lift activity-feed scroll + collapsed-reasoning pattern.
82
+
83
+ - `catalog/ui-patterns/app/agent-activity-feed/agent-activity-feed.contents.html` (from UI-pattern atomics)
84
+ - `catalog/ui-patterns/app/agent-reasoning-collapsed/agent-reasoning-collapsed.contents.html` (from UI-pattern atomics)
85
+ - `catalog/page-shells/app/agent-canvas/agent-canvas.contents.html` (from Page-shell templates)
86
+
87
+ ## chat
88
+
89
+ > Chat surfaces (thread, composer, sidebar). Lift chat-streaming-surface composition.
90
+
91
+ - `apps/genui/app/factory-chat/factory-chat.contents.html` (from GenUI app routes)
92
+ - `catalog/ui-patterns/app/chat-streaming-surface/chat-streaming-surface.contents.html` (from UI-pattern atomics)
93
+ - `catalog/page-shells/app/chat-page/chat-page.contents.html` (from Page-shell templates)
94
+
95
+ ## editor
96
+
97
+ > Editor panes (code, preview, toolbar). Lift editor-shell composition.
98
+
99
+ - `apps/genui/app/a2ui-editor/a2ui-editor.contents.html` (from GenUI app routes)
100
+ - `catalog/ui-patterns/app/editor-code-pane/editor-code-pane.contents.html` (from UI-pattern atomics)
101
+ - `catalog/page-shells/app/editor-page/editor-page.contents.html` (from Page-shell templates)
102
+
103
+ ## kanban
104
+
105
+ > Kanban / column-based boards. Lift kanban-board-3col pattern.
106
+
107
+ - `catalog/ui-patterns/app/kanban-board-3col/kanban-board-3col.contents.html` (from UI-pattern atomics)
108
+ - `catalog/page-shells/app/kanban-page/kanban-page.contents.html` (from Page-shell templates)
109
+
110
+ ## data-table
111
+
112
+ > Data tables with badges/inline actions. Lift users-table-badge composition.
113
+
114
+ - `apps/user-flow/app/onboarding/import-data/import-data.contents.html` (from User-flow templates)
115
+
116
+ ## command
117
+
118
+ > Command palette / global search. Lift command-palette overlay pattern.
119
+
120
+ - `catalog/ui-patterns/app/command-palette/command-palette.contents.html` (from UI-pattern atomics)
121
+
122
+ ## overlay
123
+
124
+ > Modals, drawers, popovers. Lift destructive-confirm-modal pattern for confirmations.
125
+
126
+ - `apps/user-flow/app/registration/confirmation/confirmation.contents.html` (from User-flow templates)
127
+ - `catalog/ui-patterns/app/destructive-confirm-modal/destructive-confirm-modal.contents.html` (from UI-pattern atomics)
128
+
129
+ ## multi-step-funnel
130
+
131
+ > Multi-step conversion funnels. Lift conversion-funnel-6step composition.
132
+
133
+ - `catalog/ui-patterns/app/conversion-funnel-6step/conversion-funnel-6step.contents.html` (from UI-pattern atomics)
134
+
135
+ ## feed
136
+
137
+ > Activity / event feeds. Lift activity-feed composition.
138
+
139
+ - `apps/genui/app/gen-ui-feed/gen-ui-feed.contents.html` (from GenUI app routes)
140
+
141
+ ## marketing
142
+
143
+ > Marketing pages, hero CTAs. Lift marketing-hero-cta composition.
144
+
145
+ - `catalog/ui-patterns/app/marketing-hero-cta/marketing-hero-cta.contents.html` (from UI-pattern atomics)
146
+ - `catalog/page-shells/app/marketing-page/marketing-page.contents.html` (from Page-shell templates)
147
+
148
+ ## error-page
149
+
150
+ > Error states (404, 500, forbidden, expired). Lift centered single-card error pattern.
151
+
152
+ - `apps/user-flow/app/auth/account-locked/account-locked.contents.html` (from User-flow templates)
153
+ - `apps/user-flow/app/auth/session-expired/session-expired.contents.html` (from User-flow templates)
154
+ - `catalog/page-shells/app/error-page/error-page.contents.html` (from Page-shell templates)
155
+
156
+ ## demo-playground
157
+
158
+ > Internal demo + playground surfaces. Use these as reference for composition style; not always production-grade.
159
+
160
+ - `apps/genui/app/a2ui/a2ui.contents.html` (from GenUI app routes)
161
+ - `apps/genui/app/css-channel-demo/css-channel-demo.contents.html` (from GenUI app routes)
162
+
163
+ ## other
164
+
165
+ > Misc canonicals. Inspect contents.html to determine UI type.
166
+
167
+ - Any `.contents.html` whose slug fragment matches none of the heuristics above lands here. Inspect each to determine its UI type, or extend the `inferUiType()` regex (see Maintenance).
168
+
169
+ ---
170
+
171
+ ## Maintenance
172
+
173
+ Re-build this index:
174
+
175
+ ```bash
176
+ node scripts/build-canonical-pattern-index.mjs
177
+ ```
178
+
179
+ If a heuristic misclassifies a path (the file lands in `other` or the wrong UI type), update the `inferUiType()` regex in `scripts/build-canonical-pattern-index.mjs`.