@adia-ai/adia-ui-factory 0.2.3 → 0.8.0

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 (144) hide show
  1. package/.claude-plugin/plugin.json +5 -7
  2. package/.mcp.json +1 -1
  3. package/CHANGELOG.md +27 -63
  4. package/README.md +32 -53
  5. package/agents/app-architect.md +28 -0
  6. package/agents/routing-corpus.json +202 -0
  7. package/agents/screen-composer.md +27 -0
  8. package/bin/adia-lint +17 -2
  9. package/commands/adia-genui.md +5 -8
  10. package/commands/adia-migrate.md +5 -6
  11. package/commands/adia-orient.md +5 -10
  12. package/commands/adia-scaffold.md +7 -13
  13. package/commands/adia-verify.md +5 -6
  14. package/package.json +5 -6
  15. package/references/a2ui-mcp-tools.md +11 -8
  16. package/references/authoring-components.md +25 -1
  17. package/references/component-model.md +52 -4
  18. package/references/contracts/a2ui-mcp-surface.md +35 -0
  19. package/references/contracts/migration-guide-format.md +34 -0
  20. package/references/data-and-hydration.md +8 -5
  21. package/references/genui-a2ui.md +12 -6
  22. package/references/llm.md +22 -39
  23. package/references/migration.md +110 -36
  24. package/references/project-shapes.md +11 -13
  25. package/references/shell-admin.md +5 -2
  26. package/references/shell-chat.md +5 -3
  27. package/references/shell-editor.md +1 -1
  28. package/references/shell-embed.md +40 -26
  29. package/references/shell-simple.md +5 -2
  30. package/references/spa-architecture.md +28 -73
  31. package/references/ssr-integration.md +1 -1
  32. package/references/verification.md +8 -12
  33. package/skills/adia-compose/SKILL.md +52 -0
  34. package/skills/{adia-ui-kit → adia-compose}/assets/figma-make/guidelines/Guidelines.md +3 -3
  35. package/skills/{adia-ui-kit → adia-compose}/assets/figma-make/guidelines/components.md +3 -3
  36. package/skills/{adia-ui-kit → adia-compose}/assets/figma-make/guidelines/styles.md +1 -1
  37. package/skills/{adia-ui-kit → adia-compose}/assets/figma-make/guidelines/tokens.md +3 -3
  38. package/skills/{adia-ui-kit → adia-compose}/assets/templates/FEEDBACK.template.md +7 -7
  39. package/skills/{adia-ui-kit → adia-compose}/assets/templates/RESPONSE.template.md +6 -7
  40. package/skills/adia-compose/references/composition-traps.md +58 -0
  41. package/skills/adia-compose/references/feedback-discipline.md +20 -0
  42. package/skills/adia-compose/references/spec-to-ui-reasoning.md +158 -0
  43. package/skills/adia-data/SKILL.md +78 -0
  44. package/skills/adia-genui/SKILL.md +83 -0
  45. package/skills/adia-host/SKILL.md +119 -0
  46. package/skills/adia-llm/SKILL.md +36 -0
  47. package/skills/adia-migrate/SKILL.md +76 -0
  48. package/skills/adia-orient/SKILL.md +123 -0
  49. package/skills/adia-project/SKILL.md +83 -0
  50. package/skills/adia-shells/SKILL.md +74 -0
  51. package/skills/adia-verify/SKILL.md +43 -0
  52. package/skills/adia-verify/references/verification.md +42 -0
  53. package/commands/adia-compose.md +0 -10
  54. package/commands/adia-wire.md +0 -13
  55. package/skills/adia-ui-compose/SKILL.md +0 -57
  56. package/skills/adia-ui-data/SKILL.md +0 -62
  57. package/skills/adia-ui-factory/SKILL.md +0 -113
  58. package/skills/adia-ui-genui/SKILL.md +0 -74
  59. package/skills/adia-ui-kit/CHANGELOG.md +0 -41
  60. package/skills/adia-ui-kit/SKILL.md +0 -442
  61. package/skills/adia-ui-kit/assets/team-scaffold/archive-README.md +0 -30
  62. package/skills/adia-ui-kit/assets/team-scaffold/drafts-README.md +0 -57
  63. package/skills/adia-ui-kit/assets/team-scaffold/inbox-README.md +0 -76
  64. package/skills/adia-ui-kit/assets/team-scaffold/outbox-README.md +0 -46
  65. package/skills/adia-ui-kit/assets/team-scaffold/team-root.README.md +0 -51
  66. package/skills/adia-ui-kit/assets/team-scaffold/tickets-README.md +0 -208
  67. package/skills/adia-ui-kit/assets/team-scaffold/tickets-REGISTRY.md +0 -81
  68. package/skills/adia-ui-kit/assets/templates/AGENTS.template.md +0 -128
  69. package/skills/adia-ui-kit/assets/templates/CLAUDE.template.md +0 -1
  70. package/skills/adia-ui-kit/assets/templates/figma-make-guidelines.md +0 -90
  71. package/skills/adia-ui-kit/evals/adversarial-content-trust.json +0 -83
  72. package/skills/adia-ui-kit/evals/evals.json +0 -55
  73. package/skills/adia-ui-kit/evals/routing-corpus.json +0 -121
  74. package/skills/adia-ui-kit/evals/teach-routing-cases.json +0 -92
  75. package/skills/adia-ui-kit/references/capabilities.md +0 -245
  76. package/skills/adia-ui-kit/references/catalog-lookup.md +0 -186
  77. package/skills/adia-ui-kit/references/changelog-archive/CHANGELOG-v1.md +0 -8
  78. package/skills/adia-ui-kit/references/changelog-archive/CHANGELOG-v2.md +0 -3585
  79. package/skills/adia-ui-kit/references/changelog-archive/CHANGELOG-v3.md +0 -1899
  80. package/skills/adia-ui-kit/references/common-gotchas-consumer.md +0 -522
  81. package/skills/adia-ui-kit/references/component-literacy.md +0 -291
  82. package/skills/adia-ui-kit/references/composition.md +0 -245
  83. package/skills/adia-ui-kit/references/correction-loop.md +0 -462
  84. package/skills/adia-ui-kit/references/css-policy.md +0 -343
  85. package/skills/adia-ui-kit/references/decision-model.md +0 -158
  86. package/skills/adia-ui-kit/references/diagnostic-output.md +0 -57
  87. package/skills/adia-ui-kit/references/docs-catalog.md +0 -239
  88. package/skills/adia-ui-kit/references/exclusions.md +0 -225
  89. package/skills/adia-ui-kit/references/feedback-authoring.md +0 -466
  90. package/skills/adia-ui-kit/references/figma-make.md +0 -244
  91. package/skills/adia-ui-kit/references/flows-auth.md +0 -307
  92. package/skills/adia-ui-kit/references/flows-content.md +0 -206
  93. package/skills/adia-ui-kit/references/flows-data.md +0 -348
  94. package/skills/adia-ui-kit/references/flows-interaction.md +0 -181
  95. package/skills/adia-ui-kit/references/flows-shells.md +0 -336
  96. package/skills/adia-ui-kit/references/gap-detection.md +0 -266
  97. package/skills/adia-ui-kit/references/harness.md +0 -284
  98. package/skills/adia-ui-kit/references/loading-protocol.md +0 -406
  99. package/skills/adia-ui-kit/references/local-link.md +0 -198
  100. package/skills/adia-ui-kit/references/mcp.md +0 -401
  101. package/skills/adia-ui-kit/references/migration.md +0 -116
  102. package/skills/adia-ui-kit/references/mission.md +0 -194
  103. package/skills/adia-ui-kit/references/mode-routing.md +0 -298
  104. package/skills/adia-ui-kit/references/patterns-agent.md +0 -223
  105. package/skills/adia-ui-kit/references/patterns-data.md +0 -1066
  106. package/skills/adia-ui-kit/references/patterns-forms.md +0 -223
  107. package/skills/adia-ui-kit/references/patterns-layout.md +0 -326
  108. package/skills/adia-ui-kit/references/patterns-recipes.md +0 -1416
  109. package/skills/adia-ui-kit/references/patterns-shells.md +0 -1097
  110. package/skills/adia-ui-kit/references/patterns-traits.md +0 -259
  111. package/skills/adia-ui-kit/references/patterns-wiring.md +0 -339
  112. package/skills/adia-ui-kit/references/pev-rationale.md +0 -137
  113. package/skills/adia-ui-kit/references/reasoning-gate.md +0 -146
  114. package/skills/adia-ui-kit/references/reasoning-methodologies.md +0 -112
  115. package/skills/adia-ui-kit/references/recon.md +0 -192
  116. package/skills/adia-ui-kit/references/rendering-model.md +0 -431
  117. package/skills/adia-ui-kit/references/response-authoring.md +0 -398
  118. package/skills/adia-ui-kit/references/session-log-schema.md +0 -203
  119. package/skills/adia-ui-kit/references/session-log-template.md +0 -118
  120. package/skills/adia-ui-kit/references/setup.md +0 -652
  121. package/skills/adia-ui-kit/references/skill-hygiene.md +0 -135
  122. package/skills/adia-ui-kit/references/spec-to-ui-reasoning.md +0 -394
  123. package/skills/adia-ui-kit/references/state.md +0 -229
  124. package/skills/adia-ui-kit/references/teach-protocol.md +0 -421
  125. package/skills/adia-ui-kit/references/theme.md +0 -142
  126. package/skills/adia-ui-kit/references/version-check.md +0 -69
  127. package/skills/adia-ui-kit/scripts/audit-decision-trace.mjs +0 -198
  128. package/skills/adia-ui-kit/scripts/audit-kit-roster.mjs +0 -366
  129. package/skills/adia-ui-kit/scripts/changelog-archive.mjs +0 -126
  130. package/skills/adia-ui-kit/scripts/check-adia-versions.py +0 -404
  131. package/skills/adia-ui-kit/scripts/run-skill-evals.mjs +0 -358
  132. package/skills/adia-ui-kit/scripts/validate-session-log.mjs +0 -242
  133. package/skills/adia-ui-kit/skill.json +0 -102
  134. package/skills/adia-ui-llm/SKILL.md +0 -51
  135. package/skills/adia-ui-migrate/SKILL.md +0 -64
  136. package/skills/adia-ui-migration/CHANGELOG.md +0 -47
  137. package/skills/adia-ui-migration/SKILL.md +0 -381
  138. package/skills/adia-ui-migration/skill.json +0 -29
  139. package/skills/adia-ui-project/SKILL.md +0 -77
  140. package/skills/adia-ui-shells/SKILL.md +0 -62
  141. package/skills/adia-ui-spa/SKILL.md +0 -52
  142. package/skills/adia-ui-ssr/SKILL.md +0 -52
  143. package/skills/adia-ui-verify/SKILL.md +0 -44
  144. /package/skills/{adia-ui-kit → adia-compose}/assets/templates/FEEDBACK-OMISSIONS-DEFENSE.template.md +0 -0
