@adia-ai/adia-ui-forge 0.8.62 → 0.8.64

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 (143) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/.codex-plugin/plugin.json +3 -3
  3. package/CHANGELOG.md +317 -275
  4. package/README.md +54 -18
  5. package/__init__.py +8 -8
  6. package/agents/a2ui-maintenance-agent.corpus.json +7 -7
  7. package/agents/a2ui-maintenance-agent.md +6 -6
  8. package/agents/a2ui-planner-agent.md +11 -11
  9. package/agents/demo-audit-agent.corpus.json +4 -4
  10. package/agents/demo-audit-agent.md +8 -9
  11. package/agents/framework-checker.md +10 -11
  12. package/agents/framework-planner-agent.md +9 -10
  13. package/agents/gen-ui-review-agent.corpus.json +4 -4
  14. package/agents/gen-ui-review-agent.md +8 -8
  15. package/agents/package-release-agent.corpus.json +4 -4
  16. package/agents/package-release-agent.md +9 -10
  17. package/agents/primitive-authoring-agent.corpus.json +1 -1
  18. package/agents/primitive-authoring-agent.md +5 -5
  19. package/agents/routing-corpus.json +46 -46
  20. package/codex.interface.json +1 -1
  21. package/commands/gen-ui-review.md +1 -1
  22. package/commands/package-release.md +2 -2
  23. package/package.json +1 -1
  24. package/plugin.yaml +2 -2
  25. package/prompts/gen-ui-review.md +1 -1
  26. package/prompts/package-release.md +2 -2
  27. package/references/contracts/a2ui-mcp-surface.md +7 -7
  28. package/references/contracts/migration-guide-format.md +6 -7
  29. package/references/shared/content-trust.md +3 -5
  30. package/skills/a2ui-maintenance/SKILL.md +13 -16
  31. package/skills/a2ui-maintenance/evals/evals.json +2 -2
  32. package/skills/a2ui-maintenance/evals/routing-corpus.json +1 -1
  33. package/skills/a2ui-maintenance/references/anti-patterns.md +10 -10
  34. package/skills/a2ui-maintenance/references/chunk-authoring.md +11 -12
  35. package/skills/a2ui-maintenance/references/corpus-discipline.md +13 -14
  36. package/skills/a2ui-maintenance/references/data-model-reactivity.md +22 -25
  37. package/skills/a2ui-maintenance/references/eval-diagnostics.md +21 -21
  38. package/skills/a2ui-maintenance/references/format-extension-decisions.md +12 -13
  39. package/skills/a2ui-maintenance/references/leverage-rules.md +6 -6
  40. package/skills/a2ui-maintenance/references/mcp-pipeline-ops.md +9 -10
  41. package/skills/a2ui-maintenance/references/mcp-tool-reference.md +11 -12
  42. package/skills/a2ui-maintenance/references/pipeline-overview.md +33 -38
  43. package/skills/a2ui-maintenance/references/semantic-fail-lifting.md +13 -14
  44. package/skills/a2ui-maintenance/references/strategy-engines.md +14 -16
  45. package/skills/a2ui-maintenance/references/surface-lifecycle.md +10 -11
  46. package/skills/a2ui-maintenance/references/zettel-calibration.md +14 -16
  47. package/skills/component-md-authoring/SKILL.md +22 -22
  48. package/skills/component-md-authoring/agents/openai.yaml +1 -1
  49. package/skills/cross-harness-authoring-standards/SKILL.md +19 -21
  50. package/skills/cross-harness-authoring-standards/agents/openai.yaml +1 -1
  51. package/skills/cross-harness-compatibility-standards/SKILL.md +35 -34
  52. package/skills/cross-harness-compatibility-standards/agents/openai.yaml +1 -1
  53. package/skills/demo-audit/SKILL.md +38 -38
  54. package/skills/demo-audit/evals/routing-corpus.json +1 -1
  55. package/skills/demo-audit/references/admin-shell-anatomy.md +21 -25
  56. package/skills/demo-audit/references/app-shell-pitfalls.md +11 -13
  57. package/skills/demo-audit/references/auto-fix-allowlist.md +3 -3
  58. package/skills/demo-audit/references/card-anatomy-sweep.md +10 -11
  59. package/skills/demo-audit/references/chat-shell-anatomy.md +25 -26
  60. package/skills/demo-audit/references/editor-shell-anatomy.md +25 -28
  61. package/skills/demo-audit/references/html-attr-sweep.md +12 -13
  62. package/skills/demo-audit/references/mode7-status-battery.md +4 -4
  63. package/skills/demo-audit/references/native-leak-annotations.md +8 -8
  64. package/skills/demo-audit/references/output-contract-worked-example.md +8 -8
  65. package/skills/demo-audit/references/probe-discipline-and-escalation.md +2 -2
  66. package/skills/demo-audit/references/visual-probe-triage.md +21 -23
  67. package/skills/gen-ui-review/SKILL.md +28 -28
  68. package/skills/gen-ui-review/agents/openai.yaml +1 -1
  69. package/skills/gen-ui-review/evals/evals.json +1 -1
  70. package/skills/gen-ui-review/evals/routing-corpus.json +1 -1
  71. package/skills/gen-ui-review/references/corpus-html-patterns.md +28 -29
  72. package/skills/gen-ui-review/references/exit-gate-mechanics.md +5 -5
  73. package/skills/gen-ui-review/references/lookup-maintenance.md +2 -2
  74. package/skills/gen-ui-review/references/loop-protocol.md +25 -25
  75. package/skills/gen-ui-review/references/rubric-cosmetic.md +10 -10
  76. package/skills/gen-ui-review/references/rubric-decompose.md +15 -16
  77. package/skills/gen-ui-review/references/rubric-score.md +23 -24
  78. package/skills/gen-ui-review/references/scorecard-worked-examples.md +12 -12
  79. package/skills/gen-ui-review/references/scores.schema.json +2 -2
  80. package/skills/llm-client-maintenance/SKILL.md +12 -12
  81. package/skills/llm-client-maintenance/evals/routing-corpus.json +1 -1
  82. package/skills/llm-client-maintenance/references/adapter-contract.md +27 -27
  83. package/skills/llm-client-maintenance/references/add-a-provider.md +25 -25
  84. package/skills/llm-client-maintenance/references/bridge-facade.md +31 -31
  85. package/skills/llm-client-maintenance/references/browser-proxy-boundary.md +17 -17
  86. package/skills/llm-client-maintenance/references/model-registry.md +14 -14
  87. package/skills/llm-client-maintenance/references/streaming-sse.md +21 -21
  88. package/skills/package-release/SKILL.md +21 -23
  89. package/skills/package-release/evals/evals.json +2 -2
  90. package/skills/package-release/evals/routing-corpus.json +2 -2
  91. package/skills/package-release/references/authorization-model.md +6 -8
  92. package/skills/package-release/references/changelog-discipline.md +25 -27
  93. package/skills/package-release/references/cut-procedure.md +129 -136
  94. package/skills/package-release/references/gates-catalog.md +42 -43
  95. package/skills/package-release/references/independent-package-release.md +14 -15
  96. package/skills/package-release/references/invariants-detail.md +9 -9
  97. package/skills/package-release/references/mechanization.md +11 -12
  98. package/skills/package-release/references/migration-guide-authoring.md +20 -20
  99. package/skills/package-release/references/notes-authoring.md +20 -20
  100. package/skills/package-release/references/recovery-paths.md +28 -28
  101. package/skills/primitive-authoring/SKILL.md +19 -19
  102. package/skills/primitive-authoring/agents/openai.yaml +1 -1
  103. package/skills/primitive-authoring/evals/evals.json +2 -2
  104. package/skills/primitive-authoring/evals/routing-corpus.json +2 -2
  105. package/skills/primitive-authoring/references/INDEX.md +22 -22
  106. package/skills/primitive-authoring/references/anti-patterns.md +76 -77
  107. package/skills/primitive-authoring/references/api-contract.md +63 -65
  108. package/skills/primitive-authoring/references/authoring-cycle.md +47 -50
  109. package/skills/primitive-authoring/references/canonical-pattern-index.md +118 -118
  110. package/skills/primitive-authoring/references/code-style.md +50 -52
  111. package/skills/primitive-authoring/references/common-gotchas.md +23 -23
  112. package/skills/primitive-authoring/references/composite-demo-protocol.md +42 -45
  113. package/skills/primitive-authoring/references/css-patterns.md +97 -97
  114. package/skills/primitive-authoring/references/for-attribute-event-contract.md +12 -12
  115. package/skills/primitive-authoring/references/form-control-sizing.md +16 -18
  116. package/skills/primitive-authoring/references/lifecycle-patterns.md +42 -42
  117. package/skills/primitive-authoring/references/llm-bridge.md +21 -21
  118. package/skills/primitive-authoring/references/module-promotion.md +51 -51
  119. package/skills/primitive-authoring/references/primitive-audit.md +16 -16
  120. package/skills/primitive-authoring/references/shell-patterns.md +81 -81
  121. package/skills/primitive-authoring/references/svg-authoring.md +62 -66
  122. package/skills/primitive-authoring/references/token-contract.md +45 -49
  123. package/skills/primitive-authoring/references/trait-pages.md +8 -8
  124. package/skills/primitive-authoring/references/worked-example.md +39 -39
  125. package/skills/primitive-authoring/references/yaml-contract.md +139 -143
  126. package/skills/site-deployment/SKILL.md +25 -26
  127. package/skills/site-deployment/agents/openai.yaml +1 -1
  128. package/skills/site-deployment/evals/routing-corpus.json +1 -1
  129. package/skills/site-deployment/references/deploy-playbooks.md +51 -53
  130. package/skills/site-docs-authoring/SKILL.md +14 -14
  131. package/skills/site-docs-authoring/agents/openai.yaml +1 -1
  132. package/skills/site-docs-authoring/evals/evals.json +1 -1
  133. package/skills/site-docs-authoring/evals/routing-corpus.json +1 -1
  134. package/skills/site-docs-authoring/intent.md +18 -18
  135. package/skills/ssr-compatibility/SKILL.md +24 -25
  136. package/skills/ssr-compatibility/agents/openai.yaml +1 -1
  137. package/skills/ssr-compatibility/evals/evals.json +1 -1
  138. package/skills/ssr-compatibility/evals/routing-corpus.json +1 -1
  139. package/skills/ssr-compatibility/references/consumer-workarounds.md +22 -23
  140. package/skills/ssr-compatibility/references/failure-shapes.md +84 -91
  141. package/skills/ssr-compatibility/references/guard-patterns.md +54 -58
  142. package/skills/ssr-compatibility/references/status-ledger.md +17 -17
  143. package/skills/ssr-compatibility/references/test-without-linkedom.md +26 -27
