@adia-ai/adia-ui-forge 0.8.17 → 0.8.19

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-forge",
3
- "version": "0.8.17",
3
+ "version": "0.8.19",
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,23 @@
1
1
  # Changelog — adia-forge
2
2
 
3
+ ## [0.8.19] — 2026-07-27
4
+
5
+ ### Fixed
6
+ - **`adia-release` promotes the repo-root CHANGELOG at cut time** (operator directive 2026-07-27). The cut promoted the 11 package changelogs but never the root one, so repo-level entries accumulated under `[Unreleased]` across five release windows (0.8.7 → 0.8.18) until an operator caught the pile-up in the site's Changelog panel. `promote-unreleased.mjs` gains the special target `root` (repo-root `CHANGELOG.md`, deliberately outside `PACKAGE_ROSTER` — the roster feeds publish/tag consumers); `release-pack.mjs` adds `root` to the promote list by detection whenever that file's `[Unreleased]` carries content — no flag, since root entries are optional per window — and the post-promotion loud guard now sweeps `root` too, which also catches a duplicate `[Unreleased]` heading (the promote swaps only the first; the second trips the guard).
7
+
8
+ ### Maintenance
9
+ - **`.claude-plugin/plugin.json` version bump** — moves in lockstep with package.json (the `/plugin update` cache key).
10
+ - **`skills/` touched in this release window** (3 file(s), e.g. `references/changelog-discipline.md`) — carried by the entries above.
11
+
12
+ ## [0.8.18] — 2026-07-27
13
+
14
+ ### Added
15
+ - **`adia-author` writes down the tag-naming rule across tiers** (`references/code-style.md`) — primitives and standalone feature composites carry `-ui`; shell/bespoke-cluster children and runtime roots are bare, namespaced by their cluster prefix. Ratified when `form-popover` shipped as the only unsuffixed standalone composite (renamed `form-popover-ui` the next release); `theme-panel` is explicitly grandfathered, not precedent.
16
+
17
+ ### Maintenance
18
+ - **`.claude-plugin/plugin.json` version bump** — moves in lockstep with package.json (the `/plugin update` cache key).
19
+ - **`skills/` touched in this release window** (1 file(s), e.g. `references/code-style.md`) — carried by the entries above.
20
+
3
21
  ## [0.8.17] — 2026-07-27
4
22
 
5
23
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/adia-ui-forge",
3
- "version": "0.8.17",
3
+ "version": "0.8.19",
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",
@@ -24,6 +24,24 @@ Every interactive surface, form control, layout container, and content block shi
24
24
 
25
25
  The catalog has 125 component directories under `packages/web-components/components/`. Before composing, confirm via `get_component_map` (the a2ui MCP) that the one you want exists — and what its real prop names are.
26
26
 
27
+ ### Tag naming across tiers (`-ui` or bare)
28
+
29
+ Two rules cover every tag in the monorepo (ratified 2026-07-27, when
30
+ `form-popover` shipped as the only standalone composite without the
31
+ suffix and was renamed to `form-popover-ui` one release later):
32
+
33
+ | Tier | Convention | Examples |
34
+ | --- | --- | --- |
35
+ | Primitives (`packages/web-components`) | **always `-ui`** | `button-ui`, `select-ui`, `nav-item-ui` |
36
+ | Standalone feature composites (`packages/web-modules`) — drop-in anywhere, consumer-facing | **always `-ui`** | `confirm-dialog-ui`, `plan-picker-ui`, `date-range-selector-ui`, `form-popover-ui` |
37
+ | Shell/bespoke-cluster children + runtime roots | **bare** — the cluster prefix is the namespace | `admin-topbar`, `chat-composer`, `editor-canvas`, `simple-shell`, `a2ui-root`, `gen-root` |
38
+
39
+ `theme-panel` (bare, standalone) predates this rule and is grandfathered —
40
+ do not copy it as precedent for a new standalone composite. The DIRECTORY
41
+ name never carries the suffix in either tier (`plan-picker/` →
42
+ `<plan-picker-ui>`), and the a2ui `component:` name is unaffected
43
+ (`FormPopover`).
44
+
27
45
  ## Layout primitives
28
46
 
29
47
  Use `<col-ui>`, `<row-ui>`, `<grid-ui>`, `<stack-ui>` for layout — never a bare `<div>`. The primitives carry the project's spacing scale (`gap`, padding tokens) and respond to density / theme providers; a plain `<div>` strands content outside that system.
@@ -22,7 +22,7 @@ node "${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/promote-unreleased.mjs"
22
22
  --version 0.X.Y --date YYYY-MM-DD --packages web-components,web-modules,a2ui/corpus