@@ -1,57 +1,131 @@
1
- ---
2
- name: migration
3
- load-when: migrating an adia-ui app — version upgrade, port-to-adia, or mode change
4
- load-size: ~2.5k tokens
5
- required-for: [adia-ui-migrate — all types]
6
- ---
1
+ # Migration — types, history, sweep depth
7
2
 
8
- # Migration types, history, discipline
9
-
10
- Consumer migrations. The framework MIGRATION GUIDE (in the framework repo) is the source of truth per version; this is the durable method + the shape of past breaks. Treat versions/examples as a snapshot.
3
+ Consumer migrations: the durable method + the shape of past breaks. The framework MIGRATION
4
+ GUIDE (`.claude/docs/MIGRATION GUIDE.md` in the framework repo — the space in the filename is
5
+ intentional) is the per-version source of truth; its required shape is
6
+ [`contracts/migration-guide-format.md`](contracts/migration-guide-format.md). Loaded by
7
+ `adia-migrate` (all migration types). Versions and examples below are a snapshot, not a registry.
11
8
 
12
9
  ## Types
13
10
 
14
- - **version-upgrade** — bump `@adia-ai/*` X→Y (lockstep; all packages move together). PATCH cuts are drop-in; MINOR/MAJOR carry breaking items.
15
- - **port-to-adia** — an existing app (raw HTML, or legacy `@agent-ui-kit`) adia-ui: a tag rename map (`aui-button`→`button-ui`, `<button>`→`<button-ui>`) + token namespace swap (`--n-*`→`--a-*`).
16
- - **mode-change** — SPA↔SSR: re-own routing (framework router vs `<router-ui>`), registration (top-level vs client-hook), and state (signals vs cookies). See `adia-ui-spa` / `adia-ui-ssr`.
17
-
18
- ## The 5-step discipline
19
-
20
- 1. **Read the guide** for the target version. Missing section pause, ask, offer to draft it.
21
- 2. **Audit** — `git grep -nlE '<pattern>'` each breaking item; cluster by component; count files/occurrences. Surface before sweeping.
22
- 3. **Sweep** mechanical per cluster:
11
+ - **version-upgrade** — bump `@adia-ai/*` X→Y (lockstep; all packages move together). PATCH
12
+ cuts are drop-in; MINOR/MAJOR carry breaking items.
13
+ - **port-to-adia** — an existing app (raw HTML, or legacy `@agent-ui-kit`) adia-ui: a tag
14
+ rename map (`aui-button`→`button-ui`, `<button>`→`<button-ui>`) + token namespace swap
15
+ (`--n-*`→`--a-*`).
16
+ - **mode-change** — SPA↔SSR: re-own routing (framework router vs `<router-ui>`), registration
17
+ (top-level vs client-hook), and state (signals vs cookies). `adia-host` carries both paths.
18
+
19
+ ## The 5-step sweep depth
20
+
21
+ 1. **Read the guide** for the version span: index bullets → per-cut sections. Each breaking
22
+ item ships an old form, a new form, and a greppable pattern (the contract above). A missing
23
+ section, or an item without a pattern/verify command, is a producer defect — pause, report
24
+ upstream; don't improvise a breaking surface.
25
+ 2. **Audit** — `git grep -nE '<pattern>'` per item; cluster by component; report file +
26
+ occurrence counts and show the list before changing anything. Consumer code has edge cases
27
+ the canonical regexes don't anticipate (a `variant="danger"` on a non-button custom element
28
+ that shares the prefix).
29
+ 3. **Sweep** — mechanical per approved cluster:
23
30
 
24
31
  ```bash
25
32
  git grep -lE 'button-ui[^>]*variant="danger"' \
26
33
  | xargs perl -i -pe 's/(<button-ui[^>]*?)variant="danger"/$1color="danger"/g'
27
34
  ```
28
35
 
