@adia-ai/adia-ui-forge 0.8.36 → 0.8.37

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.
Files changed (126) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +40 -18
  3. package/README.md +17 -17
  4. package/agents/{a2ui-builder.corpus.json → a2ui-maintenance-agent.corpus.json} +1 -1
  5. package/agents/{a2ui-builder.md → a2ui-maintenance-agent.md} +6 -6
  6. package/agents/framework-reviewer.md +6 -6
  7. package/agents/{release-builder.corpus.json → package-release-agent.corpus.json} +3 -3
  8. package/agents/{release-builder.md → package-release-agent.md} +7 -7
  9. package/agents/{component-author.corpus.json → primitive-authoring-agent.corpus.json} +2 -2
  10. package/agents/{component-author.md → primitive-authoring-agent.md} +7 -7
  11. package/agents/routing-corpus.json +40 -40
  12. package/commands/{dogfood.md → demo-audit.md} +2 -1
  13. package/commands/{gen-review.md → gen-ui-review.md} +3 -2
  14. package/commands/{release.md → package-release.md} +3 -2
  15. package/commands/{deploy.md → site-deployment.md} +2 -1
  16. package/package.json +1 -1
  17. package/references/contracts/a2ui-mcp-surface.md +16 -8
  18. package/references/contracts/migration-guide-format.md +4 -4
  19. package/scripts/demo-postwrite-pattern-gate +2 -2
  20. package/scripts/release-pretag-docs-gate +2 -2
  21. package/scripts/sidecar-prewrite-guard +3 -3
  22. package/skills/{adia-a2ui → a2ui-maintenance}/SKILL.md +6 -5
  23. package/skills/{adia-a2ui → a2ui-maintenance}/evals/evals.json +8 -8
  24. package/skills/{adia-a2ui → a2ui-maintenance}/evals/routing-corpus.json +51 -51
  25. package/skills/{adia-a2ui → a2ui-maintenance}/references/eval-diagnostics.md +2 -2
  26. package/skills/{adia-a2ui → a2ui-maintenance}/references/format-extension-decisions.md +2 -2
  27. package/skills/{adia-a2ui → a2ui-maintenance}/references/mcp-pipeline-ops.md +1 -1
  28. package/skills/{adia-a2ui → a2ui-maintenance}/references/mcp-tool-reference.md +5 -4
  29. package/skills/{adia-a2ui → a2ui-maintenance}/references/pipeline-overview.md +15 -15
  30. package/skills/{adia-a2ui → a2ui-maintenance}/references/semantic-fail-lifting.md +2 -2
  31. package/skills/{adia-dogfood → demo-audit}/SKILL.md +8 -8
  32. package/skills/{adia-dogfood → demo-audit}/evals/evals.json +7 -7
  33. package/skills/{adia-dogfood → demo-audit}/evals/routing-corpus.json +43 -43
  34. package/skills/{adia-dogfood → demo-audit}/references/visual-probe-triage.md +1 -1
  35. package/skills/{adia-dogfood → demo-audit}/scripts/analyze.mjs +2 -2
  36. package/skills/{adia-gen-review → gen-ui-review}/SKILL.md +13 -13
  37. package/skills/{adia-gen-review → gen-ui-review}/evals/evals.json +7 -7
  38. package/skills/{adia-gen-review → gen-ui-review}/evals/routing-corpus.json +43 -43
  39. package/skills/{adia-gen-review → gen-ui-review}/references/corpus-html-patterns.md +3 -3
  40. package/skills/{adia-gen-review → gen-ui-review}/references/loop-protocol.md +11 -11
  41. package/skills/{adia-gen-review → gen-ui-review}/references/scores.schema.json +1 -1
  42. package/skills/{adia-gen-review → gen-ui-review}/scripts/gen-review-coverage-audit.mjs +4 -4
  43. package/skills/{adia-gen-review → gen-ui-review}/scripts/gen-review-decompose.mjs +6 -6
  44. package/skills/{adia-gen-review → gen-ui-review}/scripts/gen-review-status.mjs +5 -5
  45. package/skills/{adia-gen-review → gen-ui-review}/scripts/validate-cycle-scores.mjs +5 -5
  46. package/skills/{adia-llm-internals → llm-client-maintenance}/SKILL.md +4 -4
  47. package/skills/{adia-llm-internals → llm-client-maintenance}/evals/evals.json +7 -7
  48. package/skills/{adia-llm-internals → llm-client-maintenance}/evals/routing-corpus.json +49 -49
  49. package/skills/{adia-release → package-release}/SKILL.md +16 -16
  50. package/skills/{adia-release → package-release}/evals/evals.json +7 -7
  51. package/skills/{adia-release → package-release}/evals/routing-corpus.json +45 -45
  52. package/skills/{adia-release → package-release}/references/changelog-discipline.md +4 -4
  53. package/skills/{adia-release → package-release}/references/cut-procedure.md +167 -28
  54. package/skills/{adia-release → package-release}/references/gates-catalog.md +4 -3
  55. package/skills/{adia-release → package-release}/references/independent-package-release.md +2 -2
  56. package/skills/{adia-release → package-release}/references/migration-guide-authoring.md +12 -3
  57. package/skills/{adia-release → package-release}/references/recovery-paths.md +5 -5
  58. package/skills/{adia-release → package-release}/scripts/bump.mjs +77 -30
  59. package/skills/{adia-release → package-release}/scripts/dispatch-publish.mjs +1 -1
  60. package/skills/{adia-release → package-release}/scripts/gate-roster.mjs +17 -6
  61. package/skills/{adia-release → package-release}/scripts/insert-stub.mjs +29 -7
  62. package/skills/{adia-release → package-release}/scripts/package-paths.mjs +51 -6
  63. package/skills/{adia-release → package-release}/scripts/pr-bridge.mjs +1 -1
  64. package/skills/{adia-release → package-release}/scripts/promote-unreleased.mjs +1 -1
  65. package/skills/{adia-release → package-release}/scripts/release-pack.mjs +18 -4
  66. package/skills/{adia-release → package-release}/scripts/tag-lockstep.mjs +2 -2
  67. package/skills/{adia-author → primitive-authoring}/SKILL.md +7 -7
  68. package/skills/{adia-author → primitive-authoring}/evals/evals.json +7 -7
  69. package/skills/{adia-author → primitive-authoring}/evals/routing-corpus.json +61 -61
  70. package/skills/{adia-author → primitive-authoring}/references/INDEX.md +2 -2
  71. package/skills/{adia-author → primitive-authoring}/references/authoring-cycle.md +1 -1
  72. package/skills/{adia-author → primitive-authoring}/references/code-style.md +5 -5
  73. package/skills/{adia-author → primitive-authoring}/references/common-gotchas.md +1 -1
  74. package/skills/{adia-author → primitive-authoring}/references/llm-bridge.md +7 -7
  75. package/skills/{adia-author → primitive-authoring}/references/module-promotion.md +3 -3
  76. package/skills/{adia-author → primitive-authoring}/references/shell-patterns.md +2 -2
  77. package/skills/{adia-author → primitive-authoring}/references/trait-pages.md +1 -1
  78. package/skills/{adia-author → primitive-authoring}/scripts/build-canonical-pattern-index.mjs +1 -1
  79. package/skills/{adia-deploy → site-deployment}/SKILL.md +4 -4
  80. package/skills/{adia-deploy → site-deployment}/evals/evals.json +8 -8
  81. package/skills/{adia-deploy → site-deployment}/evals/routing-corpus.json +49 -49
  82. package/skills/{adia-deploy → site-deployment}/references/deploy-playbooks.md +2 -2
  83. package/skills/{adia-site-docs → site-docs-authoring}/SKILL.md +4 -4
  84. package/skills/{adia-site-docs → site-docs-authoring}/evals/evals.json +7 -7
  85. package/skills/{adia-site-docs → site-docs-authoring}/evals/routing-corpus.json +51 -51
  86. package/skills/{adia-site-docs → site-docs-authoring}/intent.md +8 -8
  87. package/skills/{adia-ssr → ssr-compatibility}/SKILL.md +8 -8
  88. package/skills/{adia-ssr → ssr-compatibility}/evals/evals.json +7 -7
  89. package/skills/{adia-ssr → ssr-compatibility}/evals/routing-corpus.json +43 -43
  90. package/skills/{adia-ssr → ssr-compatibility}/references/failure-shapes.md +1 -1
  91. package/skills/{adia-ssr → ssr-compatibility}/references/guard-patterns.md +1 -1
  92. /package/skills/{adia-a2ui → a2ui-maintenance}/references/anti-patterns.md +0 -0
  93. /package/skills/{adia-a2ui → a2ui-maintenance}/references/chunk-authoring.md +0 -0
  94. /package/skills/{adia-a2ui → a2ui-maintenance}/references/corpus-discipline.md +0 -0
  95. /package/skills/{adia-a2ui → a2ui-maintenance}/references/leverage-rules.md +0 -0
  96. /package/skills/{adia-a2ui → a2ui-maintenance}/references/strategy-engines.md +0 -0
  97. /package/skills/{adia-a2ui → a2ui-maintenance}/references/zettel-calibration.md +0 -0
  98. /package/skills/{adia-dogfood → demo-audit}/references/admin-shell-anatomy.md +0 -0
  99. /package/skills/{adia-dogfood → demo-audit}/references/app-shell-pitfalls.md +0 -0
  100. /package/skills/{adia-dogfood → demo-audit}/references/card-anatomy-sweep.md +0 -0
  101. /package/skills/{adia-dogfood → demo-audit}/references/html-attr-sweep.md +0 -0
  102. /package/skills/{adia-dogfood → demo-audit}/references/native-leak-annotations.md +0 -0
  103. /package/skills/{adia-gen-review → gen-ui-review}/references/rubric-cosmetic.md +0 -0
  104. /package/skills/{adia-gen-review → gen-ui-review}/references/rubric-decompose.md +0 -0
  105. /package/skills/{adia-gen-review → gen-ui-review}/references/rubric-score.md +0 -0
  106. /package/skills/{adia-llm-internals → llm-client-maintenance}/references/adapter-contract.md +0 -0
  107. /package/skills/{adia-llm-internals → llm-client-maintenance}/references/add-a-provider.md +0 -0
  108. /package/skills/{adia-llm-internals → llm-client-maintenance}/references/bridge-facade.md +0 -0
  109. /package/skills/{adia-llm-internals → llm-client-maintenance}/references/browser-proxy-boundary.md +0 -0
  110. /package/skills/{adia-llm-internals → llm-client-maintenance}/references/model-registry.md +0 -0
  111. /package/skills/{adia-llm-internals → llm-client-maintenance}/references/streaming-sse.md +0 -0
  112. /package/skills/{adia-release → package-release}/references/notes-authoring.md +0 -0
  113. /package/skills/{adia-release → package-release}/scripts/assert-monorepo-root.mjs +0 -0
  114. /package/skills/{adia-author → primitive-authoring}/references/anti-patterns.md +0 -0
  115. /package/skills/{adia-author → primitive-authoring}/references/api-contract.md +0 -0
  116. /package/skills/{adia-author → primitive-authoring}/references/canonical-pattern-index.md +0 -0
  117. /package/skills/{adia-author → primitive-authoring}/references/composite-demo-protocol.md +0 -0
  118. /package/skills/{adia-author → primitive-authoring}/references/css-patterns.md +0 -0
  119. /package/skills/{adia-author → primitive-authoring}/references/lifecycle-patterns.md +0 -0
  120. /package/skills/{adia-author → primitive-authoring}/references/primitive-audit.md +0 -0
  121. /package/skills/{adia-author → primitive-authoring}/references/token-contract.md +0 -0
  122. /package/skills/{adia-author → primitive-authoring}/references/worked-example.md +0 -0
  123. /package/skills/{adia-author → primitive-authoring}/references/yaml-contract.md +0 -0
  124. /package/skills/{adia-ssr → ssr-compatibility}/references/consumer-workarounds.md +0 -0
  125. /package/skills/{adia-ssr → ssr-compatibility}/references/status-ledger.md +0 -0
  126. /package/skills/{adia-ssr → ssr-compatibility}/references/test-without-linkedom.md +0 -0
