@adia-ai/adia-ui-forge 0.8.57 → 0.8.59
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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/CHANGELOG.md +64 -0
- package/README.md +2 -2
- package/__init__.py +3 -3
- package/package.json +2 -2
- package/plugin.yaml +1 -1
- package/scripts/lint-rules.generated.mjs +29342 -131
- package/skills/a2ui-maintenance/references/pipeline-overview.md +8 -2
- package/skills/component-md-authoring/SKILL.md +18 -14
- package/skills/demo-audit/SKILL.md +3 -3
- package/skills/demo-audit/agents/openai.yaml +1 -1
- package/skills/demo-audit/references/admin-shell-anatomy.md +9 -1
- package/skills/demo-audit/references/app-shell-pitfalls.md +7 -2
- package/skills/demo-audit/references/chat-shell-anatomy.md +98 -0
- package/skills/demo-audit/references/editor-shell-anatomy.md +109 -0
- package/skills/package-release/references/cut-procedure.md +66 -0
- package/skills/package-release/scripts/bump.mjs +41 -2
- package/skills/package-release/scripts/gate-roster.mjs +35 -0
- package/skills/package-release/scripts/release-pack.mjs +57 -0
- package/skills/primitive-authoring/SKILL.md +5 -2
- package/skills/primitive-authoring/agents/openai.yaml +1 -1
- package/skills/primitive-authoring/references/anti-patterns.md +40 -0
- package/skills/primitive-authoring/references/css-patterns.md +12 -0
- package/skills/primitive-authoring/references/form-control-sizing.md +10 -0
- package/skills/primitive-authoring/references/shell-patterns.md +8 -1
- package/skills/primitive-authoring/references/token-contract.md +19 -1
- package/skills/primitive-authoring/references/yaml-contract.md +16 -0
|
@@ -88,6 +88,26 @@ const DEFAULT_NPM_SCOPE = '@adia-ai';
|
|
|
88
88
|
const LOCKSTEP_ROSTER = PACKAGE_ROSTER.filter((p) => p.lockstep !== false);
|
|
89
89
|
const PACKAGES = LOCKSTEP_ROSTER.map((p) => p.name);
|
|
90
90
|
|
|
91
|
+
// Reject a --substantive-packages/--stub-packages name whose roster entry is
|
|
92
|
+
// unknown or lockstep:false, BEFORE Step 1 runs (gh#2894). The 0.8.58 cut
|
|
93
|
+
// passed `adia-plugins` (lockstep:false, gh#1133) in --substantive-packages;
|
|
94
|
+
// promote-unreleased.mjs happily rewrote its CHANGELOG's [Unreleased] header
|
|
95
|
+
// to a version that package doesn't ship, and the cut only caught it at Step
|
|
96
|
+
// 5.6's unstaged-tracked-files guard — AFTER the 34-gate pre-flight had
|
|
97
|
+
// already run. Matches PACKAGE_ROSTER by name form (`adia-plugins`) or dir
|
|
98
|
+
// form minus the `packages/` prefix (`plugins/adia-plugins`).
|
|
99
|
+
function assertLockstepPackages(names) {
|
|
100
|
+
for (const pkg of names) {
|
|
101
|
+
const entry = PACKAGE_ROSTER.find(
|
|
102
|
+
(p) => p.name === pkg || p.dir.replace(/^packages\//, '') === pkg,
|
|
103
|
+
);
|
|
104
|
+
if (!entry || entry.lockstep === false) {
|
|
105
|
+
console.error(`error: --substantive-packages/--stub-packages package "${pkg}" is not lockstep:true in scripts/package-paths.mjs PACKAGE_ROSTER (unknown or class-B) — rejected before Step 1.`);
|
|
106
|
+
process.exit(2);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
91
111
|
function parseArgs(argv) {
|
|
92
112
|
const args = {
|
|
93
113
|
version: null, date: null, previous: null, mode: null,
|
|
@@ -148,6 +168,12 @@ function parseArgs(argv) {
|
|
|
148
168
|
console.error(`error: --mode must be cut|from-scratch|handoff (got: ${args.mode})`);
|
|
149
169
|
process.exit(2);
|
|
150
170
|
}
|
|
171
|
+
// Roster validation (gh#2894), before any mode-specific check below —
|
|
172
|
+
// applies to BOTH cut and from-scratch (the script's own --help text shows
|
|
173
|
+
// both flags on a `cut` invocation).
|
|
174
|
+
if (args.substantivePackages || args.stubPackages) {
|
|
175
|
+
assertLockstepPackages([...(args.substantivePackages ?? []), ...(args.stubPackages ?? [])]);
|
|
176
|
+
}
|
|
151
177
|
// Handoff needs the GH-notes body at Step 10 — validate at PARSE time, not
|
|
152
178
|
// there: on the v0.8.10 cut the missing flag surfaced only after tags and
|
|
153
179
|
// npm publish were already irreversible (Step 10 is the LAST step). An arg
|
|
@@ -1753,6 +1779,37 @@ async function selftest() {
|
|
|
1753
1779
|
process.exit(1);
|
|
1754
1780
|
}
|
|
1755
1781
|
|
|
1782
|
+
// gh#2894 — a --substantive-packages/--stub-packages name whose roster
|
|
1783
|
+
// entry is lockstep:false (or unknown) must die at PARSE time, naming
|
|
1784
|
+
// the roster file, BEFORE Step 1 runs. The 0.8.58 cut passed
|
|
1785
|
+
// `adia-plugins` (lockstep:false) in --substantive-packages and only
|
|
1786
|
+
// caught it at Step 5.6, after the 34-gate pre-flight had already run.
|
|
1787
|
+
const rosterRejectShapes = [
|
|
1788
|
+
{ flags: '--substantive-packages adia-plugins', name: 'adia-plugins' },
|
|
1789
|
+
{ flags: '--stub-packages totally-unknown-package', name: 'totally-unknown-package' },
|
|
1790
|
+
];
|
|
1791
|
+
for (const { flags, name } of rosterRejectShapes) {
|
|
1792
|
+
let failed = false;
|
|
1793
|
+
let out = '';
|
|
1794
|
+
try {
|
|
1795
|
+
execSync(
|
|
1796
|
+
`node "${scriptPath}" --mode cut --version 9.9.9 --date 2026-01-01 --previous-version 9.9.8 ${flags} --dry`,
|
|
1797
|
+
{ cwd: REPO, encoding: 'utf8' },
|
|
1798
|
+
);
|
|
1799
|
+
} catch (e) {
|
|
1800
|
+
failed = true;
|
|
1801
|
+
out = (e.stdout || '') + (e.stderr || '');
|
|
1802
|
+
}
|
|
1803
|
+
if (!failed || !out.includes(name) || !out.includes('package-paths.mjs PACKAGE_ROSTER')) {
|
|
1804
|
+
console.error(`selftest FAIL: --substantive-packages/--stub-packages "${name}" must hard-reject naming scripts/package-paths.mjs PACKAGE_ROSTER`);
|
|
1805
|
+
process.exit(1);
|
|
1806
|
+
}
|
|
1807
|
+
if (out.includes('=== Step 1') || out.includes('gate roster')) {
|
|
1808
|
+
console.error(`selftest FAIL: the roster guard for "${name}" must fire BEFORE Step 1, not after`);
|
|
1809
|
+
process.exit(1);
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1756
1813
|
// gh#765 — from-scratch with substantive-but-no-stub packages must die at
|
|
1757
1814
|
// PARSE time, naming the ride-alongs, BEFORE any pre-flight gate runs
|
|
1758
1815
|
// (v0.8.29 burned three ~15-min pre-flight re-runs discovering it at
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: primitive-authoring
|
|
3
3
|
description: >-
|
|
4
|
-
Author or modify AdiaUI framework source inside the monorepo —
|
|
4
|
+
Author or modify AdiaUI framework source inside the monorepo — components
|
|
5
5
|
(packages/web-components), shells/composites (packages/web-modules), yaml
|
|
6
|
-
SoTs, demos. Use to add a new
|
|
6
|
+
SoTs, demos. Use to add a new component, fix a prop/slot/attribute/CSS
|
|
7
7
|
variant, update a yaml, build or fix a shell (chat-shell, admin-shell,
|
|
8
8
|
editor-shell — sidebar/pane/bespoke-tier composition), promote repeated
|
|
9
9
|
inline content into a shared module, audit a component's four-axis
|
|
@@ -100,5 +100,8 @@ then the full sequence — never suppress.
|
|
|
100
100
|
[authoring-cycle.md](references/authoring-cycle.md) Step 3 are fixed
|
|
101
101
|
immediately; contract-neutral pattern drift is proposed in review, not
|
|
102
102
|
blocked on.
|
|
103
|
+
- **SoT-change / component.md check**: new or changed states or aria
|
|
104
|
+
behavior on a yaml with a `component.md` sibling → component-md-authoring's
|
|
105
|
+
authored `screenReader`/`behavioral` sections may need a pass.
|
|
103
106
|
- A one-line bug that touches no props, CSS contract, or lifecycle doesn't
|
|
104
107
|
need this skill's overhead — just read the code and edit.
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "Primitive Authoring"
|
|
3
|
-
short_description: "Author or modify AdiaUI framework source inside the monorepo —
|
|
3
|
+
short_description: "Author or modify AdiaUI framework source inside the monorepo — components (packages/web-components), shells/composites (packages/web-modules), yaml SoTs, demos."
|
|
@@ -646,6 +646,46 @@ If you author a primitive that programmatically creates another (`document.creat
|
|
|
646
646
|
|
|
647
647
|
Source: ADR-0027.
|
|
648
648
|
|
|
649
|
+
### AP-R2 · Folding two visually-converging widgets into a shared module (ADR-0101)
|
|
650
|
+
|
|
651
|
+
Two widgets can converge visually — same tab-strip chrome, same header
|
|
652
|
+
band, same "looks like the same component" impression on the catalog page
|
|
653
|
+
— while carrying fundamentally different content models underneath. That
|
|
654
|
+
visual overlap alone is not sufficient reason to extract a shared module
|
|
655
|
+
or fold one into the other.
|
|
656
|
+
|
|
657
|
+
`inspector-ui` (a live devtools pane: four fixed panes, content pushed in
|
|
658
|
+
imperatively and repeatedly after connect via `update()`/`setHTML()`) and
|
|
659
|
+
`preview-ui` (static docs demo chrome: content captured once on connect,
|
|
660
|
+
shown two fixed ways) look like the same tabbed-view widget. They aren't:
|
|
661
|
+
folding one into the other would force the static-capture component to
|
|
662
|
+
grow a second, incompatible imperative-update mode it doesn't need for the
|
|
663
|
+
job it already does correctly everywhere it's used — added surface, not
|
|
664
|
+
removed surface. Extracting a shared tab-view module was evaluated and
|
|
665
|
+
rejected for the same reason: no common substrate exists beneath the
|
|
666
|
+
visual layer that both widgets could share without also merging their
|
|
667
|
+
different content models (`tabs-ui`'s synthesized strip vs. a hand-rolled
|
|
668
|
+
`role="tablist"` div built for single-panel content-swap semantics).
|
|
669
|
+
|
|
670
|
+
**The resolution is a mirror, not a merge:** keep both components on their
|
|
671
|
+
own contracts; restyle the trailing one to match the leading one's chrome
|
|
672
|
+
via scoped CSS only (`@scope (inspector-ui)` overrides copying
|
|
673
|
+
`preview-ui`'s header-tab anatomy, tokens, and typography) — no shared
|
|
674
|
+
component, no deprecation. The tradeoff this accepts: because the
|
|
675
|
+
mirroring is duplicated CSS, not a shared module, a future visual change
|
|
676
|
+
to the pattern's source component doesn't automatically propagate: check
|
|
677
|
+
the mirrored block for parallel drift when you next touch the pattern
|
|
678
|
+
that's being mirrored — the same "primitives that compose primitives
|
|
679
|
+
don't auto-import" discipline ADR-0027 already requires (AP-R1, above),
|
|
680
|
+
now applied to a duplicated CSS block instead of a duplicated import.
|
|
681
|
+
|
|
682
|
+
Before extracting a shared module for two things that "look the same,"
|
|
683
|
+
verify they'd actually share the extracted contract (props, update
|
|
684
|
+
semantics, lifecycle) — not just its rendered chrome. If they wouldn't,
|
|
685
|
+
mirror the chrome and keep both components separate.
|
|
686
|
+
|
|
687
|
+
Source: ADR-0101.
|
|
688
|
+
|
|
649
689
|
---
|
|
650
690
|
|
|
651
691
|
## Rapid-fire anti-patterns (mined from incident history)
|
|
@@ -62,6 +62,18 @@ Every component CSS file has exactly this shape:
|
|
|
62
62
|
|
|
63
63
|
When a parent tries to override `--component-bg`, specificity beats them. The zero-specificity layer is the contract.
|
|
64
64
|
|
|
65
|
+
## Region elements never self-style (2026-09-01, ADR-0105)
|
|
66
|
+
|
|
67
|
+
Native `<header>`/`<section>`/`<footer>` and `<header-ui>`/`<section-ui>`/`<footer-ui>` (a CSS-only slot-routing anatomy stub — no JS, no events, no `.css` file of its own) are **region elements**, not components in their own right. They carry zero layout CSS. Every grid, gap, sticky rule, and slot-vocabulary variant a region renders is written in the **closest host pattern's** `@scope (host-name)` block — `page-ui`, `card-ui`, `drawer-ui`, `modal-ui`, `anchor-bar`, the shells — matching the region by tag + attribute selector (`:scope > :where(header, header-ui)`, `:has(> [slot="action"])`, etc.), never in a region-owned stylesheet.
|
|
68
|
+
|
|
69
|
+
**If you're authoring a new host pattern that accepts region children, you MUST ship its `@scope` treatment for every region shape it accepts before it ships.** A host that accepts `<header-ui>` without a matching grid/sticky/slot-vocabulary block is not a lesser-but-valid host — the region element structurally cannot supply the missing layout itself, so the host renders wrong (usually unstyled block flow, or — the observed failure mode — a stale layout that packs content into the wrong shape). gh#2760 is the live example: `page-ui`'s header-band `@scope` block didn't reproduce the retired `admin-page-header`'s action-cluster grid for a multi-item `[slot="action"]` containing a block-level `<tabs-ui>`, so tabs overlapped buttons instead of getting their own row — same markup, incomplete host coverage.
|
|
70
|
+
|
|
71
|
+
**Bare `<header>` vs `<header-ui>`** — both reach a host's tag-matched rules identically (`:where(header, header-ui, …)` gives them equal zero specificity). They diverge only on rules keyed off a named-slot attribute selector (`:has(> [slot="icon"])`, etc.) — and there the divergence isn't mechanical (`slot=` is decorative everywhere, ADR-0033), it's documentation: `header-ui`'s own yaml is the only place the icon/heading/description/action vocabulary is written down and steered toward by `a2ui.rules`. Use bare `<header>`/`<section>`/`<footer>` only for unstructured default-slot content; use the `-ui` stub whenever content decomposes into the slot vocabulary — that's the only path an author or the A2UI generator discovers it exists.
|
|
72
|
+
|
|
73
|
+
A region element acquiring its own `.css` file (layout rules scoped to `header-ui` itself, not the host) is the anti-pattern this section exists to name — see ADR-0105 for the full rationale and the corollary above.
|
|
74
|
+
|
|
75
|
+
Source: [ADR-0105](../../../../../../docs/ops/adr/adr-0105-region-elements-never-self-style.md).
|
|
76
|
+
|
|
65
77
|
## Variants vs modes — the decision tree
|
|
66
78
|
|
|
67
79
|
The rule: **variants change tokens; modes change layout.**
|
|
@@ -31,6 +31,16 @@ Decision Class 1 removes the 20ch-class field-width floor for
|
|
|
31
31
|
floor, generalizing the `button-ui` keep-square pattern already named below
|
|
32
32
|
to the field-width components.
|
|
33
33
|
|
|
34
|
+
**[amended 2026-08-31, gh#2534, ADR-0095]** The square-minimum rule governs
|
|
35
|
+
a primitive's own OUTER inline-size floor against its own block-size only —
|
|
36
|
+
it does not reach a component's INTERNAL grid-column sizing. A token like
|
|
37
|
+
`field-ui[inline]`'s `--field-label-inline-min: 10ch` floors the label
|
|
38
|
+
*column* inside the component's internal grid, to keep sibling rows
|
|
39
|
+
aligned — not to prevent the host itself from collapsing — so it is not
|
|
40
|
+
"a `--*-min-width: 20ch`-class floor" in ADR-0095's sense and is not
|
|
41
|
+
retired by the Decision above. Don't apply the square-minimum rule to an
|
|
42
|
+
internal grid-column token by analogy to the field-width class.
|
|
43
|
+
|
|
34
44
|
*(Historical, pre-ADR-0095 rule — superseded by the retirement above, kept
|
|
35
45
|
only for context on what the old convention was.)* A
|
|
36
46
|
`--*-min-width: 20ch`-class floor (the gh#781/gh#1633 pattern on
|
|
@@ -448,6 +448,13 @@ Consumers (CodeMirror layout, canvas redraw, dependent UI) listen on the shell o
|
|
|
448
448
|
|
|
449
449
|
### admin cluster (canonical reference)
|
|
450
450
|
|
|
451
|
+
**[deprecated 2026-09-01, ADR-0098]** `admin-page`/`admin-page-header`/
|
|
452
|
+
`admin-page-body` are retired deprecate-then-delete; `admin-scroll` is
|
|
453
|
+
renamed wholesale to `page-scroll`. `page-ui[band]` + `page-scroll` are
|
|
454
|
+
now the canonical page-chrome pair — this cluster still describes
|
|
455
|
+
`admin-shell`'s pre-migration internal composition, valid during the
|
|
456
|
+
deprecation window.
|
|
457
|
+
|
|
451
458
|
- **3 JS-bearing children** — `<admin-shell>` (host coordinator), `<admin-sidebar>` (resize+collapse+persist), `<admin-command>` (Cmd+K palette)
|
|
452
459
|
- **7 CSS-only structural children** — `<admin-content>`, `<admin-topbar>`, `<admin-statusbar>`, `<admin-scroll>`, `<admin-page>`, `<admin-page-header>`, `<admin-page-body>`
|
|
453
460
|
- **CSS bridge** — `packages/web-modules/shell/admin-shell/css/admin-shell.bespoke.css` (~240 LOC)
|
|
@@ -547,7 +554,7 @@ When you encounter a legacy reference in old documentation or commit history, th
|
|
|
547
554
|
|
|
548
555
|
- [authoring-cycle.md](authoring-cycle.md) — general 5-step authoring procedure; shell-specific rules layer on top
|
|
549
556
|
- [api-contract.md](api-contract.md) — prop naming, reflection policy
|
|
550
|
-
- [css-patterns.md](css-patterns.md) — two-block `@scope`, variants vs modes
|
|
557
|
+
- [css-patterns.md](css-patterns.md) — two-block `@scope`, variants vs modes; §Region elements never self-style (ADR-0105) — a shell that hosts `<header-ui>`/`<section-ui>`/`<footer-ui>` region children (e.g. a bespoke `<X-header>` wrapping one) owns their `@scope` layout the same way `page-ui`/`card-ui`/`drawer-ui`/`modal-ui` do
|
|
551
558
|
- [lifecycle-patterns.md](lifecycle-patterns.md) — teardown patterns; the cleanup-closure pile and document-listener rules above layer on top
|
|
552
559
|
- [token-contract.md](token-contract.md) — zero-raw-color + L3 alias rules apply identically to shells
|
|
553
560
|
- [module-promotion.md](module-promotion.md) — the _different_ activity of lifting a cross-cluster reusable element (theme panel, command palette) into a shared module
|
|
@@ -36,6 +36,23 @@ Every component's `.css` file must follow the **two-block @scope** pattern:
|
|
|
36
36
|
}
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
### `@scope` donor selectors must be tag-independent when a component can swap its own rendered tag (ADR-0102, 2026-09-01)
|
|
40
|
+
|
|
41
|
+
A `@scope (my-component-ui)` donor selector matches by **literal tag
|
|
42
|
+
name**. That's a live gap for any component whose own contract lets it
|
|
43
|
+
render as a different tag than its own custom-element name — `text-ui`
|
|
44
|
+
already did this coincidentally (`variant="body"` → `<p>`,
|
|
45
|
+
`variant="caption"` → `<small>`), relying on ambient/bare-tag styling
|
|
46
|
+
rather than the component's own `[variant]` rules. ADR-0102 (`text-ui`'s
|
|
47
|
+
`level` prop, promoting the element to a real `<h1>`-`<h6>`) made this a
|
|
48
|
+
first-class case, requiring the donor selector to stay tag-independent —
|
|
49
|
+
e.g. `@scope (text-ui, [data-text])` with the renderer stamping a stable
|
|
50
|
+
marker attribute on every such element regardless of the resolved tag —
|
|
51
|
+
so a promoted `<h2 level="2" variant="heading">` keeps its component
|
|
52
|
+
styling instead of silently falling back to bare-tag `:where(hN)`
|
|
53
|
+
defaults elsewhere in the cascade. Any component that can render under a
|
|
54
|
+
tag other than its own name needs the same tag-independent scope check.
|
|
55
|
+
|
|
39
56
|
## Hard rules
|
|
40
57
|
|
|
41
58
|
1. **Zero raw color values** anywhere in component CSS
|
|
@@ -139,7 +156,8 @@ mechanics; never work from this summary alone):
|
|
|
139
156
|
in a component yaml, check it against the spec's attribute inventory; the
|
|
140
157
|
only sanctioned collisions live in the spec's §11 Global-Attribute Exemption
|
|
141
158
|
List (first entries: `swatch-ui[color]` / `noodles-ui[color]`, ADR-0054;
|
|
142
|
-
granted since: `qr-code-ui[color]` / `icon-ui[weight]`, ADR-0070
|
|
159
|
+
granted since: `qr-code-ui[color]` / `icon-ui[weight]`, ADR-0070;
|
|
160
|
+
`badge-ui[weight]` → `icon-ui[weight]`, ADR-0100).
|
|
143
161
|
|
|
144
162
|
Beyond the global grammar, ADR-0063
|
|
145
163
|
(`docs/ops/adr/adr-0063-attribute-grammar-addendum.md`) ratifies seven
|
|
@@ -401,6 +401,22 @@ already has). As of this writing `deriveProps()` still synthesizes only the
|
|
|
401
401
|
three props above and `catalog-validator.js` has no trait-name check —
|
|
402
402
|
gh#2513 tracks the build.
|
|
403
403
|
|
|
404
|
+
### Sentinel-0 defaults and tag-promoting props (ADR-0102, 2026-09-01)
|
|
405
|
+
|
|
406
|
+
Not every escape hatch belongs on `CatalogComponentCommon` — a prop scoped
|
|
407
|
+
to one component's own semantics stays on that component's own yaml, even
|
|
408
|
+
when it changes the rendered tag. `text-ui`'s `level` (integer 0-6,
|
|
409
|
+
default `0`) is the worked example: `0` is the sentinel for "no
|
|
410
|
+
promotion, current behavior" — the same `0 = off` convention `text-ui`'s
|
|
411
|
+
own `lines` prop already used (`lines: 0` = no clamp) — and `1`-`6`
|
|
412
|
+
promotes the rendered element to a real native `<h1>`-`<h6>`, independent
|
|
413
|
+
of `variant` (which stays presentational-only: typography tokens, never a
|
|
414
|
+
tag). `deriveProps()` needs no change for this — a per-sidecar prop like
|
|
415
|
+
`level` already flows into the generated catalog schema generically; only
|
|
416
|
+
a `CatalogComponentCommon` universal (`slot`/`hidden`/`ariaLive`/`traits`,
|
|
417
|
+
above) needs a wire-schema edit. See ADR-0102 for the full contract
|
|
418
|
+
(renderer branch, dead-code removal, non-goals).
|
|
419
|
+
|
|
404
420
|
### `required: true` field
|
|
405
421
|
|
|
406
422
|
**When to use**: only for props where omitting them makes the component meaningless or inaccessible.
|