29
- …or a shipped codemod. **Flag, don't auto-apply, judgment items** (below).
30
-
31
- 4. **Verify** `adia-lint` clean of `LEGACY-SHELL`/`NATIVE-PRIMITIVE`; build/render; then the **leftover-drift grep** across `.css`/`.js`/`.md`/`.json`; browser probe.
36
+ Judgment items (below) are flagged, never swept.
37
+ 4. **Verify** — `adia-lint` clean of `LEGACY-SHELL`/`NATIVE-PRIMITIVE`; the app's own build +
38
+ the browser gate ([`verification.md`](verification.md)); then the leftover-drift pass below.
32
39
  5. **Report** — per-axis counts, manual-review list, gate results, next actions.
33
40
 
34
- ## Real breaking-change history (before after)
41
+ ### Sweep anti-patterns (each shipped a real regression)
35
42
 
36
- - **v0.0.20 (10 items):** `<button-ui variant="danger">` → `variant="solid" color="danger"`; stage Booleans (`completed`/`active`) → `status="completed|active"` enum (timeline/stepper/pipeline); `<table-toolbar-ui>` opt-out Booleans **inverted** (`searchable="false"` → `no-search`; default flipped); `<chat-input-ui busy>` → `loading`; `variant="error"` alias removed → `danger`; event prefixes dropped (`chat-submit`→`submit`); `<field-ui error>` moved to the child input; `<agent-trace-ui open>` → `collapsed` (**semantic flip — default-visible now**); kebab prop keys → camelCase (JS only). Safari floor → 18.
37
- - **v0.0.29 three-tier extraction:** `patterns/` moved `@adia-ai/web-components/patterns/*` `@adia-ai/web-modules/{shell,chat,editor,runtime}/*`. Import-path rewrite + add the `web-modules` dep.
38
- - **v0.4.0 — legacy shell shapes retired (ADR-0024):** `<aside data-sidebar>`→`<admin-sidebar slot>`; `<dialog data-command>`→`<admin-command>`; `[data-chat-messages/input/empty]`→`<chat-thread>/<chat-composer>/<chat-empty>`; `[data-editor-body]/[data-canvas]`→`<editor-canvas>` + `<editor-sidebar>`. JS selectors move with them. (`adia-lint` `LEGACY-SHELL` flags the remnants.)
39
- - **v0.6.0/0.6.1:** `stat-ui.{js,css}`→`stat.{js,css}` (deep-import only); `<link-ui>` token rename `--link-color-*`→`--link-fg-*` (only if you override).
43
+ - **One component per sweep.** `<(toast|alert|tag)-ui[^>]*variant="error"` looks efficient, but
44
+ perl/sed alternation captures don't substitute the matched alternative cleanly loop
45
+ `for tag in toast alert tag` instead.
46
+ - **HTML-attribute regexes don't cover JS property sites.** `<chat-input-ui busy>` and
47
+ `el.busy = true` are two audits — sweep `\.busy\s*=` separately, scoped to app code.
48
+ - **`git grep -lE | xargs perl -i`, never `find … -exec perl`.** git grep excludes
49
+ `node_modules/`, `dist/`, `.git/`; a raw find doesn't.
40
50
 
41
- ## Judgment items (flag for review — never auto-sweep)
42
-
43
- Semantic flips (`[open]`→`[collapsed]` inverts default visibility), Boolean opt-out inversions (default changes), attribution transfers (`field-ui error` → child input may not exist yet). Surface these with their call sites; let the author decide.
44
-
45
- ## What the path-only sweep misses (leftover-drift categories)
51
+ ## Real breaking-change history (before after)
46
52
 
47
- Bare-name mentions in prose docs (README/AGENTS/ROADMAP); skill-directory names; JSON metadata fields (highest impact — silent harvest miss on rebuild); inventory tables in cross-cutting docs. Use a pre/post grep diff:
53
+ - **v0.0.20 (10 items):** `<button-ui variant="danger">` `variant="solid" color="danger"`
54
+ (canonical form; the guide's mechanical sweep emits just `color="danger"`, leaning on `solid`
55
+ being the default variant); stage Booleans (`completed`/`active`) → `status="completed|active"`
56
+ enum (timeline/stepper/pipeline); `<table-toolbar-ui>` opt-out Booleans **inverted**
57
+ (`searchable="false"` → `no-search`; default flipped); `<chat-input-ui busy>` → `loading`;
58
+ `variant="error"` alias removed → `danger`; event prefixes dropped (`chat-submit`→`submit`,
59
+ `legend-toggle`→`toggle`, `slide-change`→`change`); `<field-ui error>` moved to the child
60
+ input; `<agent-trace-ui open>` → `collapsed` (**semantic flip — default-visible now**); kebab
61
+ prop keys → camelCase (JS only). Safari floor → 18.
62
+ - **v0.0.29 — three-tier extraction:** `patterns/` moved `@adia-ai/web-components/patterns/*` →
63
+ `@adia-ai/web-modules/{shell,chat,editor,runtime}/*`. Import-path rewrite + add the
64
+ `web-modules` dep.
65
+ - **v0.4.0 — legacy shell shapes retired (ADR-0024):** `<aside data-sidebar>`→`<admin-sidebar
66
+ slot>`; `<dialog data-command>`→`<admin-command>`; `[data-chat-messages/input/empty]`→
67
+ `<chat-thread>/<chat-composer>/<chat-empty>`; `[data-editor-body]/[data-canvas]`→
68
+ `<editor-canvas>` + `<editor-sidebar>`. JS selectors move with the markup. (`adia-lint`
69
+ `LEGACY-SHELL` flags the remnants.)
70
+ - **v0.6.0/0.6.1:** `stat-ui.{js,css}`→`stat.{js,css}` (deep-import only); `<link-ui>` token
71
+ rename `--link-color-*`→`--link-fg-*` (only if you override).
72
+
73
+ ## Judgment items (flag with call sites — the author decides, never a sweep)
74
+
75
+ - **Semantic flips** — `<agent-trace-ui open>`→`[collapsed]` is an inversion, not a rename:
76
+ `[open]` was default-hidden/opt-in, `[collapsed]` is default-visible/opt-out. Trace meant to
77
+ show? drop the attribute. Meant hidden? write `collapsed`.
78
+ - **Boolean opt-out inversions** — `<table-toolbar-ui>`: the legacy Booleans defaulted to
79
+ `true`, so a bare `filterable` was a no-op; only an explicit `searchable="false"` carried
80
+ intent (→ `no-search`). A regex can't tell the two apart.
81
+ - **Attribution transfers** — `<field-ui error="…">` moves the message to the child input,
82
+ which may not exist yet in the markup.
83
+ - **JS-only key renames** — kebab property keys (`el['submit-label']`→`el.submitLabel`): the
84
+ HTML attribute form is unchanged, so attribute-only consumers need no change. Audit
85
+ programmatic access only.
86
+
87
+ ## Leftover drift — what the path-only sweep misses
88
+
89
+ A vocabulary migration touches the markup but not the CSS selectors that style it, the JS
90
+ comments that mention it, or the metadata that indexes it — different files, so a markup-only
91
+ commit looks complete. Close with a pre/post grep diff:
48
92
 
49
93
  ```bash
50
94
  grep -rln 'OLD_NAME' . --include='*.md' --include='*.json' | sort > /tmp/pre.txt
51
95
  # … sweep …
52
- grep -rln 'OLD_NAME' . --include='*.md' --include='*.json' | sort > /tmp/post.txt && diff /tmp/pre.txt /tmp/post.txt
96
+ grep -rln 'OLD_NAME' . --include='*.md' --include='*.json' | sort > /tmp/post.txt
97
+ diff /tmp/pre.txt /tmp/post.txt # anything left post-sweep is a stale ref to investigate
53
98
  ```
