# adia-ui-kit-factory

Consumer plugin for building apps ON the adia-ui (`@adia-ai`) framework —
orient, scaffold, compose, wire, verify, migrate, across SPA/SSR/hybrid
hosts. Maintainer counterpart: **adia-ui-kit-forge**.

Re-engineered 2026-07 for the Fable 5 harness (design:
`.claude/docs/specs/plugin-estate-v2.md`).

## Install

**Via Git** (Claude Code plugin marketplace — requires access to this repo,
which is private):

```
/plugin marketplace add adiahealth/gen-ui-kit
/plugin install adia-ui-kit-factory@adia-ui-kit
```

(`adia-ui-kit-factory@adia-ui-kit`, not `@gen-ui-kit` — the marketplace registers under
its own manifest name, `adia-ui-kit` (`.claude-plugin/marketplace.json`), not the repo name.)

Sources the in-repo manifest at `.claude-plugin/marketplace.json` (repo
root — `claude plugin marketplace add` always expects the manifest there,
with or without `--sparse`; it does not resolve a subdirectory manifest).
The LIVE public install path (ADR-0045's npm-sourced manifest,
`@adia-ai/adia-plugins`, live since v0.1.0 — gh#1160; see
`packages/plugins/adia-plugins/README.md` for its own current version, which
cuts on its own Class-B schedule independent of the framework lockstep):

```
/plugin marketplace add https://unpkg.com/@adia-ai/adia-plugins/marketplace.json
/plugin install adia-ui-kit-factory@adia-plugins
```

The manifest's entries are unpinned npm sources, so installs track each
plugin's published `latest` — no manifest republish per plugin cut.

**Via npm** (package content only):

```
npm install @adia-ai/adia-ui-factory
```

Published in lockstep with the framework packages — pulls this plugin's
`skills`/`agents`/`commands`/`references`/`hooks` into
`node_modules/@adia-ai/adia-ui-factory` for vendoring or inspection. This
does **not** by itself register the plugin with Claude Code's `/plugin`
system — no marketplace manifest currently sources it from npm. The bare-repo
posture (ADR-0040) is about this plugin working standalone in any consumer
repo once installed some other way (no monorepo path assumptions in its own
scripts) — it does not add an npm-sourced marketplace entry.

## Skills (20 — `skills/` on disk is the roster; `check:plugin-count-claims` guards this header count)