@@ -1,6 +1,6 @@
1
1
  # Authoring shell components
2
2
 
3
- Shell components are a distinct authoring shape from single-element primitives. They wrap an entire product surface admin app, chat streaming surface, code/canvas editor, docs page, simple splash and exist almost entirely to wire JS behaviors that CSS can't express on top of author-provided DOM.
3
+ Shell components are a distinct authoring shape from single-element primitives. They wrap an entire product surface, admin app, chat streaming surface, code/canvas editor, docs page, simple splash, and exist almost entirely to wire JS behaviors that CSS can't express on top of author-provided DOM.
4
4
 
5
5
  The **canonical authoring shape is bespoke cluster-namespaced children with state-as-attribute semantics**, per ADR-0023 (bespoke shell-tier children). Every shell-specific concern earns its own custom element with a documented attribute API; state lives as reflected attributes; parent shells coordinate via `querySelector` + slot routing without centralizing child behavior.
6
6
 
@@ -12,8 +12,8 @@ Legacy `data-*` shapes (`[data-canvas]`, `<aside-ui slot="leading">`, `<dialog d
12
12
 
13
13
  You're authoring a shell when:
14
14
 
15
- - The component holds no visual content of its own the page author provides everything inside it.
16
- - The component's value is in _behavior_ keyboard shortcuts, resize handles, ResizeObserver-driven breakpoints, localStorage persistence.
15
+ - The component holds no visual content of its own, the page author provides everything inside it.
16
+ - The component's value is in _behavior_, keyboard shortcuts, resize handles, ResizeObserver-driven breakpoints, localStorage persistence.
17
17
  - More than one component instance on a page would feel wrong (one shell per surface).
18
18
  - You're decomposing a new module-tier shell (chat / editor / simple / docs / future).
19
19
  - You're adding a new bespoke child to an existing cluster (`<admin-*>`, `<chat-*>`, `<editor-*>`, `<simple-*>`).
@@ -21,9 +21,9 @@ You're authoring a shell when:
21
21
 
22
22
  You are NOT authoring a shell when:
23
23
 
24
- - The component injects its own structure (header, body, footer slots filled with default markup) that's a regular composite. Use the `card-ui` pattern ([authoring-cycle.md](authoring-cycle.md)).
25
- - The component renders a single semantic element with cosmetic variants that's a leaf primitive. Use the button-ui / badge pattern ([authoring-cycle.md](authoring-cycle.md)).
26
- - You're extracting a cross-cluster reusable element (theme panel, command palette shared across shells) that's module promotion; see [module-promotion.md](module-promotion.md).
24
+ - The component injects its own structure (header, body, footer slots filled with default markup), that's a regular composite. Use the `card-ui` pattern ([authoring-cycle.md](authoring-cycle.md)).
25
+ - The component renders a single semantic element with cosmetic variants, that's a leaf primitive. Use the button-ui / badge pattern ([authoring-cycle.md](authoring-cycle.md)).
26
+ - You're extracting a cross-cluster reusable element (theme panel, command palette shared across shells), that's module promotion; see [module-promotion.md](module-promotion.md).
27
27
 
28
28
  ## Architectural principles (read first)
29
29
 
@@ -31,7 +31,7 @@ You are NOT authoring a shell when:
31
31
  2. **State-as-attribute.** Every consumer-queryable state is a **reflected attribute** on the relevant element. CSS `:has(<el>[state])`, JS `.hasAttribute('state')`. No threshold-math state inference.
32
32
  3. **Cluster namespace.** Bespoke children use `<cluster-thing>` (no `-ui` suffix) per ADR-0015. `<admin-*>` for admin shell, `<chat-*>` for chat shell, `<editor-*>` for editor shell, `<simple-*>` for simple shell.
33
33
  4. **Each child owns one concern.** If a child accumulates 3+ unrelated behaviors, split it.
34
- 5. **Backwards-compat window during introduction, then deprecate.** When introducing a NEW cluster's bespoke family, ship 1–2 patch releases where the host reads BOTH legacy and bespoke shapes via `:is()` selectors (so consumers can migrate). Then deprecate the legacy shape in the next MINOR cut strip the host's legacy reads, drop legacy CSS lifts, document in an ADR. The admin/chat/editor families completed this cycle (ADR-0024) legacy retired after a ~9-day compat window. Future cluster expansions should follow: introduce → migrate consumers → smoke-probe → deprecate.
34
+ 5. **Backwards-compat window during introduction, then deprecate.** When introducing a NEW cluster's bespoke family, ship 1–2 patch releases where the host reads BOTH legacy and bespoke shapes via `:is()` selectors (so consumers can migrate). Then deprecate the legacy shape in the next MINOR cut, strip the host's legacy reads, drop legacy CSS lifts, document in an ADR. The admin/chat/editor families completed this cycle (ADR-0024), legacy retired after a ~9-day compat window. Future cluster expansions should follow: introduce → migrate consumers → smoke-probe → deprecate.
35
35
 
36
36
  ## The 4-concern decomposition heuristic
37
37
 
@@ -44,7 +44,7 @@ When decomposing a shell, identify behavior in this order:
44
44
  | 3. **Cluster-specific orchestration** (LLM streaming, canvas state, etc.) | the host OR a dedicated coordinator | stays in `<X-shell>` if cluster-tied; promote to coordinator if reusable |
45
45
  | 4. **Structural composition** (chrome bars, scroll surfaces, page bands) | CSS-only stubs | one stub per region (mirrors `<admin-content>` / `<admin-topbar>` / etc.) |
46
46
 
47
- **Default split for a 5-concern shell** 2-3 JS-bearing + 5-7 CSS-only stubs = 7-10 bespoke children.
47
+ **Default split for a 5-concern shell**, 2-3 JS-bearing + 5-7 CSS-only stubs = 7-10 bespoke children.
48
48
 
49
49
  ## File scaffold per child
50
50
 
@@ -53,7 +53,7 @@ When decomposing a shell, identify behavior in this order:
53
53
  ```text
54
54
  packages/web-modules/<cluster>/<cluster>-<role>/
55
55
  <cluster>-<role>.js # behavior + reflected attributes
56
- <cluster>-<role>.yaml # SoT props/events/slots/states/keywords
56
+ <cluster>-<role>.yaml # SoT, props/events/slots/states/keywords
57
57
  <cluster>-<role>.a2ui.json # GENERATED via npm run build:components
58
58
  <cluster>-<role>.html # demo shell that fetches .examples.html
59
59
  <cluster>-<role>.examples.html # living examples + behavior wiring tables
@@ -64,7 +64,7 @@ packages/web-modules/<cluster>/<cluster>-<role>/
64
64
 
65
65
  ```text
66
66
  packages/web-modules/<cluster>/<cluster>-<role>/
67
- <cluster>-<role>.yaml # SoT slots only, no props/events
67
+ <cluster>-<role>.yaml # SoT, slots only, no props/events
68
68
  <cluster>-<role>.a2ui.json # GENERATED
69
69
  <cluster>-<role>.html # demo shell
70
70
  <cluster>-<role>.examples.html # composition snippet + slot vocabulary
@@ -81,7 +81,7 @@ NO `.js`, NO own `.css` for CSS-only stubs. They render as unknown `HTMLElement`
81
81
  | **Reflected string** `[name="..."]` `[shortcut="cmd+k\|ctrl+k\|both"]` | Author-supplied configuration that other code reads |
82
82
  | **Property only (no reflect)** | Internal state, perf-sensitive, or values too varied to enumerate |
83
83
 
84
- **Multi-word camelCase props** map to kebab-case attributes via `attribute: '...'` (NOT `attr:` see Pitfall #1):
84
+ **Multi-word camelCase props** map to kebab-case attributes via `attribute: '...'` (NOT `attr:`, see Pitfall #1):
85
85
 
86
86
  ```js
87
87
  static properties = {
@@ -98,13 +98,13 @@ Each cluster's bespoke children get styled via a single bridge file:
98
98
  packages/web-modules/<cluster>/<cluster>-shell/css/<cluster>-shell.bespoke.css
99
99
  ```
100
100
 
101
- Imported **last** in `<cluster>-shell.css` so its rules layer over the legacy CSS without modification. Don't expand `:is(legacy, slot-ui, ...)` selectors throughout the legacy CSS that's invasive and makes Phase 3 (legacy removal) harder.
101
+ Imported **last** in `<cluster>-shell.css` so its rules layer over the legacy CSS without modification. Don't expand `:is(legacy, slot-ui, ...)` selectors throughout the legacy CSS, that's invasive and makes Phase 3 (legacy removal) harder.
102
102
 
103
103
  ## Step-by-step procedure
104
104
 
105
105
  ### 1. Audit existing shell
106
106
 
107
- Read the host's `.js` file. Identify the 4 concerns + which are already in primitive sub-elements (e.g., `<pane-ui resizable>` already owns pane resize don't duplicate).
107
+ Read the host's `.js` file. Identify the 4 concerns + which are already in primitive sub-elements (e.g., `<pane-ui resizable>` already owns pane resize, don't duplicate).
108
108
 
109
109
  ```bash
110
110
  wc -l packages/web-modules/<cluster>/<cluster>-shell/<cluster>-shell.js
@@ -129,7 +129,7 @@ class ChildName extends UIElement {
129
129
  camelProp: { type: String, default: '', reflect: true, attribute: 'kebab-prop' },
130
130
  };
131
131
 
132
- static template = () => null; // stamp nothing author owns DOM
132
+ static template = () => null; // stamp nothing, author owns DOM
133
133
 
134
134
  #cleanups = [];
135
135
 
@@ -152,7 +152,7 @@ customElements.define('cluster-role', ChildName);
152
152
  export { ChildName };
153
153
  ```
154
154
 
155
- **The "cleanup-closure pile" pattern** (`#cleanups: [() => …, () => …]`) scales to N drag handles each handle's setup pushes a closure that removes its specific listeners and undoes any body-level pointer-events mutation. `disconnected()` just drains the pile.
155
+ **The "cleanup-closure pile" pattern** (`#cleanups: [() => …, () => …]`) scales to N drag handles, each handle's setup pushes a closure that removes its specific listeners and undoes any body-level pointer-events mutation. `disconnected()` just drains the pile.
156
156
 
157
157
  The path to `core/element.js` from `packages/web-modules/<cluster>/<cluster>-<role>/` is **`../../../web-components/core/element.js`** (3 levels up).
158
158
 
@@ -163,18 +163,18 @@ Minimum 8-10 tests per JS-bearing child covering: registration, default reflecte
163
163
  **Critical test-setup pitfalls**:
164
164
 
165
165
  ```js
166
- // happy-dom returns 0 from getBoundingClientRect patch in beforeEach
166
+ // happy-dom returns 0 from getBoundingClientRect, patch in beforeEach
167
167
  HTMLElement.prototype.getBoundingClientRect = function () {
168
168
  const w = parseFloat(this.style?.width) || 240;
169
169
  return { width: w, height: 600, top: 0, left: 0, right: w, bottom: 600, x: 0, y: 0 };
170
170
  };
171
171
 
172
- // happy-dom doesn't ship ResizeObserver stub it
172
+ // happy-dom doesn't ship ResizeObserver, stub it
173
173
  globalThis.ResizeObserver = class {
174
174
  observe() {} unobserve() {} disconnect() {}
175
175
  };
176
176
 
177
- // happy-dom's <dialog> is partial polyfill showModal / close / open getter
177
+ // happy-dom's <dialog> is partial, polyfill showModal / close / open getter
178
178
  function patchDialogPolyfill(dialog) {
179
179
  let isOpen = false;
180
180
  Object.defineProperty(dialog, 'open', {
@@ -189,7 +189,7 @@ function patchDialogPolyfill(dialog) {
189
189
 
190
190
  For each bespoke tag, declare structural CSS in `<cluster>-shell.bespoke.css`. Reuse tokens from the existing `<cluster>-shell.tokens.css`. Don't introduce new tokens unless the structural concern is genuinely new.
191
191
 
192
- Shells follow the standard two-block `@scope` + L3 token + zero-raw-color rules from [css-patterns.md](css-patterns.md) and [token-contract.md](token-contract.md) the shell tag stem prefixes its own tokens (`--admin-shell-sidebar-w`, `--chat-shell-header-h`).
192
+ Shells follow the standard two-block `@scope` + L3 token + zero-raw-color rules from [css-patterns.md](css-patterns.md) and [token-contract.md](token-contract.md), the shell tag stem prefixes its own tokens (`--admin-shell-sidebar-w`, `--chat-shell-header-h`).
193
193
 
194
194
  ### 6. Refactor the host to coordinate
195
195
 
@@ -198,7 +198,7 @@ The host loses behavior it used to centralize. It now reflects host-level config
198
198
  **Once the cluster reaches Phase 3** (legacy deprecated, ADR-0024 pattern), the host queries ONLY the bespoke children:
199
199
 
200
200
  ```js
201
- // Bespoke-only (post-Phase-3 current state for admin/chat/editor):
201
+ // Bespoke-only (post-Phase-3, current state for admin/chat/editor):
202
202
  #findSidebar(name) {
203
203
  return this.querySelector(`<cluster>-sidebar[slot="${name}"], <cluster>-sidebar[name="${name}"]`);
204
204
  }
@@ -212,7 +212,7 @@ btn.addEventListener('click', () => {
212
212
  **During the introduction window** (1–2 patch releases before deprecation), the host reads both shapes via `:is()` selectors with a typeof-check + legacyToggle fallback:
213
213
 
214
214
  ```js
215
- // Compat window (Phase 1 + Phase 2 pre-deprecation):
215
+ // Compat window (Phase 1 + Phase 2, pre-deprecation):
216
216
  static SIDEBAR_SEL =
217
217
  ':is(<cluster>-sidebar[slot="leading"], <cluster>-sidebar[slot="trailing"], ' +
218
218
  '[data-<cluster>-sidebar], aside-ui[slot="leading"], aside-ui[slot="trailing"])';
@@ -232,14 +232,14 @@ btn.addEventListener('click', () => {
232
232
  export { ClusterShell } from './cluster-shell/cluster-shell.js';
233
233
  export { ClusterSidebar } from './cluster-sidebar/cluster-sidebar.js';
234
234
  export { ClusterCommand } from './cluster-command/cluster-command.js';
235
- // CSS-only stubs don't need exports they don't register customElements
235
+ // CSS-only stubs don't need exports, they don't register customElements
236
236
  ```
237
237
 
238
238
  ### 8. Run the build + verify gates
239
239
 
240
240
  ```bash
241
241
  node scripts/build/components.mjs # picks up new yamls, generates .a2ui.json
242
- node scripts/build/components.mjs --verify # must say "clean N files up-to-date"
242
+ node scripts/build/components.mjs --verify # must say "clean, N files up-to-date"
243
243
  npm run check:lockstep
244
244
  npm run verify:traits
245
245
  npm run smoke:engines
@@ -250,7 +250,7 @@ npx vitest run packages/web-modules/<cluster>/<cluster>-<role>/<cluster>-<role>.
250
250
 
251
251
  ### 9. Update the host's `examples.html`
252
252
 
253
- Add a "Basic shape (bespoke recommended)" section alongside the existing legacy section. Show the full bespoke composition. Add a "State as attribute" section with CSS `:has()` examples + JS API examples + ADR-0023 cross-reference.
253
+ Add a "Basic shape (bespoke, recommended)" section alongside the existing legacy section. Show the full bespoke composition. Add a "State as attribute" section with CSS `:has()` examples + JS API examples + ADR-0023 cross-reference.
254
254
 
255
255
  ### 10. Update sibling demo pages
256
256
 
@@ -258,7 +258,7 @@ Add a "Family pattern (forward-looking)" section to the shell's `<X>-shell.examp
258
258
 
259
259
  ### 11. Sweep verification (post-migration grep audit)
260
260
 
261
- After landing a vocabulary migration or deprecation, run a comprehensive grep audit across **all extensions** for the legacy pattern set. Markup-only commits frequently leave **CSS selectors and JS comments** referencing the old vocabulary they're in different files from the markup, so a markup-only commit looks complete but leaves drift.
261
+ After landing a vocabulary migration or deprecation, run a comprehensive grep audit across **all extensions** for the legacy pattern set. Markup-only commits frequently leave **CSS selectors and JS comments** referencing the old vocabulary, they're in different files from the markup, so a markup-only commit looks complete but leaves drift.
262
262
 
263
263
  ```bash
264
264
  LEGACY_PATTERNS=(
@@ -288,7 +288,7 @@ This step was added after a sweep revealed apps with leftover `adia-editor-ui` +
288
288
 
289
289
  After an apps/playgrounds/catalog reorg (ADR-0026) surfaced 4 categories that survived a full-path substitution sweep. **Full-path grep is necessary but insufficient.** Add these to any vocabulary migration's verification:
290
290
 
291
- ##### Category A Bare-name prose mentions in narrative docs
291
+ ##### Category A, Bare-name prose mentions in narrative docs
292
292
 
293
293
  ```bash
294
294
  LEGACY_NAMES=( 'old-folder-name' 'old-thing-name' )
@@ -300,7 +300,7 @@ for n in "${LEGACY_NAMES[@]}"; do
300
300
  done
301
301
  ```
302
302
 
303
- ##### Category B Skill directory names that follow folder-name convention
303
+ ##### Category B, Skill directory names that follow folder-name convention
304
304
 
305
305
  ```bash
306
306
  find . -type d -name '*-expert' -not -path '*/node_modules/*' \
@@ -313,12 +313,12 @@ for skill in $(find . -name 'SKILL.md' -not -path '*/node_modules/*'); do
313
313
  done
314
314
  ```
315
315
 
316
- ##### Category C JSON metadata fields at filename granularity
316
+ ##### Category C, JSON metadata fields at filename granularity
317
317
 
318
318
  Critical: full-path sweeps catch directory moves but miss **file renames within moved directories**.
319
319
 
320
320
  ```bash
321
- # Corpus chunks, sitemaps, catalog manifests any JSON with file paths.
321
+ # Corpus chunks, sitemaps, catalog manifests, any JSON with file paths.
322
322
  LEGACY_FILENAMES=( 'old-name.html' 'old-name.contents.html' 'old-name.contents.js' )
323
323
  for f in "${LEGACY_FILENAMES[@]}"; do
324
324
  grep -rn "$f" --include='*.json' \
@@ -326,9 +326,9 @@ for f in "${LEGACY_FILENAMES[@]}"; do
326
326
  done
327
327
  ```
328
328
 
329
- This is the **highest-impact category** corpus chunks with stale `source` or `page` fields cause silent harvest miss on the next rebuild (the chunk loads but won't be re-harvestable; no error).
329
+ This is the **highest-impact category**, corpus chunks with stale `source` or `page` fields cause silent harvest miss on the next rebuild (the chunk loads but won't be re-harvestable; no error).
330
330
 
331
- ##### Category D Inventory tables in cross-cutting docs
331
+ ##### Category D, Inventory tables in cross-cutting docs
332
332
 
333
333
  ```bash
334
334
  grep -ln 'old-name' README.md AGENTS.md docs/ops/ROADMAP.md .claude/docs/specs/INDEX.md
@@ -341,7 +341,7 @@ grep -rln 'OLD_NAME' . --include='*.md' --include='*.json' \
341
341
  | sort > /tmp/pre-rename-refs.txt
342
342
  ```
343
343
 
344
- After the rename + path sweep, take a fresh snapshot and diff anything in the post-snapshot is a stale ref to investigate.
344
+ After the rename + path sweep, take a fresh snapshot and diff, anything in the post-snapshot is a stale ref to investigate.
345
345
 
346
346
  **ADR-0026 case study**: a corpus chunk's `page` field pointed at the pre-rename `app-shell.contents.html` inside the post-rename `/playgrounds/admin-shell/app/` directory. Full-path sweep caught the directory rename but missed the filename mismatch within it. Would have caused silent harvest miss on the next rebuild.
347
347
 
@@ -355,11 +355,11 @@ After the rename + path sweep, take a fresh snapshot and diff — anything in th
355
355
  | Public API present + returns expected | per-child test file |
356
356
  | Event dispatch | per-child test file |
357
357
  | Cleanup on disconnect | per-child test file |
358
- | Backwards-compat legacy markup still works | manual visual smoke against `apps/<X>-shell/` or `site/` |
358
+ | Backwards-compat, legacy markup still works | manual visual smoke against `apps/<X>-shell/` or `site/` |
359
359
 
360
360
  ## Trigger-attribute conventions (bespoke-compatible)
361
361
 
362
- Author-controlled action buttons that the shell wires up follow a consistent convention. These are TRIGGER attributes (on buttons), not state attributes (on shell children) they still work in the bespoke world:
362
+ Author-controlled action buttons that the shell wires up follow a consistent convention. These are TRIGGER attributes (on buttons), not state attributes (on shell children), they still work in the bespoke world:
363
363
 
364
364
  ```html
365
365
  <button-ui icon="list" data-sidebar-toggle="leading"></button-ui>
@@ -369,19 +369,19 @@ The shell finds these via `querySelectorAll('[data-sidebar-toggle]')` and binds
369
369
 
370
370
  Other trigger attributes:
371
371
 
372
- - `[data-command-trigger]` opens `<admin-command>` via `.show()`
373
- - `[data-toolbar-action="<name>"]` buttons inside `<editor-toolbar>` that bubble `toolbar-action` events
372
+ - `[data-command-trigger]`, opens `<admin-command>` via `.show()`
373
+ - `[data-toolbar-action="<name>"]`, buttons inside `<editor-toolbar>` that bubble `toolbar-action` events
374
374
 
375
375
  ## Persistence (localStorage namespacing)
376
376
 
377
- Sidebar-collapsed state, pane widths, drawer-open state anything the user customizes persists in `localStorage`, keyed by the cluster + region:
377
+ Sidebar-collapsed state, pane widths, drawer-open state, anything the user customizes, persists in `localStorage`, keyed by the cluster + region:
378
378
 
379
379
  ```js
380
380
  const KEY = `adia-sidebar:${this.name}:width`; // adia-sidebar:leading:width
381
381
  localStorage.setItem(KEY, String(width));
382
382
  ```
383
383
 
384
- Read on `connected()`, write on resize/toggle. Always wrap reads in try/catch quota errors and disabled-storage modes happen.
384
+ Read on `connected()`, write on resize/toggle. Always wrap reads in try/catch, quota errors and disabled-storage modes happen.
385
385
 
386
386
  ```js
387
387
  #restoreWidth() {
@@ -396,11 +396,11 @@ Read on `connected()`, write on resize/toggle. Always wrap reads in try/catch
396
396
 
397
397
  ## Document-level listeners belong to the shell, not the children
398
398
 
399
- `Cmd+K` for the command palette, `Esc` for closing a drawer, `?` for a shortcuts overlay these listen on `document`, not on the shell element. The shell binds them in `connected()` with `addEventListener` on `document`, removes them in `disconnected()`. NEVER attach `document`-level listeners from a child component (a button-ui etc.) they'd persist after the child unmounts.
399
+ `Cmd+K` for the command palette, `Esc` for closing a drawer, `?` for a shortcuts overlay, these listen on `document`, not on the shell element. The shell binds them in `connected()` with `addEventListener` on `document`, removes them in `disconnected()`. NEVER attach `document`-level listeners from a child component (a button-ui etc.), they'd persist after the child unmounts.
400
400
 
401
401
  ## Custom events for downstream consumers
402
402
 
403
- Pane-resize, breakpoint-change, sidebar-collapse fire `CustomEvent`s that bubble through the shell so consumers can react:
403
+ Pane-resize, breakpoint-change, sidebar-collapse, fire `CustomEvent`s that bubble through the shell so consumers can react:
404
404
 
405
405
  ```js
406
406
  this.dispatchEvent(new CustomEvent('sidebar-resize', {
@@ -414,7 +414,7 @@ Consumers (CodeMirror layout, canvas redraw, dependent UI) listen on the shell o
414
414
 
415
415
  ## Pitfalls
416
416
 
417
- 1. **`attr:` vs `attribute:`.** The framework uses `attribute: '...'` for camelCase to kebab-case mapping. `attr:` is silently ignored your custom element won't react to the kebab-case attribute. Always `attribute:`. Every JS-bearing bespoke child has at least one camelCase prop (`minWidth`, `noShortcut`, `proxyUrl`, etc.); the wrong key fails silently.
417
+ 1. **`attr:` vs `attribute:`.** The framework uses `attribute: '...'` for camelCase to kebab-case mapping. `attr:` is silently ignored, your custom element won't react to the kebab-case attribute. Always `attribute:`. Every JS-bearing bespoke child has at least one camelCase prop (`minWidth`, `noShortcut`, `proxyUrl`, etc.); the wrong key fails silently.
418
418
 
419
419
  2. **Path depth in test imports.** From `packages/web-modules/<cluster>/<cluster>-<role>/`, `core/element.js` is **3 levels up** (`../../../web-components/core/element.js`), not 2.
420
420
 
@@ -424,27 +424,27 @@ Consumers (CodeMirror layout, canvas redraw, dependent UI) listen on the shell o
424
424
 
425
425
  5. **Spy timing for synchronous custom-element lifecycle.** `vi.spyOn(document, 'addEventListener')` set BEFORE `mount()` should still catch listeners added in `connected()`, but happy-dom's customElements lifecycle quirks sometimes break this. When in doubt, test the **behavior** (does Cmd+K toggle?) instead of the **mechanism** (was a listener added?).
426
426
 
427
- 6. **Don't expand legacy `:is()` selectors.** The temptation when introducing a new cluster is to add `, <X-sidebar>[slot=leading]` to every existing `:is([data-X-sidebar], aside-ui[slot=leading])` selector in the legacy CSS. Resist. Use a new bespoke.css bridge file instead easier to drop at Phase 3 deprecation. (admin/chat/editor deprecation pass collapsed all `:is(legacy, bespoke)` lifts to bespoke-only in 6 layered CSS files via a single regex pass; would have been 100x more work if lifts were sprinkled inline.)
427
+ 6. **Don't expand legacy `:is()` selectors.** The temptation when introducing a new cluster is to add `, <X-sidebar>[slot=leading]` to every existing `:is([data-X-sidebar], aside-ui[slot=leading])` selector in the legacy CSS. Resist. Use a new bespoke.css bridge file instead, easier to drop at Phase 3 deprecation. (admin/chat/editor deprecation pass collapsed all `:is(legacy, bespoke)` lifts to bespoke-only in 6 layered CSS files via a single regex pass; would have been 100x more work if lifts were sprinkled inline.)
428
428
 
429
- 7. **Host LOC will grow during the compat window, then shrink at deprecation.** When introducing a new cluster's bespoke family, both code paths coexist in the host. Expected (admin-shell hit ~305 LOC during introduction). At deprecation (Phase 3 see ADR-0024 for the canonical playbook), the host shrinks dramatically: admin-shell dropped to ~87 LOC (−71%). **Don't try to "consolidate" before deprecation lands** the dual paths are intentional during the migration window.
429
+ 7. **Host LOC will grow during the compat window, then shrink at deprecation.** When introducing a new cluster's bespoke family, both code paths coexist in the host. Expected (admin-shell hit ~305 LOC during introduction). At deprecation (Phase 3, see ADR-0024 for the canonical playbook), the host shrinks dramatically: admin-shell dropped to ~87 LOC (−71%). **Don't try to "consolidate" before deprecation lands**: the dual paths are intentional during the migration window.
430
430
 
431
- 8. **Sibling cluster siblings.** `web-modules/shell/admin-shell/` lives next to `web-modules/shell/admin-sidebar/` (cluster-namespace siblings). For chat `web-modules/chat/chat-shell/` next to `web-modules/chat/chat-thread/`. The directory layout makes the cluster family visible at `ls`.
431
+ 8. **Sibling cluster siblings.** `web-modules/shell/admin-shell/` lives next to `web-modules/shell/admin-sidebar/` (cluster-namespace siblings). For chat, `web-modules/chat/chat-shell/` next to `web-modules/chat/chat-thread/`. The directory layout makes the cluster family visible at `ls`.
432
432
 
433
433
  9. **localStorage namespace per cluster.** Each cluster's persisted state must use a cluster-distinct key prefix. Without distinct prefixes, two sidebars sharing the same slot value (`leading`) on different shells would clobber each other. Verify with a test that checks both namespaces are isolated.
434
434
 
435
- 10. **Bespoke event names ≠ legacy event names.** When a bespoke wrapper forwards an event from an inner primitive, give it a NEW name (e.g. `composer-submit` from `<chat-composer>` wrapping the inner `<chat-input-ui>`'s `submit` event). If the wrapper re-emits `submit`, the host's listener fires twice (once from the bubble, once from the re-emit). Lesson pick a bespoke event name (`<wrapper>-<concept>` like `composer-submit`, `sidebar-toggle`, `command-select`) and document the legacy→bespoke mapping in the host's `connected()`.
435
+ 10. **Bespoke event names ≠ legacy event names.** When a bespoke wrapper forwards an event from an inner primitive, give it a NEW name (e.g. `composer-submit` from `<chat-composer>` wrapping the inner `<chat-input-ui>`'s `submit` event). If the wrapper re-emits `submit`, the host's listener fires twice (once from the bubble, once from the re-emit). Lesson, pick a bespoke event name (`<wrapper>-<concept>` like `composer-submit`, `sidebar-toggle`, `command-select`) and document the legacy→bespoke mapping in the host's `connected()`.
436
436
 
437
- 11. **Delegate to primitives when they already own the concern, but don't lose state-as-attribute.** When a bespoke child wraps a primitive that already does the physical work (e.g. `<editor-sidebar>` wrapping `<pane-ui resizable>`), the bespoke tier still owns: cluster-namespace ID, state-as-attribute (`[collapsed]` reflected via `ResizeObserver` on inner pane), localStorage persistence, public API. Don't reimplement what the primitive does but don't skip the bespoke tier just because the primitive exists. Principles 4 ("each child owns one concern") + 3 ("cluster namespace") combine here the bespoke child owns the _cluster-specific framing_ of the primitive's work.
437
+ 11. **Delegate to primitives when they already own the concern, but don't lose state-as-attribute.** When a bespoke child wraps a primitive that already does the physical work (e.g. `<editor-sidebar>` wrapping `<pane-ui resizable>`), the bespoke tier still owns: cluster-namespace ID, state-as-attribute (`[collapsed]` reflected via `ResizeObserver` on inner pane), localStorage persistence, public API. Don't reimplement what the primitive does, but don't skip the bespoke tier just because the primitive exists. Principles 4 ("each child owns one concern") + 3 ("cluster namespace") combine here, the bespoke child owns the _cluster-specific framing_ of the primitive's work.
438
438
 
439
439
  12. **Persist BEFORE snapping, not after.** When `.collapse()` is called on a sidebar, the persisted width must capture the pre-collapse expanded width so `.expand()` can restore it. If you persist after setting `style.width = SNAP_THRESHOLD`, you've overwritten storage with the collapsed value and `.expand()` falls through to its default. Pattern: `if (!this.collapsed) this.#persistWidth();` BEFORE the snap. Caught in editor-sidebar via the `expand() restores from stored width or defaults to 240` test.
440
440
 
441
- 13. **Stamping default content "to be helpful."** A shell that injects a default header / nav / footer commits the page author to the shell's visual language. They will copy-paste markup INTO the shell to override; you've now created two sources of truth. Stamp nothing (`static template = () => null`). The exception: a shell MAY append a tiny structural affordance the author can't reasonably write themselves a resize-handle thumb, for instance. Keep it minimal and clean it up in `disconnected()`.
441
+ 13. **Stamping default content "to be helpful."** A shell that injects a default header / nav / footer commits the page author to the shell's visual language. They will copy-paste markup INTO the shell to override; you've now created two sources of truth. Stamp nothing (`static template = () => null`). The exception: a shell MAY append a tiny structural affordance the author can't reasonably write themselves, a resize-handle thumb, for instance. Keep it minimal and clean it up in `disconnected()`.
442
442
 
443
443
  14. **Document-level keydown handlers on child components.** If a child wants to react to global keys, it should listen for an event the SHELL emits, not bind a document-level listener itself. Otherwise the child outlives its hosting shell, leaks its listener, and starts reacting to keys in surfaces it has no business seeing.
444
444
 
445
- 15. **ResizeObserver vs media queries.** Shells respond to _their own_ width, not the viewport. When the shell collapses (because main content gets crowded), the _shell instance_ is narrow but the viewport may still be wide. Use a `ResizeObserver` on `this` (or on a bespoke child region) to compute breakpoints, and reflect the result as a reflected attribute the CSS can match. Container queries are a complementary tool; ResizeObserver fits when you need _imperative_ state (e.g. moving an element between inline and dropdown placement based on width).
445
+ 15. **ResizeObserver vs media queries.** Shells respond to _their own_ width, not the viewport. When the shell collapses (because main content gets crowded), the _shell instance_ is narrow, but the viewport may still be wide. Use a `ResizeObserver` on `this` (or on a bespoke child region) to compute breakpoints, and reflect the result as a reflected attribute the CSS can match. Container queries are a complementary tool; ResizeObserver fits when you need _imperative_ state (e.g. moving an element between inline and dropdown placement based on width).
446
446
 
447
- ## Examples 4 canonical clusters
447
+ ## Examples, 4 canonical clusters
448
448
 
449
449
  ### admin cluster (canonical reference)
450
450
 
@@ -463,45 +463,45 @@ kept in sync with `site/index.html`'s live example rather than restated here.
463
463
 
464
464
  ### chat cluster (replicated pattern)
465
465
 
466
- - **3 JS-bearing children** `<chat-shell>` (host coordinator), `<chat-thread>` (scroll+streaming+empty), `<chat-composer>` (input wrapper+disabled propagation), `<chat-sidebar>` (mirrors admin-sidebar geometry)
467
- - **3 CSS-only structural children** `<chat-header>`, `<chat-status>`, `<chat-empty>`
468
- - **CSS bridge** `packages/web-modules/chat/chat-shell/css/chat-shell.bespoke.css`
469
- - **Replication notes** `chat-sidebar.js` was a near-copy of `admin-sidebar.js` with mechanical `s/admin/chat/g` + cluster-distinct localStorage prefix. The thread and composer required new code because their concerns (scroll-to-bottom, `[streaming]` reflection, `composer-submit` forwarding) are chat-specific. **Pattern mechanically usable for clusters with structural-mirror needs (sidebars) + adapted for cluster-specific concerns.**
466
+ - **3 JS-bearing children**, `<chat-shell>` (host coordinator), `<chat-thread>` (scroll+streaming+empty), `<chat-composer>` (input wrapper+disabled propagation), `<chat-sidebar>` (mirrors admin-sidebar geometry)
467
+ - **3 CSS-only structural children**, `<chat-header>`, `<chat-status>`, `<chat-empty>`
468
+ - **CSS bridge**, `packages/web-modules/chat/chat-shell/css/chat-shell.bespoke.css`
469
+ - **Replication notes**, `chat-sidebar.js` was a near-copy of `admin-sidebar.js` with mechanical `s/admin/chat/g` + cluster-distinct localStorage prefix. The thread and composer required new code because their concerns (scroll-to-bottom, `[streaming]` reflection, `composer-submit` forwarding) are chat-specific. **Pattern mechanically usable for clusters with structural-mirror needs (sidebars) + adapted for cluster-specific concerns.**
470
470
 
471
- ### editor cluster (third replication confirms convention is locked in)
471
+ ### editor cluster (third replication, confirms convention is locked in)
472
472
 
473
- - **3 JS-bearing children** `<editor-shell>` (host with `[focus-mode]` reflected), `<editor-toolbar>` (`[full-screen]` reflected, click-bubble for `[data-toolbar-action]`), `<editor-canvas>` (`[empty]` + `[focused]` reflected, zoom API), `<editor-sidebar>` (wraps `<pane-ui resizable>` rather than reimplementing drag see Pitfall #11)
474
- - **2 CSS-only structural children** `<editor-statusbar>`, `<editor-canvas-empty>`
475
- - **CSS bridge** `packages/web-modules/editor/editor-shell/css/editor-shell.bespoke.css`
476
- - **Replication notes** Editor cluster has the **smallest bespoke family** of the three (5 children vs 9 admin / 7 chat) because:
477
- 1. `<pane-ui resizable>` already owns drag (delegation Pitfall #11)
473
+ - **3 JS-bearing children**, `<editor-shell>` (host with `[focus-mode]` reflected), `<editor-toolbar>` (`[full-screen]` reflected, click-bubble for `[data-toolbar-action]`), `<editor-canvas>` (`[empty]` + `[focused]` reflected, zoom API), `<editor-sidebar>` (wraps `<pane-ui resizable>` rather than reimplementing drag, see Pitfall #11)
474
+ - **2 CSS-only structural children**, `<editor-statusbar>`, `<editor-canvas-empty>`
475
+ - **CSS bridge**, `packages/web-modules/editor/editor-shell/css/editor-shell.bespoke.css`
476
+ - **Replication notes**, Editor cluster has the **smallest bespoke family** of the three (5 children vs 9 admin / 7 chat) because:
477
+ 1. `<pane-ui resizable>` already owns drag (delegation, Pitfall #11)
478
478
  2. No command palette (different interaction model than admin)
479
479
  3. Toolbar + statusbar suffice for chrome bars (no separate header/status separation like chat)
480
480
  - **Surfaces 2 new pitfalls** (#11 delegation, #12 persist-before-snap) caught at test time
481
- - **Family pattern proven across 3 distinct shell archetypes** admin (productivity), chat (LLM streaming), editor (design tooling). Convention is now canonical.
481
+ - **Family pattern proven across 3 distinct shell archetypes**, admin (productivity), chat (LLM streaming), editor (design tooling). Convention is now canonical.
482
482
 
483
- ### Phase 3 complete legacy deprecation
483
+ ### Phase 3 complete, legacy deprecation
484
484
 
485
485
  ADR-0024 closes the ADR-0023 arc. All three shell hosts dropped their priority-chain reads of legacy `data-*` / `<aside-ui slot>` / `<dialog data-command>` / `<header>` / `<footer>` shapes. The bespoke vocabulary is now the ONLY recognized authoring shape.
486
486
 
487
487
  **LOC payoff at deprecation:**
488
488
 
489
- - `admin-shell.js`: ~305 → ~87 LOC (−71%) host now does only mode reflection + attribute-forwarding
489
+ - `admin-shell.js`: ~305 → ~87 LOC (−71%), host now does only mode reflection + attribute-forwarding
490
490
  - `chat-shell.js`: simpler `connected()` (drops 4-way `||` chains)
491
491
  - `editor-shell.js`: simpler `connected()` (drops 2-way `||` chains)
492
492
  - CSS layered files (`admin-shell.sidebar.css`, `chat-shell.empty.css`, etc.) all `:is(legacy, bespoke)` lifts collapsed to bespoke-only
493
493
 
494
- **Lesson from deprecation:** the backwards-compat priority-chain reads were the right transition mechanism, but they accumulated 6 distinct private methods + 4 private fields + dual event-name listeners in `<admin-shell>` alone. **Keep the compat window short** ~9 days was enough time to migrate all 6 consumers + prove the pattern via smoke probe, and avoiding longer kept the host code from rotting under the dual-shape burden.
494
+ **Lesson from deprecation:** the backwards-compat priority-chain reads were the right transition mechanism, but they accumulated 6 distinct private methods + 4 private fields + dual event-name listeners in `<admin-shell>` alone. **Keep the compat window short**, ~9 days was enough time to migrate all 6 consumers + prove the pattern via smoke probe, and avoiding longer kept the host code from rotting under the dual-shape burden.
495
495
 
496
- ### simple cluster (4th replication pattern compounds)
496
+ ### simple cluster (4th replication, pattern compounds)
497
497
 
498
498
  The 4th cluster proves the pattern generalizes across **four distinct archetypes** now: admin (productivity), chat (LLM streaming), editor (design tooling), simple (minimal page). 23 bespoke shell-tier children total across the 4 clusters.
499
499
 
500
500
  **Family:**
501
501
 
502
- - `<simple-shell>` (JS-bearing host) 2 reflected attrs: `[centered]` (vertical-center content), `[full-bleed]` (drops max-width)
503
- - `<simple-content>` (CSS-only) article-body container with token-correct vertical rhythm
504
- - `<simple-hero>` (CSS-only) optional top strip with 3 named slots: `heading`, `lede`, `actions`
502
+ - `<simple-shell>` (JS-bearing host), 2 reflected attrs: `[centered]` (vertical-center content), `[full-bleed]` (drops max-width)
503
+ - `<simple-content>` (CSS-only), article-body container with token-correct vertical rhythm
504
+ - `<simple-hero>` (CSS-only), optional top strip with 3 named slots: `heading`, `lede`, `actions`
505
505
 
506
506
  **Deliberately minimal**: 1 host + 2 CSS-only children. No sidebars, no chrome bars, no command palette. Use cases: marketing splashes, error pages (404 / 500 / maintenance), thank-you pages, single-card flows (sign-in, password reset).
507
507
 
@@ -514,17 +514,17 @@ The 4th cluster proves the pattern generalizes across **four distinct archetypes
514
514
  | editor (3rd) | ~2 hours | Confirms convention is canonical; introduces delegation (wraps `<pane-ui>`) |
515
515
  | simple (4th) | **~30 minutes** | Mechanical follow-template; smallest cluster |
516
516
 
517
- **Lesson**: each successive cluster is dramatically faster than the previous. The 4th cluster used this reference as a checklist + lifted demo HTMLs from the 3rd cluster + ran tests once at the end. **Skills compound when they're maintained.** If you're considering a 5th cluster (e.g., docs-shell but see the decision-log note below about deferring it), expect ~20 minutes for a thin family or ~45 minutes for a richer one. The pattern is mechanically replicable.
517
+ **Lesson**: each successive cluster is dramatically faster than the previous. The 4th cluster used this reference as a checklist + lifted demo HTMLs from the 3rd cluster + ran tests once at the end. **Skills compound when they're maintained.** If you're considering a 5th cluster (e.g., docs-shell, but see the decision-log note below about deferring it), expect ~20 minutes for a thin family or ~45 minutes for a richer one. The pattern is mechanically replicable.
518
518
 
519
519
  **Decision-log note on docs-shell (deferred):**
520
520
 
521
- A 5th cluster `docs-shell` was considered for documentation pages (markdown rendering, TOC right-rail, frontmatter strip). **Deferred** because `<admin-shell>` already serves the docs surface at `site/index.html` the only differentiating concern (TOC right-rail) is better served by a `<docs-toc>` element placed in `<admin-shell slot="trailing">` than by a competing shell. Cluster proliferation is a real anti-pattern; introduce new clusters only when a distinct archetype emerges that admin/chat/editor/simple don't cover.
521
+ A 5th cluster `docs-shell` was considered for documentation pages (markdown rendering, TOC right-rail, frontmatter strip). **Deferred** because `<admin-shell>` already serves the docs surface at `site/index.html`, the only differentiating concern (TOC right-rail) is better served by a `<docs-toc>` element placed in `<admin-shell slot="trailing">` than by a competing shell. Cluster proliferation is a real anti-pattern; introduce new clusters only when a distinct archetype emerges that admin/chat/editor/simple don't cover.
522
522
 
523
523
  ## Legacy `data-*` shape (retired)
524
524
 
525
525
  The legacy shape used `data-*` region attributes (`[data-canvas]`, `<aside-ui slot="leading">`, `<dialog data-command>`) and CSS selectors that matched those data-attributes. **Retired per ADR-0024.**
526
526
 
527
- Pedagogically the design philosophy was identical to the bespoke shape behavior-only shells, author-owned DOM, persistence keyed by region, ResizeObserver for breakpoint signals, document-level listeners owned by the shell. The bespoke shape moves all of those concerns onto custom elements with reflected-attribute APIs instead of data-attribute sniffing. See ADR-0024 for the full deprecation rationale and the canonical "introduce → migrate → deprecate" timing.
527
+ Pedagogically the design philosophy was identical to the bespoke shape, behavior-only shells, author-owned DOM, persistence keyed by region, ResizeObserver for breakpoint signals, document-level listeners owned by the shell. The bespoke shape moves all of those concerns onto custom elements with reflected-attribute APIs instead of data-attribute sniffing. See ADR-0024 for the full deprecation rationale and the canonical "introduce → migrate → deprecate" timing.
528
528
 
529
529
  When you encounter a legacy reference in old documentation or commit history, the migration map is:
530
530
 
@@ -535,7 +535,7 @@ When you encounter a legacy reference in old documentation or commit history, th
535
535
  | `[data-canvas]` inside `<adia-editor-ui>` | `<editor-canvas>` inside `<editor-shell>` |
536
536
  | `[data-chat-messages]` / `[data-chat-input]` / `[data-chat-empty]` | `<chat-thread>` / `<chat-composer>` / `<chat-empty>` |
537
537
  | `[data-pane-side]` / `[data-pane-grow]` | `<editor-sidebar>` wrapping `<pane-ui resizable>` |
538
- | `data-app-shell-toggle` (trigger) | unchanged trigger attributes are still data-\* |
538
+ | `data-app-shell-toggle` (trigger) | unchanged, trigger attributes are still data-\* |
539
539
 
540
540
  ## After implementation
541
541
 
@@ -546,18 +546,18 @@ When you encounter a legacy reference in old documentation or commit history, th
546
546
  ## Anti-patterns specific to shells (deduplicated)
547
547
 
548
548
  - **Stamping default content "to be helpful."** Stamp nothing (Pitfall #13).
549
- - **Hand-rolling toggle behavior in the page** instead of using `data-<shell>-toggle`. The shell wires localStorage persistence + the ResizeObserver breakpoint logic; rolling your own re-derives both badly. If the shell's trigger-attribute convention doesn't fit, the shell is missing a feature request it, don't work around it.
549
+ - **Hand-rolling toggle behavior in the page** instead of using `data-<shell>-toggle`. The shell wires localStorage persistence + the ResizeObserver breakpoint logic; rolling your own re-derives both badly. If the shell's trigger-attribute convention doesn't fit, the shell is missing a feature, request it, don't work around it.
550
550
  - **Document-level keydown handlers on child components.** Pitfall #14.
551
551
  - **Persisting state without a cluster-scoped key prefix.** Pitfall #9.
552
- - **Stamping invasive `:is(legacy, bespoke)` lifts in legacy CSS.** Pitfall #6 use a new bespoke.css bridge file instead.
552
+ - **Stamping invasive `:is(legacy, bespoke)` lifts in legacy CSS.** Pitfall #6, use a new bespoke.css bridge file instead.
553
553
 
554
554
  ## Cross-references
555
555
 
556
- - [authoring-cycle.md](authoring-cycle.md) general 5-step authoring procedure; shell-specific rules layer on top
557
- - [api-contract.md](api-contract.md) prop naming, reflection policy
558
- - [css-patterns.md](css-patterns.md) two-block `@scope`, variants vs modes; §Region elements never self-style (ADR-0105) a shell that hosts `<header-ui>`/`<section-ui>`/`<footer-ui>` region children (e.g. a bespoke `<X-header>` wrapping one) owns their `@scope` layout the same way `page-ui`/`card-ui`/`drawer-ui`/`modal-ui` do
559
- - [lifecycle-patterns.md](lifecycle-patterns.md) teardown patterns; the cleanup-closure pile and document-listener rules above layer on top
560
- - [token-contract.md](token-contract.md) zero-raw-color + L3 alias rules apply identically to shells
561
- - [module-promotion.md](module-promotion.md) the _different_ activity of lifting a cross-cluster reusable element (theme panel, command palette) into a shared module
562
- - ADR-0023 / ADR-0024 / ADR-0015 decision labels for the bespoke shell-tier convention, the legacy-shape retirement, and the `<cluster-thing>` naming rule (history lives in git + PR descriptions)
563
- - the **adia-factory** plugin the _consumer_ view: how to compose with these shells once they exist
556
+ - [authoring-cycle.md](authoring-cycle.md), general 5-step authoring procedure; shell-specific rules layer on top
557
+ - [api-contract.md](api-contract.md), prop naming, reflection policy
558
+ - [css-patterns.md](css-patterns.md), two-block `@scope`, variants vs modes; §Region elements never self-style (ADR-0105), a shell that hosts `<header-ui>`/`<section-ui>`/`<footer-ui>` region children (e.g. a bespoke `<X-header>` wrapping one) owns their `@scope` layout the same way `page-ui`/`card-ui`/`drawer-ui`/`modal-ui` do
559
+ - [lifecycle-patterns.md](lifecycle-patterns.md), teardown patterns; the cleanup-closure pile and document-listener rules above layer on top
560
+ - [token-contract.md](token-contract.md), zero-raw-color + L3 alias rules apply identically to shells
561
+ - [module-promotion.md](module-promotion.md), the _different_ activity of lifting a cross-cluster reusable element (theme panel, command palette) into a shared module
562
+ - ADR-0023 / ADR-0024 / ADR-0015, decision labels for the bespoke shell-tier convention, the legacy-shape retirement, and the `<cluster-thing>` naming rule (history lives in git + PR descriptions)
563
+ - the **adia-factory** plugin, the _consumer_ view: how to compose with these shells once they exist