54
99
 
55
- ## MCP aids
56
-
57
- `search_chunks` (the updated example for a changed component) · `check_anti_patterns` (a swept file is clean) · `convert_html` (map legacy/foreign markup → current components, for ports). There's no "list breaking changes" tool — the MIGRATION GUIDE is read by hand.
100
+ Categories that have survived full-path sweeps:
101
+
102
+ - **Bare-name prose mentions** in narrative docs and inventory tables (README, roadmap-style
103
+ indexes) — grep `\b<old-name>\b` over `*.md` / `*.yaml`, not just paths.
104
+ - **Directories named after the renamed thing** (skill dirs, config dirs) — dir vs frontmatter
105
+ mismatch check:
106
+
107
+ ```bash
108
+ for skill in $(find . -name 'SKILL.md' -not -path '*/node_modules/*'); do
109
+ dir=$(basename $(dirname "$skill")); name=$(grep -m1 '^name:' "$skill" | sed 's/name: *//')
110
+ [ "$dir" != "$name" ] && echo "MISMATCH: $skill (dir=$dir, name=$name)"
111
+ done
112
+ ```
113
+
114
+ - **JSON metadata at filename granularity** (highest impact — a stale `source`/`page` field is
115
+ a *silent* harvest miss on the next rebuild; no error is raised):
116
+
117
+ ```bash
118
+ for f in 'old-name.html' 'old-name.contents.html' 'old-name.contents.js'; do
119
+ grep -rn "$f" --include='*.json' | grep -v '/dist/' | grep -v '/node_modules/'
120
+ done
121
+ ```
122
+
123
+ - **Refs to the directory itself, not the tag** — barrel JS exports, CSS `@import`s, HTML link
124
+ rels, sitemap/content paths name the old *dir*; a tag-rename sweep never touches them. Grep
125
+ the old directory name separately.
126
+ - **Relative-import depth after `git mv`** — grep each moved file for `../` imports and fix the
127
+ depth; builds don't load them, so the breakage surfaces only in the browser. A doc move needs
128
+ a *different* rewrite per linking file's own depth — precompute per file.
129
+ - **Hard-coded source lists in build scripts** — `SOURCES = […]` / include-dir arrays silently
130
+ drop renamed or new siblings while the build runs clean; diff output counts against the
131
+ pre-rename baseline after any rename.
@@ -1,13 +1,6 @@
1
- ---
2
- name: project-shapes
3
- load-when: classifying or laying out an adia-ui app's structure — picking the project shape, the four-axis layout, or page-trio vs page-DUO
4
- load-size: ~2.5k tokens
5
- required-for: [adia-ui-factory shape classifier, adia-ui-project (scaffold / add-surface / add-page)]
6
- ---
7
-
8
1
  # Project shapes & structure
9
2
 
10
- How real adia-ui apps are laid out (synthesized from the chat-ui apps). Three shapes over one four-axis layout, with a page-trio/DUO rule. The **structure rubric** at the bottom is the gate; `bin/adia-scaffold` mechanizes the layout.
3
+ How real adia-ui apps are laid out (synthesized from the chat-ui apps). Three shapes over one four-axis layout, with a page-trio/DUO rule. The **structure rubric** at the bottom is the gate; `bin/adia-scaffold` mechanizes the layout. Loaded by `adia-orient` (shape classifier) and `adia-project` (layout / scaffold / inventory).
11
4
 
12
5
  ## The four-axis layout (all shapes)
13
6
 
@@ -56,7 +49,7 @@ Use for: a suite of related surfaces (a SaaS admin, a flow set, a demo gallery).
56
49
 
57
50
  ### Shared-foundation — sibling apps over a shared core
58
51
 
59
- Root-level `spec/plan/` for cross-app concerns; each app under `app/<name>/` with its own `spec/plan/src/`; a shared `app/shared/` (DataClient, loaders, mappers, tokens). The embed pattern lives here becomes **`adia-embed-shell`**.
52
+ Root-level `spec/plan/` for cross-app concerns; each app under `app/<name>/` with its own `spec/plan/src/`; a shared `app/shared/` (DataClient, loaders, mappers, tokens). The embed pattern lives here the shell is **`<embed-shell>`** (ships in `@adia-ai/web-modules`, shell cluster); depth: [shell-embed.md](shell-embed.md).
60
53
 
61
54
  ```text
62
55
  app/
@@ -75,13 +68,18 @@ Use for: multiple embedded surfaces sharing data context, components, and an emb
75
68
 
76
69
  The `.html` shell fetches `.contents.html`, injects it, then dynamically imports `.contents.js` if present. **Rule:** add `.contents.js` only when there's behavior to wire — a DUO with a dead `.contents.js` and a trio missing its setup are both smells.
77
70
 
78
- ## State & data — which pattern (depth: `adia-ui-data`)
71
+ Two adjacent contracts:
72
+
73
+ - `setup` must be `async function setup(host)` when its body contains top-level `await` — extracting inline page script into a non-async `.contents.js` controller turns that `await` into a parse-time SyntaxError.
74
+ - CSS never takes the `.contents.` infix. HTML/JS get it because they're the dynamically-injected fragment + controller; CSS loads statically via `<link>` in the `.html` shell head and styles chrome + contents together, so its name follows scope (per-app / shared / per-component), not the page-form convention.
75
+
76
+ ## State & data — which pattern
79
77
 
80
- `signal()`/`effect()` (single-surface reactivity) · Service/Controller/Command (mutations + undo) · `DataClient.read(projection)` + pure mappers, with **`action_source` required on every `mutate`** (shared-foundation) · property-API (`el.columns = […]`, never post-connect `<option>` children) · declarative `data-*` (static flows). The data skill owns the choice; this is the map.
78
+ The state/data pattern follows the shape, not taste but the choice is owned by `adia-data`: see [data-and-hydration.md](data-and-hydration.md) for the shape pattern map.
81
79
 
82
80
  ## Components
83
81
 
84
- One folder per tag, mirroring the framework: `components/<tag>/<tag>.{js,css}` (folder name = custom-element tag). Authoring discipline: `authoring-components.md`.
82
+ One folder per tag, mirroring the framework: `components/<tag>/<tag>.{js,css}` (folder name = custom-element tag). Authoring discipline: [authoring-components.md](authoring-components.md).
85
83
 
86
84
  ## Structure rubric `[gate]`
87
85
 
@@ -93,4 +91,4 @@ A project is well-structured when (gate = all `[gate]` hold):
93
91
  - **Components foldered** `[gate]` — every custom element is `components/<tag>/<tag>.{js,css}`.
94
92
  - **No duplicated cross-surface code** `[review]` — shared logic lives in `app/shared/` (or `_shared/`), not copied per page.
95
93
 
96
- Verify target for a scaffold/edit: this rubric passes, and any new surface renders through `adia-ui-verify`.
94
+ Verify target for a scaffold/edit: this rubric passes, and any new surface renders through `adia-verify`.
@@ -2,7 +2,7 @@
2
2
  name: shell-admin
3
3
  load-when: authoring or debugging an admin-shell app frame (sidebar + topbar + command palette + pages)
4
4
  load-size: ~2.5k tokens
5
- required-for: [adia-ui-shells — admin path]
5
+ required-for: [adia-shells — admin path]
6
6
  ---
7
7
 
8
8
  # admin-shell — the app frame
@@ -49,7 +49,7 @@ Full SaaS/admin chrome from `@adia-ai/web-modules`. Register the **cluster barre
49
49
 
50
50
  ## SPA vs SSR
51
51
 
52
- SPA mounts the full markup. SSR keeps the shell + chrome fixed and swaps only the **page body** via the framework outlet — replace the inner page content with `{children}` / `<slot/>`; never mount `<router-ui>` (see `adia-ui-ssr`).
52
+ SPA mounts the full markup. SSR keeps the shell + chrome fixed and swaps only the **page body** via the framework outlet — replace the inner page content with `{children}` / `<slot/>`; never mount `<router-ui>` (see `adia-host`).
53
53
 
54
54
  ## Gotchas (mechanized where noted)
55
55
 
@@ -61,5 +61,8 @@ SPA mounts the full markup. SSR keeps the shell + chrome fixed and swaps only th
61
61
  - **Multiple `<admin-page>` in one `<admin-scroll>`** → single-axis scroll breaks; one page per scroll.
62
62
  - **`@container (…)` instead of `@container page-content (…)`** → won't react to sidebar collapse.
63
63
  - **Legacy shapes** (`<aside data-sidebar>`, `<dialog data-command>`) — retired v0.4.0 (`adia-lint` `LEGACY-SHELL`).
64
+ - **Sidebar nav is `<nav-ui>` + `<nav-item-ui>`**, never `<menu-ui>`/`<menu-item-ui>` — menu-ui is for Popover-API dropdowns, not persistent navigation.
65
+ - **Full-height mount:** an intermediate wrapper (`<main id="app">`) between `body { height: 100dvh; display: flex }` and the shell needs `flex: 1; display: flex; min-height: 0` — without it the flex chain breaks and the shell collapses to content height.
66
+ - **`header-ui` has no CSS of its own** — its icon/heading/description/action grid comes from the parent's `@scope` (`admin-page-header` provides it); bespoke chrome reusing the header-ui vocabulary must supply the grid + text ellipsis locally.
64
67
 
65
68
  Real usage: `apps/saas/app/admin-dashboard/`.
@@ -2,12 +2,12 @@
2
2
  name: shell-chat
3
3
  load-when: authoring a chat-shell LLM conversation surface
4
4
  load-size: ~1.5k tokens
5
- required-for: [adia-ui-shells — chat path]
5
+ required-for: [adia-shells — chat path]
6
6
  ---
7
7
 
8
8
  # chat-shell — the conversation surface
9
9
 
10
- LLM chat chrome from `@adia-ai/web-modules`. Register: `import '@adia-ai/web-modules/chat'`. The **LLM client/proxy/security** lives in `adia-ui-llm` — this is the _surface_; that is the _wiring_.
10
+ LLM chat chrome from `@adia-ai/web-modules`. Register: `import '@adia-ai/web-modules/chat'`. The **LLM client/proxy/security** lives in `adia-llm` — this is the _surface_; that is the _wiring_.
11
11
 
12
12
  ## Cluster roster
13
13
 
@@ -33,12 +33,14 @@ LLM chat chrome from `@adia-ai/web-modules`. Register: `import '@adia-ai/web-mod
33
33
 