23
23
  ```
24
24
 
25
- (Package args take both name and path form — `a2ui-corpus` and `a2ui/corpus` both resolve, via `package-paths.mjs`.)
25
+ (Package args take both name and path form — `a2ui-corpus` and `a2ui/corpus` both resolve, via `package-paths.mjs`. The special target `root` is the repo-root CHANGELOG.md; release-pack adds it automatically whenever that file's `[Unreleased]` carries content, so it never needs listing in `--substantive-packages`.)
26
26
 
27
27
  Non-clean cases:
28
28
 
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  // promote-unreleased.mjs — promote `## [Unreleased]` → `## [vX.Y.Z] — DATE`
3
- // in N package CHANGELOGs.
3
+ // in N package CHANGELOGs. The special target `root` is the repo-root
4
+ // CHANGELOG.md — same swap, no package resolution.
4
5
  //
5
6
  // Idempotent. Stops if any target already has the [vX.Y.Z] heading
6
7
  // (means you've already run this; would create a duplicate block).
@@ -54,8 +55,11 @@ function parseArgs(argv) {
54
55
  }
55
56
 
56
57
  function promote(repo, pkg, version, date, dry) {
57
- // Accept both name-form (a2ui-corpus) and path-form (a2ui/corpus). See package-paths.mjs.
58
- const p = resolvePackageChangelog(repo, pkg);
58
+ // Accept both name-form (a2ui-corpus) and path-form (a2ui/corpus) see
59
+ // package-paths.mjs plus `root` for the repo-root CHANGELOG (deliberately
60
+ // NOT in PACKAGE_ROSTER: the roster feeds publish/tag consumers, and the
61
+ // root file is no package).
62
+ const p = pkg === 'root' ? path.join(repo, 'CHANGELOG.md') : resolvePackageChangelog(repo, pkg);
59
63
  if (!fs.existsSync(p)) {
60
64
  console.error(` ERROR ${pkg} — CHANGELOG.md not found at ${p}`);
61
65
  return 'missing';
@@ -112,6 +116,14 @@ function selftest() {
112
116
  const r2 = promote(tmp, 'web-components', '0.2.0', '2026-02-02', false);
113
117
  if (r2 !== 'already-promoted') { console.error(`selftest FAIL: expected 'already-promoted' on re-run, got '${r2}'`); process.exit(1); }
114
118
 
119
+ // `root` target resolves to <repo>/CHANGELOG.md, no package resolution.
120
+ const rootPath = path.join(tmp, 'CHANGELOG.md');
121
+ fs.writeFileSync(rootPath, '# Root\n\n## [Unreleased]\n\n### Added\n- z\n\n## [0.1.0] — 2026-01-01\n\n- y\n');
122
+ const r3 = promote(tmp, 'root', '0.2.0', '2026-02-02', false);
123
+ if (r3 !== 'promoted') { console.error(`selftest FAIL: expected 'promoted' for root, got '${r3}'`); process.exit(1); }
124
+ const rootOut = fs.readFileSync(rootPath, 'utf8');
125
+ if (!rootOut.includes('## [0.2.0] — 2026-02-02') || rootOut.includes('## [Unreleased]')) { console.error('selftest FAIL: root heading not swapped'); process.exit(1); }
126
+
115
127
  console.log('selftest OK');
116
128
  } finally {
117
129
  fs.rmSync(tmp, { recursive: true, force: true });
@@ -312,17 +312,24 @@ function step3PreFlight(args) {
312
312
  // v0.8.4 cut — the plugin packages needed hand-promotion because nothing
313
313
  // caught it). Returns null when clean, or a short description when dirty.
314
314
  function unpromotedUnreleased(repo, pkg) {
315
- const p = resolvePackageChangelog(repo, pkg);
315
+ // `root` = the repo-root CHANGELOG — no package resolution. It went
316
+ // unwatched until 2026-07-27, when five windows of repo-level entries
317
+ // (0.8.7 → 0.8.18) were found piled up under [Unreleased].
318
+ const p = pkg === 'root' ? path.join(repo, 'CHANGELOG.md') : resolvePackageChangelog(repo, pkg);
316
319
  if (!fs.existsSync(p)) return null;
317
320
  const txt = fs.readFileSync(p, 'utf8');
318
- const m = txt.match(/^## \[Unreleased\][^\n]*\n([\s\S]*?)(?=^## \[|$(?![\s\S]))/m);
319
- if (!m) return null;
320
- // Strip subsection headers (### Added, ### Fixed, …) and blank lines — what's
321
- // left is real content, not just empty scaffolding.
322
- const body = m[1].replace(/^###.*$/gm, '').trim();
323
- if (body.length === 0) return null;
324
- const firstLine = body.split('\n')[0].slice(0, 100);
325
- return `${pkg}: "${firstLine}${firstLine.length === 100 ? '…' : ''}"`;
321
+ // EVERY [Unreleased] block, not just the first: with duplicate headings an
322
+ // empty first + non-empty second would otherwise read clean while the
323
+ // promote (which swaps only the first heading) strands the content.
324
+ for (const m of txt.matchAll(/^## \[Unreleased\][^\n]*\n([\s\S]*?)(?=^## \[|$(?![\s\S]))/gm)) {
325
+ // Strip subsection headers (### Added, ### Fixed, …) and blank lines — what's
326
+ // left is real content, not just empty scaffolding.
327
+ const body = m[1].replace(/^###.*$/gm, '').trim();
328
+ if (body.length === 0) continue;
329
+ const firstLine = body.split('\n')[0].slice(0, 100);
330
+ return `${pkg}: "${firstLine}${firstLine.length === 100 ? '…' : ''}"`;
331
+ }
332
+ return null;
326
333
  }
327
334
 
328
335
  // ── Step 4 — Promote + bump + lockfile ───────────────────────────
@@ -338,8 +345,14 @@ function step4PromoteAndBump(args) {
338
345
  // (whether a peer already promoted vs. this cycle authoring fresh) differs,
339
346
  // not whether promotion is needed. `--substantive-packages` is the same flag
340
347
  // in both modes.
341
- if (args.substantivePackages) {
342
- const cmd = `node ${SCRIPT_DIR}/promote-unreleased.mjs --version ${args.version} --date ${args.date} --packages ${args.substantivePackages.join(',')}`;
348
+ // Root CHANGELOG rides on detection, not a flag: repo-level entries are
349
+ // optional per window, so `root` joins the promote list exactly when its
350
+ // [Unreleased] carries real content. A repo-level-only window (0.8.7 was
351
+ // one) promotes root with zero substantive packages.
352
+ const promoteTargets = [...(args.substantivePackages ?? [])];
353
+ if (unpromotedUnreleased(REPO, 'root')) promoteTargets.push('root');
354
+ if (promoteTargets.length > 0) {
355
+ const cmd = `node ${SCRIPT_DIR}/promote-unreleased.mjs --version ${args.version} --date ${args.date} --packages ${promoteTargets.join(',')}`;
343
356
  sh(cmd, args);
344
357
  }
345
358
 
@@ -353,16 +366,20 @@ function step4PromoteAndBump(args) {
353
366
  // carry a non-empty [Unreleased] — that content would ride through the
354
367
  // bump unpromoted, undocumented in the tarball's own CHANGELOG.
355
368
  if (!args.dry) {
356
- const dirty = PACKAGES.map((p) => unpromotedUnreleased(REPO, p)).filter(Boolean);
369
+ // `root` in the sweep also catches a DUPLICATE [Unreleased] heading (the
370
+ // promote swaps only the first occurrence, so a second one survives to
371
+ // trip this guard — the 0.8.18 root file had exactly that shape).
372
+ const dirty = [...PACKAGES, 'root'].map((p) => unpromotedUnreleased(REPO, p)).filter(Boolean);
357
373
  if (dirty.length > 0) {
358
- console.error(`\nERROR: ${dirty.length} package(s) still have non-empty [Unreleased] content after promotion:`);
374
+ console.error(`\nERROR: ${dirty.length} target(s) still have non-empty [Unreleased] content after promotion:`);
359
375
  for (const d of dirty) console.error(` ✗ ${d}`);
360
376
  console.error('\n Add the missing package(s) to --substantive-packages (or --stub-packages if');
361
- console.error(' they are pure ride-alongs) and re-run. See changelog-discipline.md §Detecting');
362
- console.error(' source-changing packages.');
377
+ console.error(' they are pure ride-alongs) and re-run; a dirty `root` means a duplicate');
378
+ console.error(' [Unreleased] heading in the repo-root CHANGELOG — merge the blocks by hand.');
379
+ console.error(' See changelog-discipline.md §Detecting source-changing packages.');
363
380
  process.exit(1);
364
381
  }
365
- console.log(` ✓ no unpromoted [Unreleased] content across all ${PACKAGES.length} packages`);
382
+ console.log(` ✓ no unpromoted [Unreleased] content across all ${PACKAGES.length} packages + root`);
366
383
  }
367
384
 
368
385
  sh(`node ${SCRIPT_DIR}/bump.mjs --from ${args.previous} --to ${args.version}`, args);