@adia-ai/adia-ui-factory 0.8.56 → 0.8.58

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 (56) hide show
  1. package/.claude-plugin/plugin.json +3 -2
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/.mcp.json +1 -1
  4. package/CHANGELOG.md +56 -0
  5. package/README.md +69 -1
  6. package/agents/surface-qa-agent.md +1 -1
  7. package/commands/app-audit.md +4 -2
  8. package/commands/app-migration.md +1 -1
  9. package/commands/app-planning.md +3 -2
  10. package/commands/find-unused.md +3 -3
  11. package/commands/gen-ui-wiring.md +1 -1
  12. package/commands/project-scaffolding.md +3 -2
  13. package/commands/surface-qa.md +1 -1
  14. package/commands/theme-audit.md +2 -2
  15. package/hermes-mcp.yaml +1 -1
  16. package/package.json +2 -2
  17. package/plugin.yaml +1 -1
  18. package/prompts/app-audit.md +4 -2
  19. package/prompts/app-migration.md +1 -1
  20. package/prompts/app-planning.md +3 -2
  21. package/prompts/find-unused.md +3 -3
  22. package/prompts/gen-ui-wiring.md +1 -1
  23. package/prompts/project-scaffolding.md +3 -2
  24. package/prompts/surface-qa.md +1 -1
  25. package/prompts/theme-audit.md +2 -2
  26. package/references/authoring-components.md +1 -1
  27. package/references/composed-surface-rubric.md +1 -1
  28. package/references/shell-admin.md +12 -12
  29. package/scripts/adia-lint.mjs +193 -0
  30. package/scripts/adia-manifest-cache.mjs +77 -0
  31. package/scripts/adia-manifest-lite.mjs +137 -0
  32. package/scripts/adia-posture.mjs +81 -0
  33. package/scripts/adia-scaffold +93 -12
  34. package/scripts/lab-posture-guard.mjs +174 -0
  35. package/scripts/lint-rules.generated.mjs +22210 -0
  36. package/scripts/tests/lab-posture-guard.selftest.mjs +248 -0
  37. package/scripts/ui-architect-pi-selftest.mjs +18 -0
  38. package/skills/app-audit/references/gap-classes.md +1 -1
  39. package/skills/app-migration/SKILL.md +1 -1
  40. package/skills/app-planning/SKILL.md +5 -1
  41. package/skills/find-unused/SKILL.md +1 -1
  42. package/skills/find-unused/references/recon-and-audits.md +3 -3
  43. package/skills/pattern-catalog/SKILL.md +20 -0
  44. package/skills/pattern-catalog/references/annotations.yaml +2 -1
  45. package/skills/pattern-catalog/references/pattern-index.md +70 -70
  46. package/skills/project-scaffolding/SKILL.md +6 -6
  47. package/skills/screen-composition/SKILL.md +6 -4
  48. package/skills/screen-composition/assets/figma-make/guidelines/components.md +10 -14
  49. package/skills/shell-selection/SKILL.md +2 -2
  50. package/skills/surface-qa/SKILL.md +1 -1
  51. package/skills/surface-qa/references/ci-recipe.md +2 -2
  52. package/skills/surface-qa/references/verification.md +5 -2
  53. package/skills/theme-audit/SKILL.md +2 -2
  54. package/skills/token-selection/references/a-alias-layer.md +2 -2
  55. package/hooks/hooks.json +0 -15
  56. package/scripts/adia-lint +0 -411
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adia-ui-kit-factory",
3
- "version": "0.8.56",
3
+ "version": "0.8.58",
4
4
  "description": "Author and verify apps built ON the adia-ui (@adia-ai) light-DOM web-component framework \u2014 orient, scaffold, compose, wire, verify, and migrate across SPA and SSR rendering modes. Wires the a2ui MCP for catalog retrieval, UI generation, and validation.",
5
5
  "author": {
6
6
  "name": "Kim",
@@ -15,5 +15,6 @@
15
15
  "web-components",
16
16
  "app-authoring",
17
17
  "consumer"
18
- ]
18
+ ],
19
+ "hooks": "./adapters/claude/hooks.json"
19
20
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adia-ui-kit-factory",
3
- "version": "0.8.56",
3
+ "version": "0.8.58",
4
4
  "description": "Author and verify apps built ON the adia-ui (@adia-ai) light-DOM web-component framework — orient, scaffold, compose, wire, verify, and migrate across SPA and SSR rendering modes. Wires the a2ui MCP for catalog retrieval, UI generation, and validation.",