34
34
  ## Wiring to the LLM
35
35
 
36
- Set `proxy-url` (or, dev-only, `apiKey`) and the shell **auto-sends on submit** via `streamChat` and renders the stream for you. Otherwise it just emits `submit` — you call your endpoint and drive the UI with `appendChunk`/`done`/`error`. **Security:** the production pattern is a same-origin smart proxy that holds the key server-side — never ship a provider key to the browser. Full client/proxy contract: `adia-ui-llm`.
36
+ Set `proxy-url` (or, dev-only, `apiKey`) and the shell **auto-sends on submit** via `streamChat` and renders the stream for you. Otherwise it just emits `submit` — you call your endpoint and drive the UI with `appendChunk`/`done`/`error`. **Security:** the production pattern is a same-origin smart proxy that holds the key server-side — never ship a provider key to the browser. Full client/proxy contract: `adia-llm`.
37
37
 
38
38
  ## Gotchas
39
39
 
40
40
  - Import the **chat barrel**; piecemeal imports leave children unregistered.
41
+ - `chat-input-ui` internally renders `textarea-ui` + `select-ui` (web-components primitives, invisible in your authored HTML) — the app must register those primitives too, or they stay undefined and collapse to 0px.
41
42
  - Legacy shapes (`[data-chat-messages]`, `[data-chat-input]`, `[data-chat-empty]`, `[data-chat-name]`) were retired v0.4.0 — use the bespoke tags (`adia-lint` `LEGACY-SHELL`).
42
43
  - SSR: register `<chat-shell>` client-side like any component; keep the key server-side.
44
+ - **Reasoning/trace panels must surface their own reliability** — a bare status label (`Domain: data`) reads identically at 3% and 95% confidence; a label that hides the data needed to judge it is pragmatically deceptive. Show the confidence with the claim.
43
45
 
44
46
  Real usage: `apps/genui/app/factory-chat/`.
@@ -2,7 +2,7 @@
2
2
  name: shell-editor
3
3
  load-when: authoring an editor-shell — a design tool / canvas with resizable side panes
4
4
  load-size: ~1.2k tokens
5
- required-for: [adia-ui-shells — editor path]
5
+ required-for: [adia-shells — editor path]
6
6
  ---
7
7
 
8
8
  # editor-shell — the canvas + panes
@@ -1,43 +1,57 @@
1
1
  ---
2
2
  name: shell-embed
3
- load-when: authoring an embedded adia-ui surface (a light-DOM element a host page sizes/centers), or anticipating the official adia-embed-shell
3
+ load-when: authoring an embedded adia-ui surface a `<embed-shell>` framing a primary app plus secondary panels a host page sizes/centers
4
4
  load-size: ~1.5k tokens
5
- required-for: [adia-ui-shells — embed path]
5
+ required-for: [adia-shells — embed path]
6
6
  ---
7
7
 
8
- # adia-embed-shell — embedded surface _(FORTHCOMING)_
8
+ # embed-shell — embedded multi-surface frame
9
9
 
10
- > **Status: emerging.** There is **no `<adia-embed-shell>` web-module shipped yet.** This is the official promotion-in-progress of the **embedded-app pattern** (the patient-labs / population-health surfaces). Build to the pattern below today; when the shell ships, re-bake this reference (paired with the pinned a2ui MCP snapshot) and the API firms up. Treat exact shapes here as **[PATTERN]** (verified from the embedded app), not **[SHELL-API]** (doesn't exist yet).
10
+ Embedded chrome from `@adia-ai/web-modules` (shell cluster). Register: `import '@adia-ai/web-modules/shell'`. Peer of admin/editor/chat shells; light-DOM, content-agnostic it orchestrates layout of whatever `[app]` + `[panel]` children it's given, never the content or data.
11
11
 
12
- An **embedded surface** is a self-contained light-DOM custom element that a _host page_ (an EHR, another SaaS) positions and sizes — adia-ui owns the surface, the host owns the frame.
12
+ ## Cluster roster
13
13
 
14
- ## The pattern (today) **[PATTERN]**
14
+ `<embed-shell>` (host coordinator) — the only tag. Its children are consumer-supplied: one `[app]` primary surface + any number of `[panel="<name>"]` secondary surfaces. Extends `UIElement`, so `traits="resizable"` applies the real `resizable` trait (drag any edge + a `resize-end` event), not a CSS stand-in.
15
15
 
16
- - **A self-booting container.** One custom element (`<my-surface>`); `connected()` guarded by a `#booted` flag (it re-fires on DOM moves); it fetches its data and renders its own subtree. No framework wrapper, no shadow DOM.
17
- - **The host sizes/centers it.** The placer page positions the element (absolute + `translate(-50%, -50%)`); the surface does **not** hardcode width/height (size-agnostic — the consumer owns extent).
18
- - **Panel layout:** pinned header/footer + scrollable body via flex + `min-block-size: 0` on the scroll region.
19
- - **Data via projection.** `DataClient.read({ type, params })` returns typed projections from pure mappers (`app/shared/corpus/mappers/`); the surface never calls a backend directly. **Every `mutate` carries `action_source`** (attribution is required; the client throws without it). Depth: `adia-ui-data`.
20
- - **Routing (in-DOM tabs):** a content-less `<router-ui>` whose URL you manage yourself with `history.replaceState()` to preserve the host's query params — do **not** set `router.routes` (that fetches + replaces). (This is the one place an embedded surface uses `<router-ui>`; it's a self-contained SPA island, not an SSR page — see the hybrid note in `adia-ui-data`.)
21
- - **Shared foundation.** Multiple embedded surfaces live under a `shared-foundation` project shape (`app/shared/` for DataClient/mappers/tokens) — see `project-shapes.md`.
16
+ ## Canonical skeleton
22
17
 
