@adia-ai/adia-ui-forge 0.8.31 → 0.8.33

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-forge",
3
- "version": "0.8.31",
3
+ "version": "0.8.33",
4
4
  "description": "Maintain the adia-ui (@adia-ai) framework itself \u2014 author primitives and shells, run the A2UI generation pipeline and its corpus, review gen-UI quality, sweep QA, cut releases, deploy. The maintainer counterpart to adia-factory (the consumer/app-author plugin).",
5
5
  "author": {
6
6
  "name": "Kim",
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog — adia-ui-kit-forge
2
2
 
3
+ ## [0.8.33] — 2026-08-11
4
+
5
+ ### Fixed
6
+ - **adia-release `release-pack.mjs` resume completes missing stubs, defers versionArg gates, warns on stale cache copy.** The half-cut-tree resume path (all 13 package versions already bumped) previously assumed Step 4a's promote/stub pass had also fully completed — but a stub package's `[Unreleased]` reads empty either way, so a resume after an abort mid-stub silently skipped it rather than completing it, leaving that package's `[X.Y.Z]` section missing entirely at tag time. Resume now re-runs `insert-stub.mjs` for any package still missing its `[X.Y.Z]` heading before continuing to the idempotent substeps, defers gates that need the CUT-version hygiene check until after that repair, and warns when the plugin's own bundled copy of this script differs from the repo's live one (a stale marketplace cache silently running old release logic). `cut-procedure.md`'s 4-resume section documents the corrected sequencing.
7
+ - **`adia-gen-review`'s corpus-HTML pattern reference no longer teaches the retired `badge-ui variant="accent"` (gh#873-876).** Its worked example used the value the variant/color/tone convention cut retired in favor of `primary` — updated so the reference doesn't contradict the shipped contract.
8
+ - **adia-release `release-pack.mjs` Step 5 now stages `catalog-data.js`, the third file `derive-genui-catalog.mjs` writes.** Found live during this cut: Step 4d.5 regenerates `base.json`/`adia-pack.json`/`catalog-data.js` together (the latter is the browser-safe ES-module projection of the first two), but the Step 5 file allowlist (gh#617) only ever named the first two — `catalog-data.js` sat modified-but-uncommitted in the release commit, invisible in a local working tree (the file exists, just stale) but caught immediately by CI's fresh checkout via `check:genui-catalog`'s drift check. Added to the allowlist alongside its siblings.
9
+
10
+ ### Maintenance
11
+ - **`.claude-plugin/plugin.json` version bump** — moves in lockstep with package.json (the `/plugin update` cache key).
12
+ - **`skills/` touched in this release window** (3 file(s), e.g. `references/corpus-html-patterns.md`) — carried by the entries above.
13
+
14
+ ## [0.8.32] — 2026-08-09
15
+
16
+ ### Fixed
17
+ - **adia-release `release-pack.mjs` deploy-settle wait tracks the dispatched run, requires success, and names the blocking run (gh#762).** The handoff's "waiting for the deploy workflow to settle" loop polled the NEWEST run's status in silence — it could settle on the previous completed run before the dispatch registered, accepted failure/cancelled as settled, and gave no signal while a forgotten run WAITING at the `production-site` gate held the `deploy-site` concurrency group (56h on Aug 5–8). Now: the newest run id is captured before dispatch, the handoff waits for the NEW run to appear, polls that run by id, requires `conclusion=success` (anything else fails the handoff), and each poll reports the gate-waiting blocker's id, wait-start, and URL ("BLOCKED BEHIND run X — approve or cancel it"). Pairs with the repo-level `reap-stale-deploy-gate.yml` auto-cancel (root CHANGELOG).
18
+ - **adia-release `release-pack.mjs` from-scratch guard rails (gh#765).** Three fixes from the v0.8.29 cut's failure modes: (1) a parse-time guard — `--mode from-scratch` whose `--substantive-packages` ∪ `--stub-packages` doesn't cover the 13-package roster now fails immediately, naming the ride-alongs that would be missing a `[VERSION]` CHANGELOG section, instead of dying at Step 4e after the ~15-min pre-flight (mirrors the handoff `--gh-notes-file` guard); (2) `sh()`/`shQuiet()` failures now print the failing command, exit status, and the inner command's captured stdout/stderr instead of a bare execSync stack; (3) a half-cut tree (all versions bumped + CHANGELOGs promoted by an aborted Step-4 run) is detected before pre-flight and the run RESUMES — hygiene gates validate at the cut version and Step 4 skips promote/stub/bump — instead of false-failing gate 24; a mixed partial bump hard-stops with a recovery pointer.
19
+ - **adia-release `dispatch-publish.mjs --verify-triggered` is conclusion-aware and registry-gated (gh#763).** The verify used to count ANY run for the tag as "triggered" — after the v0.8.29 runner-starvation wave stamped 5 publish runs `cancelled`, it would have reported all 13 packages fine and refused to re-dispatch the dead ones. Now: a run satisfies the check only on `conclusion: success`; in-progress/queued runs get a bounded wait (6 × 20s rechecks); cancelled/failed/timed-out runs and missing runs are re-dispatch candidates. Every re-dispatch is additionally gated behind `npm view <pkg>@<version>` — a version already on the registry is published regardless of run conclusion (the v0.8.29 compose run published, then got stamped cancelled by the 10m job clock), so re-dispatching over it would only 403. Registry remains the final arbiter; workflow conclusions are explicitly not publish truth. Selftest extended with negative controls for both the run-classification and the registry-gate decision tables.
20
+
21
+ ### Maintenance
22
+ - **`.claude-plugin/plugin.json` version bump** — moves in lockstep with package.json (the `/plugin update` cache key).
23
+ - **`skills/` touched in this release window** (13 file(s), e.g. `adia-author/SKILL.md`) — carried by the entries above.
24
+
3
25
  ## [0.8.31] — 2026-08-07
4
26
 
5
27
  ### Maintenance
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/adia-ui-forge",
3
- "version": "0.8.31",
3
+ "version": "0.8.33",
4
4
  "description": "Maintain the adia-ui (@adia-ai) framework itself \u2014 author primitives and shells, run the A2UI generation pipeline and its corpus, review gen-UI quality, sweep QA, cut releases, deploy. The maintainer counterpart to adia-factory (the consumer/app-author plugin).",
5
5
  "keywords": [
6
6
  "adia-ui",
@@ -41,15 +41,13 @@ wins any tie with this skill. Monorepo source read while authoring (yaml, CSS,
41
41
  | Contract / token / lifecycle drift audit on an existing component | [token-contract.md](references/token-contract.md) + [anti-patterns.md](references/anti-patterns.md) |
42
42
  | Demo for a composite/module — any `packages/web-modules/**/*.{examples,contents}.html` | [composite-demo-protocol.md](references/composite-demo-protocol.md) — NOT the primitive or promotion paths; they lack the canonical-survey discipline |
43
43
  | Convention question ("is this idiomatic?") | [code-style.md](references/code-style.md) — cite the rule, don't expand it inline |
44
+ | Trait detail page (`site/pages/traits/<name>/`) | [trait-pages.md](references/trait-pages.md) — the ADR-0019 required-section template (traits are this skill's charter; adia-site-docs does not own `traits/`) |
44
45
 
45
- Depth references, loaded only when an entry reference cross-links them:
46
- [api-contract.md](references/api-contract.md) ·
47
- [css-patterns.md](references/css-patterns.md) ·
48
- [lifecycle-patterns.md](references/lifecycle-patterns.md) ·
49
- [yaml-contract.md](references/yaml-contract.md) ·
50
- [canonical-pattern-index.md](references/canonical-pattern-index.md) ·
51
- [common-gotchas.md](references/common-gotchas.md) ·
52
- [worked-example.md](references/worked-example.md).
46
+ Full retrieval map one line per reference file, grouped by axis:
47
+ [references/INDEX.md](references/INDEX.md). Depth references (api-contract,
48
+ css-patterns, lifecycle-patterns, yaml-contract, canonical-pattern-index,
49
+ common-gotchas, worked-example) load only when an entry reference or the
50
+ INDEX cross-links them.
53
51
 
54
52
  The `demo-postwrite-pattern-gate` hook enforces the demo `Pattern source:`
55
53
  citation mechanically on every web-modules demo write; the
@@ -0,0 +1,41 @@
1
+ # adia-author references — retrieval map
2
+
3
+ One line per file: `path — the question it answers`. Load this first; load
4
+ files on demand from it. Axes are topical (no severity ranking applies).
5
+ The skill body's task-shape table routes entry asks; this map covers every
6
+ file, including the depth references those entries cross-link.
7
+
8
+ ## Authoring lifecycle
9
+
10
+ - [primitive-audit.md](primitive-audit.md) — does the primitive I'm about to build already exist? The mandatory §0 affordance-by-affordance audit before ANY new component.
11
+ - [authoring-cycle.md](authoring-cycle.md) — what are the 5 steps to author a new primitive or modify an existing one, and which rules are non-negotiable?
12
+ - [worked-example.md](worked-example.md) — what does a finished primitive look like end to end? Two annotated builds: `<badge-ui>` (presentation) and `<counter-ui>` (form-associated).
13
+
14
+ ## Contracts (API · yaml · tokens · CSS · lifecycle)
15
+
16
+ - [api-contract.md](api-contract.md) — how do I declare `static properties` (type, default, reflect, attribute) and avoid the `attr:` silent-typo trap?
17
+ - [yaml-contract.md](yaml-contract.md) — what fields does the `<name>.yaml` SoT take, and how does it feed the generated `.a2ui.json` sidecar?
18
+ - [token-contract.md](token-contract.md) — is this component's CSS honoring the two-block `@scope` token contract, and why does it misrender under a theme?
19
+ - [css-patterns.md](css-patterns.md) — how do I structure a component stylesheet: two-block `@scope`, variants vs modes, token layering?
20
+ - [lifecycle-patterns.md](lifecycle-patterns.md) — how do I keep `connected()`/`disconnected()` symmetric for listeners, timers, observers, and popovers?
21
+
22
+ ## Shells & modules
23
+
24
+ - [shell-patterns.md](shell-patterns.md) — how do I author a shell or a bespoke cluster child (`<admin-*>` `<chat-*>` `<editor-*>` `<simple-*>`) per ADR-0023?
25
+ - [module-promotion.md](module-promotion.md) — when and how do I lift a UI block duplicated across consumers into a shared `packages/web-modules/` element (the 5-phase arc)?
26
+
27
+ ## Demos & doc surfaces
28
+
29
+ - [composite-demo-protocol.md](composite-demo-protocol.md) — what discipline governs any `packages/web-modules/**` demo edit, beyond what the `Pattern source:` gate can see?
30
+ - [canonical-pattern-index.md](canonical-pattern-index.md) — which canonical `.contents.html` files should I survey for a given UI type? Auto-generated; regenerate via `scripts/build-canonical-pattern-index.mjs`.
31
+ - [trait-pages.md](trait-pages.md) — which sections, in which order, must a `site/pages/traits/<name>/` detail page carry (ADR-0019 template)?
32
+
33
+ ## Conventions & failure catalog
34
+
35
+ - [code-style.md](code-style.md) — is this idiomatic AdiaUI? The conventions (`<*-ui>` elements, tokens, layout primitives) and the working feature each protects.
36
+ - [anti-patterns.md](anti-patterns.md) — what named mistake shapes (AP-##) has the coherence audit already caught, so I don't repeat them?
37
+ - [common-gotchas.md](common-gotchas.md) — which authoring traps render broken without console errors: CSS/composition layering (§§1–5), async-lifecycle races (§6), regeneration ordering (§7)?
38
+
39
+ ## Historical
40
+
41
+ - [llm-bridge.md](llm-bridge.md) — superseded 2026-07-18 pointer: `@adia-ai/llm` internals belong to `adia-llm-internals`; kept only to redirect.
@@ -597,6 +597,30 @@ disconnected() {
597
597
 
598
598
  ---
599
599
 
600
+ ## Registration / Composition
601
+
602
+ ### AP-R1 · Cross-primitive `import` inside a primitive's `.js`
603
+
604
+ ```javascript
605
+ // ❌ input.js
606
+ import '../button/button.js';
607
+ import '../icon/icon.js';
608
+ ```
609
+
610
+ Each primitive's `.js` registers ONLY its own `customElements.define()`. Composition is the **consumer's** responsibility, never the producer's: demo shells, consumer apps, and barrel-using packages import every primitive a fragment uses explicitly, or take the full barrel (`import '@adia-ai/web-components'`).
611
+
612
+ Three load-bearing arguments:
613
+
614
+ 1. **The composition graph is dynamic.** `<input-ui>` stamps `<button-ui>` + `<icon-ui>` only when `type="number"`; static ES modules have no conditional import, so a producer-side import makes every plain-text-input consumer pay the stepper's bundle cost. Bundle size would couple to the compose-graph instead of the usage graph.
615
+ 2. **The full barrel exists.** Consumers who don't want to think about composition import the barrel; piecemeal importers have opted into caring about bundle size, so explicit is the right tradeoff for them.
616
+ 3. **The silent failure has a one-liner detector.** An unregistered composed tag renders as an inert `HTMLUnknownElement` with no console error; probe with `customElements.get('button-ui')` (the `smoke:consumers` sweep spot-checks registration).
617
+
618
+ If you author a primitive that programmatically creates another (`document.createElement('button-ui')`, or via `innerHTML`/template strings), verify its demo shell `<x>.html` imports each composed primitive explicitly.
619
+
620
+ Source: ADR-0027.
621
+
622
+ ---
623
+
600
624
  ## Rapid-fire anti-patterns (mined from incident history)
601
625
 
602
626
  One line each; every entry is a shipped bug or a CRITICAL gate. Same axes as above, too small for a full entry.
@@ -203,3 +203,18 @@ render() {
203
203
  ```
204
204
 
205
205
  Rule of thumb: if CSS can do the work via an attribute selector (`:scope[disabled] { ... }`), prefer CSS. Reserve `render()` for propagating state into child inputs, recalculating positions, or reflecting data changes that attribute selectors can't express.
206
+
207
+ ## Popover `placement` defaults — picked by popover-to-trigger width ratio
208
+
209
+ Every popover-bearing primitive exposes a consumer-overridable `placement` attribute (declare it in the yaml SoT, with the default documented in the prop description). The DEFAULT is selected by the popover's natural width relative to its trigger — never by component identity:
210
+
211
+ | Case | Default |
212
+ |---|---|
213
+ | Popover ≈ trigger width (≤ ~1.5×; listboxes / action menus, anything that `matchWidth`s the trigger) | `bottom-start` |
214
+ | Popover materially wider than trigger (calendar grids, date/time/color pickers, filter panels) | `bottom` (centered) |
215
+ | Trigger sits at a container's right edge **by construction** (toolbar spillover, right-pinned "more") | `bottom-end` |
216
+ | Non-bottom-axis surfaces | component-specific: `tooltip-ui` → `top`, `nav-group-ui` collapsed flyout → `right` |
217
+
218
+ Rationale: `bottom-start` on a wide popover under a right-anchored trigger fires `anchor.js`'s right-edge overflow recovery, snapping the panel far past the trigger's left edge (the v0.6.35 date-range-picker incident — ~800px panel under a ~280px button). Centered `bottom` shifts at most half the overflow distance. Classification is done once at authoring time; borderline cases resolve by what the popover *wants*: if it `matchWidth`s the trigger it is trigger-width regardless of absolute size. "The consumer placed their button on the right of the layout" is a call-site `placement="bottom-end"` override, never a default. The overflow-recovery logic in `anchor.js` is the safety net and stays unchanged — the default's job is to make recovery rarely fire.
219
+
220
+ Source: ADR-0034.
@@ -355,6 +355,25 @@ When a card-like container (drawer-ui, pane-ui, full-height cards) needs a heade
355
355
 
356
356
  **Gotcha:** the sticky background must be opaque. If the header is transparent, content scrolls visibly underneath. Match the sticky element's `background` to the panel's `--*-bg` token.
357
357
 
358
+ ## Cascade layers — precedence is declared once, never fought per-rule
359
+
360
+ `styles/index.css` declares the single ordered list, before any layered rule:
361
+
362
+ ```css
363
+ @layer reset, tokens, elements, components, utilities, context, overrides;
364
+ ```
365
+
366
+ The precedence law: a later layer beats an earlier layer **regardless of selector specificity**; within a layer, normal specificity applies. Component `@scope` rules live inside `@layer components` — `@scope` sets proximity *inside* the layer, `@layer` sets inter-group order; they compose, they are not substitutes.
367
+
368
+ Consequences for component authors:
369
+
370
+ - **Consumer overrides always win** — unlayered consumer CSS, or `@layer overrides`, beats everything by layer order. Never add `:where()` wraps, matched-specificity selectors, or `!important` to "let the consumer win"; the layer order already guarantees it.
371
+ - **Utilities beat component defaults by layer order.** The global attribute API (`api/*`) sits in `utilities`, so a component cannot out-specific it — a component repurposing a global attribute (e.g. `color=` on a filled control) must opt out explicitly (see the `[color]`/`[weight]` trap below).
372
+ - **`@layer` governs rule precedence only.** The `var()` token chains (token indirection), genuine element-default `:where()`s, and allowlisted a11y `!important`s serve other mechanisms and are not retired by layers.
373
+ - `npm run check:cascade-layers` gates the model: canonical order declared once, `!important` ≤ allowlist, no precedence-`:where()` in layered files.
374
+
375
+ Source: ADR-0038.
376
+
358
377
  ## Raw-CSS traps (mined from incident history)
359
378
 
360
379
  One line each; every entry is a shipped bug.
@@ -291,6 +291,26 @@ disconnected() {
291
291
 
292
292
  Especially important for components that are attached/detached frequently (modals, toasts, drawers) — each incarnation leaks its old subtree into memory if refs aren't released.
293
293
 
294
+ ## Adopt-or-stamp — slot content is the source of truth
295
+
296
+ Every slot-bearing primitive that exposes both an attribute and a slot for the same logical content (`text=` + `<span slot="text">`) follows the `#stampMark` / `#wasStamped` policy (working reference: `list-item-ui`, `packages/web-components/components/list/list.js`):
297
+
298
+ 1. `connected()` either **adopts** the consumer-authored slotted child or **stamps** a new one, tagging it `data-{component}-stamped`.
299
+ 2. `render()` may only mutate or remove elements carrying the stamp. An unstamped slotted child is consumer-owned — leave it alone, even when the matching attribute is empty.
300
+ 3. The attribute stays a convenience shorthand that produces a stamped element; both forms render identically and are interchangeable.
301
+
302
+ The failure this prevents: a `render()` that overwrites `textContent` from the attribute, or hides/removes the slot element when the attribute is empty, silently destroys consumer-authored rich content (links, `<strong>`, inline icons). A 13-page audit found 73 slot uses where the slotted form is the only way to author the intended content (the destructive render() confirmed in 3 primitives) before the policy landed — from the page author's seat it looks like "the docs are broken", from the component author's seat like "the consumer didn't pass the attribute"; both are wrong-blame. Source: ADR-0010.
303
+
304
+ ### `slot="caret"` — the one disclosure-indicator slot
305
+
306
+ The expand/collapse (and dropdown) directional-indicator slot is named `caret` on every primitive that exposes composition slots (`tree-item-ui`, `pane-ui`, `accordion-item-ui`, `nav-group-ui`, `select-ui`). `chevron` is retired — a hard rename with no compat alias, and `--*-chevron-*` tokens became `--*-caret-*` (Phosphor names the glyphs `caret-right` / `caret-down`; there is no `chevron-*` icon).
307
+
308
+ - The mechanism is adopt-or-stamp (above): honor a declarative `<… slot="caret">` child if present, else stamp `<icon-ui slot="caret" name="caret-right">`. The same adopt-or-stamp rule applies to **every** composition slot the stamp builds (e.g. `slot="actions"`).
309
+ - Rotation is driven by host open-state CSS (`:scope[open]` / `[collapsed]` / `[expanded]`) — never by swapping the icon name.
310
+ - Internal-render components with no composition slots (`agent-trace`, `agent-reasoning`) keep `[data-*-caret]` styling hooks — the convention governs slots, not internal hooks.
311
+
312
+ Source: ADR-0036.
313
+
294
314
  ## Anti-patterns to watch for
295
315
 
296
316
  - **Inline arrow + `removeEventListener`** — the listener isn't really being removed. Silent leak.
@@ -89,7 +89,7 @@ Every component's `.css` file must follow the **two-block @scope** pattern:
89
89
 
90
90
  If you find a raw value elsewhere, either:
91
91
 
92
- - Fix it by adding a proper token at `packages/web-components/styles/tokens.css`
92
+ - Fix it by adding a proper token in the owning `styles/` dimension×layer file (see "Where foundation styles live" below — `tokens.css` is a compat barrel, not a home)
93
93
  - Or document the exemption in `.claude/docs/specs/component-token-contract.md`
94
94
 
95
95
  ## Chrome token palette
@@ -107,6 +107,20 @@ Added in v0.5.0 — use these for UI chrome:
107
107
 
108
108
  For charts, stat colors, category markers — use `--a-data-0` through `--a-data-9`. Do NOT hardcode chart colors.
109
109
 
110
+ ## Where foundation styles live — dimension × layer
111
+
112
+ Every file under `packages/web-components/styles/` answers two questions — which *dimension* (color · type · space · size · radius · motion · elevation) and which *layer* (primitive → semantic → role → element → attribute-API → context → reset) — and declares its cell with a machine-readable header tag `adia:<layer>/<dimension>`. When adding or moving a declaration:
113
+
114
+ - Spatial / size / radius / motion / elevation primitives → `styles/foundation/` (one file per dimension)
115
+ - Type scale (L1/L2), roles (L3), native-element defaults → `styles/type/`
116
+ - Color layers → `styles/colors/` (parameters · primitives-* · surfaces · scrims · semantics/*)
117
+ - The global attribute API (`[size]` `[density]` `[gap]` `[padding]` `[margin]` …) → `styles/api/{sizing,text,layout}.css` — the ONLY home for global `[attr]` selectors
118
+ - `tokens.css`, `typography.css`, `colors/semantics.css`, etc. remain **compat barrels** at their public paths — they only `@import`; never add declarations to them
119
+
120
+ Enforced by `scripts/release/check-foundation-layer-placement.mjs` (wired into `npm run check`): header tags present, primitive files free of attribute selectors, the attribute API confined to `api/`, no orphan files. Import order inside `colors/semantics/` is load-bearing (`aliases.css` last) and asserted in file headers.
121
+
122
+ Source: ADR-0035.
123
+
110
124
  ## When to update this reference
111
125
 
112
126
  If you add a new token category (like `--a-chrome-*` was added), update both this file and `.claude/docs/specs/component-token-contract.md`. The spec doc is the live source of truth; this file is the practitioner's checklist.
@@ -0,0 +1,38 @@
1
+ # Trait Detail Pages — the required template
2
+
3
+ Every trait detail page under `site/pages/traits/<name>/` follows one binding section template. The 41-page Phase B sweep proved what a loose contract produces: bimodal quality (portfolio-grade pages next to abstract-box demos) and zero pages teaching restraint. Navigate-by-template is the contract. Source: ADR-0019.
4
+
5
+ Trait pages are this skill's charter (traits live in `packages/web-components/traits/`); `adia-site-docs` owns `site/pages/{architecture,getting-started,guides,patterns,reference}` — not `traits/`.
6
+
7
+ ## Required sections, in order
8
+
9
+ | # | Section (`data-property=`) | Required? | Purpose |
10
+ |---|---|---|---|
11
+ | 1 | `<header>` | always | `<h1>` trait name + `<tag-ui>trait</tag-ui>` + category tag matching `_catalog.json` SoT |
12
+ | 2 | `hero` | always | "In context" demo with real product chrome — no abstract boxes |
13
+ | 3 | `minimal` | always | Single-tag declarative usage; `<h2>` reads "Bare minimum" |
14
+ | 4 | `declarative` | always | `<X traits="trait-name">` code-ui block |
15
+ | 5 | `config` | conditional | Only when the trait declares `config` attrs in `defineTrait()`; live sliders + readouts |
16
+ | 6 | `reduced-motion` | motion-only | Toggleable `prefers-reduced-motion: reduce` degradation demo. Required for any trait touching transform / opacity / animation |
17
+ | 7 | `accessibility` | always | 1–3 bullets on pointer-only, touch-confused, AT-silent reality |
18
+ | 8 | `performance` | always | 1 sentence on rAF cost, observer count, listener cost |
19
+ | 9 | `when-not-to-use` | always | 2–4 opinionated bullets on overuse, audience-mismatch, perf cost |
20
+ | 10 | `api` | always | Auto-stamped from `_api-table.js` |
21
+ | 11 | `composes` | always | ≥ 1 trait demonstrated in a WORKING composition; ≥ 4 traits listed |
22
+
23
+ Trait-specific extras (`state`, `lifecycle`, scenario `usage` recipes) slot between `config` (5) and `accessibility` (7).
24
+
25
+ ## Naming and sourcing rules
26
+
27
+ - `data-property` values are lowercase hyphen-separated tokens; canonical set as in the table. **`minimal`, not `bare`.**
28
+ - `data-artifact-label` values are lowercase descriptive prose ("settle log") OR code-shaped strings describing the demo axis (`speed=120 (slow, dramatic)`). HTML-entity escapes inside attribute values are correct.
29
+ - Header category tag MUST match the trait's `category` in `packages/web-components/traits/_catalog.json` — catalog SoT, never page-author judgment.
30
+ - Imports in code-ui blocks use the full package name `@adia-ai/web-components/traits` — never internal aliases like `@traits/index.js`.
31
+ - Inline-style tokens use canonical names: `--a-border` (dashed borders), `--a-radius-md` / `--a-radius-lg`, `--a-accent` / `--a-accent-fg`.
32
+
33
+ ## The two teeth
34
+
35
+ - **`when-not-to-use` is the highest-impact section.** A page shipping without it defaults to "this trait is good, please use it" — wrong for every trait once overuse, density, or a11y tradeoffs enter. The restraint mandate is the pedagogy differentiator (Motion / GSAP / React Spring all whiff on it). Fewer than 2 bullets requires an articulated why.
36
+ - **Composes must SHOW.** Listing trait names with prose-only descriptions does not satisfy the contract — at least one composition runs as a working demo.
37
+
38
+ No mechanical gate enforces this template yet (ADR-0019's verify-trait-pages.mjs was deferred and does not exist) — the 11-section contract is enforced at PR review. Source: ADR-0019.
@@ -113,7 +113,7 @@ matches both native `h1–h6`/`p` and their transpiled
113
113
  <header>
114
114
  <span slot="heading">
115
115
  <text-ui strong>Title</text-ui>
116
- <badge-ui text="New" variant="accent"></badge-ui>
116
+ <badge-ui text="New" variant="primary"></badge-ui>
117
117
  </span>
118
118
  <p slot="description">Subtitle</p>
119
119
  </header>
@@ -95,7 +95,7 @@ assumed pass, or "published" without the actual `npm view` output.
95
95
 
96
96
  `release-pack.mjs` walks the cycle in two phases per invariant 3 — `--mode cut` / `--mode from-scratch` promote `[Unreleased]` (BOTH modes now — a peer's hand-authored section must be promoted whichever mode cut it, and a loud guard blocks the bump if any roster package still carries non-empty `[Unreleased]` content after promotion), run Step 4f (pre-tag coverage `--fix` with the authoritative F-N1 matcher), and stop at the release commit (PR → merge); `--mode handoff` tags/publishes/deploys from post-merge main. **`--go` is the standard invocation for an operator-initiated release** — the single authorization covers the cycle; evidence blocks print as a log; granular `--yes`/`--push`/`--publish` remain for cautious manual runs (`--push` still refuses to auto-fire past any F-N1 finding). `--mode batch` is not implemented — it hard-rejects with a pointer to [`recovery-paths.md`](references/recovery-paths.md) §Scenario 2 rather than silently misbehaving; a real batch tags each version at its own release-merge SHA, which this single-version orchestrator has no model for. Pre-flight (Step 3) runs the full 24-gate roster from the single shared list in `scripts/gate-roster.mjs` — the same list [`cut-procedure.md`](references/cut-procedure.md) §3.1 transcribes, so a subset run requires editing that one file, not two.
97
97
 
98
- The pieces are callable alone: `bump.mjs` (version fields + the factory `.mcp.json` a2ui-mcp pin, invariant 8), `promote-unreleased.mjs` / `insert-stub.mjs` (CHANGELOG blocks), `tag-lockstep.mjs` (12 tags, `--delete` to move), `dispatch-publish.mjs` (publish workflows; `--verify-triggered` re-dispatches only missing runs; `--after` enforces batch ordering — dispatch output states what WAS done, never a to-do phrased as if it hadn't fired), and `pr-bridge.mjs` (the two-phase bridge: pushes the release branch, opens the PR, waits out CI, and merges ONLY on all-non-fail-soft-checks-green + zero unresolved threads + no changes-requested; any other state stops with evidence — §5.7 mechanized, `--dry` to preview the decision). All fail fast outside a monorepo root (`assert-monorepo-root.mjs`); fork-configurable via `--host` / `--scope`. All 6 CLI scripts (plus `scripts/release/check-release.mjs`) have a `selftest` mode wired into CI's Plugin bin selftests step; the 3 helper modules (`assert-monorepo-root`, `gate-roster`, `package-paths`) are exercised through the CLIs' selftests, not their own.
98
+ The pieces are callable alone: `bump.mjs` (version fields + the factory `.mcp.json` a2ui-mcp pin, invariant 8), `promote-unreleased.mjs` / `insert-stub.mjs` (CHANGELOG blocks), `tag-lockstep.mjs` (12 tags, `--delete` to move), `dispatch-publish.mjs` (publish workflows; `--verify-triggered` re-dispatches missing AND dead — cancelled/failed/timed-out — runs, each gated behind an npm-registry check so an already-published version is never re-dispatched (gh#763); `--after` enforces batch ordering — dispatch output states what WAS done, never a to-do phrased as if it hadn't fired), and `pr-bridge.mjs` (the two-phase bridge: pushes the release branch, opens the PR, waits out CI, and merges ONLY on all-non-fail-soft-checks-green + zero unresolved threads + no changes-requested; any other state stops with evidence — §5.7 mechanized, `--dry` to preview the decision). All fail fast outside a monorepo root (`assert-monorepo-root.mjs`); fork-configurable via `--host` / `--scope`. All 6 CLI scripts (plus `scripts/release/check-release.mjs`) have a `selftest` mode wired into CI's Plugin bin selftests step; the 3 helper modules (`assert-monorepo-root`, `gate-roster`, `package-paths`) are exercised through the CLIs' selftests, not their own.
99
99
 
100
100
  Site deploy (Step 10 of the handoff) dispatches `deploy-site.yml` (its own pre-flight verify → snapshot → hardened rsync → post-deploy verify → auto-rollback), never a raw local `rsync` — H1 (forge-campaign gh#268 audit): the script had drifted into bypassing that pipeline even though this doc already taught the workflow-dispatch path. The dispatch rides the cycle's single authorization (`--go`, or `--publish` on a manual run); the workflow's own GitHub environment gate is the human stop for the deploy itself.
101
101
 
@@ -148,27 +148,51 @@ tag-creation/tag-push/publish-dispatch commands until it passes, and
148
148
  shipped tarballs with unpromoted `[Unreleased]` CHANGELOG headers because
149
149
  this class of check ran only after tagging).
150
150
 
151
- **4-resume. Resuming a cut that died mid-Step-4** (v0.8.10 hit this live):
152
- `release-pack.mjs` always re-runs the FULL sequence from Step 1, and
153
- `promote-unreleased.mjs` hard-errors on already-promoted packages ("already
154
- has ## [X.Y.Z]") — so once ANY promotion has landed, re-invoking the
155
- orchestrator can never get past Step 4. That error is the idempotency guard
156
- working, not a corrupted state. Do NOT hand-edit CHANGELOGs back to
157
- `[Unreleased]` to appease it. Instead, verify the promotion state with the
158
- authoritative gate `node scripts/release/check-release-docs.mjs --version
159
- X.Y.Z` covers every lockstep package (including the nested `a2ui/*` and
160
- `plugins/*` paths a shallow `packages/*` glob misses) and fails on any
161
- leftover `[Unreleased]` content or missing `[X.Y.Z]` heading; it will still
162
- flag the not-yet-generated `docs/releases/vX.Y.Z.md`, which 4e below creates.
163
- Then run the remaining steps as the standalone pieces the Mechanization
164
- section already names, in this order:
151
+ **4-resume. Resuming a cut that died mid-Step-4** (v0.8.10 and v0.8.29 hit
152
+ this live). Two shapes, told apart by the lockstep versions — `release-pack.mjs`
153
+ checks them itself at startup (gh#765):
154
+
155
+ - **Complete bump (all 13 `package.json` versions at the cut version):**
156
+ just RE-RUN the same `release-pack.mjs` command. It detects the half-cut
157
+ tree before the pre-flight, prints a `[resume]` banner, validates the
158
+ hygiene gates at the CUT version (no more gate-24 false-fail at the
159
+ previous version), and skips promote/stub/bump in Step 4 re-running only
160
+ the idempotent substeps (lockfile, catalog, 4e notes, 4f coverage, 4g
161
+ hygiene) and continuing to Step 5. The `promote-unreleased.mjs`
162
+ "already has ## [X.Y.Z]" hard-error can no longer be reached on this path.
163
+ Do NOT hand-edit CHANGELOGs back to `[Unreleased]` first the resumed run
164
+ expects the promoted state and re-proves it with the
165
+ unpromoted-`[Unreleased]` guard.
166
+ - **Mixed versions (some packages bumped, some not — a mid-bump abort):**
167
+ the orchestrator hard-stops listing the stragglers and will neither resume
168
+ nor roll back. Recover by hand: restore the tree (`git status` /
169
+ `git checkout -- <files>`, or finish the bump with `bump.mjs` directly),
170
+ then re-run. To verify the promotion state along the way, the
171
+ authoritative gate is `node scripts/release/check-release-docs.mjs
172
+ --version X.Y.Z` — it covers every lockstep package (including the nested
173
+ `a2ui/*` and `plugins/*` paths a shallow `packages/*` glob misses) and
174
+ fails on any leftover `[Unreleased]` content or missing `[X.Y.Z]` heading;
175
+ it will still flag the not-yet-generated `docs/releases/vX.Y.Z.md`, which
176
+ 4e creates.
177
+
178
+ The fully manual fallback (both shapes, if the orchestrator itself is
179
+ suspect) remains the standalone pieces the Mechanization section names, in
180
+ this order. **Stubs complete BEFORE 4f** (the v0.8.32 resume proved the
181
+ ordering): the bump-complete marker cannot see whether insert-stub ran — a
182
+ stub package's `[Unreleased]` is empty, so every guard passes with its
183
+ `[X.Y.Z]` section entirely absent; running the coverage `--fix` before the
184
+ stub sections exist leaves it nothing to append to, and the gap resurfaces
185
+ as F-N1 warns at the push boundary, costing a tag move:
165
186
 
166
187
  ```bash
167
- node "${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/bump.mjs" --from X.Y.Z-1 --to X.Y.Z # 4b
188
+ node "${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/insert-stub.mjs" \
189
+ --version X.Y.Z --date YYYY-MM-DD --previous-version X.Y.Z-1 \
190
+ --substantive "<one-line>" --xref "<anchor>" --packages <missing-stubs> # 4a-stub — FIRST, only the missing ones (hard-errors on existing sections)
191
+ node "${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/bump.mjs" --from X.Y.Z-1 --to X.Y.Z # 4b (skip if versions already moved)
168
192
  npm install --package-lock-only --no-audit --no-fund # 4c
169
193
  npm run check:lockstep # 4d
170
194
  node scripts/build/derive-genui-catalog.mjs # 4d.5 — catalogId carries the bumped version (gh#617)
171
- node scripts/release/check-release.mjs --pending-version X.Y.Z --fix # 4f
195
+ node scripts/release/check-release.mjs --pending-version X.Y.Z --fix # 4f — AFTER the stubs exist
172
196
  node scripts/release/generate-release-notes.mjs --version X.Y.Z --write # 4e
173
197
  node scripts/release/check-release-docs.mjs --version X.Y.Z # 4e gate
174
198
  node scripts/release/check-cut-hygiene.mjs --version X.Y.Z # 4g — post-bump proof
@@ -300,7 +324,7 @@ Log the current registry snapshot (per-package versions + `dist-tags.latest`) be
300
324
 
301
325
  ```bash
302
326
  node "${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/dispatch-publish.mjs" \
303
- --version X.Y.Z --verify-triggered # re-dispatches only missing runs
327
+ --version X.Y.Z --verify-triggered # re-dispatches missing/dead runs; registry-gated (gh#763)
304
328
  ```
305
329
 
306
330
  Wait for the workflows to settle, then verify against the **registry**, never the workflow's green check:
@@ -71,7 +71,7 @@
71
71
 
72
72
  ```bash
73
73
  node "${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/dispatch-publish.mjs" \
74
- --version X.Y.Z --verify-triggered # re-dispatches ONLY packages with no run; idempotent
74
+ --version X.Y.Z --verify-triggered # re-dispatches packages with no run OR a dead (cancelled/failed/timed-out) run; registry-gated; idempotent
75
75
  ```
76
76
 
77
77
  For a batch, preserve npm-latest ordering (`--after <prev>`). Verify against the registry, not the workflows. **Prevention:** push tags one-at-a-time ([`cut-procedure.md`](cut-procedure.md) §Step 8); `` `${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/release-pack.mjs` `` does this automatically and follows with `--verify-triggered`.