5
5
  "author": {
6
6
  "name": "Kim",
package/.mcp.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "mcpServers": {
3
3
  "adia-gen-ui": {
4
4
  "command": "npx",
5
- "args": ["-y", "@adia-ai/mcp@0.8.56", "gen-ui"]
5
+ "args": ["-y", "@adia-ai/mcp@0.8.58", "gen-ui"]
6
6
  }
7
7
  }
8
8
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,61 @@
1
1
  # Changelog — adia-ui-kit-factory
2
2
 
3
+ ## [0.8.58] — 2026-09-02
4
+
5
+ ### Added
6
+ - **Lab posture: `.adia` marker, manifest-derived lint rules, and a blocking `PreToolUse` write-time hook (ADR-0108 D1/D5, LLD-0019 C3-C5, gh#2856).** New `Posture` README section documents the `.adia` marker schema (`{ "posture": "lab" | "bare-repo", "kit"?: { "web-components": "<version>" } }`) and `scripts/adia-posture.mjs`'s `readPosture(repoRoot)` reader, posture is explicit, never inferred from `package.json`/`node_modules`. New `lab-posture-guard` hook (`PreToolUse`, `Write|Edit`): in lab posture, denies a write containing an unknown tag/attribute/enum value or an `internal`-tier value (the same four rules, `UNKNOWN-TAG`/`UNKNOWN-ATTR`/`ENUM-VALUE`/`INTERNAL-TIER`, imported straight from this plugin's own vendored `lint-rules.generated.mjs`, never a second matcher implementation), with the violating tag/attr/value and the manifest's allowed set in the deny reason; a no-op in bare-repo/absent posture, `adia-lint`'s existing advisory `PostToolUse` contract is completely unchanged. New `SessionStart` hook warms the manifest cache once per session. Reads a lab consumer's LIVE installed manifest via a new dependency-free `adia-manifest-lite.mjs` (JSON-only: none of the four rules needs `a2ui.rules`, so no vendored yaml parser required for this path), cached per-session by `adia-manifest-cache.mjs` under the OS temp dir (never a tracked repo artifact). Hook integration selftest (`npm run check:lab-posture-guard-selftest`) builds two tmp-dir fixture consumer repos (lab / bare-repo, mirroring `get-manifest.test.js`'s own fixture-building pattern rather than committing a real `.a2ui.json` fixture, which would trip `adia-ui-forge`'s `sidecar-prewrite-guard`) and asserts an unknown-tag write is denied in lab and allowed bare-repo, plus clean/enum/internal-tier/unknown-attr/non-markup cases. Non-Claude harnesses (Codex/Hermes/Pi) get no hook runtime, see the README's "Non-Claude harnesses" section for the `npm run lint`-at-`warn` degradation. Hooks roster: 1 → 2 (site's `factory-plugin.html` roster page updated, `check:plugin-count-claims` guards it).
7
+
8
+ ### Changed
9
+ - **`adia-scaffold spa`'s `_SPA_JS`/`_SPA_HTML` templates register only the tags the placeholder markup uses (page-ui, text-ui) via per-component JS + CSS imports, instead of the full `@adia-ai/web-components` / `@adia-ai/web-components/css` barrel (ADR-0107, gh#2765).** The generated app's `dist/` shrinks from ~39MB / 9,092 files (barrel JS + the full Phosphor icon glob) to ~272KB / 3 files, JS gzip 430KB→7.7KB, CSS gzip drops similarly, measured against this checkout's own `packages/web-components`. The barrel stays fully supported: the template's own header comment documents the two-line kitchen-sink opt-in (`import '@adia-ai/web-components'` + `/css`) for consumers who want zero-config registration of every primitive. Scaffolded SPAs now also get a `README.md` with a "Bundle cost" section stating this trade-off, the icon-subsetting `installIconLoaders()` path, and the kitchen-sink opt-in.
10
+ - **`selftest`'s `_resolve_export` wildcard-match tie-break fixed to match Node's own exports-resolution specificity rule** (prefix+suffix length, not prefix length alone), the old tie-break could resolve a `.css`-suffixed subpath against the wrong same-prefix wildcard key (e.g. `./components/*` instead of the more specific `./components/*.css`), which the previous templates never exercised but the new per-component CSS imports do.
11
+
12
+ ### Fixed
13
+ - **Pi `ui-architect` self-test now re-execs with Node's explicit TypeScript-stripping flag on early Node 22 patch releases (gh#2803).** Direct and npm-script invocations can import `ui-architect.extension.ts` across the supported Node 22 major instead of depending on the later patch that enabled type stripping by default.
14
+ - **ATTR-TYPO/LLM-KEY-IN-CLIENT/NATIVE-PRIMITIVE matcher hardening (gh#2708)**, propagated to this plugin's vendored `scripts/lint-rules.generated.mjs` via `npm run build:lint-rules`, byte-identical to the shared lint-rule bank, no change to `adia-lint.mjs`'s own CLI contract.
15
+
16
+ ### Docs
17
+ - **generated lint-rule bank refreshed (gh#2821, gh#2824)**: a full `npm run build:lint-rules` regen pass, run while rebasing the heatmap/noodles a11y fixes onto main, picked up `enum_meta` `tier`/`when` data already landed upstream (gh#2864) that this vendored copy hadn't caught up to yet; vendored copy byte-identical to the shared bank, no behavior change.
18
+ - **generated lint-rule bank refreshed (gh#2845)**: `gen-composition-rules.mjs`'s scan widened from `<dirname>.yaml` only to every `*.yaml` in a component directory, discovering 14 previously-missed child/sub-item yamls; composition-rule count 34 → 48 (mechanics: repo-root CHANGELOG.md); vendored copy byte-identical to the shared bank, no behavior change.
19
+ - **generated lint-rule bank refreshed (gh#2801)**: `gen-composition-rules.mjs`'s eligibility widened from 1 to 34 generated composition rules, decoupled from component.md pilot status (mechanics: repo-root CHANGELOG.md); vendored copy byte-identical to the shared bank, no behavior change.
20
+ - **generated lint-rule bank refreshed** by wave-5A component.md eligibility regen (lld-0018).
21
+ - **generated lint-rule bank refreshed** by wave-5B component.md eligibility regen (lld-0018).
22
+ - **generated lint-rule bank refreshed**: wave-1A component.md eligibility added 3 composition rules (accordion/action-list/choice-card, lld-0018).
23
+ - **pattern-catalog `pattern-index.md` regenerated** for the header-ui→header sweep (gh#2793).
24
+ - **pattern-catalog `pattern-index.md` regenerated** for the admin-page-header pattern entry's admin-scroll→page-scroll wrapper swap (gh#2734).
25
+ - **`pattern-catalog/SKILL.md`'s ADR-0084 forward note gains a build-status update (gh#2736).** The generated `templates/` npm mirror generator has landed (`@adia-ai/web-components`'s CHANGELOG carries the mechanics); this note records that the `ships:`/resolution-table flip is a separate, not-yet-built wave.
26
+ - **ADR-0098 drain: remaining admin-page prose in `references/shell-admin.md`, `skills/app-audit/references/gap-classes.md`, and `skills/screen-composition/assets/figma-make/guidelines/components.md` rewritten to `<page-ui band>`.** These three still described the retired `<admin-page>`/`<admin-page-header>`/`<admin-page-body>` triad as the current admin-shell chrome pattern, the canonical skeleton example, the chrome-tier allocation table, a gotcha row on the "without inner column-owner" failure mode, and the Figma-Make shells vocabulary/skeleton all now show `<page-ui band>` with `<header-ui>`/`<section-ui>` children. `demo-audit/references/admin-shell-anatomy.md`, `demo-audit/references/app-shell-pitfalls.md`, and `primitive-authoring/references/shell-patterns.md` were checked and already carry correct dated ADR-0098 deprecation notes from an earlier harvest, no change needed there.
27
+ - **`pattern-catalog/references/pattern-index.md` + `annotations.yaml` updated for `admin-page-header.html`'s ADR-0098 rewrite (gh#2748).** The docs-site pattern page itself moved off the deprecated `<admin-page-header>`/`<admin-page>`/`<admin-page-body>` triad onto `<page-ui band>`'s direct `<header-ui>` child (root `CHANGELOG.md` carries the page-rewrite detail); `annotations.yaml`'s `admin-page-header.html` entry intent/keywords updated to name `page-ui band` instead of `admin-page`, and `pattern-index.md` regenerated (`npm run build:patterns-index`) picks up the entry's `components:` list dropping `admin-page`/`admin-page-body`/`admin-page-header`/`alert-ui` for `page-ui`.
28
+ - **`pattern-catalog/references/pattern-index.md` regenerated (`npm run build:patterns-index`, gh#2730).** `patterns-form-drawer`'s `components:` list re-sorts after gh#2730's label-variant sweep collapsed `form-drawer.examples.html`'s `field-ui[inline]`-wrapping-`switch-ui` anti-pattern to the switch's own `[label]` attribute, shifting the extracted tag order.
29
+ - **ADR-0098 knowledge-harvest applied to `shell-selection/SKILL.md`.** The "Canonical nesting" verify-target row gains a dated deprecation note (2026-09-01) pointing the `admin-page`/`admin-scroll` nesting example at its successor, `page-ui[band]` nested inside `page-scroll`.
30
+ - **`pattern-catalog/references/pattern-index.md` regenerated (`npm run build:patterns-index`, gh#2706).** The "Admin Shell" and "Admin Content Aside" pattern entries' `components:` lists still named `admin-page`/`admin-page-header`/`admin-page-body`/`admin-scroll`, their source demo (`packages/web-components/patterns/admin-shell/admin-shell.examples.html`) had been missed by the earlier ADR-0098 sweep; now byte-resynced to `page-scroll`/`page-ui[band]` after the source migration.
31
+ - **ADR-0084 knowledge-harvest applied to `pattern-catalog/SKILL.md`.** The "Reach the source" section's "none of them a template screen" `ships:` claim gains a dated forward-note (2026-09-01) recording ADR-0084's ruled `templates/` npm-mirror plan for all 121 template screens (Class R, build-at-publish); the 6 `/site/pages/patterns/` docs-hub pages stay `monorepo-only` permanently either way.
32
+ - **ADR-0102 harvest: `surface-qa/references/verification.md:62`'s superseded heading-role workaround corrected.** The Accessibility checklist row recommending `role="heading"` + `aria-level` or an `<h*>` wrapper for a `text-ui variant="heading"` node now points at `text-ui[level]` (1-6, promotes to a real native `<h1>`-`<h6>`) as the current fix, citing ADR-0102 (2026-09-01); `variant` stays presentational-only, `level` is the tag-promoting prop.
33
+
34
+ ### Chore
35
+ - **Author email in `package.json` updated to `kim.granlund@adia.ai`** (was a stale personal address).
36
+
37
+ ### Maintenance
38
+ - **`.claude-plugin/plugin.json` version bump** — moves in lockstep with package.json (the `/plugin update` cache key).
39
+ - **`.codex-plugin/` touched in this release window** (1 file(s), e.g. `.codex-plugin/plugin.json`) — carried by the entries above.
40
+ - **`adapters/` touched in this release window** (1 file(s), e.g. `claude/hooks.json`) — carried by the entries above.
41
+
42
+ ## [0.8.57] — 2026-09-01
43
+
44
+ ### Fixed
45
+ - **Plugin passes `author-cross-harness-plugins`'s cross-harness validator, modulo one documented, permanent, irreconcilable exception (gh#2668).** `${CLAUDE_PLUGIN_ROOT}`/`$ARGUMENTS`/the `` !`command` `` auto-injection syntax removed from shared `app-migration`, `theme-audit`, `screen-composition`, `project-scaffolding`, `app-planning`, `surface-qa`, and `find-unused` SKILL.md bodies, all eight `commands/*.md` bodies, and their linked reference docs (`surface-qa/references/verification.md`, `find-unused/references/recon-and-audits.md`) — portable `<plugin-root>` prose / "named in the current request" phrasing throughout; `screen-composition`/`app-planning`'s Claude-only auto-injected context block became an explicit "run this yourself" instruction, since Codex has no equivalent auto-injection surface. `hooks/hooks.json` moved to `adapters/claude/hooks.json` (unchanged, `${CLAUDE_PLUGIN_ROOT}`-based); new root `hooks.json` mirrors the same event/matcher/action signature with a monorepo-root-relative command path for Codex's convention-discovered hook surface — an unverified-but-honest fallback documented in `HARNESS-NOTES.md`. **The one exception:** every skill's `disable-model-invocation:`/`user-invocable:` frontmatter — required, explicitly, by this repo's own blocking `check:skills` gate (`plugin-estate-v2.md`'s species-pinning charter) — collides head-on with the validator's opposite requirement on the exact same file, and Codex is hard-pinned to read root `skills/` directly (no adapter escape). The repo's own charter is not weakened; `npm run check:cross-harness-plugins` (`scripts/verify/check-cross-harness-plugins.mjs`, wrapping the vendored `scripts/verify/vendor/validate_cross_harness.py`) treats only this one exact finding shape as accepted (logged as INFO) and still fails on every other portability class. Full reasoning in `HARNESS-NOTES.md`.
46
+
47
+ ### Docs
48
+ - **`skills/pattern-catalog/references/pattern-index.md` regenerated for `page-scroll`'s addition (gh#2639, ADR-0098 Fork 4).** `build:patterns-index`'s re-extraction picked up `admin-dashboard.contents.html`/`admin-shell.contents.html`'s tag-list changes under the page-chrome consolidation (`admin-page*` → `page-ui[band]`, `admin-scroll` → `page-scroll`).
49
+
50
+ ### Changed
51
+ - **`adia-lint` rewritten as a thin Node CLI (`adia-lint.mjs`) consuming a shared generated lint-rule bank, retiring the Python script (gh#2638, LLD-0016, ADR-0099).** The consumer-app structural checks (the forge-shared rules plus SSR-DOUBLE-ROUTER, SSR-TOPLEVEL-IMPORT, HARDCODED-OPEN, SHELL-NESTING, SHELL-RESIZE, LLM-KEY-IN-CLIENT, GENUI-UNVALIDATED, GENUI-DOC-CONFLICT) now live as individually-fixtured rule modules under `scripts/lint/rules/` in the repo root, built into `scripts/lint/generated/rule-bank.mjs` and vendored byte-identically into this plugin, `adia-ui-forge`, and the gen-ui MCP's factory vendor tree (`npm run build:lint-rules`, freshness gated by `npm run check:lint-rules-fresh`). `hooks/hooks.json`'s PostToolUse command changes from `python3 ".../adia-lint" --hook` to `node ".../adia-lint.mjs" --hook`; the CLI contract (including `-h`/`--help`), exit codes, and PostToolUse never-block invariant are unchanged. `npm run check:lint-rules-selftest` proves finding-parity against the retired script's own `selftest()` fixtures. The gen-ui MCP factory server's `lint` tool (`audit_structure`) now spawns the Node CLI directly instead of shelling through `python3`.
52
+
53
+ ### Maintenance
54
+ - **`.claude-plugin/plugin.json` version bump** — moves in lockstep with package.json (the `/plugin update` cache key).
55
+ - **`.codex-plugin/` touched in this release window** (1 file(s), e.g. `.codex-plugin/plugin.json`) — carried by the entries above.
56
+ - **`agents/` touched in this release window** (1 file(s), e.g. `agents/surface-qa-agent.md`) — carried by the entries above.
57
+ - **`prompts/` touched in this release window** (8 file(s), e.g. `prompts/app-audit.md`) — carried by the entries above.
58
+
3
59
  ## [0.8.56] — 2026-08-31
4
60
 
5
61
  ### Added
package/README.md CHANGED
@@ -96,6 +96,58 @@ surface-qa-agent) · `component-model.md` · the `references/contracts/` twins.
96
96
 
97
97
  `adia-lint` — PostToolUse advisory consumer-smell repair loop (selftest ships).
98
98
 
99
+ `lab-posture-guard` — PreToolUse (`Write|Edit`) blocking guard, active only
100
+ in lab posture (see Posture, below); a no-op in bare-repo/absent posture,
101
+ leaving `adia-lint`'s existing advisory contract completely unchanged. A
102
+ `SessionStart` hook warms its manifest cache once per session; selftest
103
+ ships (`npm run check:lab-posture-guard-selftest`).
104
+
105
+ ## Posture
106
+
107
+ Two postures a consumer repo can declare (ADR-0108, LLD-0019 §C3): **bare-repo**
108
+ (ADR-0040, the default — this plugin working standalone with no monorepo
109
+ path assumptions) and **lab** (ADR-0108 — a repo that installs
110
+ `@adia-ai/web-components` at root, pinned, and scaffolds prototypes that
111
+ consume those root packages directly as app source). Posture is an
112
+ EXPLICIT marker, never inferred from `package.json` or `node_modules` —
113
+ that inventory-only question ("what's installed") is `adia-info`'s
114
+ separate probe, not a declared-intent signal.
115
+
116
+ A repo-root `.adia` JSON file declares posture:
117
+
118
+ ```json
119
+ {
120
+ "posture": "lab",
121
+ "kit": { "web-components": "9.9.9" }
122
+ }
123
+ ```
124
+
125
+ - `posture` — `"lab"` or `"bare-repo"`, required when the file exists. An
126
+ explicit `"bare-repo"` value is accepted alongside the file's plain
127
+ absence — both read as bare-repo behavior — so a repo can affirmatively
128
+ opt OUT of lab posture in its own marker rather than relying on absence
129
+ alone.
130
+ - `kit` — optional. When present, `kit["web-components"]` records the
131
+ installed `@adia-ai/web-components` version the marker was written
132
+ against — informational only; nothing in this plugin reads it yet.
133
+ - Missing file, unparseable JSON, or a missing/unrecognized `posture`
134
+ value all resolve to bare-repo behavior — the permissive branch. A
135
+ corrupt marker must never accidentally turn ON the stricter lab-posture
136
+ enforcement below.
137
+
138
+ `scripts/adia-posture.mjs` (`readPosture(repoRoot)` → `'lab' | 'bare-repo' |
139
+ 'absent'`) is the one reader both `lab-posture-guard` and a future
140
+ `scaffold_prototype` (LLD-0019 §C6, not shipped yet) key on.
141
+
142
+ **In lab posture**, `lab-posture-guard` validates every `Write`/`Edit`
143
+ against the installed manifest's tags, attributes, and declared enum
144
+ values (`UNKNOWN-TAG`/`UNKNOWN-ATTR`/`ENUM-VALUE`/`INTERNAL-TIER`) and
145
+ DENIES a violating write before it lands, naming the offending
146
+ tag/attr/value and the manifest's allowed set in the deny reason so the
147
+ agent can self-correct. **In bare-repo or absent posture**, this hook is a
148
+ complete no-op — `adia-lint`'s existing `PostToolUse` advisory contract is
149
+ the only signal, unchanged.
150
+
99
151
  ## Commands
100
152
 
101
153
  `/project-scaffolding` · `/app-planning` · `/surface-qa` · `/app-migration` · `/find-unused` · `/gen-ui-wiring` · `/app-audit` · `/theme-audit`
@@ -111,7 +163,7 @@ attributes vs. the shipped component contracts), and `adia-theme-audit.mjs`
111
163
 
112
164
  ## MCP
113
165
 
114
- `adia-gen-ui` server pinned: `@adia-ai/mcp@0.8.56` (tool SoT:
166
+ `adia-gen-ui` server pinned: `@adia-ai/mcp@0.8.58` (tool SoT:
115
167
  `packages/gen-ui/mcp/TOOLS.md` — the `gen-ui` section; stability rule in
116
168
  `references/contracts/`; pin lives in `.mcp.json` — `check:plugin-count-claims`
117
169
  guards this README copy against it). The server is `adia-mcp gen-ui`,
@@ -136,6 +188,22 @@ plain Node script with no Claude-specific runtime dependency; the
136
188
  Hermes, and becomes a genuine `prompts/theme-audit.md` Pi command via
137
189
  `build:harness-manifests`.
138
190
 
191
+ **Lab posture's write-time enforcement (ADR-0108 D5/D6) is a Claude Code-only
192
+ guarantee** — `AGENTS.md`'s own "Non-Claude harnesses" section states plainly
193
+ that none of Codex, Hermes, or Pi has a manifest key for `hooks/`, and none
194
+ has a hook runtime a `PreToolUse` deny could wire into, so `lab-posture-guard`
195
+ simply does not run on any of the three. They degrade to `npm run lint` in
196
+ the monorepo (or the equivalent vendored `adia-lint`/lint-bank invocation in
197
+ a consumer repo) picking up the same four manifest-derived rules
198
+ (`UNKNOWN-TAG`/`UNKNOWN-ATTR`/`ENUM-VALUE`/`INTERNAL-TIER`, ADR-0108 D5) at
199
+ `warn` severity — after the fact, at lint/check time, rather than before the
200
+ write lands. `get_manifest` reachability without the hook is not uniform
201
+ either: Codex reaches it through its plain `.mcp.json` passthrough; Hermes
202
+ needs its `hermes-mcp.yaml` fragment hand-merged into `~/.hermes/config.yaml`;
203
+ Pi has no native MCP support at all, so a Pi session without the third-party
204
+ `pi-mcp-adapter` gets neither the manifest nor the lint-time signal until a
205
+ `npm run check`/`npm run lint` pass runs.
206
+
139
207
  - **Codex** (gh#1888): `.codex-plugin/plugin.json` + per-skill
140
208
  `agents/openai.yaml`, derived; `mcpServers` points at the same
141
209
  `.mcp.json` Claude Code uses. No manifest key for hooks/commands/agents —
@@ -39,7 +39,7 @@ a surface can pass the browser gate and still be a keyword-driven premature
39
39
  render (D1) that never traced to a resolved plan. Every claim in the
40
40
  proof cites its evidence (the probe JSON, the screenshot path, the element
41
41
  inspected). The structure row's evidence comes from running
42
- `python3 "${CLAUDE_PLUGIN_ROOT}/scripts/adia-lint"` over the surface's files via
42
+ `node "${CLAUDE_PLUGIN_ROOT}/scripts/adia-lint.mjs"` over the surface's files via
43
43
  Bash — the PostToolUse hook never fires for a seat that writes nothing. A
44
44
  gate that cannot run (no dev server, Playwright missing) is reported
45
45
  UNMEASURED with the reason, never silently skipped; a dispatch without a
@@ -6,9 +6,11 @@ disable-model-invocation: false
6
6
  user-invocable: true
7
7
  ---
8
8
 
9
- Probe the project context. **$ARGUMENTS**
9
+ Probe the project context named in the current request (a directory, if given).
10
10
 
11
- Run `python3 "${CLAUDE_PLUGIN_ROOT}/scripts/adia-info" $ARGUMENTS` and present
11
+ Run `python3 "<plugin-root>/scripts/adia-info" [dir]` (`<plugin-root>` is `$CLAUDE_PLUGIN_ROOT`
12
+ in Claude Code; the plugin's installed directory in Codex) with the directory named in the
13
+ current request, and present
12
14
  the JSON as a short read: each field with the signal it derived from and
13
15
  the decision it drives (screen-composition §Current project context carries the
14
16
  field→decision table). An `installedVersion` that is null or mismatched vs
@@ -6,7 +6,7 @@ disable-model-invocation: false
6
6
  user-invocable: true
7
7
  ---
8
8
 
9
- Migrate this codebase. **$ARGUMENTS**
9
+ Migrate this codebase to the target version named in the current request.
10
10
 
11
11
  Invoke **`app-migration`**: read the shipped guide for the span
12
12
  (`node_modules/@adia-ai/web-components/MIGRATION.md` — install/upgrade the
@@ -6,10 +6,11 @@ disable-model-invocation: false
6
6
  user-invocable: true
7
7
  ---
8
8
 
9
- Orient in this repo. **$ARGUMENTS**
9
+ Orient in this repo, answering the path or question named in the current request.
10
10
 
11
11
  Invoke **`app-planning`** and produce the Orientation Record in the skill's
12
12
  own contract shape (Rendering mode · Project shape · Shell · Task ·
13
13
  Screen plan (start mode) · → Route · Verify target · Open questions), each
14
14
  axis citing its signal. Self-check the record with
15
- `python3 "${CLAUDE_PLUGIN_ROOT}/scripts/record-lint" -` before returning it.
15
+ `python3 "<plugin-root>/scripts/record-lint" -` (`<plugin-root>` is `$CLAUDE_PLUGIN_ROOT` in
16
+ Claude Code; the plugin's installed directory in Codex) before returning it.
@@ -6,12 +6,12 @@ disable-model-invocation: false
6
6
  user-invocable: true
7
7
  ---
8
8
 
9
- Find what this app is not using. **$ARGUMENTS**
9
+ Find what this app is not using, scoped to the target version named in the current request.
10
10
 
11
11
  Invoke **`find-unused`**: enumerate every `@adia-ai/*` package each manifest
12
12
  declares (never assume `web-components`), recon the three versions that differ
13
- per package — declared range, installed, and the target: **the version in
14
- `$ARGUMENTS` when one was given, registry latest only as the default**. Read
13
+ per package — declared range, installed, and the target: **the version named in the current request
14
+ when one was given, registry latest only as the default**. Read
15
15
  every `node_modules/@adia-ai/*/CHANGELOG.md` section across the
16
16
  installed→target span, then run all four audits — inert opt-in layers, redundant local
17
17
  workarounds, retired enum values sitting in persisted state, and shipped
@@ -6,7 +6,7 @@ disable-model-invocation: false
6
6
  user-invocable: true
7
7
  ---
8
8
 
9
- Build a gen-UI experience. **$ARGUMENTS**
9
+ Build the gen-UI experience or surface named in the current request.
10
10
 
11
11
  Invoke **`gen-ui-wiring`** and run its generate → validate → render → iterate
12
12
  loop; generated A2UI is validated as data before it is serialized, and the
@@ -6,10 +6,11 @@ disable-model-invocation: false
6
6
  user-invocable: true
7
7
  ---
8
8
 
9
- Scaffold an adia-ui surface. **$ARGUMENTS**
9
+ Scaffold the adia-ui surface named in the current request.
10
10
 
11
11
  Invoke **`project-scaffolding`** to classify the project shape and target, then use
12
- `${CLAUDE_PLUGIN_ROOT}/scripts/adia-scaffold` for the mechanical skeleton. Shapes
12
+ `<plugin-root>/scripts/adia-scaffold` (`<plugin-root>` is `$CLAUDE_PLUGIN_ROOT` in Claude
13
+ Code; the plugin's installed directory in Codex) for the mechanical skeleton. Shapes
13
14
  the bin doesn't one-shot (rollup, shared-foundation) are composed per the
14
15
  project-shapes reference. `inventory <app-root>` scores an existing app
15
16
  against the structure rubric (4 mechanized gates with cited paths + the 2
@@ -6,7 +6,7 @@ disable-model-invocation: false
6
6
  user-invocable: true
7
7
  ---
8
8
 
9
- Verify a composed surface. **$ARGUMENTS**
9
+ Verify the composed surface named in the current request.
10
10
 
11
11
  Dispatch the **surface-qa-agent** agent on the named surface/URL — the exit
12
12
  gate runs ISOLATED from whatever context built the surface (generator ≠
@@ -6,9 +6,9 @@ disable-model-invocation: false
6
6
  user-invocable: true
7
7
  ---
8
8
 
9
- Audit a theme stylesheet. **$ARGUMENTS**
9
+ Audit the theme stylesheet named in the current request.
10
10
 
11
- Run `node "${CLAUDE_PLUGIN_ROOT}/scripts/adia-theme-audit.mjs" --theme <file>
11
+ Run `node "<plugin-root>/scripts/adia-theme-audit.mjs" --theme <file>
12
12
  --app <root>` (add `--json`/`--json-out <path>` for a structured report,
13
13
  `--strict` to exit non-zero on any `high`-confidence finding, `--class
14
14
  1,2,3,4` to narrow scope). No `@adia-ai/web-components` resolvable from
package/hermes-mcp.yaml CHANGED
@@ -6,5 +6,5 @@ mcp_servers:
6
6
  command: "npx"
7
7
  args:
8
8
  - "-y"
9
- - "@adia-ai/mcp@0.8.56"
9
+ - "@adia-ai/mcp@0.8.58"
10
10
  - "gen-ui"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/adia-ui-factory",
3
- "version": "0.8.56",
3
+ "version": "0.8.58",
4
4
  "description": "Author and verify apps built ON the adia-ui (@adia-ai) light-DOM web-component framework — orient, scaffold, compose, wire, verify, and migrate across SPA and SSR rendering modes. Wires the a2ui MCP for catalog retrieval, UI generation, and validation.",
5
5
  "keywords": [
6
6
  "adia-ui",
@@ -14,7 +14,7 @@
14
14
  "license": "MIT",
15
15
  "author": {
16
16
  "name": "Kim",
17
- "email": "kim@sublimeheroics.com"
17
+ "email": "kim.granlund@adia.ai"
18
18
  },
19
19
  "bugs": {
20
20
  "email": "kim.granlund@adia.ai"
package/plugin.yaml CHANGED
@@ -1,4 +1,4 @@
1
1
  name: "adia-ui-kit-factory"
2
- version: "0.8.56"
2
+ version: "0.8.58"
3
3
  description: "Author and verify apps built ON the adia-ui (@adia-ai) light-DOM web-component framework — orient, scaffold, compose, wire, verify, and migrate across SPA and SSR rendering modes. Wires the a2ui MCP for catalog retrieval, UI generation, and validation."
4
4
  manifest_version: 1
@@ -3,9 +3,11 @@ description: "Probe this repo's adia-ui project context — declared vs installe
3
3
  argument-hint: "[dir]"
4
4
  ---
5
5
 
6
- Probe the project context. **$ARGUMENTS**
6
+ Probe the project context named in the current request (a directory, if given).
7
7
 
8
- Run `python3 "${CLAUDE_PLUGIN_ROOT}/scripts/adia-info" $ARGUMENTS` and present
8
+ Run `python3 "<plugin-root>/scripts/adia-info" [dir]` (`<plugin-root>` is `$CLAUDE_PLUGIN_ROOT`
9
+ in Claude Code; the plugin's installed directory in Codex) with the directory named in the
10
+ current request, and present
9
11
  the JSON as a short read: each field with the signal it derived from and
10
12
  the decision it drives (screen-composition §Current project context carries the
11
13
  field→decision table). An `installedVersion` that is null or mismatched vs
@@ -3,7 +3,7 @@ description: "Migrate consumer code across @adia-ai versions (or port a foreign
3
3
  argument-hint: "[target version]"
4
4
  ---
5
5
 
6
- Migrate this codebase. **$ARGUMENTS**
6
+ Migrate this codebase to the target version named in the current request.
7
7
 
8
8
  Invoke **`app-migration`**: read the shipped guide for the span
9
9
  (`node_modules/@adia-ai/web-components/MIGRATION.md` — install/upgrade the
@@ -3,10 +3,11 @@ description: "Orient in an existing adia-ui app — classify rendering mode, pro
3
3
  argument-hint: "[path or question]"
4
4
  ---
5
5
 
6
- Orient in this repo. **$ARGUMENTS**
6
+ Orient in this repo, answering the path or question named in the current request.
7
7
 
8
8
  Invoke **`app-planning`** and produce the Orientation Record in the skill's
9
9
  own contract shape (Rendering mode · Project shape · Shell · Task ·
10
10
  Screen plan (start mode) · → Route · Verify target · Open questions), each
11
11
  axis citing its signal. Self-check the record with
12
- `python3 "${CLAUDE_PLUGIN_ROOT}/scripts/record-lint" -` before returning it.
12
+ `python3 "<plugin-root>/scripts/record-lint" -` (`<plugin-root>` is `$CLAUDE_PLUGIN_ROOT` in
13
+ Claude Code; the plugin's installed directory in Codex) before returning it.
@@ -3,12 +3,12 @@ description: "Find what a clean @adia-ai upgrade left unused — opt-in layers n
3
3
  argument-hint: "[target version]"
4
4
  ---
5
5
 
6
- Find what this app is not using. **$ARGUMENTS**
6
+ Find what this app is not using, scoped to the target version named in the current request.
7
7
 
8
8
  Invoke **`find-unused`**: enumerate every `@adia-ai/*` package each manifest
9
9
  declares (never assume `web-components`), recon the three versions that differ
10
- per package — declared range, installed, and the target: **the version in
11
- `$ARGUMENTS` when one was given, registry latest only as the default**. Read
10
+ per package — declared range, installed, and the target: **the version named in the current request
11
+ when one was given, registry latest only as the default**. Read
12
12
  every `node_modules/@adia-ai/*/CHANGELOG.md` section across the
13
13
  installed→target span, then run all four audits — inert opt-in layers, redundant local
14
14
  workarounds, retired enum values sitting in persisted state, and shipped
@@ -3,7 +3,7 @@ description: "Author a generative-UI experience — wire the a2ui runtime, gener
3
3
  argument-hint: "[experience or surface]"
4
4
  ---
5
5
 
6
- Build a gen-UI experience. **$ARGUMENTS**
6
+ Build the gen-UI experience or surface named in the current request.
7
7
 
8
8
  Invoke **`gen-ui-wiring`** and run its generate → validate → render → iterate
9
9
  loop; generated A2UI is validated as data before it is serialized, and the
@@ -3,10 +3,11 @@ description: "Scaffold a new adia-ui app, page, or component skeleton (scripts/a
3
3
  argument-hint: "[spa|ssr|page|component|inventory|selftest] [name or app-root]"
4
4
  ---
5
5
 
6
- Scaffold an adia-ui surface. **$ARGUMENTS**
6
+ Scaffold the adia-ui surface named in the current request.
7
7
 
8
8
  Invoke **`project-scaffolding`** to classify the project shape and target, then use
9
- `${CLAUDE_PLUGIN_ROOT}/scripts/adia-scaffold` for the mechanical skeleton. Shapes
9
+ `<plugin-root>/scripts/adia-scaffold` (`<plugin-root>` is `$CLAUDE_PLUGIN_ROOT` in Claude
10
+ Code; the plugin's installed directory in Codex) for the mechanical skeleton. Shapes
10
11
  the bin doesn't one-shot (rollup, shared-foundation) are composed per the
11
12
  project-shapes reference. `inventory <app-root>` scores an existing app
12
13
  against the structure rubric (4 mechanized gates with cited paths + the 2
@@ -3,7 +3,7 @@ description: "Run the consumer-side exit gate on a composed surface — dispatch
3
3
  argument-hint: "[url or surface] [key selectors]"
4
4
  ---
5
5
 
6
- Verify a composed surface. **$ARGUMENTS**
6
+ Verify the composed surface named in the current request.
7
7
 
8
8
  Dispatch the **surface-qa-agent** agent on the named surface/URL — the exit
9
9
  gate runs ISOLATED from whatever context built the surface (generator ≠
@@ -3,9 +3,9 @@ description: "Classify a consumer app's theme.css against what the adia-ui frame
3
3
  argument-hint: "[--theme <file>] [--app <root>]"
4
4
  ---
5
5
 
6
- Audit a theme stylesheet. **$ARGUMENTS**
6
+ Audit the theme stylesheet named in the current request.
7
7
 
8
- Run `node "${CLAUDE_PLUGIN_ROOT}/scripts/adia-theme-audit.mjs" --theme <file>
8
+ Run `node "<plugin-root>/scripts/adia-theme-audit.mjs" --theme <file>
9
9
  --app <root>` (add `--json`/`--json-out <path>` for a structured report,
10
10
  `--strict` to exit non-zero on any `high`-confidence finding, `--class
11
11
  1,2,3,4` to narrow scope). No `@adia-ai/web-components` resolvable from
@@ -109,4 +109,4 @@ The mechanizable smells the framework audits — these seed the phase-(c) hook a
109
109
  | Hardcoded `open` on `<modal-ui>`/`<drawer-ui>` | drive via `.open = true` |
110
110
  | `new URL('…', import.meta.url)` literal for a corpus path | hold the path in a variable first |
111
111
 
112
- Run the plugin's `scripts/adia-lint` on changed files, the framework's `audit:*` gates when you have the repo, and `mcp__a2ui__check_anti_patterns` / `validate_schema` on generated markup.
112
+ Run the plugin's `scripts/adia-lint.mjs` on changed files, the framework's `audit:*` gates when you have the repo, and `mcp__a2ui__check_anti_patterns` / `validate_schema` on generated markup.
@@ -72,5 +72,5 @@ Verdict: <shippable | blocked: D#…>
72
72
 
73
73
  _Provenance: the two-axis COMPOSE/REALIZE defect-quadrant model is adapted from a component-grading
74
74
  rubric; the audit tool names and slot contracts here are the factory plugin's own (`adia-lint`,
75
- `adia-probe`, the current primitive catalog). Verify a named audit rule against `scripts/adia-lint`'s
75
+ `adia-probe`, the current primitive catalog). Verify a named audit rule against `scripts/adia-lint.mjs`'s
76
76
  actual rule set before citing it._
@@ -11,7 +11,7 @@ Full SaaS/admin chrome from `@adia-ai/web-modules`. Register the **cluster barre
11
11
 
12
12
  ## Cluster roster
13
13
 
14
- `<admin-shell>` (host coordinator) · `<admin-sidebar>` (resizable/collapsible, `slot="leading"|"trailing"`) · `<admin-command>` (Cmd+K palette) · `<admin-content>` (center column) · `<admin-topbar>` / `<admin-statusbar>` (chrome bars, shared slots) · `<admin-scroll>` (vertical scroll container) · `<admin-page>` + `<admin-page-header>` / `<admin-page-body>` / `<admin-page-footer>` (the page; header/footer sticky, body scrolls) · `<admin-entity-item>` (icon+label+badge identity row). Three are JS-bearing (shell, sidebar, command); the rest are CSS-only structure.
14
+ `<admin-shell>` (host coordinator) · `<admin-sidebar>` (resizable/collapsible, `slot="leading"|"trailing"`) · `<admin-command>` (Cmd+K palette) · `<admin-content>` (center column) · `<admin-topbar>` / `<admin-statusbar>` (chrome bars, shared slots) · `<admin-scroll>` (vertical scroll container) · `<page-ui band>` + `<header-ui>` / `<section-ui>` / `<footer-ui>` (the page; header/footer always-sticky with border+background, body scrolls) · `<admin-entity-item>` (icon+label+badge identity row). Three are JS-bearing (shell, sidebar, command); the rest are CSS-only structure.
15
15
 
16
16
  ## Canonical skeleton
17
17
 
@@ -29,10 +29,10 @@ Full SaaS/admin chrome from `@adia-ai/web-modules`. Register the **cluster barre
29
29
  <breadcrumb-ui slot="heading">…</breadcrumb-ui>
30
30
  </admin-topbar>
31
31
  <admin-scroll>
32
- <admin-page>
33
- <admin-page-header slot="header"><header-ui><span slot="heading">Page</span></header-ui></admin-page-header>
34
- <admin-page-body slot="body"><section-ui>…</section-ui></admin-page-body>
35
- </admin-page>
32
+ <page-ui band max-width="xwide" padding="10">
33
+ <header-ui><span slot="heading">Page</span></header-ui>
34
+ <section-ui>…</section-ui>
35
+ </page-ui>
36
36
  </admin-scroll>
37
37
  <admin-statusbar slot="footer"><span>Status</span></admin-statusbar>
38
38
  </admin-content>
@@ -69,14 +69,14 @@ the command palette, the statusbar). Page-tier content is *per-route* — it cha
69
69
  |---|---|---|---|
70
70
  | Shell sidebar | `<admin-sidebar slot="leading">` | primary nav; brand in `slot="header"`; **user-account controls only** in `slot="footer"` | app-wide preference toggles (scheme/theme) |
71
71
  | Shell topbar | `<admin-topbar>` in `<admin-content>` | sidebar-toggle + **breadcrumb** (location); trailing cluster = **persistent global controls** (theme, notifications, command trigger) | page title, page CTAs, dataset search |
72
- | Page header | `<admin-page-header>` | **page title `<h1>`** + page-scoped CTAs (`+ New`, `Export`) via `slot="action"` | location chrome |
73
- | Page body | `<admin-page-body>` | the page's primary content | — |
72
+ | Page header | `<header-ui>` inside `<page-ui band>` | **page title `<h1>`** + page-scoped CTAs (`+ New`, `Export`) via `slot="action"` | location chrome |
73
+ | Page body | `<section-ui>` inside `<page-ui band>` | the page's primary content | — |
74
74
  | Table toolbar | `<table-toolbar-ui>` adjacent to `<table-ui>` | in-page search/filter/sort scoped to the table | — |
75
75
  | Statusbar | `<admin-statusbar slot="footer">` | app-wide status (sync, row count, version) | — |
76
76
 
77
77
  **The four allocation anti-patterns** (each silently breaks the model, no warning):
78
78
 
79
- - ❌ **Page CTA (`+ New Claim`) in the shell topbar** → it sticks across every route and never updates. Page CTAs go in `<admin-page-header>`.
79
+ - ❌ **Page CTA (`+ New Claim`) in the shell topbar** → it sticks across every route and never updates. Page CTAs go in the `<header-ui>` inside `<page-ui band>`, via `slot="action"`.
80
80
  - ❌ **Page title (`<h1>`) in the topbar** instead of a breadcrumb → the topbar carries *location*; the breadcrumb's terminal segment IS the page title.
81
81
  - ❌ **Dataset-search input in the topbar** → a search scoped to one dataset belongs in `<table-toolbar-ui>`, not the app-wide topbar.
82
82
  - ❌ **Scheme/theme toggle in the sidebar footer** → it reads as nav and vanishes when the sidebar collapses. App-wide preferences belong in the topbar trailing cluster.
@@ -88,16 +88,16 @@ SPA mounts the full markup. SSR keeps the shell + chrome fixed and swaps only th
88
88
  ## Gotchas (mechanized where noted)
89
89
 
90
90
  - **Piecemeal import** → `AdminSidebar`/`AdminCommand` unregistered; `.toggle()/.show()` undefined. Import the barrel.
91
- - **Wrapping shell children in `<col-ui>`/`<row-ui>`** → breaks the grid (it reads tag selectors). Generics go _inside_ `admin-content`/`admin-page-body`.
92
- - **Raw `<header>` vs `<header-ui>` inside `<admin-page-header>`**: both work the shell CSS targets `admin-page-header > :is(header, header-ui)` identically (css/admin-shell.templates.css:39), and the CHANGELOG's canonical composition uses raw `<header>`. Pick either; just don't wrap the header in a layout primitive.
91
+ - **Wrapping shell children in `<col-ui>`/`<row-ui>`** → breaks the grid (it reads tag selectors). Generics go _inside_ `admin-content`/`page-ui`'s `<section-ui>`.
92
+ - **Raw `<header>` vs `<header-ui>` inside `<page-ui band>`**: prefer `<header-ui>`. `page-ui`'s `@scope` rules target both `<header>` and `<header-ui>` identically for geometry, but only `<header-ui>` has the shadow DOM that routes `slot="heading"` / `slot="description"` / `slot="icon"` / `slot="action"` children a raw `<header>` matches the layout rhythm but silently drops every named slot.
93
93
  - **`[resizable]` without a child `<div data-sidebar-resize>`** → no drag handle.
94
94
  - **Hardcoded `[collapsed]`** → won't auto-clear on resize; use `.collapse()/.expand()`.
95
- - **Multiple `<admin-page>` in one `<admin-scroll>`** → single-axis scroll breaks; one page per scroll.
95
+ - **Multiple `<page-ui>` in one `<admin-scroll>`** → single-axis scroll breaks; one page per scroll.
96
96
  - **`@container (…)` instead of `@container page-content (…)`** → won't react to sidebar collapse.
97
97
  - **Legacy shapes** (`<aside data-sidebar>`, `<dialog data-command>`) — retired v0.4.0 (`adia-lint` `LEGACY-SHELL`).
98
98
  - **Sidebar nav is `<nav-ui>` + `<nav-item-ui>`**, never `<menu-ui>`/`<menu-item-ui>` — menu-ui is for Popover-API dropdowns, not persistent navigation.
99
99
  - **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.
100
- - **`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.
100
+ - **`header-ui` has no CSS of its own** — its icon/heading/description/action grid comes from the parent's `@scope` (`page-ui` provides it); bespoke chrome reusing the header-ui vocabulary must supply the grid + text ellipsis locally.
101
101
  - **A persistent `<drawer-ui data-mobile-nav-drawer>` is always in the DOM** (relocated mobile-nav mechanism, gh#1984/ADR-0090), not conditionally created — only its visibility responds to a container query at the shell's mobile-nav breakpoint. A selector assuming exactly one `drawer-ui`/`dialog` on the page (e.g. an E2E test) must scope past it: `drawer-ui:not([data-mobile-nav-drawer])`.
102
102
 
103
103
  Real usage: `apps/saas/app/admin-dashboard/`.