23
- ## Authoring checklist (until the shell ships)
18
+ ```html
19
+ <embed-shell>
20
+ <patient-labs app></patient-labs> <!-- the primary surface (exactly one) -->
21
+ <settings-panel panel="settings"></settings-panel><!-- a secondary surface (any number) -->
22
+ <chat-panel panel="chat"></chat-panel>
23
+ <!-- triggers anywhere inside, delegated: -->
24
+ <button-ui opens="chat">Chat</button-ui> <!-- toggles the named panel -->
25
+ <button-ui close></button-ui> <!-- dismisses the open panel -->
26
+ </embed-shell>
27
+ ```
24
28
 
25
- 1. Scaffold a `shared-foundation` app; put the surface under `app/<surface>/src/`.
26
- 2. Author the surface as a self-booting light-DOM container (`#booted` guard, render in `connected()`).
27
- 3. Wire data through `DataClient`/mappers; attribute every mutation.
28
- 4. Keep it size-agnostic; let the host place it.
29
- 5. Verify in a host harness (renders, zero console errors, non-zero box) — `adia-ui-verify`.
29
+ ## Contract · state · events
30
30
 
31
- ## Live worked example
31
+ - **Children:** `[app]` = primary (one); `[panel="<name>"]` = secondaries. `[opens="<name>"]` toggles a panel; `[close]` dismisses the open one — both delegated off a click on any descendant.
32
+ - **State (reflected, ADR-0023):** `embed-shell[panel="chat"]` = which panel is open (`''`/absent = none); the open panel carries `[active]` (the CSS show/slide hook).
33
+ - **Events:** `embed:open` (in) `detail:{panel}` = request to open/toggle; `embed:change` (out) `detail:{panel}` = emitted after the open panel changes.
34
+ - **Methods:** `.open(name)` · `.close()` · `.toggle(name)`; getter `.panel` (open name or `''`). Escape closes the open panel.
35
+ - **Layout (embed-shell.css):** ≥760px the open panel is a 50/50 column beside the app; <760px it's a cover sheet (`translateY` — **identity at rest**, so popovers inside still anchor via CSS anchor-positioning; an offsetting `translate(-50%,-50%)` would break them).
32
36
 
33
- The canonical implementation of this pattern is `apps/embedded-app/` in the adia-ui monorepo — the patient-labs and population-health surfaces built to the shared-foundation shape. Use it as the reference when authoring a new embedded surface:
37
+ ## Authoring the surfaces inside it
34
38
 
35
- - `apps/embedded-app/app/patient-labs/` patient-labs surface (full Live/Labs/History/Briefing tab set, DataClient projection wiring, ambient-pill footer)
36
- - `apps/embedded-app/app/population-health/` — population-health surface (coverage metrics, facility breakdown)
37
- - `apps/embedded-app/app/shared/` — DataClient, CorpusLoader, mappers, shared tokens
39
+ The shell owns the frame; each `[app]`/`[panel]` surface is a self-booting light-DOM container you author to the embedded-surface pattern:
38
40
 
39
- If you have access to the monorepo, read `apps/embedded-app/` before authoring a new embedded surface the shape is proven and annotated.
41
+ - **Self-booting** `connected()` guarded by a `#booted` flag (it re-fires on DOM moves), fetches its data, renders its own subtree. No shadow DOM.
42
+ - **Size-agnostic** — the surface never hardcodes width/height; the shell (and the host page) own extent.
43
+ - **Data via projection** — `DataClient.read({ type, params })` returns typed projections from pure mappers (`app/shared/corpus/mappers/`); the surface never calls a backend directly. **Every `mutate` carries `action_source`** — the client throws without it. Depth: [`data-and-hydration.md`](data-and-hydration.md).
44
+ - **In-DOM tabs** — a content-less `<router-ui>` whose URL you manage with `history.replaceState()` to preserve the host's query params; do **not** set `router.routes` (that fetches + `innerHTML`-replaces).
45
+ - **Shared foundation** — multiple embedded surfaces live under a `shared-foundation` project shape (`app/shared/` for DataClient/mappers/tokens); see [`project-shapes.md`](project-shapes.md).
40
46
 
41
- ## When it ships
47
+ ## SPA vs SSR
42
48
 
43
- The official `adia-embed-shell` is expected to package this chrome (the panel layout, the host-placement contract, the embed bridge) as a web-module like the others. At that point: register via the cluster barrel, replace the hand-rolled container chrome with the shell, and keep the DataClient/projection + attribution wiring. Until then, the pattern above **is** the contract.
49
+ The shell mounts the full markup in SPA. For SSR, register it client-side like any component and keep any data keys server-side; it's a self-contained SPA island, not an SSR page (see the hybrid note in [`data-and-hydration.md`](data-and-hydration.md)).
50
+
51
+ ## Gotchas
52
+
53
+ - **Piecemeal import** → `EmbedShell` unregistered; `.open()`/`.toggle()` undefined and panels collapse. Import the **shell barrel**.
54
+ - **A trigger `opens="x"` with no matching `[panel="x"]` child** → `.open()` no-ops silently.
55
+ - **Wrapping `[app]`/`[panel]` children in a layout `<div>`** → breaks the `:scope > [panel]` direct-child selectors the shell and CSS use.
56
+
57
+ Real usage: `apps/embedded-app/` — patient-labs and population-health surfaces built to the shared-foundation shape (`app/patient-labs/`, `app/population-health/`, shared `app/shared/`). Read it before authoring a new embedded surface.
@@ -2,12 +2,12 @@
2
2
  name: shell-simple
3
3
  load-when: authoring a simple-shell — marketing / error / landing / auth page (minimal centered chrome)
4
4
  load-size: ~0.8k tokens
5
- required-for: [adia-ui-shells — simple path]
5
+ required-for: [adia-shells — simple path]
6
6
  ---
7
7
 
8
8
  # simple-shell — minimal centered chrome
9
9
 
10
- The lightest shell from `@adia-ai/web-modules` — for marketing, landing, error (404/500/maintenance), thank-you, and auth pages. Register: `import '@adia-ai/web-modules/shell'` (simple ships in the shell cluster). Behavior-only host + two CSS-only children.
10
+ The lightest shell from `@adia-ai/web-modules` — for marketing, landing, error (404/500/maintenance), thank-you, and auth pages. Register: `import '@adia-ai/web-modules/simple'` (simple is its own cluster barrel — the `/shell` barrel does not register it). Behavior-only host + two CSS-only children.
11
11
 
