@adia-ai/adia-ui-factory 0.8.51 → 0.8.53

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adia-ui-kit-factory",
3
- "version": "0.8.51",
3
+ "version": "0.8.53",
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",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adia-ui-kit-factory",
3
- "version": "0.8.51",
3
+ "version": "0.8.53",
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.51", "gen-ui"]
5
+ "args": ["-y", "@adia-ai/mcp@0.8.53", "gen-ui"]
6
6
  }
7
7
  }
8
8
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog — adia-ui-kit-factory
2
2
 
3
+ ## [0.8.53] — 2026-08-26
4
+
5
+ ### Changed
6
+ - **`skills/pattern-catalog/references/pattern-index.md` regenerated** (`npm run build:patterns-index`) — mechanical reindex, no skill/agent behavior change this cut.
7
+
8
+ ### Maintenance
9
+ - **Lockstep version bump only.** No other source changes in this package; bumped to maintain the lockstep version coherence enforced by `scripts/release/check-lockstep.mjs`. Substantive v0.8.53 work shipped in Framework-wide docs+catalog audit (177 primitives/modules), nav collapsible click-handler bug fix, corpus-wide Related-link sweep (181 fixes), SegmentedControl catalog-rename cascade fix, mobile-nav-drawer consumer contract docs (gh#2032, gh#2031, gh#2068, gh#2076, gh#2116, gh#2133). See `packages/web-components/CHANGELOG.md#0853--2026-08-26` for details.
10
+ - **`.claude-plugin/plugin.json` version bump** — moves in lockstep with package.json (the `/plugin update` cache key).
11
+ - **`.codex-plugin/` touched in this release window** (1 file(s), e.g. `.codex-plugin/plugin.json`) — carried by the `pattern-index.md` regen above; no separate content change.
12
+
13
+ ## [0.8.52] — 2026-08-25
14
+
15
+ ### Added
16
+ - **`ui-architect` portable skill + `BuildResult`/`VerifyProof` linters + repo-root Codex git-sourced marketplace (ADR-0091 C1/C2/C3/C4/C7, PR #1998).** `skills/ui-architect/SKILL.md` (with `agents/openai.yaml`, auto-derived via `codex-manifests.mjs`) authors one contract-grammar skill body every harness consumes as a thin adapter, backed by new `scripts/build-result-lint` and `scripts/verify-proof-lint` (record-lint family) plus `check:ui-architect-skill-portable` (85 → 88 gates in `npm run check`). `.agents/plugins/marketplace.json` (repo root) is the new git-install Codex channel — verified against OpenAI's own merged `codex-rs` marketplace-resolution source rather than assuming the Claude-side schema. `agents/ui-architect.md` slimmed to identity/tool-wall/dispatch-mechanics/failure-branches/stopping-predicate; the app-planning/screen-composition/surface-qa agent files already met the target shape.
17
+ - **`ui-architect` Pi extension (`pi/ui-architect.extension.ts`) + Hermes `delegate_task` `/ui-architect` command (ADR-0091 C5/C6, PR #2000).** Both harnesses verified live against their actual installed runtimes before building: Pi via the installed `@earendil-works/pi-coding-agent` v0.84.3's own subprocess-based subagent-dispatch pattern (live end-to-end run: dispatched all three isolated planning/composition/QA passes as real child `pi` processes, wrote a real file, took a real screenshot, returned a clean `VerifyProof`); Hermes via the installed Hermes Agent's own `PluginContext.register_command`/`delegate_tool.py` source, gated by the same portable-contract linters and live-verified via `hermes plugins doctor --ci`. Pi/Hermes native adapters framed as in-progress in the README rather than claiming unverified parity.
18
+
19
+ ### Changed
20
+ - **All agent seats pinned to `sonnet` + `xhigh` reasoning effort, replacing the prior fable/opus ceiling-ladder pins (PR #1999).** Operator's direct standing instruction, applied across `app-planning-agent`, `screen-composition-agent`, `surface-qa-agent`, `ui-architect`. See root `CHANGELOG.md` for the cross-plugin note.
21
+
22
+ ### Fixed
23
+ - **The new repo-root Codex marketplace manifest collided on `"name": "adia-plugins"` with the npm-sourced sibling, so `codex plugin marketplace add`/`codex plugin add` resolved ambiguously (PR #2002).** Renamed to `adia-ui-kit`, matching the existing Claude-side `adia-ui-kit`/`adia-plugins` split exactly; README + getting-started site page install references updated.
24
+
25
+ ### Docs
26
+ - **README's install section hand-typed a stale "published v0.1.0" for `@adia-ai/adia-plugins`, now at v0.2.0 (PR #1996).** Dropped the hardcoded version number (it would only go stale again on the next Class-B cut) in favor of a pointer to the `adia-plugins` README, which carries its own current version and Non-Claude-harness detail.
27
+
28
+ ### Maintenance
29
+ - **`.claude-plugin/plugin.json` version bump** — moves in lockstep with package.json (the `/plugin update` cache key).
30
+ - **`.codex-plugin/` touched in this release window** (1 file(s), e.g. `.codex-plugin/plugin.json`) — carried by the entries above.
31
+
3
32
  ## [0.8.51] — 2026-08-24
4
33
 
5
34
  ### Added
package/README.md CHANGED
@@ -24,7 +24,9 @@ Sources the in-repo manifest at `.claude-plugin/marketplace.json` (repo
24
24
  root — `claude plugin marketplace add` always expects the manifest there,
25
25
  with or without `--sparse`; it does not resolve a subdirectory manifest).
26
26
  The LIVE public install path (ADR-0045's npm-sourced manifest,
27
- `@adia-ai/adia-plugins`, published v0.1.0 — gh#1160):
27
+ `@adia-ai/adia-plugins`, live since v0.1.0 — gh#1160; see
28
+ `packages/plugins/adia-plugins/README.md` for its own current version, which
29
+ cuts on its own Class-B schedule independent of the framework lockstep):
28
30
 
29
31
  ```
30
32
  /plugin marketplace add https://unpkg.com/@adia-ai/adia-plugins/marketplace.json
@@ -49,7 +51,7 @@ posture (ADR-0040) is about this plugin working standalone in any consumer
49
51
  repo once installed some other way (no monorepo path assumptions in its own
50
52
  scripts) — it does not add an npm-sourced marketplace entry.
51
53
 
52
- ## Skills (18 — `skills/` on disk is the roster; `check:plugin-count-claims` guards this header count)
54
+ ## Skills (19 — `skills/` on disk is the roster; `check:plugin-count-claims` guards this header count)
53
55
 
54
56
  | Skill | Species | Job |
55
57
  |---|---|---|
@@ -61,6 +63,7 @@ scripts) — it does not add an npm-sourced marketplace entry.
61
63
  | `screen-composition` | procedural | compose screens from the catalog; consumes the Domain Plan for PRD/spec input (§SpecToUi walked planning-side, gh#1207) |
62
64
  | `shell-selection` | procedural | shell-family selection + per-shell contracts |
63
65
  | `wave-coordination` | procedural | team-wave mechanics — parallel-branch integration, PR/conflict rules, shared-file ownership, wave sizing; preloaded by ui-architect |
66
+ | `ui-architect` | procedural | portable (runtime-neutral) whole-deliverable coordination workflow — gear selection, wave decomposition, generator-≠-reviewer separation, the four-lap cap, failure routing, the closing VerifyProof; the Claude-only dispatch mechanics live in `agents/ui-architect.md` + `wave-coordination` instead |
64
67
  | `data-wiring` | knowledge | data/state patterns (signals, controllers, DataClient, hybrid) |
65
68
  | `chart-selection` | knowledge | which component renders a chart/graph/sparkline/gauge/heatmap, and data/legend/theming wiring |
66
69
  | `table-composition` | knowledge | `table-ui` usage — chrome, striping, sort/resize, inline-edit-grid, tree rows |
@@ -98,7 +101,7 @@ surface-qa-agent) · `component-model.md` · the `references/contracts/` twins.
98
101
 
99
102
  ## MCP
100
103
 
101
- `adia-gen-ui` server pinned: `@adia-ai/mcp@0.8.51` (tool SoT:
104
+ `adia-gen-ui` server pinned: `@adia-ai/mcp@0.8.53` (tool SoT:
102
105
  `packages/gen-ui/mcp/TOOLS.md` — the `gen-ui` section; stability rule in
103
106
  `references/contracts/`; pin lives in `.mcp.json` — `check:plugin-count-claims`
104
107
  guards this README copy against it). The server is `adia-mcp gen-ui`,
@@ -120,13 +123,51 @@ wiring the `adia-gen-ui` MCP server.
120
123
  - **Codex** (gh#1888): `.codex-plugin/plugin.json` + per-skill
121
124
  `agents/openai.yaml`, derived; `mcpServers` points at the same
122
125
  `.mcp.json` Claude Code uses. No manifest key for hooks/commands/agents —
123
- a command degrades to invoking its skill directly.
126
+ a command degrades to invoking its skill directly, and `ui-architect`'s
127
+ whole-deliverable coordination — gear selection, wave decomposition,
128
+ generator-≠-reviewer separation, the four-lap cap, failure routing, the
129
+ closing `VerifyProof` — ships as its own portable `ui-architect` skill
130
+ instead of a Claude subagent (ADR-0091): invoke it by name ("use the
131
+ `ui-architect` skill…"); it runs planning/composition/QA as isolated
132
+ dispatches where the installed Codex runtime supports that, or serially in
133
+ one context otherwise. Install via npm (see the `adia-plugins` package
134
+ README) or straight from Git, for anyone with repo access:
135
+ ```
136
+ codex plugin marketplace add adiahealth/gen-ui-kit --ref main
137
+ codex plugin add adia-ui-kit-factory@adia-ui-kit
138
+ codex plugin add adia-ui-kit-forge@adia-ui-kit
139
+ ```
140
+ Sourced from the repo-root `.agents/plugins/marketplace.json` — the
141
+ Codex-side sibling of this repo's own in-repo Claude channel
142
+ (`.claude-plugin/marketplace.json`), hand-authored the same way, never
143
+ generated (ADR-0091 D2/D3). Both plugins register under the manifest's own
144
+ name, `adia-ui-kit` — not the repo name, and a distinct marketplace name
145
+ from the npm-sourced channel above it (`adia-plugins`), mirroring the
146
+ existing Claude-side split exactly, kept deliberately parallel rather
147
+ than merged.
124
148
  - **Hermes** (gh#1954): `plugin.yaml` + `__init__.py` at this package's
125
149
  root — point Hermes's plugin loading at the installed
126
150
  `node_modules/@adia-ai/adia-ui-factory` directory. **MCP is user-level
127
151
  config in Hermes, not a plugin field** — merge the generated
128
152
  `hermes-mcp.yaml` into your own `~/.hermes/config.yaml` by hand; there is
129
- no automatic wiring. No hooks/agents surface.
153
+ no automatic wiring. `__init__.py` registers the portable `ui-architect`
154
+ skill (ADR-0091) alongside every other skill, **and** a generated
155
+ `/ui-architect` command — verified against the installed Hermes Agent's
156
+ real `PluginContext.register_command` + `dispatch_tool` and
157
+ `tools/delegate_tool.py`'s `delegate_task(goal, context, role, …)` (not
158
+ assumed from documentation): each of planning, composition, and QA runs
159
+ as its own isolated `delegate_task` child (`role="leaf"`), gated by the
160
+ same `OrientationRecord`/`BuildResult`/`VerifyProof` linters every other
161
+ runtime calls, capped at four plan→build→QA laps per wave (LLD §C6, SPEC
162
+ REQ-012/013). Generated by `scripts/build/harness-manifests.mjs` — never
163
+ hand-edit `__init__.py`. Verified live via `hermes plugins doctor --ci`
164
+ (clean registration against the real `PluginContext`) plus a Python-level
165
+ selftest (`check:harness-manifests-selftest`) driving the actual
166
+ generated orchestration logic against a scripted `delegate_task` double
167
+ and the real linters; a live interactive `/ui-architect` dispatch was not
168
+ completed (sandboxed dev environment blocks mutating Hermes's global
169
+ plugin-enable state) — the mechanism itself is real and independently
170
+ verified, not simulated. No hooks/agents surface otherwise.
130
171
  - **Pi** (gh#1954): discovered via the `pi-package` keyword and the `"pi"`
131
172
  field in `package.json`; `prompts/*.md` gives every `/command` a genuine
132
173
  Pi prompt template. **Pi core has no native MCP support at all** — the
@@ -134,8 +175,23 @@ wiring the `adia-gen-ui` MCP server.
134
175
  [`pi-mcp-adapter`](https://pi.dev) package, which reads this plugin's
135
176
  `.mcp.json` unmodified (same shape, no substitution needed). Install that
136
177
  adapter first; without it, this plugin's headline feature — the a2ui MCP
137
- wiring — silently does nothing on a stock Pi install. No hooks/agents
138
- surface.
178
+ wiring — silently does nothing on a stock Pi install. The portable
179
+ `ui-architect` skill (ADR-0091) is discoverable via the `"pi": {"skills":
180
+ [...]}` glob like every other skill, **and** `pi/ui-architect.extension.ts`
181
+ (`"pi": {"extensions": [...]}`) registers a real `/ui-architect` command:
182
+ verified against the installed `@earendil-works/pi-coding-agent` v0.84.3 —
183
+ its own shipped (if not core-builtin) `examples/extensions/subagent/`
184
+ proves the pattern this extension reuses — dispatching planning,
185
+ composition, and QA as isolated nested `pi --mode json -p --no-session`
186
+ child processes, each tool-walled to mirror this plugin's Claude seats
187
+ (`read,grep,find,ls,bash` for planning/QA; add `edit,write` only for
188
+ composition) and gated by the same three portable-contract linters
189
+ (LLD §C5, SPEC REQ-011). Live-verified end to end in this change's own
190
+ development — a real `pi -e ./pi/ui-architect.extension.ts` session ran
191
+ `/ui-architect` against a live model, dispatched all three isolated
192
+ passes, wrote a real file, took a real screenshot, and returned a clean
193
+ `VerifyProof` — not simulated against an unverified API. No hooks/agents
194
+ surface otherwise.
139
195
 
140
196
  ## Requirements
141
197
 
package/__init__.py CHANGED
@@ -1,97 +1,333 @@
1
1
  """adia-ui-kit-factory — generated by scripts/build/harness-manifests.mjs. Do not edit."""
2
2
  import os
3
+ from pathlib import Path
3
4
 
4
5
  _HERE = os.path.dirname(os.path.abspath(__file__))
5
6
 
7
+ # --- ui-architect (ADR-0091 C6) --------------------------------------------
8
+ # Generated /ui-architect Hermes command, implementing the portable ui-architect
9
+ # workflow (skills/ui-architect/SKILL.md) through delegate_task. Never
10
+ # hand-edit this block — regenerate via scripts/build/harness-manifests.mjs.
11
+ # The skill body is read from disk at COMMAND-INVOCATION time, never copied
12
+ # into this file, so SKILL.md stays the single authored copy (SPEC REQ-001).
13
+ import json as _ui_architect_json
14
+ import re as _ui_architect_re
15
+ import subprocess as _ui_architect_subprocess
16
+
17
+ _UI_ARCHITECT_MAX_LAPS = 4
18
+
19
+ _UI_ARCHITECT_ORIENTATION_TEMPLATE = "\n".join([
20
+ "Rendering mode: SPA | SSR | hybrid — signal: <file/dep/marker or words>",
21
+ "Project shape: single-surface | rollup | shared-foundation — signal: <…>",
22
+ "Shell: admin | chat | editor | simple | embed | none — signal: <…>",
23
+ "Task: <task> — signal: <the request>",
24
+ "→ Route: <skill(s)>, per the task table",
25
+ "Verify target: <what the executor produces to call this done>",
26
+ "Open questions: <unresolved axes, each with its named fallback; blank if none>",
27
+ ])
28
+
29
+ _UI_ARCHITECT_BUILD_TEMPLATE = "\n".join([
30
+ "BuildResult",
31
+ "surface: <screen/task identifier>",
32
+ "orientationRef: <the OrientationRecord this build executed>",
33
+ "filesChanged: <files written/edited, one per line or comma-list>",
34
+ "gatesRun: <builder self-checks and their pass/fail — adia-lint, validate_schema, check_anti_patterns, etc.>",
35
+ "evidence: <what was run/observed — probe output paths, screenshots, validator logs>",
36
+ "selfCheck: pass | fail | UNMEASURED — <reason> (NEVER a ship/hold verdict)",
37
+ 'openIssues: <unresolved items, or "none">',
38
+ ])
39
+
40
+ _UI_ARCHITECT_VERIFY_TEMPLATE = "\n".join([
41
+ "url: <the probed page>",
42
+ "consoleErrors: pass | fail | UNMEASURED — <each error verbatim; empty = pass; UNMEASURED carries the reason>",
43
+ "boundingBoxes: pass | fail — <selector: w×h per key element; any 0×0 = fail>",
44
+ "screenshot: <path> @ deviceScaleFactor 2",
45
+ 'imageRead: <what the pixels actually show — REQUIRED prose, never "looks fine">',
46
+ "perf: <navigation timing>ms vs <budget>ms — ADVISORY, never gates | UNMEASURED — <reason>",
47
+ "a11y: pass | fail | UNMEASURED — region role/label · overlays · heading roles · keyboard path · AA contrast",
48
+ "structure: adia-lint clean on every written file | <findings>",
49
+ "verdict: ship | hold — <one line naming the blocker if hold>",
50
+ ])
51
+
52
+ def _ui_architect_skill_body():
53
+ path = os.path.join(_HERE, "skills", "ui-architect", "SKILL.md")
54
+ with open(path, "r", encoding="utf-8") as f:
55
+ raw = f.read()
56
+ m = _ui_architect_re.match(r"^---\n[\s\S]*?\n---\n?([\s\S]*)$", raw)
57
+ return (m.group(1) if m else raw).strip()
58
+
59
+ def _ui_architect_run_linter(linter_name, text):
60
+ linter_path = os.path.join(_HERE, "scripts", linter_name)
61
+ proc = _ui_architect_subprocess.run([linter_path, "-"], input=text, capture_output=True, text=True)
62
+ return proc.returncode == 0, (proc.stdout + proc.stderr).strip()
63
+
64
+ def _ui_architect_delegate(ctx, goal, context_text):
65
+ raw = ctx.dispatch_tool("delegate_task", {"goal": goal, "context": context_text, "role": "leaf"})
66
+ try:
67
+ data = _ui_architect_json.loads(raw)
68
+ except Exception:
69
+ return "", "error"
70
+ results = data.get("results") or []
71
+ if not results:
72
+ return "", "error"
73
+ return results[0].get("summary") or "", results[0].get("status") or "error"
74
+
75
+ def _ui_architect_parse_gear(text):
76
+ m = _ui_architect_re.search(r"^Gear:\s*([12])", text, _ui_architect_re.M | _ui_architect_re.I)
77
+ return 2 if m and m.group(1) == "2" else 1
78
+
79
+ def _ui_architect_parse_waves(text):
80
+ m = _ui_architect_re.search(r"^##\s*Waves\s*\n(.*?)(?:\n##|\n---|\Z)", text, _ui_architect_re.M | _ui_architect_re.S | _ui_architect_re.I)
81
+ if not m:
82
+ return ["the deliverable"]
83
+ waves = [_ui_architect_re.sub(r"^[-*]\s*", "", w).strip() for w in m.group(1).splitlines()]
84
+ waves = [w for w in waves if w]
85
+ return waves or ["the deliverable"]
86
+
87
+ def _ui_architect_parse_verdict(text):
88
+ m = _ui_architect_re.search(r"^verdict:\s*(ship|hold)\b", text, _ui_architect_re.M | _ui_architect_re.I)
89
+ return m.group(1).lower() if m else "unknown"
90
+
91
+ def _ui_architect_planning_prompt(skill_body):
92
+ parts = [
93
+ skill_body,
94
+ "---",
95
+ "You are running the PLANNING pass of the ui-architect workflow above, in an isolated context",
96
+ "(a separate delegate_task child with no memory of any other pass). Apply Gear selection to",
97
+ "the goal given to you.",
98
+ "",
99
+ "Your final answer MUST start with a line 'Gear: 1' or 'Gear: 2', followed by exactly one",
100
+ "app-planning OrientationRecord in EXACTLY this flat label: value line shape — no markdown",
101
+ "headings, no table, no extra prose around it:",
102
+ "",
103
+ _UI_ARCHITECT_ORIENTATION_TEMPLATE,
104
+ "",
105
+ "If Gear 2 (a PRD/multi-surface brief), also include a '## Waves' heading with one",
106
+ "'- <surface name>' line per surface — omit that heading entirely for Gear 1. Emit nothing else.",
107
+ ]
108
+ return "\n".join(parts)
109
+
110
+ def _ui_architect_composition_prompt(skill_body):
111
+ parts = [
112
+ skill_body,
113
+ "---",
114
+ "You are running the COMPOSITION pass of the ui-architect workflow above, in an isolated",
115
+ "context, downstream of an OrientationRecord given to you as INPUT SIGNAL below — re-derive",
116
+ "from it, never adopt it uninspected. Build the surface it describes.",
117
+ "",
118
+ "Your final answer MUST be exactly one BuildResult record in EXACTLY this flat label: value",
119
+ "line shape — no markdown headings, no table, no extra prose around it — never a ship/hold",
120
+ "verdict, that field is VerifyProof-exclusive:",
121
+ "",
122
+ _UI_ARCHITECT_BUILD_TEMPLATE,
123
+ "",
124
+ "Emit nothing else.",
125
+ ]
126
+ return "\n".join(parts)
127
+
128
+ def _ui_architect_qa_prompt(skill_body):
129
+ parts = [
130
+ skill_body,
131
+ "---",
132
+ "You are running the QA pass of the ui-architect workflow above, in an isolated context,",
133
+ "independent of and downstream from the BuildResult given to you as INPUT SIGNAL below. You",
134
+ "did not write this code — verify it fresh; never self-certify the builder's own report.",
135
+ "",
136
+ "Your final answer MUST be exactly one VerifyProof record in EXACTLY this flat label: value",
137
+ "line shape — no markdown headings, no table, no extra prose around it, carrying the",
138
+ "ship/hold verdict:",
139
+ "",
140
+ _UI_ARCHITECT_VERIFY_TEMPLATE,
141
+ "",
142
+ "Emit nothing else.",
143
+ ]
144
+ return "\n".join(parts)
145
+
146
+ def _ui_architect_run_wave(ctx, skill_body, top_orientation, wave, max_laps, log):
147
+ findings = []
148
+ last_verify = ""
149
+ last_build = ""
150
+ lap = 0
151
+ while lap < max_laps:
152
+ lap += 1
153
+ log(" [%s] lap %d/%d — planning" % (wave, lap, max_laps))
154
+ if lap == 1:
155
+ plan_task = "Wave: %s\n\nTop-level OrientationRecord (INPUT SIGNAL):\n%s" % (wave, top_orientation)
156
+ else:
157
+ plan_task = "Wave: %s\n\nPrior VerifyProof findings to re-plan against (INPUT SIGNAL):\n%s" % (wave, last_verify)
158
+ plan_out, _plan_status = _ui_architect_delegate(ctx, plan_task, _ui_architect_planning_prompt(skill_body))
159
+ ok, lint_out = _ui_architect_run_linter("record-lint", plan_out)
160
+ if not ok:
161
+ findings.append("[%s] lap %d planning record-lint findings:\n%s" % (wave, lap, lint_out))
162
+
163
+ log(" [%s] lap %d — composition" % (wave, lap))
164
+ build_task = "Wave: %s\n\nOrientationRecord (INPUT SIGNAL):\n%s" % (wave, plan_out)
165
+ build_out, _build_status = _ui_architect_delegate(ctx, build_task, _ui_architect_composition_prompt(skill_body))
166
+ last_build = build_out
167
+ ok, lint_out = _ui_architect_run_linter("build-result-lint", build_out)
168
+ if not ok:
169
+ findings.append("[%s] lap %d build-result-lint findings:\n%s" % (wave, lap, lint_out))
170
+
171
+ log(" [%s] lap %d — QA" % (wave, lap))
172
+ verify_task = "Wave: %s\n\nBuildResult (INPUT SIGNAL):\n%s" % (wave, build_out)
173
+ verify_out, _verify_status = _ui_architect_delegate(ctx, verify_task, _ui_architect_qa_prompt(skill_body))
174
+ last_verify = verify_out
175
+ ok, lint_out = _ui_architect_run_linter("verify-proof-lint", verify_out)
176
+ if not ok:
177
+ findings.append("[%s] lap %d verify-proof-lint findings:\n%s" % (wave, lap, lint_out))
178
+
179
+ verdict = _ui_architect_parse_verdict(verify_out)
180
+ if max_laps == 1:
181
+ return {"wave": wave, "laps": lap, "verdict": verdict, "verifyProof": last_verify, "buildResult": last_build, "findings": findings}
182
+ if verdict == "ship":
183
+ return {"wave": wave, "laps": lap, "verdict": verdict, "verifyProof": last_verify, "buildResult": last_build, "findings": findings}
184
+ if lap >= max_laps:
185
+ findings.append("[%s] hit the %d-lap cap without a ship verdict — escalating, not re-running blind." % (wave, max_laps))
186
+ return {"wave": wave, "laps": lap, "verdict": "escalated", "verifyProof": last_verify, "buildResult": last_build, "findings": findings}
187
+ return {"wave": wave, "laps": max_laps, "verdict": "escalated", "verifyProof": last_verify, "buildResult": last_build, "findings": findings}
188
+
189
+ def _ui_architect_command(raw_args, ctx):
190
+ brief = (raw_args or "").strip()
191
+ lines_out = []
192
+ log = lambda line: lines_out.append(line)
193
+ if not brief:
194
+ return "ui-architect: no brief given. Usage: /ui-architect <brief, or a path to a PRD>"
195
+ if not hasattr(ctx, "dispatch_tool"):
196
+ return "ui-architect: no delegate_task-capable ctx available — cannot dispatch isolated passes."
197
+ skill_body = _ui_architect_skill_body()
198
+ log("ui-architect: dispatching an isolated PLANNING pass to size the brief (Gear selection)...")
199
+ initial_out, _status = _ui_architect_delegate(ctx, brief, _ui_architect_planning_prompt(skill_body))
200
+ ok, lint_out = _ui_architect_run_linter("record-lint", initial_out)
201
+ gear = _ui_architect_parse_gear(initial_out)
202
+ waves = _ui_architect_parse_waves(initial_out) if gear == 2 else ["the deliverable"]
203
+ max_laps = _UI_ARCHITECT_MAX_LAPS if gear == 2 else 1
204
+ log("ui-architect: GEAR %d — %d wave(s): %s" % (gear, len(waves), ", ".join(waves)))
205
+ if not ok:
206
+ log("ui-architect: initial OrientationRecord failed record-lint:\n%s" % lint_out)
207
+ outcomes = [_ui_architect_run_wave(ctx, skill_body, initial_out, wave, max_laps, log) for wave in waves]
208
+ shipped = sum(1 for o in outcomes if o["verdict"] == "ship")
209
+ escalated = [o for o in outcomes if o["verdict"] == "escalated"]
210
+ header = "ui-architect — GEAR %d — %d/%d wave(s) shipped" % (gear, shipped, len(outcomes))
211
+ if escalated:
212
+ header += ", %d escalated" % len(escalated)
213
+ lines_out.append(header)
214
+ lines_out.append("")
215
+ for o in outcomes:
216
+ lap_word = "lap" if o["laps"] == 1 else "laps"
217
+ lines_out.append("## %s — %s (%d %s)" % (o["wave"], o["verdict"], o["laps"], lap_word))
218
+ lines_out.append(o["verifyProof"] or "(no VerifyProof produced)")
219
+ if o["findings"]:
220
+ lines_out.append("Findings:")
221
+ for f in o["findings"]:
222
+ lines_out.append(" - %s" % f)
223
+ lines_out.append("")
224
+ return "\n".join(lines_out)
225
+
226
+ def _ui_architect_command_handler(_ctx_closure):
227
+ def _handler(raw_args):
228
+ return _ui_architect_command(raw_args, _ctx_closure)
229
+ return _handler
230
+
6
231
 
7
232
  def register(ctx):
8
233
  ctx.register_skill(
9
234
  name="app-audit",
10
- path=os.path.join(_HERE, "skills", "app-audit", "SKILL.md"),
235
+ path=Path(os.path.join(_HERE, "skills", "app-audit", "SKILL.md")),
11
236
  description="Diagnose an EXISTING AdiaUI consumer codebase before touching it — recon, classify manifest/version/spec/capability drift, root-cause a DRIFT-caused wrong-output symptom via four-layer triage (skill/codebase/substrate/spec) instead of patching the surface. Use for \"audit this UI\", \"is this up to date\", \"what should we migrate\", \"doesn't match the reference\", \"looks unstyled/off-brand\", or a suspected-drifted @adia-ai/* consumer's version health check. Produces a ranked, evidence-cited report; hands CONFIRMED sweeps to a builder. NOT greenfield planning (app-planning) or confirmed migration (app-migration); NOT a live blank-screen/console-error check (surface-qa) or chart-specific render behavior (chart-selection); NOT a duplication review (project-scaffolding).",
12
237
  )
13
238
  ctx.register_skill(
14
239
  name="app-migration",
15
- path=os.path.join(_HERE, "skills", "app-migration", "SKILL.md"),
240
+ path=Path(os.path.join(_HERE, "skills", "app-migration", "SKILL.md")),
16
241
  description="Migrates a consumer adia-ui app. Use when asked to \"migrate to v0.X\", \"upgrade @adia-ai/web-components\", \"fix breaking changes\", \"sweep deprecated attributes\", port @agent-ui-kit/foreign code, or change SPA↔SSR mode — grep audit, mechanical sweep, verify gates. NOT for authoring the MIGRATION GUIDE or cutting releases (package-release, forge plugin).",
17
242
  )
18
243
  ctx.register_skill(
19
244
  name="app-planning",
20
- path=os.path.join(_HERE, "skills", "app-planning", "SKILL.md"),
245
+ path=Path(os.path.join(_HERE, "skills", "app-planning", "SKILL.md")),
21
246
  description="Cold-start router for adia-ui consumer-app work. Use when the user asks to \"build/start an adia-ui app\", \"add a surface\", \"orient in this repo\" — run FIRST, also on any task whose mode/shape/shell is undecided — classifies four axes on cited signals, emits an Orientation Record, routes onward. NOT for framework-monorepo internals (adia-ui-forge).",
22
247
  )
23
248
  ctx.register_skill(
24
249
  name="chart-selection",
25
- path=os.path.join(_HERE, "skills", "chart-selection", "SKILL.md"),
250
+ path=Path(os.path.join(_HERE, "skills", "chart-selection", "SKILL.md")),
26
251
  description="Which adia-ui component renders a chart, graph, sparkline, gauge, or heatmap, and how to wire its data/legend/theming. Use for \"add a chart\", \"show a graph\", \"visualize this data\", \"add a sparkline/gauge/heatmap\", or \"wire a chart legend\". NOT for the surrounding screen (screen-composition), color tokens (token-selection), or data fetching (data-wiring).",
27
252
  )
28
253
  ctx.register_skill(
29
254
  name="data-wiring",
30
- path=os.path.join(_HERE, "skills", "data-wiring", "SKILL.md"),
255
+ path=Path(os.path.join(_HERE, "skills", "data-wiring", "SKILL.md")),
31
256
  description="Data, state & hydration in an adia-ui app. Use for \"wire up the data\" / \"manage state\" / \"hydrate the island\": pattern choice (signals · shared app store · Service/Command · DataClient · property-API · data-*), hydration per mode (SPA/SSR/hybrid). NOT for host bootstrap/registration (host-wiring) or screen markup (screen-composition).",
32
257
  )
33
258
  ctx.register_skill(
34
259
  name="domain-planning",
35
- path=os.path.join(_HERE, "skills", "domain-planning", "SKILL.md"),
260
+ path=Path(os.path.join(_HERE, "skills", "domain-planning", "SKILL.md")),
36
261
  description="Reasons through a PRD, spec, mockup, schema, or role/user-story BEFORE any UI is chosen — resolves intent, domain entities, roles, tasks, and decisions, then scores a semantic wireframe against the gate dimensions. Use when the input is spec-shaped (not a signed-off wireframe) and no Domain Plan exists yet for it. Preloaded by app-planning-agent; produces the Orientation Record's Domain Plan block. NOT for composing the UI itself (screen-composition) or classifying rendering mode/shape/shell (app-planning).",
37
262
  )
38
263
  ctx.register_skill(
39
264
  name="find-unused",
40
- path=os.path.join(_HERE, "skills", "find-unused", "SKILL.md"),
265
+ path=Path(os.path.join(_HERE, "skills", "find-unused", "SKILL.md")),
41
266
  description="Finds what a non-breaking @adia-ai upgrade shipped that this app never picked up — the opt-in layers nothing imports, the local workarounds a fixed bug made redundant, retired enum values still sitting in stored state, and the fixes that never reached MIGRATION.md. Use after \"we bumped and nothing broke\", \"what are we missing from 0.8.x\", \"are we actually using what we ship\", \"why is [scale]/[theme] doing nothing\", or when a PATCH span needs adoption rather than repair. NOT for breaking-change repair (app-migration); NOT for a first-time install (adia-scaffold); NOT for diagnosing a broken surface (app-audit).",
42
267
  )
43
268
  ctx.register_skill(
44
269
  name="gen-ui-wiring",
45
- path=os.path.join(_HERE, "skills", "gen-ui-wiring", "SKILL.md"),
270
+ path=Path(os.path.join(_HERE, "skills", "gen-ui-wiring", "SKILL.md")),
46
271
  description="Authors a runtime generative-UI experience on the a2ui runtime — mounts a2ui-root/gen-root, wires generate_ui + data resolvers, grounds generation in a corpus (harvest chunks from our own pages, point retrieval at it). Use when END USERS generate UI at runtime. NOT for one-shot 'generate UI for X' screen asks (screen-composition), chat features (llm-wiring), or pipeline/MCP tool ops (adia-forge's a2ui-maintenance).",
47
272
  )
48
273
  ctx.register_skill(
49
274
  name="host-wiring",
50
- path=os.path.join(_HERE, "skills", "host-wiring", "SKILL.md"),
275
+ path=Path(os.path.join(_HERE, "skills", "host-wiring", "SKILL.md")),
51
276
  description="Wires an adia-ui app's host — picks SPA/SSR/hybrid, then that mode's registration, CSS cascade, routing ownership. Use for \"wire the host\", \"set up index.html\", unstyled or non-upgrading elements, \"HTMLElement is not defined\" under Next/Nuxt/SvelteKit/Astro. NOT for screen UI (screen-composition), data/state (data-wiring), app layout (project-scaffolding).",
52
277
  )
53
278
  ctx.register_skill(
54
279
  name="llm-wiring",
55
- path=os.path.join(_HERE, "skills", "llm-wiring", "SKILL.md"),
280
+ path=Path(os.path.join(_HERE, "skills", "llm-wiring", "SKILL.md")),
56
281
  description="The @adia-ai/llm client contract — chat/streamChat streaming, StreamChunk, smart-proxy vs passthrough key security, chat-shell wiring. Use when adding a chat box or any LLM feature to an adia-ui app. NOT for runtime gen-UI (gen-ui-wiring), one-shot intent→screen composition (screen-composition), or @adia-ai/llm internals (adia-forge's llm-client-maintenance).",
57
282
  )
58
283
  ctx.register_skill(
59
284
  name="pattern-catalog",
60
- path=os.path.join(_HERE, "skills", "pattern-catalog", "SKILL.md"),
285
+ path=Path(os.path.join(_HERE, "skills", "pattern-catalog", "SKILL.md")),
61
286
  description="Index of adia-ui's pre-assembled surfaces — hundreds of patterns and template screens (auth, registration, onboarding, settings, dashboards) — see references/pattern-index.md for the current count. Use BEFORE composing any screen from primitives, or when asked \"is there an existing pattern/template for X\", \"start from a pattern\", \"what patterns exist\". Answers and points at source — NOT for composing new UI (screen-composition), shell chrome (shell-selection), or authoring new patterns (primitive-authoring).",
62
287
  )
63
288
  ctx.register_skill(
64
289
  name="project-scaffolding",
65
- path=os.path.join(_HERE, "skills", "project-scaffolding", "SKILL.md"),
290
+ path=Path(os.path.join(_HERE, "skills", "project-scaffolding", "SKILL.md")),
66
291
  description="Lays out an adia-ui app's on-disk structure — picks the project shape (single-surface / rollup / shared-foundation) and page form, scaffolds via scripts/adia-scaffold, gates on the structure rubric. Use for \"scaffold an app\", \"add a page/surface/component\" WHEN mode/shape are already decided, \"audit its structure\". If mode/shape/shell are still undecided, app-planning classifies first. NOT for the UI inside a screen (screen-composition) or host wiring (host-wiring).",
67
292
  )
68
293
  ctx.register_skill(
69
294
  name="screen-composition",
70
- path=os.path.join(_HERE, "skills", "screen-composition", "SKILL.md"),
295
+ path=Path(os.path.join(_HERE, "skills", "screen-composition", "SKILL.md")),
71
296
  description="Composes adia-ui screens from light-DOM catalog primitives — discovers tags/props via the a2ui MCP, themes via --a-* tokens. Use when asked to build or 'generate UI for' a screen, page, form, dashboard, or nav pattern, or when a PRD/spec/mockup needs UI. NOT for shell chrome (shell-selection), host wiring (host-wiring), runtime gen-UI (gen-ui-wiring).",
72
297
  )
73
298
  ctx.register_skill(
74
299
  name="shell-selection",
75
- path=os.path.join(_HERE, "skills", "shell-selection", "SKILL.md"),
300
+ path=Path(os.path.join(_HERE, "skills", "shell-selection", "SKILL.md")),
76
301
  description="Picks and composes an adia-ui page-chrome shell (@adia-ai/web-modules): admin (app frame), chat (LLM), editor (canvas+panes), simple (marketing/error), or embed. Use when asked to \"use a shell\", \"sidebar + topbar layout\", \"embed this surface\", or on shell markup debugging. NOT for screens inside it (screen-composition) or host/SSR wiring (host-wiring).",
77
302
  )
78
303
  ctx.register_skill(
79
304
  name="surface-qa",
80
- path=os.path.join(_HERE, "skills", "surface-qa", "SKILL.md"),
305
+ path=Path(os.path.join(_HERE, "skills", "surface-qa", "SKILL.md")),
81
306
  description="Browser-QA gate for a CONSUMER app's adia-ui surfaces — renders headless with zero console/page errors, non-zero bounding boxes, and a screenshot actually read, plus AdiaUI a11y checks. Use when shipping a surface, on \"verify/QA this page\" or a page that renders blank/empty, or when \"tests pass\" is the only evidence. NOT for composing/fixing UI (screen-composition), structural lint (adia-lint hook), or the framework repo's own dogfood/ demo-page sweep (forge's demo-audit).",
82
307
  )
83
308
  ctx.register_skill(
84
309
  name="table-composition",
85
- path=os.path.join(_HERE, "skills", "table-composition", "SKILL.md"),
310
+ path=Path(os.path.join(_HERE, "skills", "table-composition", "SKILL.md")),
86
311
  description="Answers how to display/organize tabular data with table-ui: contained vs uncontained chrome, [raw] (consumer-owned body, a separate axis), opt-in striped rows, and resize/sort defaults that flip between JS .columns and declarative col-def forms, plus inline-edit-grid and tree/hierarchical-row patterns. Use for \"how do I show a table\", \"add sorting/filtering\", \"make this table striped\", \"columns aren't resizable\", \"card vs bare table\", \"editable/spreadsheet grid\", \"tree table / nested rows\", \"huge list of rows\". NOT for composing the surrounding screen (screen-composition), data wiring (data-wiring), OTHER non-table patterns (pattern-catalog), or 1000+-row virtualized lists (list-window-ui).",
87
312
  )
88
313
  ctx.register_skill(
89
314
  name="token-selection",
90
- path=os.path.join(_HERE, "skills", "token-selection", "SKILL.md"),
315
+ path=Path(os.path.join(_HERE, "skills", "token-selection", "SKILL.md")),
91
316
  description="Answers which color token/role an adia-ui surface should use — role grammar, pairing laws, state families, the --a-* alias layer. Use when asked \"which token for this background/text/hover/border\" or \"--a-* or --md-sys-color-*\". ANSWERS only. NOT for composing the screen (screen-composition) or designing/verifying palettes (framework-side).",
92
317
  )
318
+ ctx.register_skill(
319
+ name="ui-architect",
320
+ path=Path(os.path.join(_HERE, "skills", "ui-architect", "SKILL.md")),
321
+ description="Whole-deliverable coordinator: runs the app-planning → screen-composition → surface-qa workflow for one entire multi-surface app or feature — gear selection, wave decomposition, generator-≠-reviewer separation, the four-lap plan/build/QA cap per wave, failure routing, and the closing VerifyProof. Portable across runtimes — permits native multi-agent delegation where the installed runtime provides it, and states the serial single-context fallback otherwise. Use on an end-to-end delivery ask: \"make an app that...\", \"coordinate everything in this PRD and open the PRs\", \"take this spec and ship the whole thing\". NOT for a lone classify/plan ask with nothing to deliver yet (app-planning keeps that territory entirely); NOT for one already-scoped screen (screen-composition); NOT for grading an existing surface (surface-qa).",
322
+ )
93
323
  ctx.register_skill(
94
324
  name="wave-coordination",
95
- path=os.path.join(_HERE, "skills", "wave-coordination", "SKILL.md"),
325
+ path=Path(os.path.join(_HERE, "skills", "wave-coordination", "SKILL.md")),
96
326
  description="Team-pattern mechanics for a multi-screen build under ui-architect's GEAR 2 — who merges parallel screen-composition-agent branches and when, PR/branch mechanics, shared-file conflict rules for shared-foundation shapes, and how to size a wave. Use when planning or running a GEAR-2 dispatch: \"how do two parallel screen-composition-agent branches come back together\", \"who merges\", \"how big should a wave be\", \"what happens when two screens touch the same shared file\". NOT for picking a project's shape (project-scaffolding, which this skill's conflict rules build on) or composing one screen (screen-composition).",
97
327
  )
328
+ ctx.register_command(
329
+ "ui-architect",
330
+ _ui_architect_command_handler(ctx),
331
+ description="Whole-deliverable coordinator: runs the app-planning → screen-composition → surface-qa workflow for one entire multi-surface app or feature — gear selection, wave decomposition, generator-≠-reviewer separation, the four-lap plan/build/QA cap per wave, failure routing, and the closing VerifyProof. Portable across runtimes — permits native multi-agent delegation where the installed runtime provides it, and states the serial single-context fallback otherwise. Use on an end-to-end delivery ask: \"make an app that...\", \"coordinate everything in this PRD and open the PRs\", \"take this spec and ship the whole thing\". NOT for a lone classify/plan ask with nothing to deliver yet (app-planning keeps that territory entirely); NOT for one already-scoped screen (screen-composition); NOT for grading an existing surface (surface-qa).",
332
+ args_hint="<brief, or a path to a PRD>",
333
+ )
@@ -12,10 +12,10 @@ skills:
12
12
  - domain-planning
13
13
  # Explicit pin (gh#618, tier corrected gh#1045): never `inherit` — the
14
14
  # caller's tier would silently decide what model produces the Orientation
15
- # Record. Planning seats sit on the ceiling ladder's fable+high row; the
16
- # executing screen-composition-agent deliberately runs a cheaper Coding-row tier.
17
- model: fable
18
- effort: high
15
+ # Record. Operator's explicit standing instruction for this seat family:
16
+ # sonnet + xhigh.
17
+ model: sonnet
18
+ effort: xhigh
19
19
  ---
20
20
 
21
21
  The app-planning-agent classifies before it plans — rendering mode, project
@@ -14,6 +14,7 @@ skills:
14
14
  # non-preloadable shape; end-user runtime gen-UI is its own charter's
15
15
  # territory, distinct from this seat's one-shot screen composition.
16
16
  model: sonnet
17
+ effort: xhigh
17
18
  ---
18
19
 
19
20
  The screen-composition-agent executes an Orientation Record (or a directly-scoped
@@ -10,11 +10,10 @@ tools: Read, Grep, Glob, Bash
10
10
  skills:
11
11
  - surface-qa
12
12
  # Explicit pin (gh#618, tier corrected gh#1045): a review/critic seat's
13
- # verdict must not depend on the caller's model tier — never `inherit`,
14
- # never below fable (Review row of the ceiling ladder). Deliberately ABOVE
15
- # screen-composition-agent's Coding-row tier: the critic outranks the maker.
16
- model: fable
17
- effort: high
13
+ # verdict must not depend on the caller's model tier — never `inherit`.
14
+ # Operator's explicit standing instruction for this seat family: sonnet + xhigh.
15
+ model: sonnet
16
+ effort: xhigh
18
17
  ---
19
18
 
20
19
  The surface-qa-agent grades work it did not build — the reviewer seat that