@@ -5,7 +5,7 @@
5
5
  > failure routing), [`changelog-discipline.md`](changelog-discipline.md) (promotion
6
6
  > + F-N1 enrichment), [`recovery-paths.md`](recovery-paths.md) (when it goes wrong).
7
7
  > The concrete gate names, the roster's package paths (`scripts/package-paths.mjs`,
8
- > 13 lockstep as of gh#607), and `ui-kit.exe.xyz` deploy are the @adia-ai
8
+ > 16 lockstep as of gh#1240's MCP-distribution fold), and `ui-kit.exe.xyz` deploy are the @adia-ai
9
9
  > monorepo's worked example of the portable discipline. `$REPO` = repo root.
10
10
 
11
11
  Two entry variants, converging at Step 5:
@@ -13,7 +13,7 @@ Two entry variants, converging at Step 5:
13
13
  - **Variant A — deploy handoff:** a peer pre-cut the release commit + CHANGELOG + bump + lockfile. Re-baseline, verify, run **Step 4e** (release docs + team notes — the peer's cut may not have generated them, and the pretag gate blocks Step 6 without them), then resume at Step 6 (tag).
14
14
  - **Variant B — author from scratch:** source landed under `## [Unreleased]` with no bump. Do Step 4 (promotion + bump + lockfile), then the full tail.
15
15
 
16
- `` `${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/release-pack.mjs` `` (bundled) mechanizes the sequence in two phases per invariant 3 — the cut modes stop at the release commit (PR → CI → merge happens between), `--mode handoff` runs tag→publish→deploy from post-merge main. **Run with `--go` for an operator-initiated release** (single authorization, §below); the steps below are the manual/diagnostic form and run straight through on the same one-go model.
16
+ `` `${CLAUDE_PLUGIN_ROOT}/skills/package-release/scripts/release-pack.mjs` `` (bundled) mechanizes the sequence in two phases per invariant 3 — the cut modes stop at the release commit (PR → CI → merge happens between), `--mode handoff` runs tag→publish→deploy from post-merge main. **Run with `--go` for an operator-initiated release** (single authorization, §below); the steps below are the manual/diagnostic form and run straight through on the same one-go model.
17
17
 
18
18
  | Step | Action | Mutates? |
19
19
  | --- | --- | --- |
@@ -71,7 +71,7 @@ node scripts/build/site-a2ui.mjs --stale # /site/components/* converted row
71
71
  npm run harvest:chunks # chunk corpus from site/apps/playgrounds/catalog
72
72
  npm run check:embeddings-fresh # ← run this FIRST; if it passes, SKIP the rebuild below
73
73
  npm run build:embeddings:chunks # ONLY if the line above failed (needs OPENAI_API_KEY)
74
- node scripts/release/check-token-semantics-sync.mjs --fix # adia-tokens role-roster + alias-layer
74
+ node scripts/release/check-token-semantics-sync.mjs --fix # token-selection role-roster + alias-layer
75
75
  npm run build -w @adia-ai/llm # tsc artifact build:bundle-js resolves
76
76
  npm run build:bundles # dist CSS+JS bundles
77
77
  ```
@@ -110,25 +110,30 @@ npm run dogfood:status # 17 P0/P1 dogfood floor
110
110
  npm run check:examples-md-fresh # 18 .examples.md vs .examples.html
111
111
  npm run verify:site-a2ui # 19 site-a2ui rows vs source fragments
112
112
  npm run verify:contrast # 20 WCAG AA — canvas-text AND text-on-fill
113
- npm run check:token-semantics-sync # 21 adia-tokens generated refs vs token sources
113
+ npm run check:token-semantics-sync # 21 token-selection generated refs vs token sources
114
114
  npm run check:demo-routes # 22 demo surfaces routed + patterns indexed
115
115
  npm run check:brand-assets # 23 brand mark token-driven, not baked raster
116
116
  node scripts/release/check-cut-hygiene.mjs --version <mode-dependent> # 24 README CDN-pin + version currency — CUT modes pre-flight at the PREVIOUS version (the README claim is exact-match and only moves at the Step-4 bump; Step 4g re-proves at the cut version), HANDOFF pre-flights at the CUT version (post-merge, the claim already moved)
117
117
  python3 packages/plugins/adia-ui-factory/scripts/adia-scaffold selftest # 25 scaffold specifiers resolve in packed @adia-ai/web-components + @adia-ai/web-modules tarballs (gh#1132)
118
- node scripts/release/check-dx-sweep-freshness.mjs # 26 DX sweep fresh + not regression-RED (gh#1137 REQ-05 — FLAGGED, needs adia-release confirmation; see the script's own header)
118
+ node scripts/release/check-dx-sweep-freshness.mjs # 26 DX sweep fresh + not regression-RED (gh#1137 REQ-05 — FLAGGED, needs package-release confirmation; see the script's own header)
119
119
  npm run eval:diff -- --engine free-form # 27 fresh free-form eval run (evals/health input, gh#1135)
120
120
  node scripts/release/write-eval-health.mjs --version <cut> # 28 evals/health/<version>.json committed, AC-01/AC-02 run for real (gh#1135, WS-4 SPEC REQ-06)
121
+ node scripts/release/check-estate-split-latch.mjs # 29 no lockstep cut mid estate-split (ADR-0048 / gh#1192) — see the note below
121
122
  ```
122
123
 
124
+ **Gate 29 was the ADR-0048 latch; since P5 it is a permanent invariant.** Between P1 and P5 the repo was correct in-repo but deliberately **not publishable** (old-name stubs marked `private: true` that the roster still mapped, plus dependency edges onto workspace packages no cut published), and a cut in that window would have shipped broken packages that npm cannot unpublish. **P5 cleared it by landing the real thing** — the six stubs became publishable shims and `PACKAGE_ROSTER` gained the three remaining new names, at which point all 9 offending edges resolved and the gate went green on its own. No gate logic was changed.
125
+
126
+ It asserts three tree properties over the lockstep roster — no roster dir is `private: true`, and no roster package depends on a workspace package that is either private or absent from the roster — which is why it self-cleared rather than needing a removal. Never bypass it by flipping `private` or hand-editing the roster; that is the exact failure it exists to prevent. Self-check: `node scripts/release/check-estate-split-latch.mjs selftest`. It stays in the roster permanently: it now costs nothing and catches any FUTURE unpublishable dependency edge.
127
+
123
128
  Any red → route via [`gates-catalog.md`](gates-catalog.md); fix at the source, re-run the narrowest gate, then re-run the full sequence. The canonical miss: a cut that ran 7 of the gates shipped a stale-embeddings defect that surfaced a day later and cost a tag-move recovery.
124
129
 
125
130
  ## §Step 4 — (Variant B, or ANY variant with uncommitted `[Unreleased]` content) Promote, bump, lockfile
126
131
 
127
132
  **Run 4a whenever a hand-authored `## [Unreleased]` section is still sitting uncommitted, not only on a strict Variant B.** `release-pack.mjs --mode cut` (a peer's pre-staged content, not yet promoted) needs it exactly as much as `--mode from-scratch` does — the v0.8.4 near-miss was `--mode cut` skipping this step entirely because the doc (and the script) only associated promotion with "from scratch". Both modes now run it and both hard-fail before the bump if any roster package still carries non-empty `[Unreleased]` content afterward.
128
133
 
129
- **4a. Promote** `## [Unreleased]` → `## [vX.Y.Z] — YYYY-MM-DD` per package (`` `${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/promote-unreleased.mjs` ``); author fresh blocks for changed-but-unlogged packages; stub the pure ride-alongs (`` `${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/insert-stub.mjs` ``). Classification recipe + shapes: [`changelog-discipline.md`](changelog-discipline.md).
134
+ **4a. Promote** `## [Unreleased]` → `## [vX.Y.Z] — YYYY-MM-DD` per package (`` `${CLAUDE_PLUGIN_ROOT}/skills/package-release/scripts/promote-unreleased.mjs` ``); author fresh blocks for changed-but-unlogged packages; stub the pure ride-alongs (`` `${CLAUDE_PLUGIN_ROOT}/skills/package-release/scripts/insert-stub.mjs` ``). Classification recipe + shapes: [`changelog-discipline.md`](changelog-discipline.md).
130
135
 
131
- **4b. Bump.** PATCH vs MINOR: **MINOR is reserved for API-surface breaks only** (removed/renamed prop, attribute, slot, event, token, or tag). Visible behavior changes, re-scalings, and opt-in features stay PATCH; a CHANGELOG bullet saying "(MINOR behavior change)" is prose, not a semver directive. Unqualified "bump version" = PATCH; don't round-trip to ask. `node "${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/bump.mjs" --from X.Y.Z-1 --to X.Y.Z`. On a MINOR cut, also bump the internal `@adia-ai/*` `^ranges` separately (bump.mjs touches `"version"` fields only) — and a MINOR cut owes a MIGRATION GUIDE section ([`migration-guide-authoring.md`](migration-guide-authoring.md)).
136
+ **4b. Bump.** PATCH vs MINOR: **MINOR is reserved for API-surface breaks only** (removed/renamed prop, attribute, slot, event, token, or tag). Visible behavior changes, re-scalings, and opt-in features stay PATCH; a CHANGELOG bullet saying "(MINOR behavior change)" is prose, not a semver directive. Unqualified "bump version" = PATCH; don't round-trip to ask. `node "${CLAUDE_PLUGIN_ROOT}/skills/package-release/scripts/bump.mjs" --from X.Y.Z-1 --to X.Y.Z`. On a MINOR cut, also bump the internal `@adia-ai/*` `^ranges` separately (bump.mjs touches `"version"` fields only) — and a MINOR cut owes a MIGRATION GUIDE section ([`migration-guide-authoring.md`](migration-guide-authoring.md)).
132
137
 
133
138
  **4c. Lockfile.** `npm install --package-lock-only --no-audit --no-fund` — must land in the release commit. The publish workflows open with `npm ci`, which hard-fails on a version/lockfile mismatch: a bump without the regenerated lockfile passes locally and breaks **every** publish at clean-install.
134
139
 
@@ -190,10 +195,10 @@ stub sections exist leaves it nothing to append to, and the gap resurfaces
190
195
  as F-N1 warns at the push boundary, costing a tag move:
191
196
 
192
197
  ```bash
193
- node "${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/insert-stub.mjs" \
198
+ node "${CLAUDE_PLUGIN_ROOT}/skills/package-release/scripts/insert-stub.mjs" \
194
199
  --version X.Y.Z --date YYYY-MM-DD --previous-version X.Y.Z-1 \
195
200
  --substantive "<one-line>" --xref "<anchor>" --packages <missing-stubs> # 4a-stub — FIRST, only the missing ones (hard-errors on existing sections)
196
- 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)
201
+ node "${CLAUDE_PLUGIN_ROOT}/skills/package-release/scripts/bump.mjs" --from X.Y.Z-1 --to X.Y.Z # 4b (skip if versions already moved)
197
202
  npm install --package-lock-only --no-audit --no-fund # 4c
198
203
  npm run check:lockstep # 4d
199
204
  node scripts/build/derive-genui-catalog.mjs # 4d.5 — catalogId carries the bumped version (gh#617)
@@ -245,7 +250,7 @@ unresolved AND no review requests changes; any other state stops with the
245
250
  evidence, never force-merges):
246
251
 
247
252
  ```bash
248
- node "${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/pr-bridge.mjs" \
253
+ node "${CLAUDE_PLUGIN_ROOT}/skills/package-release/scripts/pr-bridge.mjs" \
249
254
  --branch "release/vX.Y.Z" --title "release: vX.Y.Z lockstep" --body-file <path>
250
255
  ```
251
256
 
@@ -271,7 +276,7 @@ The operator's initiating instruction covers the whole cycle — no per-step re-
271
276
 
272
277
  | Evidence logged before | Content | Why it's still printed |
273
278
  | --- | --- | --- |
274
- | Tagging (Step 6) | The planned tag list (umbrella + 13 per-package) | The log line a recovery diagnoses from |
279
+ | Tagging (Step 6) | The planned tag list (umbrella + 16 per-package) | The log line a recovery diagnoses from |
275
280
  | Pushing (Step 8) | The Step 7 F-N1 output + tag list + `origin/main..HEAD` count | F-N1's first real evidence — and its ERROR path still hard-stops unconditionally |
276
281
  | Publishing (Step 9) | The current registry snapshot (versions + `dist-tags.latest`) | Ordering is verified against what's LIVE, mechanically |
277
282
  | Deploying (Step 10) | The `deploy-site.yml` dispatch (never a raw rsync) | The workflow carries its own GitHub-environment human gate |
@@ -293,8 +298,8 @@ Stage its CHANGELOG edits into the release commit (the Step-5 allowlist already
293
298
  Log the planned tag list (evidence table above), then tag **at `main`'s post-merge HEAD** (post-bump fixes belong in the tarball; the window's last merge is the tag point — exception: batch push tags each version at its own release-merge SHA):
294
299
 
295
300
  ```bash
296
- node "${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/tag-lockstep.mjs" \
297
- --version X.Y.Z # umbrella vX.Y.Z + 13 <pkg>-vX.Y.Z (11 npm + 2 plugins, 2026-07-15 policy)
301
+ node "${CLAUDE_PLUGIN_ROOT}/skills/package-release/scripts/tag-lockstep.mjs" \
302
+ --version X.Y.Z # umbrella vX.Y.Z + 16 <pkg>-vX.Y.Z (14 npm + 2 plugins)
298
303
  ```
299
304
 
300
305
  ## §Step 7 — F-N1 release trip-wire
@@ -313,14 +318,20 @@ only the tags push here. Log the Step 7 F-N1 results + the tag list
313
318
  is 0 (a non-zero count means local commits bypassed the PR flow — stop
314
319
  and route them through a PR first); then:
315
320
 
321
+ The package list comes from the roster, never a hand list. This block used to
322
+ enumerate the names inline and silently went stale — it pushed 11 tags against
323
+ a 14-package roster (`agent`, `persona` and `a2ui-protocol-mcp` missing), which
324
+ is three packages that would simply never publish.
325
+
316
326
  ```bash
317
- for t in web-components-vX.Y.Z web-modules-vX.Y.Z llm-vX.Y.Z a2ui-runtime-vX.Y.Z \
318
- a2ui-compose-vX.Y.Z a2ui-corpus-vX.Y.Z a2ui-mcp-vX.Y.Z \
319
- a2ui-retrieval-vX.Y.Z a2ui-validator-vX.Y.Z \
320
- adia-ui-factory-vX.Y.Z adia-ui-forge-vX.Y.Z; do
321
- git -C "$REPO" push origin "$t" # ONE tag per push — batched multi-tag
322
- done # pushes drop the create event (Scenario 7)
323
- git -C "$REPO" push origin vX.Y.Z # umbrella last; triggers nothing
327
+ PKGS=$(node -e "import('${CLAUDE_PLUGIN_ROOT}/skills/package-release/scripts/package-paths.mjs')
328
+ .then(m => console.log(m.PACKAGE_ROSTER.filter(p => p.lockstep !== false).map(p => p.name).join(' ')))")
329
+ echo "$PKGS" # log it — this IS the tag list evidence
330
+
331
+ for p in $PKGS; do
332
+ git -C "$REPO" push origin "${p}-vX.Y.Z" # ONE tag per push batched multi-tag
333
+ done # pushes drop the create event (Scenario 7)
334
+ git -C "$REPO" push origin vX.Y.Z # umbrella last; triggers nothing
324
335
  ```
325
336
 
326
337
  ## §Step 9 — Publish
@@ -328,16 +339,22 @@ git -C "$REPO" push origin vX.Y.Z # umbrella last; triggers nothing
328
339
  Log the current registry snapshot (per-package versions + `dist-tags.latest`) before dispatching. For batch pushes, verify ordering against that snapshot: **oldest version publishes and settles first** — `npm dist-tag latest` is set by publish order. Then:
329
340
 
330
341
  ```bash
331
- node "${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/dispatch-publish.mjs" \
342
+ node "${CLAUDE_PLUGIN_ROOT}/skills/package-release/scripts/dispatch-publish.mjs" \
332
343
  --version X.Y.Z --verify-triggered # re-dispatches missing/dead runs; registry-gated (gh#763)
333
344
  ```
334
345
 
335
346
  Wait for the workflows to settle, then verify against the **registry**, never the workflow's green check:
336
347
 
337
348
  ```bash
338
- for pkg in web-components web-modules llm agent persona a2ui-runtime a2ui-compose \
339
- a2ui-corpus a2ui-mcp a2ui-retrieval a2ui-validator adia-ui-factory \
340
- adia-ui-forge; do
349
+ # The name list comes from PACKAGE_ROSTER, never a hand-typed loop — this loop
350
+ # WAS hand-typed and went stale the moment ADR-0048 changed the roster (it still
351
+ # named the six pre-split a2ui packages and omitted a2ui-protocol-mcp, so a
352
+ # "verified" cut would have skipped checking the package most likely to be
353
+ # missing: the new one).
354
+ for pkg in $(node -e '
355
+ import("./packages/plugins/adia-ui-forge/skills/package-release/scripts/package-paths.mjs")
356
+ .then(m => console.log(m.PACKAGE_ROSTER.filter(p => p.lockstep !== false).map(p => p.name).join(" ")))
357
+ '); do
341
358
  echo -n "$pkg: "; npm view "@adia-ai/$pkg" version
342
359
  done
343
360
  npm view @adia-ai/web-components dist-tags.latest # must equal X.Y.Z
@@ -348,9 +365,10 @@ Zero workflows fired after a tag push → [`recovery-paths.md`](recovery-paths.m
348
365
  ## §Step 10 — GH releases + site deploy dispatch
349
366
 
350
367
  ```bash
351
- for pkg in web-components web-modules llm agent persona a2ui-runtime a2ui-compose \
352
- a2ui-corpus a2ui-mcp a2ui-retrieval a2ui-validator adia-ui-factory \
353
- adia-ui-forge; do
368
+ for pkg in $(node -e '
369
+ import("./packages/plugins/adia-ui-forge/skills/package-release/scripts/package-paths.mjs")
370
+ .then(m => console.log(m.PACKAGE_ROSTER.filter(p => p.lockstep !== false).map(p => p.name).join(" ")))
371
+ '); do
354
372
  gh release create "$pkg-vX.Y.Z" --title "@adia-ai/$pkg vX.Y.Z" --notes-file <body>.md
355
373
  done
356
374
  # Site deploy goes through the pipeline, never a raw rsync. The dispatch is
@@ -359,7 +377,7 @@ done
359
377
  gh workflow run "Deploy site (ui-kit.exe.xyz)" --repo adiahealth/gen-ui-kit --ref main
360
378
  ```
361
379
 
362
- Deploy discipline (the release tenant of the demo-site host; VM/service ops belong to the deploy skill, `adia-deploy`):
380
+ Deploy discipline (the release tenant of the demo-site host; VM/service ops belong to the deploy skill, `site-deployment`):
363
381
 
364
382
  - `deploy-site.yml` owns build → pre-flight verify → snapshot → rsync → post-deploy verify → auto-rollback; it builds from `main`, so the release commit must already be merged (§Step 5.7 guarantees this). A raw local rsync bypasses every one of those gates — `release-pack.mjs` dispatches the workflow (never rsyncs directly) behind its own confirm (H1, forge-campaign gh#268 audit: the script had drifted from this already-documented procedure).
365
383
  - **Verify deployed FILES, never SPA routes.** The docs site returns HTTP 200 + the same ~5 KB shell for *every* path; an unmatched route renders blank with no error. Curl a content file and grep for real bytes:
@@ -377,8 +395,129 @@ Any Step-2 stashes: `git stash pop`; flag conflicts to the operator.
377
395
 
378
396
  Always author notes at end-of-cycle without being asked — context is freshest now. Single version → [`notes-authoring.md`](notes-authoring.md); ≥2 versions since the last broadcast → its §Rollup section. Surface the draft inline for copy-paste; the operator owns posting. Skip only on an explicit "no notes".
379
397
 
398
+ ## §The 0.8.37 estate-split cut (ADR-0048 P7 — ONE cut, then delete this section)
399
+
400
+ Extra steps for the single cut that ships the a2ui/gen-ui split. They ride the
401
+ normal procedure above; nothing here replaces a step.
402
+
403
+ **It is a PATCH cut, by operator ruling (2026-08-13, gh#1192)** — `0.8.37`, not
404
+ the `0.9.0` MINOR the ADR originally proposed. Two consequences to hold in mind
405
+ before Step 4:
406
+
407
+ - **Invariant 2 (PATCH asymmetry) applies UNCHANGED — no exception.** Internal
408
+ `@adia-ai/*` dep ranges stay `^0.8.0` on a patch cut, and that is exactly
409
+ right here: `targetCaret("0.8.37")` is `^0.8.0`, the three new names
410
+ (`a2ui`, `gen-ui`, `mcp` — retargeted gh#1240, 2026-08-14: this row named
411
+ `gen-ui-mcp` before the MCP-distribution fold; that name never published,
412
+ `mcp` is its real successor) publish `0.8.37` as their FIRST version, and
413
+ `^0.8.0` resolves to it. The shims' deps on their successors are already
414
+ `^0.8.0` and need no edit; web-modules' EXACT peer pin moves to `0.8.37` by
415
+ the normal peer rule (gh#489), which `bump.mjs` does. So nothing in the split
416
+ needs a hand-written range — if `check:lockstep` asks for one, something else
417
+ is wrong; do not hand-pin.
418
+ - **A patch-shaped number does NOT relax the MIGRATION GUIDE obligation**
419
+ (invariant 7). Six published names are being replaced; the guide is owed
420
+ regardless of what the version looks like. This is the one failure mode the
421
+ small number invites.
422
+
423
+ **In the release commit (Step 4/5), not before and not after:**
424
+
425
+ 1. **Flip the factory `.mcp.json` pin NAME** — `@adia-ai/a2ui-mcp` →
426
+ `@adia-ai/mcp` (retargeted gh#1240, 2026-08-14: this step named
427
+ `@adia-ai/gen-ui-mcp` before the MCP-distribution fold folded that planned
428
+ name — and `@adia-ai/a2ui-protocol-mcp` — into `@adia-ai/mcp` before either
429
+ ever published; the flip target moved, the flip's own timing did not), in
430
+ `packages/plugins/adia-ui-factory/.mcp.json` AND its README prose echo
431
+ (invariant 8 covers both files; `bump.mjs` moves the VERSION on either
432
+ name, so only the name is hand-edited). The pinned args also grow a
433
+ subcommand: `["-y", "@adia-ai/mcp@0.8.37", "gen-ui"]` — the fold's bin
434
+ dispatches `adia-mcp gen-ui`/`adia-mcp protocol`, not a bare invocation.
435
+ **Why here and not at P5:** invariant 8 forces the pin to the CURRENT
436
+ lockstep version. Before this cut that is `0.8.36`, and `@adia-ai/mcp`
437
+ has no `0.8.36` on npm — flipping early pins every local-marketplace install
438
+ to `npx -y @adia-ai/mcp@0.8.36`, i.e. a registry 404. In this commit
439
+ the version becomes `0.8.37`, which this cut publishes. (The reasoning is
440
+ unchanged by the 0.9.0 → 0.8.37 ruling, and unchanged again by the
441
+ gen-ui-mcp → mcp retarget; only the numbers and the name moved.)
442
+ `check:lockstep` accepts any name this pin has carried (`MCP_PIN_RE`), so
443
+ the flip is gate-clean regardless of which name is live when this runs.
444
+
445
+ **Config-KEY standardization rides the same flip (operator ruling
446
+ 2026-08-14):** the `mcpServers` key names move with the pin so a tool
447
+ call's prefix always names which server answered. Factory `.mcp.json`:
448
+ key `a2ui` → `adia-gen-ui`. Repo root `.mcp.json`: key `adia-ui` →
449
+ `adia-gen-ui`. New entries added later use `adia-protocol` /
450
+ `adia-factory` for the other subcommands. Key renames change consumers'
451
+ tool prefixes (`mcp__a2ui__*` → `mcp__adia-gen-ui__*`) — that is the
452
+ point, and it ships in the same breaking-named cut as everything else.
453
+
454
+ **After Step 9 (publish), once `npm view <old-name> version` shows the shim on
455
+ the registry — never before:**
456
+
457
+ 2. **Deprecate the six retired names.** The list is projected from the shim
458
+ generator's SPEC, so it cannot drift from what actually shipped:
459
+
460
+ ```bash
461
+ node scripts/build/generate-shims.mjs --deprecate-commands
462
+ ```
463
+
464
+ It prints the `npm deprecate` lines plus an `npm info <pkg> deprecated`
465
+ verify line per package. Print-only by design — read them, then run them.
466
+ **Verify by reading the field back**; a zero exit code is not proof.
467
+ Deprecating before the shim publishes leaves consumers at a dead end (the
468
+ pointer text names a package version that isn't there yet).
469
+
470
+ 3. **Verify a shim tarball's CONTENTS, not just the publish exit code** — e.g.
471
+ `npm pack @adia-ai/a2ui-runtime@0.8.37 && tar -tzf *.tgz`, then import one
472
+ subpath from a scratch dir and confirm it resolves through to
473
+ `@adia-ai/a2ui`.
474
+
475
+ **Follow-up PR (not this cut):** delete `packages/shims/`, the roster rows
476
+ marked `shim: true`, their six `publish-a2ui-*.yml` workflows,
477
+ `resolvePackageDir`'s `shims/` fallback, `scripts/build/generate-shims.mjs`,
478
+ `scripts/verify/check-shim-bridges.mjs`, the `check:shims` gate, and this
479
+ section. Then narrow `MCP_PIN_RE` + `bump.mjs`'s pin pattern back to the single
480
+ new name.
481
+
380
482
  ---
381
483
 
484
+ ## §The 0.8.38 cut (the clean-world cut — delete this section after it lands)
485
+
486
+ The first cut AFTER the estate-split bridge. Pre-conditions, all verifiable
487
+ before Step 1:
488
+
489
+ 1. **The shim-deletion follow-up PR has MERGED** (the §0.8.37 follow-up above:
490
+ `packages/shims/` gone, roster shim rows gone, the six `publish-a2ui-*.yml`
491
+ workflows gone, `check:shims`/`check-shim-bridges` retired). The roster file
492
+ is the member count — never transcribe it.
493
+ 2. **Deprecation pointers have been live for the full inter-cut window** —
494
+ `npm info <old-name> deprecated` returns the pointer text for all six names.
495
+ This is the stated policy for the one-cycle bridge: caret-floating consumers
496
+ landed on the shims at 0.8.37, were pointed at the successors the entire
497
+ window, and hard-stop only now. If the window was shorter than ~a week of
498
+ real consumer exposure, surface to the operator before cutting rather than
499
+ deleting the bridge out from under them.
500
+ 3. **`fix-old-names` has swept the known consumer repos** against the shim
501
+ names (the closeout half of the rename wave + split).
502
+ 4. Whatever landed between cuts rides along normally — as of authoring, the
503
+ queued candidates are the directory wave (gh#1244, npm-invisible), the
504
+ catalog tiers (gh#1243, new `@adia-ai/a2ui` subpath `./catalog`), and the
505
+ factory MCP server (gh#1241, a third `adia-mcp` subcommand — NOT a new
506
+ package). None changes the roster; if one does by then, the roster file
507
+ already reflects it and the gates enforce it.
508
+
509
+ **In the release commit:** nothing estate-split-specific remains — this is a
510
+ normal cut. If gh#1241 landed, the factory `.mcp.json` MAY additionally
511
+ register `adia-factory` (`["-y", "@adia-ai/mcp@0.8.38", "factory"]`) — an
512
+ addition, not a flip.
513
+
514
+ **After publish:** the standard registry verify, plus one split-closure check:
515
+ `npm view @adia-ai/a2ui-compose versions` should END at 0.8.37 (the shim's one
516
+ and only publish) — a 0.8.38 appearing under any retired name means a workflow
517
+ survived the follow-up PR; kill the tag and investigate before anything else.
518
+
519
+ Delete this section in the same PR that closes the split's tracking record.
520
+
382
521
  ## §Variant A shortcut
383
522
 
384
523
  Peer pre-cut the release commit: Step 1 (confirm HEAD is the `release(*): vX.Y.Z` commit) → Step 2 → Step 3 → skip 4–5 → resume at Step 6. The peer's release commit must already be MERGED to `main` via its PR before tagging (invariant 3 — tag at post-merge HEAD, never at an unpushed local commit); an unmerged pre-cut commit goes through Step 5.7 first, it doesn't shortcut past it.
@@ -14,9 +14,9 @@ Row layout per gate: **What** · **Typical failure** · **Recovery**.
14
14
 
15
15
  ### `npm run check:lockstep`
16
16
 
17
- - **What:** all 13 lockstep `@adia-ai/*` packages declare the same `version` (the class-B `adia-plugins` package is excluded — `lockstep: false`, `scripts/package-paths.mjs`); internal `@adia-ai/*` dep ranges match policy (`^X.Y.0` during PATCH cycles, bumped at MINOR).
17
+ - **What:** all 16 lockstep `@adia-ai/*` packages declare the same `version` (the class-B `adia-plugins` package is excluded — `lockstep: false`, `scripts/package-paths.mjs`); internal `@adia-ai/*` dep ranges match policy (`^X.Y.0` during PATCH cycles, bumped at MINOR).
18
18
  - **Typical failure:** one package forgot to bump; a peer edited an internal range mid-PATCH; a `^0.0.x` range slipped in.
19
- - **Recovery:** version drift → `` `${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/bump.mjs` ``; range drift → `npm run check:lockstep:fix` auto-aligns, then re-run.
19
+ - **Recovery:** version drift → `` `${CLAUDE_PLUGIN_ROOT}/skills/package-release/scripts/bump.mjs` ``; range drift → `npm run check:lockstep:fix` auto-aligns, then re-run.
20
20
  - **Why `^0.0.x` is forbidden:** npm pre-1.0 semver only widens the caret when major+minor aren't both zero — `^0.0.6` resolves to `>=0.0.6 <0.0.7`, locked to exactly 0.0.6. An internal dep pinned that way silently installs a *stale* sibling on every fresh `npm i` (this shipped a real ~4-day-latent bug before the lockstep policy). The `^X.Y.0` floor (Y≥1) widens correctly across patches; trust the gate, don't reason about caret semantics by hand. Moot at 1.0.0.
21
21
 
22
22
  ### `node scripts/release/check-release.mjs --all-pending` (F-N1, the release trip-wire)
@@ -133,6 +133,7 @@ The two `audit:*` probes are soft-fail pre-cut (exit-1 only on criticals); findi
133
133
 
134
134
  - **What:** `chunk-embeddings.json` at-or-newer-than `chunks/_index.json`.
135
135
  - **Recovery:** `npm run build:embeddings:chunks` (needs `OPENAI_API_KEY`; ~6s). Stage `chunk-embeddings.json` + note the regen in the a2ui-corpus CHANGELOG `[vX.Y.Z]`.
136
+ - **2026-08-12 — isolated agent worktrees can't run this recovery step.** An agent dispatched with `isolation: worktree` (e.g. `build-lead` resolving a merge conflict) structurally can't read the dotenv secrets file — a permission guard blocks copying it into the worktree, correctly, since that's a content-revealing operation on a secret file. Hit twice in one sweep: two separate PRs (#1113, #1117) each needed this recovery step mid-conflict-resolution and both had to hand off to a human running the command directly in their own terminal / via `!`. This step is a standing human-in-the-loop point, not something to keep dispatching an agent for.
136
137
 
137
138
  ### `npm run check:css-bundles-fresh` / `npm run check:js-bundles-fresh`
138
139
 
@@ -204,7 +205,7 @@ npm run check:demo-shells
204
205
 
205
206
  Add `verify:corpus` + `check:embeddings-fresh` if chunks were touched; `check:lightningcss-build` if CSS was touched; F-N1 if unpushed release tags exist.
206
207
 
207
- **Full pre-cut sweep** — the 28-gate roster in [`cut-procedure.md`](cut-procedure.md) §Step 3, sourced from `` `${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/gate-roster.mjs` `` (the ONE list; `release-pack.mjs` imports and runs it in full — a subset run is impossible without editing that file). ~90s wall time.
208
+ **Full pre-cut sweep** — the 29-gate roster in [`cut-procedure.md`](cut-procedure.md) §Step 3, sourced from `` `${CLAUDE_PLUGIN_ROOT}/skills/package-release/scripts/gate-roster.mjs` `` (the ONE list; `release-pack.mjs` imports and runs it in full — a subset run is impossible without editing that file). ~90s wall time.
208
209
 
209
210
  **Omnibus** — `npm run check` invokes everything. Heavy; use when re-baselining a stale checkout.
210
211
 
@@ -19,7 +19,7 @@
19
19
  | | Class A — lockstep cut | Class B — independent package |
20
20
  | --- | --- | --- |
21
21
  | Versioning | whole set bumps together | own version line per package |
22
- | Lockstep gate | enforced (`check:lockstep` 13/13) | **excluded** — a class-B package is not listed in `check-lockstep.mjs`'s `PACKAGES` (`adia-plugins`, the current member, is filtered out via `lockstep: false` — `scripts/package-paths.mjs`) |
22
+ | Lockstep gate | enforced (`check:lockstep` 16/16) | **excluded** — a class-B package is not listed in `check-lockstep.mjs`'s `PACKAGES` (`adia-plugins`, the current member, is filtered out via `lockstep: false` — `scripts/package-paths.mjs`) |
23
23
  | Umbrella tag | `vX.Y.Z` + per-package | **none** — per-package tags only (versions differ) |
24
24
  | `dist-tag` ordering | cross-package publish order matters | N/A |
25
25
  | Demo-site deploy | yes | **none** |
@@ -47,7 +47,7 @@ Substrate fixes (release scripts / lockfile / CHANGELOG), landed once as a norma
47
47
 
48
48
  ## §Verify target
49
49
 
50
- The published package(s) on the **npm registry** at their independent versions, plus the npm-published `@adia-ai/adia-plugins` manifest referencing the new versions (ADR-0045). NOT "all 13 lockstep packages + the demo site" — that's class A.
50
+ The published package(s) on the **npm registry** at their independent versions, plus the npm-published `@adia-ai/adia-plugins` manifest referencing the new versions (ADR-0045). NOT "all 16 lockstep packages + the demo site" — that's class A.
51
51
 
52
52
  ## §Gotchas (from the first independent cut, factory v0.2.2 + forge v0.1.2)
53
53
 
@@ -5,11 +5,11 @@
5
5
  > the release ships the breaking change, so the release authors the guide section
6
6
  > consumers follow. The shared producer/consumer format contract is
7
7
  > [`../../../references/contracts/migration-guide-format.md`](../../../references/contracts/migration-guide-format.md)
8
- > — the factory's `adia-migrate` skill consumes exactly that shape.
8
+ > — the factory's `app-migration` skill consumes exactly that shape.
9
9
  >
10
10
  > Scope split: **producer (this skill)** authors the guide section + migrates the
11
11
  > framework's own in-repo surfaces. **Consumer** (a downstream app sweep) is the
12
- > factory plugin's `adia-migrate` — decline and redirect. Designing the breaking
12
+ > factory plugin's `app-migration` — decline and redirect. Designing the breaking
13
13
  > change itself is a contract decision upstream of both.
14
14
 
15
15
  The guide lives at **`.claude/docs/MIGRATION GUIDE.md`** (the space in the filename is intentional; it's a first-class consumer artifact). One section per breaking release, newest at top; a consumer jumping versions reads the merged span. Every cut — breaking or not — gets a version-scope bullet in the top index (see the format contract).
@@ -37,7 +37,9 @@ Author (or extend) a section when the cut removes or renames a **public API symb
37
37
  git grep -nE '<pattern that surfaces call sites>'
38
38
 
39
39
  **Sweep:**
40
- git grep -nlE '<pattern>' | xargs perl -i -pe 's/<old>/<new>/g'
40
+ git grep -lz '<pattern>' | while IFS= read -r -d '' f; do
41
+ perl -i -pe 's/<old>/<new>/g' "$f"
42
+ done
41
43
  ```
42
44
 
43
45
  Shape rules:
@@ -46,6 +48,13 @@ Shape rules:
46
48
  - **Mechanical vs manual, explicitly labeled.** A pure rename ships a `perl -i -pe`; a semantic flip ships a "manual review — here's why" note (sed can't tell author intent).
47
49
  - **One component per sweep regex** — alternation captures (`<(toast|alert)-ui`) don't preserve the matched alternative cleanly.
48
50
  - **HTML-attribute regexes only match HTML/JSX** — author a separate JS-side regex when the symbol has a programmatic form (`el.variant = 'danger'`).
51
+ - **`git grep -lz | while read -d '' f; do perl -i … "$f"; done`, never `| xargs perl -i`.**
52
+ The `xargs` form hangs on zero matches (GNU xargs still runs perl once with no file argument,
53
+ and `perl -i -pe` then blocks reading stdin instead of no-op'ing) and, if `<old>`/`<new>` are
54
+ passed as shell variables rather than literals, an `@`-bearing replacement (`@adia-ai/...`)
55
+ parses as perl array interpolation when inlined into `-pe` and silently substitutes empty
56
+ (gh#1233) — pass such strings through the environment instead of the perl source, per
57
+ `.claude/docs/MIGRATION GUIDE.md`'s `§0.8.37` `sweep()` helper.
49
58
 
50
59
  **3. Migrate the in-repo surfaces FIRST.** Before the cut ships, run the audit + sweep against `apps/`, `playgrounds/`, `catalog/`, `packages/web-components/components/*/*.html` — the release must not ship broken examples of the thing it changed, and dogfooding the recipe here is what proves it works for consumers.
51
60
 
@@ -17,7 +17,7 @@
17
17
 
18
18
  1. Verify the skip: `npm view <pkg> versions --json | tail` (no X+1 on npm) + `git tag --list 'vX.Y.*'` (no tags). Run pre-flight at the peer's commit to confirm it's shippable.
19
19
  2. Correct the version via a **new commit on top** (not amend — the peer's commit stays for history).
20
- 3. Sweep every occurrence of the wrong version: 13 × package.json, 13 × CHANGELOG (headers + body refs — the lockstep roster, `scripts/package-paths.mjs`), any doc/CSS-comment refs, then regenerate the lockfile. Preserve filename references that intentionally encode the original label.
20
+ 3. Sweep every occurrence of the wrong version: 16 × package.json, 16 × CHANGELOG (headers + body refs — the lockstep roster, `scripts/package-paths.mjs`), any doc/CSS-comment refs, then regenerate the lockfile. Preserve filename references that intentionally encode the original label.
21
21
  4. Commit as `fix(release): correct vX.Y.Z+1 version-skip → vX.Y.Z` documenting the discovery, then resume [`cut-procedure.md`](cut-procedure.md) at Step 5.
22
22
 
23
23
  ## §Scenario 2 — Batch push
@@ -38,7 +38,7 @@
38
38
 
39
39
  **Shape:** source + CHANGELOG entries landed under `## [Unreleased]`, no bump, no release commit.
40
40
 
41
- **Resolution:** [`cut-procedure.md`](cut-procedure.md) Variant B; `` `${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/promote-unreleased.mjs` `` mechanizes the heading swap; fresh blocks per [`changelog-discipline.md`](changelog-discipline.md) §Authoring.
41
+ **Resolution:** [`cut-procedure.md`](cut-procedure.md) Variant B; `` `${CLAUDE_PLUGIN_ROOT}/skills/package-release/scripts/promote-unreleased.mjs` `` mechanizes the heading swap; fresh blocks per [`changelog-discipline.md`](changelog-discipline.md) §Authoring.
42
42
 
43
43
  ## §Scenario 4 — `[Unreleased]` extension (early cut + entangled fix)
44
44
 
@@ -63,18 +63,18 @@
63
63
 
64
64
  ## §Scenario 7 — Tags pushed, ZERO publish workflows fired
65
65
 
66
- **Shape:** all 14 tags exist on origin (umbrella + one per lockstep-roster package — `scripts/package-paths.mjs`), but no `publish-<pkg>.yml` run exists for them; npm latest unchanged; nothing errored.
66
+ **Shape:** all 17 tags exist on origin (umbrella + one per lockstep-roster package — `scripts/package-paths.mjs`), but no `publish-<pkg>.yml` run exists for them; npm latest unchanged; nothing errored.
67
67
 
68
68
  **Root cause:** pushing many tags in **one** `git push` fires a single batched create event that GitHub Actions routinely drops. Re-pushing is a no-op (the tags already exist remotely).
69
69
 
70
70
  **Resolution:**
71
71
 
72
72
  ```bash
73
- node "${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/dispatch-publish.mjs" \
73
+ node "${CLAUDE_PLUGIN_ROOT}/skills/package-release/scripts/dispatch-publish.mjs" \
74
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
- 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`.
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/package-release/scripts/release-pack.mjs` `` does this automatically and follows with `--verify-triggered`.
78
78
 
79
79
  ## §Scenario 8 — Cut on the wrong branch
80
80
 
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
- // bump.mjs — bump the 11 lockstep @adia-ai/* package.json from --from to --to.
2
+ // bump.mjs — bump every lockstep @adia-ai/* package.json from --from to --to.
3
+ // The set is PACKAGE_ROSTER (package-paths.mjs) filtered to `lockstep !== false`,
4
+ // never a count written here — that number went stale twice.
3
5
  //
4
6
  // Idempotent. Stops on the first mismatch (a package not at --from) so
5
7
  // you can investigate before any mutation lands.
@@ -8,11 +10,11 @@
8
10
  // node bump.mjs --from 0.6.20 --to 0.6.21
9
11
  // node bump.mjs --from 0.6.20 --to 0.6.21 --dry
10
12
  //
11
- // Part of the adia-release skill. Replaces the /tmp/vXXX-prep.mjs
13
+ // Part of the package-release skill. Replaces the /tmp/vXXX-prep.mjs
12
14
  // improvisation pattern from the v0.6.13 → v0.6.21 cycles.
13
15
  //
14
16
  // Exits:
15
- // 0 — all 11 bumped successfully (or --dry succeeded)
17
+ // 0 — every lockstep package bumped successfully (or --dry succeeded)
16
18
  // 1 — at least one package not at --from (no mutation)
17
19
  // 2 — bad args / file I/O error
18
20
  // 3 — partial mutation (shouldn't happen if --from validation passes)
@@ -43,26 +45,51 @@ const SIBLING_MANIFESTS = Object.fromEntries(
43
45
  // version (SKILL invariant 8 — the consumer never floats). bump.mjs left this
44
46
  // untouched through the 0.8.6 + 0.8.7 cuts, forcing a manual edit + amend each
45
47
  // time; check-lockstep's mcp-pin guard now catches a drift, this moves it.
48
+ //
49
+ // Each package maps to an ARRAY of pin configs — the factory README's own
50
+ // prose echo of the same pin (`server pinned: @adia-ai/a2ui-mcp@X`) is a
51
+ // SECOND file invariant 8 covers, and check:plugin-count-claims' "factory
52
+ // README a2ui-mcp pin" EQUALITY check enforces the two agree. Left out of
53
+ // this array through the 0.8.35 + 0.8.36 cuts, it needed the same manual
54
+ // hand-commit .mcp.json used to (gh#1198) — same file set, invariant 8's own
55
+ // scope, so it moves alongside .mcp.json rather than getting its own key.
56
+ //
57
+ // ANY NAME THIS PIN HAS CARRIED (ADR-0048 P5, retargeted gh#1240 2026-08-14):
58
+ // the generation MCP is `@adia-ai/mcp` (its `gen-ui` surface) since the
59
+ // MCP-distribution fold — P2 had planned `@adia-ai/gen-ui-mcp`, which never
60
+ // published — but the pin keeps naming the old package until the P7 cut
61
+ // flips it in the release commit itself (see lockstep-checks.mjs MCP_PIN_RE
62
+ // for why an early flip pins consumers to a 404). Matching every name means
63
+ // the version bump works before AND after either flip, with no cut-day edit
64
+ // to this file.
65
+ const A2UI_MCP_PIN = {
66
+ label: 'generation-MCP pin (@adia-ai/{a2ui-mcp,gen-ui-mcp,mcp})',
67
+ // `@adia-ai/{a2ui-mcp,gen-ui-mcp,mcp}@<from>` (not a prefix of a longer version) → `@<to>`
68
+ // Escape EVERY regex metacharacter in `from`, not just dots: a prerelease or
69
+ // build-metadata version (`0.9.0-rc+1`) would otherwise inject `+` as a
70
+ // quantifier and silently match the wrong text. Same class as the truncating
71
+ // MCP_PIN_RE suffix CodeRabbit found on PR #1229.
72
+ pattern: (from) =>
73
+ new RegExp(`(@adia-ai/(?:(?:a2ui|gen-ui)-mcp|mcp)@)${from.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?![\\d.])`),
74
+ replace: (to) => `$1${to}`,
75
+ };
46
76
  const PINNED_REFS = {
47
- 'packages/plugins/adia-ui-factory': {
48
- file: '.mcp.json',
49
- label: '@adia-ai/a2ui-mcp pin',
50
- // `@adia-ai/a2ui-mcp@<from>` (not a prefix of a longer version) → `@<to>`
51
- pattern: (from) => new RegExp(`(@adia-ai/a2ui-mcp@)${from.replace(/\./g, '\\.')}(?![\\d.])`),
52
- replace: (to) => `$1${to}`,
53
- },
77
+ 'packages/plugins/adia-ui-factory': [
78
+ { ...A2UI_MCP_PIN, file: '.mcp.json' },
79
+ { ...A2UI_MCP_PIN, file: 'README.md', label: 'generation-MCP pin (README prose)' },
80
+ ],
54
81
  // web-modules' internal peers pin the EXACT lockstep version (gh#489 —
55
82
  // a caret peer never moves on a lockstep bump; adia-v2 sat on llm 0.8.19
56
83
  // under web-modules 0.8.22). Same file bumpAll already rewrote for
57
84
  // "version", so this pass re-reads it from disk before matching. The
58
85
  // check:lockstep peer-exact-mismatch guard catches a pin left behind.
59
- 'packages/web-modules': {
86
+ 'packages/web-modules': [{
60
87
  file: 'package.json',
61
88
  label: '@adia-ai/* exact peer pins',
62
89
  pattern: (from) =>
63
90
  new RegExp(`("@adia-ai/[a-z0-9-]+":\\s*")${from.replace(/\./g, '\\.')}(?![\\d.])`, 'g'),
64
91
  replace: (to) => `$1${to}`,
65
- },
92
+ }],
66
93
  };
67
94
 
68
95
  // Repo-level version references (path relative to REPO ROOT, not a package) —
@@ -131,13 +158,15 @@ function loadPackageJsons(repo) {
131
158
  // half-bumped tree whose re-run then failed validateAllAtFrom.)
132
159
  function loadPinnedRefs(repo) {
133
160
  const out = [];
134
- for (const [pkg, cfg] of Object.entries(PINNED_REFS)) {
135
- const p = path.join(repo, pkg, cfg.file);
136
- if (!fs.existsSync(p)) {
137
- console.error(`error: missing ${p}`);
138
- process.exit(2);
161
+ for (const [pkg, cfgs] of Object.entries(PINNED_REFS)) {
162
+ for (const cfg of cfgs) {
163
+ const p = path.join(repo, pkg, cfg.file);
164
+ if (!fs.existsSync(p)) {
165
+ console.error(`error: missing ${p}`);
166
+ process.exit(2);
167
+ }
168
+ out.push({ pkg, cfg, path: p, txt: fs.readFileSync(p, 'utf8') });
139
169
  }
140
- out.push({ pkg, cfg, path: p, txt: fs.readFileSync(p, 'utf8') });
141
170
  }
142
171
  for (const cfg of REPO_PINNED_REFS) {
143
172
  const p = path.join(repo, cfg.file);
@@ -206,17 +235,19 @@ function bumpAll(packages, from, to, dry) {
206
235
  if (!dry) fs.writeFileSync(sibPath, sibOut);
207
236
  console.log(` ${dry ? '[dry] ' : ''}bumped ${p.pkg}/${sibling}: ${from} → ${to}`);
208
237
  }
209
- const pin = PINNED_REFS[p.pkg];
210
- if (pin) {
211
- const pinPath = path.join(path.dirname(p.path), pin.file);
212
- const pinTxt = fs.readFileSync(pinPath, 'utf8');
213
- const pinOut = bumpPinnedRef(pinTxt, pin, from, to);
214
- if (pinOut === pinTxt) {
215
- console.error(`error: ${p.pkg}/${pin.file} — ${pin.label} '@${from}' not found (invariant 8 drift? set the pin to @${from} and re-run, or use \`npm run check:lockstep:fix\`)`);
216
- return -1;
238
+ const pins = PINNED_REFS[p.pkg];
239
+ if (pins) {
240
+ for (const pin of pins) {
241
+ const pinPath = path.join(path.dirname(p.path), pin.file);
242
+ const pinTxt = fs.readFileSync(pinPath, 'utf8');
243
+ const pinOut = bumpPinnedRef(pinTxt, pin, from, to);
244
+ if (pinOut === pinTxt) {
245
+ console.error(`error: ${p.pkg}/${pin.file} — ${pin.label} '@${from}' not found (invariant 8 drift? set the pin to @${from} and re-run, or use \`npm run check:lockstep:fix\`)`);
246
+ return -1;
247
+ }
248
+ if (!dry) fs.writeFileSync(pinPath, pinOut);
249
+ console.log(` ${dry ? '[dry] ' : ''}bumped ${p.pkg}/${pin.file} ${pin.label}: @${from} → @${to}`);
217
250
  }
218
- if (!dry) fs.writeFileSync(pinPath, pinOut);
219
- console.log(` ${dry ? '[dry] ' : ''}bumped ${p.pkg}/${pin.file} ${pin.label}: @${from} → @${to}`);
220
251
  }
221
252
  }
222
253
  return bumped;
@@ -268,7 +299,7 @@ function selftest() {
268
299
  }
269
300
 
270
301
  // PINNED_REFS — the .mcp.json a2ui-mcp pin (invariant 8) moves in lockstep.
271
- const pinCfg = PINNED_REFS['packages/plugins/adia-ui-factory'];
302
+ const [pinCfg, readmePinCfg] = PINNED_REFS['packages/plugins/adia-ui-factory'];
272
303
  const pinBefore = '{"args":["-y","@adia-ai/a2ui-mcp@0.8.6"]}';
273
304
  const pinAfter = bumpPinnedRef(pinBefore, pinCfg, '0.8.6', '0.8.7');
274
305
  if (!pinAfter.includes('@adia-ai/a2ui-mcp@0.8.7') || pinAfter.includes('@0.8.6')) {
@@ -279,9 +310,25 @@ function selftest() {
279
310
  console.error('selftest FAIL: PINNED_REFS pattern prefix-matched a longer version'); process.exit(1);
280
311
  }
281
312
 
313
+ // gh#1198 — the factory README's OWN prose echo of the same pin
314
+ // (`server pinned: @adia-ai/a2ui-mcp@X`) must move alongside .mcp.json;
315
+ // check:plugin-count-claims' "factory README a2ui-mcp pin" EQUALITY check
316
+ // is what caught this drift on the v0.8.35 + v0.8.36 cuts.
317
+ if (readmePinCfg.file !== 'README.md') {
318
+ console.error('selftest FAIL: expected the second factory pin config to target README.md'); process.exit(1);
319
+ }
320
+ const readmeBefore = '`a2ui` server pinned: `@adia-ai/a2ui-mcp@0.8.6` (tool SoT: `packages/mcp/TOOLS.md`)';
321
+ const readmeAfter = bumpPinnedRef(readmeBefore, readmePinCfg, '0.8.6', '0.8.7');
322
+ if (!readmeAfter.includes('@adia-ai/a2ui-mcp@0.8.7') || readmeAfter.includes('@0.8.6')) {
323
+ console.error('selftest FAIL: PINNED_REFS did not bump the README a2ui-mcp prose pin'); process.exit(1);
324
+ }
325
+ if (bumpPinnedRef('@adia-ai/a2ui-mcp@0.8.60', readmePinCfg, '0.8.6', '0.8.7') !== '@adia-ai/a2ui-mcp@0.8.60') {
326
+ console.error('selftest FAIL: README pin pattern prefix-matched a longer version'); process.exit(1);
327
+ }
328
+
282
329
  // web-modules exact peer pins (gh#489) — all three move in one pass; a
283
330
  // longer version must not prefix-match.
284
- const peerCfg = PINNED_REFS['packages/web-modules'];
331
+ const [peerCfg] = PINNED_REFS['packages/web-modules'];
285
332
  const peersBefore = '{"peerDependencies":{"@adia-ai/web-components": "0.8.22","@adia-ai/a2ui-runtime": "0.8.22","@adia-ai/llm": "0.8.22"}}';
286
333
  const peersAfter = bumpPinnedRef(peersBefore, peerCfg, '0.8.22', '0.8.23');
287
334
  if ((peersAfter.match(/0\.8\.23/g) || []).length !== 3 || peersAfter.includes('0.8.22')) {