12
12
  ## Cluster roster
13
13
 
@@ -34,5 +34,8 @@ The lightest shell from `@adia-ai/web-modules` — for marketing, landing, error
34
34
 
35
35
  - Controls inside are `*-ui` (`button-ui`, `link-ui`), not raw `<button>`/`<a>` (`adia-lint` `NATIVE-PRIMITIVE`).
36
36
  - For an error page, a page-DUO (no `.contents.js`) is usually right — see `project-shapes.md`.
37
+ - **Where the message goes on a status page:** an `<alert-ui>` carries the full message when the user is a *passive* recipient (session expired, account locked); the header `text-ui` carries it when the user is *actively* confirming something they initiated — the alert then only supplements.
38
+ - **Prose/marketing header:** the centered pattern is a `<col-ui>` inside `<header>` with kicker + display heading + deck — not ad-hoc heading stacks.
39
+ - Reach for this canonical shell before hand-rolling centered chrome: a new auth/status/maintenance surface (centered card, brand mark, heading, content) composes `simple-shell` + `<card-ui raw>` — authoring a parallel bespoke shell is how a 117-line stylesheet earns a 33-line replacement.
37
40
 
38
41
  Real usage: `apps/errors/`, `apps/user-flow/` (auth).
@@ -1,10 +1,10 @@
1
1
  # SPA architecture
2
2
 
3
- The client-rendered path (vanilla / Vite static host). The framework is SPA-native: the components register at load, the page is one document, and routing/state live in the browser. If you're inside Next/Nuxt/SvelteKit/Astro instead, you're on the **SSR** path — see [ssr-integration.md](ssr-integration.md); the two diverge sharply on routing, registration, and state.
3
+ The client-rendered path: a static host, Vite or vanilla. The framework is SPA-native components register at load, the page is one document, routing and state live in the browser. Inside Next/Nuxt/SvelteKit/Astro instead? That's the **SSR** path — [ssr-integration.md](ssr-integration.md); the two diverge sharply on registration, routing, and state.
4
4
 
5
5
  ## The host document
6
6
 
7
- One static `index.html` whose job is to load CSS in cascade order and register components once:
7
+ One static `index.html` whose job is to load CSS in cascade order and register components once. npm-consumer paths, which is what `adia-scaffold spa` emits (pre-wired for Vite — `vite.config.js` + `package.json` included):
8
8
 
9
9
  ```html
10
10
  <!doctype html>
@@ -12,13 +12,12 @@ One static `index.html` whose job is to load CSS in cascade order and register c
12
12
  <head>
13
13
  <meta charset="utf-8" />
14
14
  <meta name="viewport" content="width=device-width, initial-scale=1" />
15
- <link rel="stylesheet" href="/packages/web-components/styles/host.css" /> <!-- foundation: tokens + resets + page frame -->
16
- <link rel="stylesheet" href="/packages/web-components/styles/index.css" /> <!-- barrel: every component's CSS -->
17
- <link rel="stylesheet" href="/packages/web-components/styles/verse.css" /> <!-- opt-in register (only if a surface uses [verse]) -->
18
- <link rel="stylesheet" href="./index.css" /> <!-- page framing: sizes + centers the surface -->
19
- <link rel="stylesheet" href="./components/my-surface/my-surface.css" /> <!-- the surface's own chrome -->
20
- <script type="module" src="/packages/web-components/index.js"></script> <!-- registers EVERY primitive (incl. router-ui) -->
21
- <script type="module" src="./components/my-surface/my-surface.js"></script>
15
+ <link rel="stylesheet" href="/node_modules/@adia-ai/web-components/styles/host.css" /> <!-- foundation: tokens + resets + page frame -->
16
+ <link rel="stylesheet" href="/node_modules/@adia-ai/web-components/styles/index.css" /> <!-- barrel: every component's CSS (link BOTH) -->
17
+ <link rel="stylesheet" href="/node_modules/@adia-ai/web-components/styles/verse.css" /> <!-- opt-in register (only if a surface uses [verse]) -->
18
+ <link rel="stylesheet" href="./index.css" /> <!-- page framing: sizes + centers the surface -->
19
+ <link rel="stylesheet" href="./components/my-surface/my-surface.css" /> <!-- the surface's own chrome -->
20
+ <script type="module" src="./components/my-surface/my-surface.js"></script> <!-- its FIRST import is the registration barrel -->
22
21
  </head>
23
22
  <body>
24
23
  <my-surface verse></my-surface>
@@ -26,54 +25,33 @@ One static `index.html` whose job is to load CSS in cascade order and register c
26
25
  </html>
27
26
  ```
28
27
 
29
- > **Note monorepo paths vs real npm consumer.** The `/packages/web-components/...` paths above only resolve inside the AdiaUI monorepo dev server. A real npm consumer has two options:
30
- >
31
- > - **Vite (recommended):** `npm install @adia-ai/web-components`, add a `vite.config.js` (the `adia-scaffold spa` bin emits one), then replace the paths with `/node_modules/@adia-ai/web-components/styles/host.css` etc. — Vite serves node_modules at that prefix during dev and bundles them at build time. Bare `@adia-ai/*` specifiers in `.js` files resolve automatically.
32
- > - **Import-map / CDN (no bundler):** add an `<script type="importmap">` mapping `"@adia-ai/web-components"` to your CDN URL (e.g. `https://esm.sh/@adia-ai/web-components`), and adjust the CSS link hrefs to match.
33
- >
34
- > The `adia-scaffold spa` output is pre-wired for the Vite path (`vite.config.js` + `package.json` included).
28
+ Vite serves `/node_modules/...` hrefs during dev and bundles them at build; bare `@adia-ai/*` specifiers in `.js` files resolve automatically. Two alternatives:
35
29
 
36
- **Cascade order is load-bearing** (later wins): foundation barrel register page component. Invariants:
30
+ - **Import-map / CDN (no bundler):** a `<script type="importmap">` mapping `"@adia-ai/web-components"` to a CDN URL (e.g. `https://esm.sh/@adia-ai/web-components`); adjust the CSS hrefs to match.
31
+ - **Monorepo dev server (framework contributors only):** `/packages/web-components/styles/*.css` + `/packages/web-components/index.js` paths — not a consumer deployment mode.
37
32
 
38
- - Link **both** `host.css` and `styles/index.css`. Post-0.7.6 the barrel was split — `host.css` is foundation-only, so linking it alone renders primitives unstyled.
39
- - **One** registration script (`/packages/web-components/index.js`). Don't piecemeal-import primitives across the page.
40
- - Never hand-roll `:where(html,body){}` — the foundation owns the page frame; re-rolling it drifts from the system (the classic serif-leak bug).
41
- - A register has two halves: link `verse.css` **and** put `verse` on the surface. One without the other is a no-op.
42
-
43
- ## Four-axis project structure
44
-
45
- ```text
46
- my-app/
47
- ├── spec/ design axis — BRIEF · ARCHITECTURE · SPEC · screen specs
48
- ├── plan/ execution axis — ROADMAP · MILESTONES · PLAN
49
- ├── skills/ procedural-knowledge axis — the app's own expert skill (optional)
50
- └── app/
51
- ├── shared/ cross-surface source — DataClient, loaders, mappers, images
52
- └── <surface>/src/
53
- ├── index.html the host shell (above)
54
- ├── index.css page framing
55
- └── components/<tag>/<tag>.{js,css}
56
- ```
33
+ ## Registration & cascade invariants
57
34
 