| Skill | Species | Job |
|---|---|---|
| `app-planning` | procedural | classify a GREENFIELD app (mode/shape/shell/state) → Orientation Record |
| `domain-planning` | procedural | reasoning ladder (intent/domain/roles/tasks/decisions → scored wireframe) → Domain Plan block; preloaded by app-planning-agent |
| `app-audit` | procedural | diagnose an EXISTING/brownfield consumer repo — recon, gap classes, root-cause triage → ranked report |
| `project-scaffolding` | procedural | project shapes + scaffolding decisions |
| `pattern-catalog` | knowledge | index of pre-assembled patterns/template screens — check BEFORE composing from primitives |
| `screen-composition` | procedural | compose screens from the catalog; consumes the Domain Plan for PRD/spec input (§SpecToUi walked planning-side, gh#1207) |
| `shell-selection` | procedural | shell-family selection + per-shell contracts |
| `wave-coordination` | procedural | team-wave mechanics — parallel-branch integration, PR/conflict rules, shared-file ownership, wave sizing; preloaded by ui-architect |
| `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 |
| `data-wiring` | knowledge | data/state patterns (signals, controllers, DataClient, hybrid) |
| `chart-selection` | knowledge | which component renders a chart/graph/sparkline/gauge/heatmap, and data/legend/theming wiring |
| `table-composition` | knowledge | `table-ui` usage — chrome, striping, sort/resize, inline-edit-grid, tree rows |
| `host-wiring` | procedural | wire the host — SPA / SSR / hybrid; icons + module barrels + CDN |
| `gen-ui-wiring` | procedural | generative-UI experiences on the a2ui runtime + MCP; agentic-UX patterns |
| `llm-wiring` | knowledge | consuming `@adia-ai/llm` (client, streaming, proxy security) |
| `token-selection` | knowledge | which color token/role — the `--a-*` / `--md-sys-color-*` layer choice |
| `app-migration` | procedural | version upgrades + port-to-adia sweeps (breaking changes) |
| `find-unused` | procedural | what a *non*-breaking upgrade left unused — inert opt-in layers, stale workarounds |
| `surface-qa` | procedural | browser + a11y + composition exit gate |
| `theme-audit` | procedural | classify a consumer theme.css against the framework's own defaults/tokens/primitives — restated defaults, re-derived tokens, dead selectors, hand-built component work; audit + report only, no rewrites (gh#2257) |

## Agents (4)

`ui-architect` (whole-deliverable coordinator — GEAR 1 novice one-shot through
GEAR 2 PRD-driven decompose/plan/iterate; dispatches the other three, never
duplicates their charters, holds no Write/Edit) · `app-planning-agent` (read-only
planner → Orientation Record) · `screen-composition-agent` (builder; holds the
validate-before-serialize trust gate on generated A2UI) · `surface-qa-agent`
(read-only QA/composition-quality seat; VerifyProof + the COMPOSE/REALIZE
rubric).

## Plugin-root references (composed against by multiple skills)

`agentic-ux-patterns.md` (the six agent-surface UX patterns → gen-ui-wiring) ·
`composed-surface-rubric.md` (the two-axis COMPOSE/REALIZE quality rubric →
surface-qa-agent) · `component-model.md` · the `references/contracts/` twins.

## Hooks

`adia-lint` — PostToolUse advisory consumer-smell repair loop (selftest ships).

`lab-posture-guard` — PreToolUse (`Write|Edit`) blocking guard, active only
in lab posture (see Posture, below); a no-op in bare-repo/absent posture,
leaving `adia-lint`'s existing advisory contract completely unchanged. A
`SessionStart` hook warms its manifest cache once per session; selftest
ships (`npm run check:lab-posture-guard-selftest`).

## Posture

Two postures a consumer repo can declare (ADR-0108, LLD-0019 §C3): **bare-repo**
(ADR-0040, the default — this plugin working standalone with no monorepo
path assumptions) and **lab** (ADR-0108 — a repo that installs
`@adia-ai/web-components` at root, pinned, and scaffolds prototypes that
consume those root packages directly as app source). Posture is an
EXPLICIT marker, never inferred from `package.json` or `node_modules` —
that inventory-only question ("what's installed") is `adia-info`'s
separate probe, not a declared-intent signal.

A repo-root `.adia` JSON file declares posture:

```json
{
  "posture": "lab",
  "kit": { "web-components": "9.9.9" }
}
```

- `posture` — `"lab"` or `"bare-repo"`, required when the file exists. An
  explicit `"bare-repo"` value is accepted alongside the file's plain
  absence — both read as bare-repo behavior — so a repo can affirmatively
  opt OUT of lab posture in its own marker rather than relying on absence
  alone.
- `kit` — optional. When present, `kit["web-components"]` records the
  installed `@adia-ai/web-components` version the marker was written
  against — informational only; nothing in this plugin reads it yet.
- Missing file, unparseable JSON, or a missing/unrecognized `posture`
  value all resolve to bare-repo behavior — the permissive branch. A
  corrupt marker must never accidentally turn ON the stricter lab-posture
  enforcement below.

`scripts/adia-posture.mjs` (`readPosture(repoRoot)` → `'lab' | 'bare-repo' |
'absent'`) is the one reader both `lab-posture-guard` and a future
`scaffold_prototype` (LLD-0019 §C6, not shipped yet) key on.

**In lab posture**, `lab-posture-guard` validates every `Write`/`Edit`
against the installed manifest's tags, attributes, and declared enum
values (`UNKNOWN-TAG`/`UNKNOWN-ATTR`/`ENUM-VALUE`/`INTERNAL-TIER`) and
DENIES a violating write before it lands, naming the offending
tag/attr/value and the manifest's allowed set in the deny reason so the
agent can self-correct. **In bare-repo or absent posture**, this hook is a
complete no-op — `adia-lint`'s existing `PostToolUse` advisory contract is
the only signal, unchanged.

## Commands

`/project-scaffolding` · `/app-planning` · `/surface-qa` · `/app-migration` · `/find-unused` · `/gen-ui-wiring` · `/app-audit` · `/theme-audit`

## Scripts

Consumer-runnable checks shipped as plain Node scripts under `scripts/`, no
MCP server required: `adia-probe.mjs` (the browser-QA gate; see
`surface-qa`), `adia-preflight.mjs` (every CI-step prerequisite, each
failure named with its remedy), `adia-contract-check.mjs` (authored markup
attributes vs. the shipped component contracts), and `adia-theme-audit.mjs`
(the theme.css redundancy classifier; see `theme-audit`).

## MCP

`adia-gen-ui` server pinned: `@adia-ai/mcp@0.8.66` (tool SoT:
`packages/gen-ui/mcp/TOOLS.md` — the `gen-ui` section; stability rule in
`references/contracts/`; pin lives in `.mcp.json` — `check:plugin-count-claims`
guards this README copy against it). The server is `adia-mcp gen-ui`,
published as `@adia-ai/mcp` (gh#1240 folded the ADR-0048 P2 rename target
`@adia-ai/gen-ui-mcp` and P4's `@adia-ai/a2ui-protocol-mcp` into one package
before either ever published; the pin name and the `mcpServers` key flipped
in the P7 release commit per the 2026-08-14 config-key ruling — new entries
for the other subcommands use `adia-protocol` / `adia-factory`).

## Non-Claude harnesses

External install beyond Claude Code (AGENTS.md's own "Non-Claude harnesses"
section carries the shared degradation table this summarizes). **The MCP
story is the one place neither non-Claude harness matches Claude Code's
plug-and-play install** — read this section before promising Pi/Hermes
support for this plugin specifically, since its whole value proposition is
wiring the `adia-gen-ui` MCP server.

`theme-audit`'s `adia-theme-audit` bin runs identically on every harness — a
plain Node script with no Claude-specific runtime dependency; the
`/theme-audit` command degrades to invoking the skill directly on Codex and
Hermes, and becomes a genuine `prompts/theme-audit.md` Pi command via
`build:harness-manifests`.

**Lab posture's write-time enforcement (ADR-0108 D5/D6) is a Claude Code-only
guarantee** — `AGENTS.md`'s own "Non-Claude harnesses" section states plainly
that none of Codex, Hermes, or Pi has a manifest key for `hooks/`, and none
has a hook runtime a `PreToolUse` deny could wire into, so `lab-posture-guard`
simply does not run on any of the three. They degrade to `npm run lint` in
the monorepo (or the equivalent vendored `adia-lint`/lint-bank invocation in
a consumer repo) picking up the same four manifest-derived rules
(`UNKNOWN-TAG`/`UNKNOWN-ATTR`/`ENUM-VALUE`/`INTERNAL-TIER`, ADR-0108 D5) at
`warn` severity — after the fact, at lint/check time, rather than before the
write lands. `get_manifest` reachability without the hook is not uniform
either: Codex reaches it through its plain `.mcp.json` passthrough; Hermes
needs its `hermes-mcp.yaml` fragment hand-merged into `~/.hermes/config.yaml`;
Pi has no native MCP support at all, so a Pi session without the third-party
`pi-mcp-adapter` gets neither the manifest nor the lint-time signal until a
`npm run check`/`npm run lint` pass runs.

- **Codex** (gh#1888): `.codex-plugin/plugin.json` + per-skill
  `agents/openai.yaml`, derived; `mcpServers` points at the same
  `.mcp.json` Claude Code uses. No manifest key for hooks/commands/agents —
  a command degrades to invoking its skill directly, and `ui-architect`'s
  whole-deliverable coordination — gear selection, wave decomposition,
  generator-≠-reviewer separation, the four-lap cap, failure routing, the
  closing `VerifyProof` — ships as its own portable `ui-architect` skill
  instead of a Claude subagent (ADR-0091): invoke it by name ("use the
  `ui-architect` skill…"); it runs planning/composition/QA as isolated
  dispatches where the installed Codex runtime supports that, or serially in
  one context otherwise. Install via npm (see the `adia-plugins` package
  README) or straight from Git, for anyone with repo access:
  ```
  codex plugin marketplace add adiahealth/gen-ui-kit --ref main
  codex plugin add adia-ui-kit-factory@adia-ui-kit
  codex plugin add adia-ui-kit-forge@adia-ui-kit
  ```
  Sourced from the repo-root `.agents/plugins/marketplace.json` — the
  Codex-side sibling of this repo's own in-repo Claude channel
  (`.claude-plugin/marketplace.json`), hand-authored the same way, never
  generated (ADR-0091 D2/D3). Both plugins register under the manifest's own
  name, `adia-ui-kit` — not the repo name, and a distinct marketplace name
  from the npm-sourced channel above it (`adia-plugins`), mirroring the
  existing Claude-side split exactly, kept deliberately parallel rather
  than merged.
- **Hermes** (gh#1954): `plugin.yaml` + `__init__.py` at this package's
  root — point Hermes's plugin loading at the installed
  `node_modules/@adia-ai/adia-ui-factory` directory. **MCP is user-level
  config in Hermes, not a plugin field** — merge the generated
  `hermes-mcp.yaml` into your own `~/.hermes/config.yaml` by hand; there is
  no automatic wiring. `__init__.py` registers the portable `ui-architect`
  skill (ADR-0091) alongside every other skill, **and** a generated
  `/ui-architect` command — verified against the installed Hermes Agent's
  real `PluginContext.register_command` + `dispatch_tool` and, inside that
  same external Hermes Agent install (not a path in this repo),
  `tools/delegate_tool.py`'s `delegate_task(goal, context, role, …)` (not
  assumed from documentation): each of planning, composition, and QA runs
  as its own isolated `delegate_task` child (`role="leaf"`), gated by the
  same `OrientationRecord`/`BuildResult`/`VerifyProof` linters every other
  runtime calls, capped at four plan→build→QA laps per wave (LLD §C6, SPEC
  REQ-012/013). Generated by `scripts/build/harness-manifests.mjs` — never
  hand-edit `__init__.py`. Verified live via `hermes plugins doctor --ci`
  (clean registration against the real `PluginContext`) plus a Python-level
  selftest (`check:harness-manifests-selftest`) driving the actual
  generated orchestration logic against a scripted `delegate_task` double
  and the real linters; a live interactive `/ui-architect` dispatch was not
  completed (sandboxed dev environment blocks mutating Hermes's global
  plugin-enable state) — the mechanism itself is real and independently
  verified, not simulated. No hooks/agents surface otherwise.
- **Pi** (gh#1954): discovered via the `pi-package` keyword and the `"pi"`
  field in `package.json`; `prompts/*.md` gives every `/command` a genuine
  Pi prompt template. **Pi core has no native MCP support at all** — the
  `adia-gen-ui` server only reaches Pi through the third-party
  [`pi-mcp-adapter`](https://pi.dev) package, which reads this plugin's
  `.mcp.json` unmodified (same shape, no substitution needed). Install that
  adapter first; without it, this plugin's headline feature — the a2ui MCP
  wiring — silently does nothing on a stock Pi install. The portable
  `ui-architect` skill (ADR-0091) is discoverable via the `"pi": {"skills":
  [...]}` glob like every other skill, **and** `pi/ui-architect.extension.ts`
  (`"pi": {"extensions": [...]}`) registers a real `/ui-architect` command:
  verified against the installed `@earendil-works/pi-coding-agent` v0.84.3 —
  its own shipped (if not core-builtin) `examples/extensions/subagent/`
  proves the pattern this extension reuses — dispatching planning,
  composition, and QA as isolated nested `pi --mode json -p --no-session`
  child processes, each tool-walled to mirror this plugin's Claude seats
  (`read,grep,find,ls,bash` for planning/QA; add `edit,write` only for
  composition) and gated by the same three portable-contract linters
  (LLD §C5, SPEC REQ-011). Live-verified end to end in this change's own
  development — a real `pi -e ./pi/ui-architect.extension.ts` session ran
  `/ui-architect` against a live model, dispatched all three isolated
  passes, wrote a real file, took a real screenshot, and returned a clean
  `VerifyProof` — not simulated against an unverified API. No hooks/agents
  surface otherwise.

## Release gate

`release_gate.py`'s G15 (harness overlay freshness) FAILs for this plugin and
is accepted, not fixed, per ticket 10035: `release_gate.py` has no config hook
(no `.claude/workflow.json` field, no per-plugin file) that G15 itself reads,
so there is no override mechanism to declare here as code, the acceptance is
recorded here as doctrine instead.

Conductor ruling, gh#3764 issuecomment-5647637558 (2026-09-12, unattended
run, authorized by issuecomment-5647495831), quoted verbatim as the ruling
record:

> Conductor ruling, 2026-09-12 (unattended run, authorization
> issuecomment-5647495831; posted here so it is citable, not only in PR
> #4403's body and the plugin READMEs).
>
> release_gate.py G15 fails for both adia-ui-forge (8 findings, ticket
> 10026) and adia-ui-factory (13 findings, ticket 10035). Verified cause:
> G15 (harness 3.19.6, release_gate.py:917-932) is a bare subprocess of
> harness_emit.py --verify (:899-950) with no allow, skip, env or config
> hook, and its overlay convention differs from this repo's own manifest
> generators (scripts/build/codex-manifests.mjs,
> scripts/build/harness-manifests.mjs), which are gated fresh by
> check:codex-manifests-fresh and check:harness-manifests-fresh. Ruling:
> this repo keeps its generators as the source of truth; G15 is declared
> ACCEPTED (not fixed) for both plugins, recorded in each plugin README's
> release-gate section; the convention mismatch is routed upstream to the
> adia-harness conductor per the 2026-09-10 routing ruling. Tickets 10026
> and 10035 stay OPEN until the upstream fix lands. Not a skip of any
> lockstep (class A) gate; those are the 36-gate roster in package-release.

Tickets: 10035 (this plugin), 10026 (adia-ui-forge, same class). Both stay
OPEN pending the upstream harness-plugin item this ruling routes the fix to,
filed by the adia-harness conductor on 2026-09-12 as adia-harness local
ticket 137.

## Requirements

Python 3 (hook) + Node 22. Installs standalone in consumer repos (no monorepo
path assumptions — ADR-0040 bare-repo posture).

## Feedback / bugs

`npm view @adia-ai/adia-ui-factory bugs` — a monitored address reachable with
no adiahealth repo access; triage owner: **kimba**. Filed an issue in-session
via the `report_issue` MCP tool? It writes a local
`qa/findings/issues/<id>.md` + `.json` and transmits nothing — attach the
generated markdown to that address yourself.

---

Versioned with the `@adia-ai` lockstep (`npm run check:lockstep`) — see `CHANGELOG.md` for the current version;
content merged between cuts is invisible to the installed plugin cache until
the next lockstep bump or a manual sync.