58
- Keep design (`spec/`), plan (`plan/`), and source (`app/`) on separate axes the spec is the contract, the plan is the sequence, the app is the build.
35
+ **Cascade order is load-bearing** (later wins): foundation barrel register page component.
59
36
 
60
- ## The page-trio (and when it collapses)
37
+ - Link **both** `host.css` and `styles/index.css`. The styles barrel is split: `host.css` carries only the foundation (tokens + resets + page frame), so linking it alone renders primitives unstyled.
38
+ - CSS arrives via `<link>`, never via the JS import — Vite serves component CSS outside the module graph, so a JS-only side-effect import registers the element but leaves it unstyled.
39
+ - **One registration script** — the side-effecting barrel `import '@adia-ai/web-components'`. Don't piecemeal-import primitives: composites render internal `*-ui` tags (e.g. `chat-input-ui` internally renders `textarea-ui` + `select-ui`) that stay unregistered and collapse to 0px unless the barrel ran.
40
+ - Bespoke shell children need the **cluster barrel** (`@adia-ai/web-modules/shell`, `/chat`, `/editor`, `/simple`) — importing `admin-shell.js` alone registers only the host tag, not `admin-sidebar` / `admin-page` / the other children.
41
+ - Never hand-roll `:where(html,body){}` — the foundation owns the page frame; re-rolling it drifts from the system (the classic serif-leak bug).
42
+ - A `verse` register has two halves: link `verse.css` **and** put `verse` on the surface. One without the other is a no-op.
43
+ - `themes.css` (named palettes) is **not** in the styles barrel — link it separately. `data-scheme` switches light/dark; `data-theme` picks the named palette.
44
+ - Guards: `defineIfFree(tag, ctor)` (`core/register.js`) for defines; a `#booted` flag in `connected()` — the callback re-fires on DOM moves. And `customElements.whenDefined(name)` never rejects: a `Promise.all([...whenDefined])` boot gate hangs forever on one unimported tag — chrome renders (tag-keyed CSS), the page stays "empty", zero console errors.
61
45
 
62
- A standalone page is a triplet:
46
+ ## The surface container
63
47
 
64
- | File | Owns |
65
- | --- | --- |
66
- | `<page>.html` | static shell — meta, CSS/script links, a mount point |
67
- | `<page>.contents.html` | the markup, **fetched** and injected at runtime |
68
- | `<page>.contents.js` | `setup(root)` — wires behavior after the markup lands |
48
+ An app surface is a self-booting custom element — it fetches its data and renders its own subtree in `connected()`:
69
49
 
70
50
  ```js
71
- const root = document.getElementById('demo-root');
72
- root.innerHTML = await (await fetch('./page.contents.html')).text();
73
- (await import('./page.contents.js')).default?.(root); // setup(root)
51
+ connected() { if (this.#booted) return; this.#booted = true; this.#load(); }
74
52
  ```
75
53
 
76
- In a real app the trio usually **collapses into the surface's own custom-element lifecycle** — the container fetches its data and renders its own subtree in `connected()`, so there's no separate `.contents.*` pair. Use the trio for playground/demo pages; use a self-booting container for app surfaces.
54
+ Standalone demo/playground pages use the page-trio instead; the trio/DUO decision table, the four-axis layout, and the structure rubric live in [project-shapes.md](project-shapes.md) (owned by `adia-project`).
77
55
 
78
56
  ## Routing — content-less `<router-ui>`
79
57
 
@@ -91,33 +69,10 @@ my-surface router-ui[data-route-path="/live"] .live { display: flex; }
91
69
 
92
70
  A **content-mode** route (one carrying `content`) makes the router fetch and `innerHTML`-replace — which wipes stamped views, scroll, and focus. That's the wrong tool for in-DOM tabs. Never `innerHTML` a view on switch; show/hide.
93
71
 
94
- ## Data-flow `DataClient` → projection
95
-
96
- The UI never talks to a backend. It reads typed **projections** from a `DataClient`; a pure mapper is the swap seam between fixtures and a real API:
97
-
98
- ```text
99
- DataClient.read({ type: 'LabRecommendationSet', params }) ← the only surface the UI sees
100
- → runMapper(query, loader) ← pure (sources) => Projection; the v1-fixture ⇄ v2-API seam
101
- → CorpusLoader.load*() ← fetches the source data
102
- ```
103
-
104
- - Components consume **projections only** — no backend calls, no re-deriving projections in the view, no per-view reshaping (the projection type _is_ the contract).
105
- - **Attribution is structural:** every `mutate` requires an `action_source`; the client throws without it.
106
-
107
- ```js
108
- await client.mutate({ type: 'order', payload }, { action_source: btn.dataset.action });
109
- ```
110
-
111
- ## State — single owner
112
-
113
- One owner per piece of state, no shadow copies:
114
-
115
- - the **route** owns which view is active,
116
- - the **component** owns selection sets / UI toggles,
117
- - the **DataClient** owns fetched data.
72
+ ## Data & state
118
73
 
119
- A control mutates the route; an observer or CSS reflects the route **into** the DOM never the reverse. Mirror with a `MutationObserver` on `data-route-path`, not a second source of truth.
74
+ Owned by `adia-data` — see [data-and-hydration.md](data-and-hydration.md) for the five data-flow patterns, the DataClient → projection pipeline, the attribution gate (`mutate` throws without an `action_source`), and single-owner state (the route owns the active view; a control mutates the route, an observer/CSS reflects it back never a second source of truth).
120
75
 
121
76
  ## Exit gate
122
77
 
123
- A surface isn't done when it compiles — it's done when it passes the **browser gate** (zero console errors on load, non-zero bounding boxes, and you've _read_ the screenshot) plus the a11y and git checks. That gate is the `adia-ui-verify` skill. Re-baseline git at the start of every turn, stage explicit allowlists (never `git add -A` on a shared clone), and confirm the branch before committing.
78
+ A surface isn't done when it compiles — it's done when it passes the **browser gate**: zero console errors on load, non-zero bounding boxes, and the screenshot actually read. That gate is the `adia-verify` skill.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Consuming adia-ui components **inside an SSR framework** (Next.js, Nuxt, SvelteKit, Astro, …). Same components, same UI, **wildly different architecture** from the SPA path — the framework owns routing, registration must be deferred to the client, and state can't live in component-lifetime signals.
4
4
 
5
- > **Honesty about sources.** The framework's `rendering-model` doc explicitly covers Next/Nuxt/SvelteKit/Astro — those patterns are marked **[D]** (documented) below. Frameworks it names in its routing table but doesn't give wiring for (Remix, Rails/Turbo, Django/HTMX, Phoenix) are marked **[G]** — the _rule_ (one route owner; client-only registration) holds, but the wiring is your framework's standard pattern, not something the kit ships. Don't present **[G]** patterns as kit-guaranteed.
5
+ > **Honesty about sources.** The kit's own documentation explicitly covers Next/Nuxt/SvelteKit/Astro — those patterns are marked **[D]** (documented) below. Frameworks it names in its routing table but doesn't give wiring for (Remix, Rails/Turbo, Django/HTMX, Phoenix) are marked **[G]** — the _rule_ (one route owner; client-only registration) holds, but the wiring is your framework's standard pattern, not something the kit ships. Don't present **[G]** patterns as kit-guaranteed.
6
6
 
7
7
  ## Why SSR is different
8
8