@adia-ai/adia-ui-forge 0.1.2 → 0.8.0

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 (196) hide show
  1. package/.claude-plugin/plugin.json +3 -6
  2. package/CHANGELOG.md +27 -26
  3. package/README.md +35 -40
  4. package/agents/a2ui-engineer.md +26 -0
  5. package/agents/component-author.md +29 -0
  6. package/agents/framework-verifier.md +26 -0
  7. package/agents/release-engineer.md +25 -0
  8. package/agents/routing-corpus.json +318 -0
  9. package/bin/demo-postwrite-pattern-gate +94 -0
  10. package/bin/forge-lint +17 -2
  11. package/bin/sidecar-prewrite-guard +104 -0
  12. package/commands/deploy.md +9 -0
  13. package/commands/dogfood.md +10 -0
  14. package/commands/gen-review.md +9 -0
  15. package/commands/release.md +10 -0
  16. package/hooks/hooks.json +15 -0
  17. package/package.json +3 -5
  18. package/references/contracts/a2ui-mcp-surface.md +35 -0
  19. package/references/contracts/migration-guide-format.md +34 -0
  20. package/references/shared/content-trust.md +19 -74
  21. package/skills/adia-a2ui/SKILL.md +99 -0
  22. package/skills/{adia-ui-a2ui → adia-a2ui}/evals/routing-corpus.json +10 -10
  23. package/skills/adia-a2ui/references/anti-patterns.md +27 -0
  24. package/skills/adia-a2ui/references/chunk-authoring.md +71 -0
  25. package/skills/adia-a2ui/references/corpus-discipline.md +68 -0
  26. package/skills/adia-a2ui/references/eval-diagnostics.md +86 -0
  27. package/skills/adia-a2ui/references/format-extension-decisions.md +66 -0
  28. package/skills/adia-a2ui/references/leverage-rules.md +52 -0
  29. package/skills/adia-a2ui/references/mcp-pipeline-ops.md +83 -0
  30. package/skills/adia-a2ui/references/mcp-tool-reference.md +59 -0
  31. package/skills/adia-a2ui/references/pipeline-overview.md +115 -0
  32. package/skills/adia-a2ui/references/semantic-fail-lifting.md +74 -0
  33. package/skills/adia-a2ui/references/strategy-engines.md +109 -0
  34. package/skills/adia-a2ui/references/zettel-calibration.md +104 -0
  35. package/skills/adia-author/SKILL.md +112 -0
  36. package/skills/adia-author/evals/routing-corpus.json +222 -0
  37. package/skills/{adia-ui-authoring → adia-author}/references/anti-patterns.md +33 -4
  38. package/skills/{adia-ui-authoring → adia-author}/references/authoring-cycle.md +9 -11
  39. package/skills/adia-author/references/canonical-pattern-index.md +243 -0
  40. package/skills/{adia-ui-authoring → adia-author}/references/code-style.md +19 -21
  41. package/skills/adia-author/references/common-gotchas.md +129 -0
  42. package/skills/adia-author/references/composite-demo-protocol.md +271 -0
  43. package/skills/{adia-ui-authoring → adia-author}/references/css-patterns.md +19 -6
  44. package/skills/{adia-ui-authoring → adia-author}/references/lifecycle-patterns.md +1 -0
  45. package/skills/{adia-ui-authoring → adia-author}/references/llm-bridge.md +3 -5
  46. package/skills/{adia-ui-authoring → adia-author}/references/module-promotion.md +22 -40
  47. package/skills/{adia-ui-authoring → adia-author}/references/primitive-audit.md +2 -4
  48. package/skills/{adia-ui-authoring → adia-author}/references/shell-patterns.md +12 -18
  49. package/skills/{adia-ui-authoring → adia-author}/references/token-contract.md +5 -7
  50. package/skills/{adia-ui-authoring → adia-author}/references/worked-example.md +3 -3
  51. package/skills/{adia-ui-authoring → adia-author}/references/yaml-contract.md +3 -3
  52. package/skills/{adia-ui-authoring → adia-author}/scripts/build-canonical-pattern-index.mjs +3 -3
  53. package/skills/adia-deploy/SKILL.md +128 -0
  54. package/skills/adia-deploy/references/deploy-playbooks.md +276 -0
  55. package/skills/adia-dogfood/SKILL.md +124 -0
  56. package/skills/adia-dogfood/references/admin-shell-anatomy.md +77 -0
  57. package/skills/adia-dogfood/references/app-shell-pitfalls.md +66 -0
  58. package/skills/adia-dogfood/references/card-anatomy-sweep.md +66 -0
  59. package/skills/adia-dogfood/references/html-attr-sweep.md +63 -0
  60. package/skills/adia-dogfood/references/native-leak-annotations.md +73 -0
  61. package/skills/adia-dogfood/references/visual-probe-triage.md +80 -0
  62. package/skills/{adia-ui-dogfood → adia-dogfood}/scripts/analyze.mjs +30 -14
  63. package/skills/adia-gen-review/SKILL.md +127 -0
  64. package/skills/adia-gen-review/references/corpus-html-patterns.md +279 -0
  65. package/skills/adia-gen-review/references/loop-protocol.md +216 -0
  66. package/skills/adia-gen-review/references/rubric-cosmetic.md +100 -0
  67. package/skills/adia-gen-review/references/rubric-decompose.md +101 -0
  68. package/skills/adia-gen-review/references/rubric-score.md +214 -0
  69. package/skills/{adia-ui-gen-review → adia-gen-review}/references/scores.schema.json +2 -2
  70. package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-coverage-audit.mjs +4 -4
  71. package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-decompose.mjs +10 -6
  72. package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-status.mjs +5 -5
  73. package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/validate-cycle-scores.mjs +5 -5
  74. package/skills/adia-llm-internals/SKILL.md +77 -0
  75. package/skills/{adia-ui-llm → adia-llm-internals}/references/adapter-contract.md +4 -2
  76. package/skills/{adia-ui-llm → adia-llm-internals}/references/add-a-provider.md +4 -5
  77. package/skills/{adia-ui-llm → adia-llm-internals}/references/bridge-facade.md +10 -3
  78. package/skills/{adia-ui-llm → adia-llm-internals}/references/browser-proxy-boundary.md +1 -1
  79. package/skills/{adia-ui-llm → adia-llm-internals}/references/model-registry.md +5 -3
  80. package/skills/{adia-ui-llm → adia-llm-internals}/references/streaming-sse.md +2 -2
  81. package/skills/adia-release/SKILL.md +72 -0
  82. package/skills/adia-release/references/changelog-discipline.md +119 -0
  83. package/skills/adia-release/references/cut-procedure.md +247 -0
  84. package/skills/adia-release/references/gates-catalog.md +222 -0
  85. package/skills/adia-release/references/independent-package-release.md +52 -0
  86. package/skills/adia-release/references/migration-guide-authoring.md +86 -0
  87. package/skills/adia-release/references/notes-authoring.md +90 -0
  88. package/skills/adia-release/references/recovery-paths.md +106 -0
  89. package/skills/{adia-ui-release → adia-release}/scripts/bump.mjs +27 -2
  90. package/skills/{adia-ui-release → adia-release}/scripts/dispatch-publish.mjs +72 -6
  91. package/skills/{adia-ui-release → adia-release}/scripts/insert-stub.mjs +6 -4
  92. package/skills/adia-release/scripts/package-paths.mjs +50 -0
  93. package/skills/{adia-ui-release → adia-release}/scripts/promote-unreleased.mjs +4 -3
  94. package/skills/{adia-ui-release → adia-release}/scripts/release-pack.mjs +48 -27
  95. package/skills/{adia-ui-release → adia-release}/scripts/tag-lockstep.mjs +15 -3
  96. package/skills/adia-site-docs/SKILL.md +68 -0
  97. package/skills/adia-site-docs/evals/audit-report.md +30 -0
  98. package/skills/adia-site-docs/evals/routing-corpus.json +176 -0
  99. package/skills/adia-site-docs/intent.md +72 -0
  100. package/bin/lib/audit-axes.mjs +0 -555
  101. package/bin/lib/dry-run-irreversible.mjs +0 -236
  102. package/bin/lib/run-skill-evals.mjs +0 -487
  103. package/bin/lib/teach-router.mjs +0 -250
  104. package/commands/adia-forge-a2ui.md +0 -10
  105. package/commands/adia-forge-author.md +0 -10
  106. package/commands/adia-forge-dogfood.md +0 -8
  107. package/commands/adia-forge-llm.md +0 -8
  108. package/commands/adia-forge-orient.md +0 -10
  109. package/commands/adia-forge-release.md +0 -8
  110. package/commands/adia-forge-review.md +0 -10
  111. package/references/shared/pev-rationale.md +0 -64
  112. package/references/shared/skill-conventions.md +0 -133
  113. package/skills/adia-ui-a2ui/CHANGELOG.md +0 -32
  114. package/skills/adia-ui-a2ui/SKILL.md +0 -243
  115. package/skills/adia-ui-a2ui/evals/adversarial-corpus.json +0 -75
  116. package/skills/adia-ui-a2ui/evals/teach-routing-cases.json +0 -100
  117. package/skills/adia-ui-a2ui/references/anti-patterns.md +0 -24
  118. package/skills/adia-ui-a2ui/references/chunk-authoring.md +0 -88
  119. package/skills/adia-ui-a2ui/references/corpus-discipline.md +0 -56
  120. package/skills/adia-ui-a2ui/references/eval-diagnostics.md +0 -127
  121. package/skills/adia-ui-a2ui/references/fragment-graph.md +0 -91
  122. package/skills/adia-ui-a2ui/references/mcp-pipeline-ops.md +0 -106
  123. package/skills/adia-ui-a2ui/references/mcp-tool-reference.md +0 -398
  124. package/skills/adia-ui-a2ui/references/pipeline-overview.md +0 -175
  125. package/skills/adia-ui-a2ui/references/semantic-fail-lifting.md +0 -120
  126. package/skills/adia-ui-a2ui/references/strategy-engines.md +0 -111
  127. package/skills/adia-ui-a2ui/references/teach-protocol.md +0 -220
  128. package/skills/adia-ui-a2ui/references/zettel-calibration.md +0 -93
  129. package/skills/adia-ui-a2ui/scripts/audit-a2ui-roster.mjs +0 -96
  130. package/skills/adia-ui-a2ui/scripts/teach-route.mjs +0 -157
  131. package/skills/adia-ui-a2ui/skill.json +0 -38
  132. package/skills/adia-ui-authoring/CHANGELOG.md +0 -32
  133. package/skills/adia-ui-authoring/SKILL.md +0 -256
  134. package/skills/adia-ui-authoring/assets/case-studies/admin-shell-decomposition.md +0 -101
  135. package/skills/adia-ui-authoring/assets/case-studies/maxtokens-32768-discovery.md +0 -109
  136. package/skills/adia-ui-authoring/assets/case-studies/theme-panel-promotion.md +0 -113
  137. package/skills/adia-ui-authoring/evals/adversarial-design-plan-gates.json +0 -138
  138. package/skills/adia-ui-authoring/evals/routing-corpus.json +0 -245
  139. package/skills/adia-ui-authoring/references/canonical-pattern-index.md +0 -179
  140. package/skills/adia-ui-authoring/references/common-gotchas.md +0 -93
  141. package/skills/adia-ui-authoring/references/composite-demo-protocol.md +0 -1084
  142. package/skills/adia-ui-authoring/references/teach-protocol.md +0 -428
  143. package/skills/adia-ui-authoring/scripts/audit-authoring-roster.mjs +0 -148
  144. package/skills/adia-ui-authoring/skill.json +0 -45
  145. package/skills/adia-ui-dogfood/CHANGELOG.md +0 -17
  146. package/skills/adia-ui-dogfood/README.md +0 -62
  147. package/skills/adia-ui-dogfood/SKILL.md +0 -866
  148. package/skills/adia-ui-dogfood/skill.json +0 -40
  149. package/skills/adia-ui-forge/SKILL.md +0 -88
  150. package/skills/adia-ui-forge/evals/routing-corpus.json +0 -30
  151. package/skills/adia-ui-gen-review/CHANGELOG.md +0 -108
  152. package/skills/adia-ui-gen-review/SKILL.md +0 -266
  153. package/skills/adia-ui-gen-review/references/loop-protocol.md +0 -712
  154. package/skills/adia-ui-gen-review/references/rubric-cosmetic.md +0 -144
  155. package/skills/adia-ui-gen-review/references/rubric-decompose.md +0 -117
  156. package/skills/adia-ui-gen-review/references/rubric-score.md +0 -249
  157. package/skills/adia-ui-gen-review/references/teach-protocol.md +0 -214
  158. package/skills/adia-ui-gen-review/skill.json +0 -22
  159. package/skills/adia-ui-llm/CHANGELOG.md +0 -25
  160. package/skills/adia-ui-llm/SKILL.md +0 -165
  161. package/skills/adia-ui-llm/evals/adversarial-corpus.json +0 -75
  162. package/skills/adia-ui-llm/evals/routing-corpus.json +0 -30
  163. package/skills/adia-ui-llm/evals/teach-routing-cases.json +0 -73
  164. package/skills/adia-ui-llm/references/teach-protocol.md +0 -78
  165. package/skills/adia-ui-llm/scripts/audit-llm-roster.mjs +0 -93
  166. package/skills/adia-ui-llm/scripts/teach-route.mjs +0 -119
  167. package/skills/adia-ui-llm/skill.json +0 -33
  168. package/skills/adia-ui-release/CHANGELOG.md +0 -23
  169. package/skills/adia-ui-release/SKILL.md +0 -295
  170. package/skills/adia-ui-release/assets/case-studies/2026-05-20-batch-push-v0.6.14-v0.6.15.md +0 -144
  171. package/skills/adia-ui-release/assets/case-studies/2026-05-20-corpus-drift-remediation-v0.6.15.md +0 -155
  172. package/skills/adia-ui-release/assets/case-studies/2026-05-20-version-skip-correction-v0.6.12.md +0 -114
  173. package/skills/adia-ui-release/assets/case-studies/2026-05-21-author-from-scratch-v0.6.18.md +0 -139
  174. package/skills/adia-ui-release/assets/case-studies/2026-05-21-feedback37-retraction-v0.6.21.md +0 -124
  175. package/skills/adia-ui-release/assets/case-studies/2026-05-21-fn1-enrichment-pass-v0.6.19.md +0 -125
  176. package/skills/adia-ui-release/assets/case-studies/2026-05-21-stale-test-detection-v0.6.20.md +0 -142
  177. package/skills/adia-ui-release/assets/case-studies/2026-05-23-freshness-gate-recovery-v0.6.32.md +0 -97
  178. package/skills/adia-ui-release/assets/case-studies/2026-05-26-catalog-drift-recurring-v0.6.40.md +0 -147
  179. package/skills/adia-ui-release/assets/templates/stub-changelog.template.md +0 -22
  180. package/skills/adia-ui-release/evals/evals.json +0 -164
  181. package/skills/adia-ui-release/references/changelog-discipline.md +0 -250
  182. package/skills/adia-ui-release/references/cycle-happy-path.md +0 -520
  183. package/skills/adia-ui-release/references/exe-deploy.md +0 -149
  184. package/skills/adia-ui-release/references/gates-catalog.md +0 -778
  185. package/skills/adia-ui-release/references/ledger-discipline.md +0 -232
  186. package/skills/adia-ui-release/references/migration-guide-authoring.md +0 -174
  187. package/skills/adia-ui-release/references/multi-agent-baseline.md +0 -207
  188. package/skills/adia-ui-release/references/notes-authoring.md +0 -212
  189. package/skills/adia-ui-release/references/recovery-paths.md +0 -215
  190. package/skills/adia-ui-release/references/rollup-notes.md +0 -208
  191. package/skills/adia-ui-release/references/teach-protocol.md +0 -468
  192. package/skills/adia-ui-release/scripts/audit-gate-roster.mjs +0 -196
  193. package/skills/adia-ui-release/scripts/make-ledger.mjs +0 -179
  194. package/skills/adia-ui-release/skill.json +0 -75
  195. /package/skills/{adia-ui-authoring → adia-author}/references/api-contract.md +0 -0
  196. /package/skills/{adia-ui-release → adia-release}/scripts/assert-monorepo-root.mjs +0 -0
@@ -0,0 +1,90 @@
1
+ # `notes-authoring.md` — release notes: GH body · Slack post · multi-version rollup
2
+
3
+ > Load for the end-of-cycle notes step or a notes-only request. The GH release
4
+ > body attached to each per-package tag is the **durable record**; drafts live in
5
+ > the session scratch dir and the operator copy-pastes — the skill never posts to
6
+ > Slack directly.
7
+
8
+ ## §Author from the diff, not the intent
9
+
10
+ Draft every note from `git diff <prev-tag>..HEAD` — what actually shipped — never from an ADR's intent or a PR's plan. The first two commands of any draft:
11
+
12
+ ```bash
13
+ git log --oneline <prev-tag>..HEAD
14
+ git diff --stat <prev-tag>..HEAD
15
+ ```
16
+
17
+ If the diff contradicts the plan (a feature deferred, a refactor split across cuts), the notes follow the diff. This is a discipline because the inverse shipped a real defect: notes copied from an ADR once claimed features that were never staged into the cut. F-N1 catches *undocumented diff*, not *over-claimed notes* — that guard is the author's.
18
+
19
+ ## §The GH release body (single version, ~80–200 lines)
20
+
21
+ All 9 packages get the same body (`gh release create <pkg>-vX.Y.Z --notes-file …`).
22
+
23
+ ```markdown
24
+ ## vX.Y.Z — <one-line tagline>
25
+
26
+ 9-package lockstep **PATCH** cut. <One sentence on scope.>
27
+
28
+ ### `@adia-ai/<substantive-pkg>`
29
+ - **<Bold-prefix headline>.** <Why → what → inline file paths.> Closes <FEEDBACK-NN>.
30
+
31
+ ### Ride-along stubs
32
+ `@adia-ai/<pkg1>`, `@adia-ai/<pkg2>`, … — version bump only.
33
+
34
+ ### Verification
35
+ - `check:lockstep` OK at `X.Y.Z / ^X.Y.0`
36
+ - **N/N vitest** · typecheck clean · `components --verify` clean · `verify:traits` 100%
37
+ - `check:demo-shells` · `check:lightningcss-build` · `verify:corpus` 0/0 · `check:embeddings-fresh` OK
38
+ - `smoke:engines` · `smoke:register-engine` 11/11 · `eval:diff zettel` cov=N% avg=N · `check:links` clean
39
+
40
+ ### Install
41
+ npm i @adia-ai/web-components@X.Y.Z @adia-ai/web-modules@X.Y.Z
42
+ ```
43
+
44
+ Shape rules: open with the tagline, no preamble; group by `### @adia-ai/<pkg>`; bold-prefix every bullet then why → what → files; cite tickets by ID; before/after fenced blocks for consumer-facing markup changes; paste the verification block from the actual pre-flight output (the cycle-to-cycle repetition is a feature); a breaking cut adds a `⚠️` paragraph linking its MIGRATION GUIDE section. Anti-patterns: marketing language, vague verbs, missing file paths.
45
+
46
+ ## §The Slack post (~40–80 lines)
47
+
48
+ ```markdown
49
+ 🚀 **AdiaUI vX.Y.Z** is out — <tagline>
50
+
51
+ npm i @adia-ai/web-components@X.Y.Z @adia-ai/web-modules@X.Y.Z
52
+
53
+ 9-package lockstep PATCH cut. <One sentence.>
54
+
55
+ ## 🔷 `@adia-ai/<pkg>` — <headline>
56
+ <2–4 sentences.> Closes <FEEDBACK-NN>.
57
+
58
+ ## ✅ Verification
59
+ <one line>
60
+
61
+ ## 📚 Links
62
+ - GH Releases: https://github.com/adiahealth/gen-ui-kit/releases (filter `vX.Y.Z`)
63
+ - Live demos: https://ui-kit.exe.xyz/site/playground/gen-ui
64
+ ```
65
+
66
+ - **Install matrix is mandatory, npm + CDN, both packages**: npm always names `web-components` AND `web-modules`; the CDN block pins the `@0.X` minor range — `web-components@0.X/dist/web-components.min.{css,js}` plus `web-modules@0.X/dist/everything.min.js` as the all-in-one `<script>`.
67
+ - Section emojis: 🔷 feature · 🛠️ DX fix · 🎨 visual · 🧹 cleanup · ⚠️ breaking/behavior · ✅ verification · 📚 links.
68
+ - Slack renders standard markdown (fenced blocks, `-` bullets, `[text](URL)`); no legacy mrkdwn needed.
69
+ - **Every `https://ui-kit.exe.xyz/site/<route>` URL must be verified against `site/sitemap.json` first** — the docs site is an SPA; an unmatched route returns 200 and renders blank, so curl proves nothing. The Gen UI Canvas is `/site/playground/gen-ui`; there is no `/site/gen-ui/`.
70
+
71
+ Save drafts to the session scratch dir (e.g. `<scratch>/release-vX.Y.Z/notes.md`); strip nothing for Slack, and the GH body is the same content without the rocket header.
72
+
73
+ ## §Rollup — multi-version retrospective (≥2 versions since the last broadcast)
74
+
75
+ Audience: consumers or teammates catching up on a window (vA.B.C → vX.Y.Z). Five-section skeleton:
76
+
77
+ 1. **Header** — `🚀 AdiaUI vA.B.C → vX.Y.Z — <tagline>` + install snippet + 2–4 sentences on the window (releases count, what held across all of them, e.g. "internal deps held at ^0.X.0 throughout").
78
+ 2. **🧵 Framing section — pick ONE:**
79
+ - **A. The arcs** — 2–4 thematic strands, each naming its contributing versions. Best mid-cycle with no dominant theme.
80
+ - **B. The headline event** — one milestone (graduation, ADR ratification) with the package cuts as substrate. Best for milestone windows.
81
+ - **C. Net deltas** — what consumers see differently end-to-end: new primitives / new APIs / breaking-ish / retired, as a bullet list. Best when the window includes a MINOR or a consumer is jumping a big gap.
82
+ 3. **📦 Per-version breakdown** — one block per version: `### vN.M.X — <tagline>` + 3–5 bold-prefix bullets if substantive, 1–2 if small, a single sentence for stub-only versions. **Skip ride-along stubs entirely** — naming 6 no-change packages per version is noise.
83
+ 4. **⚠️ Behavior-changes table** (only if the window has ≥2): `| Version | Change | Opt-out / migration |`.
84
+ 5. **✅ Verification baseline** — one paragraph, not the full table: test-count growth across the window, gate roster held, eval floors held, F-N1 clean at every cut. Show the *growth* — it's the window's velocity signal. Close with one paragraph naming what the window accomplished, echoing the framing.
85
+
86
+ Save to `<scratch>/release-vA.B.C-vX.Y.Z/rollup.md`; announcement-grade rollups (milestone, MINOR) can also land as a GH "milestone" release body — operator's call.
87
+
88
+ ## §What NOT to put in notes
89
+
90
+ Internal ticket fields; implementation details with no consumer effect; "coming soon" futures; jokes — notes are a permanent artifact attached to a tag.
@@ -0,0 +1,106 @@
1
+ # `recovery-paths.md` — the 8 recovery scenarios
2
+
3
+ > Load on any F-N1 / pre-flight failure, for a batch push, or for post-release
4
+ > recovery. Each scenario: the **shape** (what the repo state looks like), the
5
+ > **resolution** (commands + judgment calls), and what to record. Every scenario
6
+ > is grounded in a real past incident; the durable record of a recovery is the
7
+ > fix commit's message + the PR description — write the "what happened / root
8
+ > cause / fix" there.
9
+
10
+ ---
11
+
12
+ ## §Scenario 1 — Version-skip correction
13
+
14
+ **Shape:** a peer's release commit mislabels the version — package.json bumped 0.6.X → 0.6.X+2, skipping X+1; the CHANGELOG body may narrate the work as two releases. No tags yet, npm latest still 0.6.X, commit unpushed. (Real case: a "v0.6.13" cut that was actually v0.6.12's work.)
15
+
16
+ **Resolution:**
17
+
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
+ 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: 9 × package.json, 9 × CHANGELOG (headers + body refs), any doc/CSS-comment refs, then regenerate the lockfile. Preserve filename references that intentionally encode the original label.
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
+
23
+ ## §Scenario 2 — Batch push
24
+
25
+ **Shape:** multiple unpushed `release(*): vX.Y.Z` commits since the last published tag; operator asks to "publish what's accumulated."
26
+
27
+ **Resolution:**
28
+
29
+ 1. Order the release commits oldest → newest.
30
+ 2. Per version: **tag at that version's release-commit SHA** (the batch-push exception to tag-at-HEAD), run F-N1 against each tag candidate.
31
+ 3. Push `main` once; push tags one-at-a-time (Scenario 7 prevention).
32
+ 4. **Publish in version order, oldest first, waiting for each version's workflows to settle** before dispatching the next — `npm dist-tag latest` is set by publish order. `dispatch-publish.mjs --after <prev-version>` gates on `dist-tag latest` to enforce it. If mis-ordered: `npm dist-tag add @adia-ai/<pkg>@<newest> latest` per package.
33
+ 5. One GH release note per version; site deploy once at the END (reflects HEAD).
34
+
35
+ ## §Scenario 3 — Author from scratch (`[Unreleased]` promotion)
36
+
37
+ **Shape:** source + CHANGELOG entries landed under `## [Unreleased]`, no bump, no release commit.
38
+
39
+ **Resolution:** [`cut-procedure.md`](cut-procedure.md) Variant B; `scripts/promote-unreleased.mjs` mechanizes the heading swap; fresh blocks per [`changelog-discipline.md`](changelog-discipline.md) §Authoring.
40
+
41
+ ## §Scenario 4 — `[Unreleased]` extension (early cut + entangled fix)
42
+
43
+ **Shape:** a peer pre-cut the release commit early, then landed more commits including a fix that **completes the early cut's own scope** (e.g. the demo-shell import its feature broke) entangled with unrelated `[Unreleased]` work. The early commit fails a release-blocking gate (`check:demo-shells`).
44
+
45
+ **Resolution — two options:**
46
+
47
+ 1. Cherry-pick the fix onto the release commit (off-mainline tag; messy archaeology). Only when the operator explicitly wants minimal scope.
48
+ 2. **Extend the release to HEAD** ← default. Verify HEAD passes all gates; promote the `[Unreleased]` content into `[vX.Y.Z]`; author fresh blocks for changed-but-unlogged packages; commit the CHANGELOG merge with a message documenting the boundary decision; tag at the new HEAD.
49
+
50
+ ## §Scenario 5 — Stale test detection
51
+
52
+ **Shape:** a pre-flight test fails, but it asserts a behavior a peer *deliberately* changed (the change is CHANGELOG-documented); the test was never updated.
53
+
54
+ **Resolution:** read three things — the assertion, the CHANGELOG entry, the production source. Code matches the CHANGELOG's described behavior → the test is stale: update the assertion, add an inline comment naming the vX.Y.Z change, optionally pin the new contract with an extra assertion, include the test update in the release allowlist. Code matches the test → real regression (or the CHANGELOG is wrong) — fix that instead.
55
+
56
+ ## §Scenario 6 — Concurrent peer mid-cycle
57
+
58
+ **Shape:** the working tree shifts between commands; `git stash pop` reports a conflict or "kept the stash."
59
+
60
+ **Resolution:** don't fight the peer. First confirm the release itself is fully shipped (tags pushed + npm + GH releases + site). Then for a kept stash: `git stash show -p stash@{0}` vs the file's recent `git log -p` — if the peer committed the same content, the stash is redundant → `git stash drop`; if not, leave the stash for the peer/operator to decide. Never blind-drop.
61
+
62
+ ## §Scenario 7 — Tags pushed, ZERO publish workflows fired
63
+
64
+ **Shape:** all 10 tags exist on origin, but no `publish-<pkg>.yml` run exists for them; npm latest unchanged; nothing errored.
65
+
66
+ **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).
67
+
68
+ **Resolution:**
69
+
70
+ ```bash
71
+ node "${CLAUDE_PLUGIN_ROOT}/skills/adia-release/scripts/dispatch-publish.mjs" \
72
+ --version X.Y.Z --verify-triggered # re-dispatches ONLY packages with no run; idempotent
73
+ ```
74
+
75
+ 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); `release-pack.mjs` does this automatically and follows with `--verify-triggered`.
76
+
77
+ ## §Scenario 8 — Cut on the wrong branch
78
+
79
+ **Shape:** the cut landed on a peer's local feature branch instead of `main`; caught at push (origin/main at a mid-window commit, tags pointing at the feature tip). (Real case: v0.7.4.)
80
+
81
+ **Resolution:** recovery is clean **while nothing has published** — publish is `workflow_dispatch`-driven, so npm stays put through the tangle. If the feature tip fast-forwards from main: `git branch -f main <release-sha>` → `git checkout main` → `git push origin main`. Verify `origin/main == vX.Y.Z tag == HEAD` **before** dispatching any publish. Moving tags while local is free (`tag-lockstep.mjs --delete` + re-tag); moving them after a publish is not. **Prevention:** the Step-1 `branch --show-current` check.
82
+
83
+ ---
84
+
85
+ ## §Decision flowchart
86
+
87
+ ```text
88
+ Pre-flight or F-N1 gate failed?
89
+ ├── check:demo-shells fails at the release-commit candidate?
90
+ │ └── a later fix exists but is entangled with [Unreleased] work → Scenario 4
91
+ │ otherwise → author the fix in this cycle (Variant B)
92
+ ├── check:lockstep fails → peer edited an internal range mid-PATCH → fix in place
93
+ ├── verify:corpus fails → corpus drift → route to the A2UI-pipeline skill
94
+ ├── check:embeddings-fresh fails → npm run build:embeddings:chunks; stage; note in
95
+ │ the a2ui-corpus CHANGELOG
96
+ ├── test:unit fails → Scenario 5 (stale test) OR real regression
97
+ │ (assertion vs CHANGELOG vs source tells them apart)
98
+ └── F-N1 cosmetic warns → enrichment pass (changelog-discipline.md)
99
+
100
+ Multiple unpushed release commits → Scenario 2 (batch push)
101
+ Commit's CHANGELOG version ≠ its bump → Scenario 1 (version skip)
102
+ Tags on origin, no publish runs, npm stale → Scenario 7 (batched tag push)
103
+ branch --show-current ≠ main → Scenario 8 (wrong branch)
104
+ Working tree dirty at cycle start → classify + stash per
105
+ cut-procedure.md §Step 2
106
+ ```
@@ -8,7 +8,7 @@
8
8
  // node bump.mjs --from 0.6.20 --to 0.6.21
9
9
  // node bump.mjs --from 0.6.20 --to 0.6.21 --dry
10
10
  //
11
- // Phase 1 of the adia-ui-release skill. Replaces the /tmp/vXXX-prep.mjs
11
+ // Part of the adia-release skill. Replaces the /tmp/vXXX-prep.mjs
12
12
  // improvisation pattern from the v0.6.13 → v0.6.21 cycles.
13
13
  //
14
14
  // Exits:
@@ -31,8 +31,21 @@ const PACKAGES = [
31
31
  'packages/a2ui/retrieval',
32
32
  'packages/a2ui/runtime',
33
33
  'packages/a2ui/validator',
34
+ // Plugins joined the lockstep 2026-07-15 (see check-lockstep.mjs's POLICY
35
+ // note). Each also carries a .claude-plugin/plugin.json whose version MUST
36
+ // move with package.json — plugin.json is the /plugin-update cache key
37
+ // (verify:plugins gates the sync).
38
+ 'packages/plugins/adia-ui-factory',
39
+ 'packages/plugins/adia-ui-forge',
34
40
  ];
35
41
 
42
+ // Sibling manifests bumped in the same pass as package.json (path relative to
43
+ // the package dir). Today: the Claude Code plugin manifests.
44
+ const SIBLING_MANIFESTS = {
45
+ 'packages/plugins/adia-ui-factory': '.claude-plugin/plugin.json',
46
+ 'packages/plugins/adia-ui-forge': '.claude-plugin/plugin.json',
47
+ };
48
+
36
49
  function parseArgs(argv) {
37
50
  const args = { from: null, to: null, dry: false, repo: process.cwd() };
38
51
  for (let i = 0; i < argv.length; i++) {
@@ -96,6 +109,18 @@ function bumpAll(packages, from, to, dry) {
96
109
  if (!dry) fs.writeFileSync(p.path, out);
97
110
  bumped++;
98
111
  console.log(` ${dry ? '[dry] ' : ''}bumped ${p.pkg}: ${from} → ${to}`);
112
+ const sibling = SIBLING_MANIFESTS[p.pkg];
113
+ if (sibling) {
114
+ const sibPath = path.join(path.dirname(p.path), sibling);
115
+ const sibTxt = fs.readFileSync(sibPath, 'utf8');
116
+ const sibOut = sibTxt.replace(FROM_PATTERN, TO_LITERAL);
117
+ if (sibOut === sibTxt) {
118
+ console.error(`error: ${p.pkg}/${sibling} — regex did not match (plugin.json out of sync?)`);
119
+ return -1;
120
+ }
121
+ if (!dry) fs.writeFileSync(sibPath, sibOut);
122
+ console.log(` ${dry ? '[dry] ' : ''}bumped ${p.pkg}/${sibling}: ${from} → ${to}`);
123
+ }
99
124
  }
100
125
  return bumped;
101
126
  }
@@ -106,7 +131,7 @@ function main() {
106
131
  if (!validateAllAtFrom(packages, args.from)) process.exit(1);
107
132
  const bumped = bumpAll(packages, args.from, args.to, args.dry);
108
133
  if (bumped < 0) process.exit(3);
109
- console.log(`\n[bump] ${bumped}/9 ${args.dry ? '(dry — no files written)' : 'bumped'}`);
134
+ console.log(`\n[bump] ${bumped}/${PACKAGES.length} ${args.dry ? '(dry — no files written)' : 'bumped'}`);
110
135
  if (!args.dry) {
111
136
  console.log(`\n[next] regenerate the lockfile:`);
112
137
  console.log(` npm install --package-lock-only --no-audit --no-fund`);
@@ -14,7 +14,7 @@
14
14
  // # Dry mode:
15
15
  // node dispatch-publish.mjs --version 0.6.22 --dry
16
16
  //
17
- // Phase 2 of the adia-ui-release skill. Mechanizes the dispatch loop
17
+ // Part of the adia-release skill. Mechanizes the dispatch loop
18
18
  // + handles the npm-latest ordering rule for batch pushes.
19
19
 
20
20
  import { execSync } from 'node:child_process';
@@ -35,12 +35,16 @@ const PACKAGES = [
35
35
  'a2ui-mcp',
36
36
  'a2ui-retrieval',
37
37
  'a2ui-validator',
38
+ // Plugins joined the lockstep 2026-07-15 (check-lockstep.mjs POLICY note).
39
+ 'adia-ui-factory',
40
+ 'adia-ui-forge',
38
41
  ];
39
42
 
40
43
  function parseArgs(argv) {
41
44
  const args = {
42
45
  version: null, after: null, dry: false, sleepBefore: 4,
43
46
  scope: process.env.ADIA_NPM_SCOPE || DEFAULT_SCOPE,
47
+ verifyTriggered: false,
44
48
  };
45
49
  for (let i = 0; i < argv.length; i++) {
46
50
  const k = argv[i];
@@ -49,13 +53,19 @@ function parseArgs(argv) {
49
53
  else if (k === '--dry') args.dry = true;
50
54
  else if (k === '--sleep-before') args.sleepBefore = parseInt(argv[++i], 10);
51
55
  else if (k === '--scope') args.scope = argv[++i];
56
+ else if (k === '--verify-triggered') args.verifyTriggered = true;
52
57
  else if (k === '-h' || k === '--help') {
53
- console.log('Usage: node dispatch-publish.mjs --version X.Y.Z [--after X.Y.Z-1] [--dry] [--sleep-before <seconds>] [--scope @org]');
58
+ console.log('Usage: node dispatch-publish.mjs --version X.Y.Z [--after X.Y.Z-1] [--verify-triggered] [--dry] [--sleep-before <seconds>] [--scope @org]');
54
59
  console.log('');
55
- console.log('--after Verify npm latest is at the given version before dispatching this one.');
56
- console.log(' Used in batch push to ensure publish ordering.');
57
- console.log(`--scope npm scope the packages publish under (default: ${DEFAULT_SCOPE};`);
58
- console.log(' or set $ADIA_NPM_SCOPE). The --after npm-latest check uses this scope.');
60
+ console.log('--after Verify npm latest is at the given version before dispatching this one.');
61
+ console.log(' Used in batch push to ensure publish ordering.');
62
+ console.log('--verify-triggered Instead of dispatching, check whether each per-package publish-<pkg>.yml');
63
+ console.log(' ALREADY has a run for the <pkg>-vX.Y.Z tag (i.e. push-on-tag fired), and');
64
+ console.log(' re-dispatch ONLY the misses. This is the recovery for the batch-tag-push');
65
+ console.log(' skip (recovery-paths.md §Scenario 7) — a single `git push <10 tags>` can');
66
+ console.log(' trigger ZERO publish workflows. Idempotent: re-dispatches nothing if all 9 fired.');
67
+ console.log(`--scope npm scope the packages publish under (default: ${DEFAULT_SCOPE};`);
68
+ console.log(' or set $ADIA_NPM_SCOPE). The --after npm-latest check uses this scope.');
59
69
  process.exit(0);
60
70
  }
61
71
  }
@@ -115,12 +125,68 @@ async function sleep(ms) {
115
125
  return new Promise((res) => setTimeout(res, ms));
116
126
  }
117
127
 
128
+ // Has publish-<pkg>.yml already run for the <pkg>-vX.Y.Z tag? Tag-triggered runs
129
+ // report the tag in the `headBranch` field, so we filter the run list by it.
130
+ // Returns true if at least one run exists for that tag, false otherwise.
131
+ function triggeredForTag(pkg, version, dry) {
132
+ const tag = `${pkg}-v${version}`;
133
+ const cmd = `gh run list --workflow="publish-${pkg}.yml" --branch "${tag}" --limit 1 --json databaseId -q '.[0].databaseId // empty'`;
134
+ if (dry) {
135
+ console.log(` [dry] ${cmd}`);
136
+ return false; // dry preview assumes not-triggered so the re-dispatch path is shown
137
+ }
138
+ try {
139
+ const out = execSync(cmd, { encoding: 'utf8' }).trim();
140
+ return out.length > 0;
141
+ } catch {
142
+ // gh error (auth / rate limit / missing workflow) — treat as "unknown".
143
+ // Conservative: report not-triggered so the miss is re-dispatched rather than
144
+ // silently skipped. A redundant re-dispatch is harmless (publish is idempotent
145
+ // per the workflow's own guard); a skipped publish is the failure we're fixing.
146
+ return false;
147
+ }
148
+ }
149
+
150
+ // The batch-tag-push-skip recovery: verify each per-package publish workflow
151
+ // actually triggered off its pushed tag, and re-dispatch ONLY the misses.
152
+ // Idempotent — re-dispatches nothing if all 9 fired.
153
+ function verifyTriggeredAndRedispatch(version, dry) {
154
+ console.log(`\nVerifying publish-on-tag triggered for all ${PACKAGES.length} packages (v${version}):`);
155
+ const misses = [];
156
+ for (const pkg of PACKAGES) {
157
+ if (triggeredForTag(pkg, version, dry)) {
158
+ console.log(` ✓ ${pkg} — publish run found for ${pkg}-v${version}`);
159
+ } else {
160
+ console.log(` ✗ ${pkg} — NO publish run for ${pkg}-v${version} (batch-push skip) → will re-dispatch`);
161
+ misses.push(pkg);
162
+ }
163
+ }
164
+ if (misses.length === 0) {
165
+ console.log(`\n[verify-triggered] all ${PACKAGES.length} publish workflows triggered — nothing to re-dispatch.`);
166
+ return 0;
167
+ }
168
+ console.log(`\n[verify-triggered] ${misses.length} miss(es) — re-dispatching via workflow_dispatch:`);
169
+ let redispatched = 0;
170
+ for (const pkg of misses) {
171
+ if (dispatch(pkg, version, dry)) redispatched++;
172
+ }
173
+ console.log(`\n[verify-triggered] re-dispatched ${redispatched}/${misses.length} ${dry ? '(dry)' : ''}`);
174
+ return redispatched === misses.length ? 0 : 1;
175
+ }
176
+
118
177
  async function main() {
119
178
  const args = parseArgs(process.argv.slice(2));
120
179
  // Fail-fast guard: refuse to run npm/gh against a non-monorepo directory.
121
180
  assertMonorepoRoot(process.cwd());
122
181
  if (args.after) checkAfter(args.after, args.scope);
123
182
 
183
+ // Recovery mode — verify each publish workflow triggered off its pushed tag,
184
+ // re-dispatch only the misses (the batch-tag-push skip). Skips the blind dispatch.
185
+ if (args.verifyTriggered) {
186
+ const rc = verifyTriggeredAndRedispatch(args.version, args.dry);
187
+ process.exit(rc);
188
+ }
189
+
124
190
  if (args.sleepBefore > 0 && !args.dry) {
125
191
  console.log(`Sleeping ${args.sleepBefore}s to let GH index the new tags...`);
126
192
  await sleep(args.sleepBefore * 1000);
@@ -11,13 +11,13 @@
11
11
  // --previous-version 0.6.21 \
12
12
  // --packages llm,a2ui/compose,a2ui/mcp,a2ui/retrieval,a2ui/runtime,a2ui/validator
13
13
  //
14
- // Phase 2 of the adia-ui-release skill. Replaces the inline stub-
14
+ // Part of the adia-release skill. Replaces the inline stub-
15
15
  // insertion pattern used across v0.6.18 / v0.6.19 / v0.6.20 / v0.6.21
16
16
  // cycles.
17
17
 
18
18
  import fs from 'node:fs';
19
- import path from 'node:path';
20
19
  import process from 'node:process';
20
+ import { resolvePackageChangelog } from './package-paths.mjs';
21
21
 
22
22
  function parseArgs(argv) {
23
23
  const args = {
@@ -72,7 +72,8 @@ function buildStub(version, date, substantive, xref) {
72
72
  }
73
73
 
74
74
  function insertStub(repo, pkg, anchor, stubBlock, version, dry) {
75
- const p = path.join(repo, 'packages', pkg, 'CHANGELOG.md');
75
+ // Accept both name-form (a2ui-corpus) and path-form (a2ui/corpus). See package-paths.mjs.
76
+ const p = resolvePackageChangelog(repo, pkg);
76
77
  if (!fs.existsSync(p)) {
77
78
  console.error(` ERROR ${pkg} — CHANGELOG.md not found at ${p}`);
78
79
  return 'missing';
@@ -101,7 +102,8 @@ function main() {
101
102
  const PREV_HEADING_RE = new RegExp(`^## \\[${args.previous.replace(/\./g, '\\.')}\\] — \\d{4}-\\d{2}-\\d{2}$`, 'm');
102
103
  const stubBlock = buildStub(args.version, args.date, args.substantive, args.xref);
103
104
  const results = args.packages.map((pkg) => {
104
- const p = path.join(args.repo, 'packages', pkg, 'CHANGELOG.md');
105
+ // Accept both name-form (a2ui-corpus) and path-form (a2ui/corpus). See package-paths.mjs.
106
+ const p = resolvePackageChangelog(args.repo, pkg);
105
107
  if (!fs.existsSync(p)) {
106
108
  console.error(` ERROR ${pkg} — not found`);
107
109
  return 'missing';
@@ -0,0 +1,50 @@
1
+ // package-paths.mjs — package name-vs-path-form normalization for the
2
+ // standalone CHANGELOG helpers.
3
+ //
4
+ // THE PROBLEM (bit all 6 a2ui packages at the v0.7.14 cut):
5
+ // The 6 a2ui packages live on disk under `packages/a2ui/<x>/` but carry the
6
+ // flat npm-name `a2ui-<x>` everywhere else in the tooling (release-pack.mjs,
7
+ // dispatch-publish.mjs, tag-lockstep.mjs all use the NAME form). The two
8
+ // standalone helpers (promote-unreleased.mjs, insert-stub.mjs) joined
9
+ // `packages/<arg>/CHANGELOG.md` with the raw arg, so calling them with the
10
+ // name form silently 404'd: `CHANGELOG.md not found at packages/a2ui-corpus/`.
11
+ // release-pack.mjs maps internally (`a2ui-corpus` → `packages/a2ui/corpus`),
12
+ // so the two forms diverged: name-form failed standalone, path-form failed
13
+ // when handed to the orchestrator-style callers.
14
+ //
15
+ // THE FIX:
16
+ // Accept BOTH forms. Try `packages/<arg>` first (path form, backward-compat).
17
+ // If that directory is absent AND the arg matches `a2ui-<x>`, fall back to
18
+ // `packages/a2ui/<x>`. So `a2ui-corpus` (name) and `a2ui/corpus` (path) both
19
+ // resolve to `packages/a2ui/corpus/`.
20
+ //
21
+ // Pure Node, stdlib only — keeps the scripts free of runtime deps.
22
+
23
+ import fs from 'node:fs';
24
+ import path from 'node:path';
25
+
26
+ // Resolve a package arg to its on-disk directory under packages/, accepting
27
+ // BOTH the name form (`a2ui-corpus`) and the path form (`a2ui/corpus`).
28
+ // Returns the resolved relative directory (e.g. `a2ui/corpus`); does not
29
+ // require the directory to exist (so callers can render a legible not-found
30
+ // error against the resolved path).
31
+ export function resolvePackageDir(repo, pkg) {
32
+ if (fs.existsSync(path.join(repo, 'packages', pkg))) return pkg;
33
+ const m = /^a2ui-(.+)$/.exec(pkg);
34
+ if (m) {
35
+ const nested = path.join('a2ui', m[1]);
36
+ if (fs.existsSync(path.join(repo, 'packages', nested))) return nested;
37
+ }
38
+ // Plugins live under packages/plugins/<name> but carry the flat name form
39
+ // (adia-ui-forge) in the release tooling — same name-vs-path split as a2ui.
40
+ if (/^adia-ui-/.test(pkg)) {
41
+ const nested = path.join('plugins', pkg);
42
+ if (fs.existsSync(path.join(repo, 'packages', nested))) return nested;
43
+ }
44
+ return pkg; // fall through with the original arg so the not-found error names what was asked for
45
+ }
46
+
47
+ // Resolve a package arg to its CHANGELOG.md path, accepting both forms.
48
+ export function resolvePackageChangelog(repo, pkg) {
49
+ return path.join(repo, 'packages', resolvePackageDir(repo, pkg), 'CHANGELOG.md');
50
+ }
@@ -11,7 +11,7 @@
11
11
  // node promote-unreleased.mjs --version 0.6.22 --date 2026-05-22 \
12
12
  // --packages web-components,web-modules --dry
13
13
  //
14
- // Phase 2 of the adia-ui-release skill. Replaces the inline heading-swap
14
+ // Part of the adia-release skill. Replaces the inline heading-swap
15
15
  // pattern used across v0.6.18 / v0.6.19 / v0.6.20 / v0.6.21 cycles.
16
16
  //
17
17
  // Exits:
@@ -20,8 +20,8 @@
20
20
  // 2 — bad args / file I/O error
21
21
 
22
22
  import fs from 'node:fs';
23
- import path from 'node:path';
24
23
  import process from 'node:process';
24
+ import { resolvePackageChangelog } from './package-paths.mjs';
25
25
 
26
26
  function parseArgs(argv) {
27
27
  const args = { version: null, date: null, packages: null, dry: false, repo: process.cwd() };
@@ -52,7 +52,8 @@ function parseArgs(argv) {
52
52
  }
53
53
 
54
54
  function promote(repo, pkg, version, date, dry) {
55
- const p = path.join(repo, 'packages', pkg, 'CHANGELOG.md');
55
+ // Accept both name-form (a2ui-corpus) and path-form (a2ui/corpus). See package-paths.mjs.
56
+ const p = resolvePackageChangelog(repo, pkg);
56
57
  if (!fs.existsSync(p)) {
57
58
  console.error(` ERROR ${pkg} — CHANGELOG.md not found at ${p}`);
58
59
  return 'missing';
@@ -1,12 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  // release-pack.mjs — full-cycle orchestrator for an AdiaUI lockstep release.
3
3
  //
4
- // Chains the 6 Phase-1/2/3 CLI helpers (bump · promote-unreleased ·
5
- // insert-stub · tag-lockstep · dispatch-publish · make-ledger) plus the
6
- // non-helper steps (lockfile · pre-flight gates · git commit · F-N1 ·
7
- // push · GH releases · site deploy) into one walked sequence with 3
8
- // operator checkpoints (before tag, before push, before publish) per
9
- // SKILL.md §Posture.
4
+ // Chains the 5 CLI helpers (bump · promote-unreleased · insert-stub ·
5
+ // tag-lockstep · dispatch-publish) plus the non-helper steps (lockfile ·
6
+ // pre-flight gates · git commit · F-N1 · push · GH releases · site
7
+ // deploy) into one walked sequence with 3 operator checkpoints (before
8
+ // tag, before push, before publish) per SKILL.md operator checkpoints.
10
9
  //
11
10
  // MODES:
12
11
  // --mode cut (Mode 1 — peer pre-staged [Unreleased] or pure stub)
@@ -33,8 +32,8 @@
33
32
  // --skip-gates : (DEBUG ONLY) skip pre-flight (don't ship anything!)
34
33
  // --skip-site : skip the EXE deploy step (rare; for offline cycles)
35
34
  //
36
- // PHASE 4 of the adia-ui-release skill. Mechanizes the 11-step cycle from
37
- // references/cycle-happy-path.md while preserving operator judgment at
35
+ // Part of the adia-release skill. Mechanizes the cut procedure from
36
+ // references/cut-procedure.md while preserving operator judgment at
38
37
  // the 3 mutating-checkpoint boundaries.
39
38
 
40
39
  import { execSync } from 'node:child_process';
@@ -269,21 +268,50 @@ function step7Fn1(args) {
269
268
  }
270
269
 
271
270
  // ── Step 8 — Push ────────────────────────────────────────────────
271
+ // Pushes per-package tags ONE-AT-A-TIME, not as a single batch.
272
+ //
273
+ // THE BATCH-PUSH SKIP (bit the v0.7.14 cut): `git push origin <10 tags>` in one
274
+ // invocation fires the push-on-tag `create` event ONCE for the whole ref-set,
275
+ // and GitHub Actions routinely drops it — ZERO publish-on-tag workflows trigger.
276
+ // (recovery-paths.md §Scenario 7 — the batch-tag-push publish-skip.)
277
+ // Pushing each per-package tag separately gives the per-package publish-<pkg>.yml
278
+ // `on: push: tags:` trigger its own create event. The umbrella tag is pushed
279
+ // last (it triggers nothing — convention only).
280
+ //
281
+ // Step 9 dispatches all 9 publishes via workflow_dispatch regardless, so the
282
+ // orchestrated cut does NOT rely on push-on-tag firing. The one-at-a-time push
283
+ // is defensive (a cut aborted after Step 8 still has its publishes triggered)
284
+ // AND keeps the standalone tag-lockstep.mjs hint honest.
272
285
  async function step8Push(args) {
273
- console.log('\n=== Step 8 — Push main + tags ===');
286
+ console.log('\n=== Step 8 — Push main + tags (per-tag, NOT batched) ===');
274
287
  const count = shQuiet('git rev-list --count origin/main..HEAD', args).trim();
275
288
  console.log(` commits to push: ${count}`);
276
- await checkpoint(`Ready to push ${count} commit(s) + 10 tags`, args);
289
+ await checkpoint(`Ready to push ${count} commit(s) + 10 tags (one-at-a-time)`, args);
277
290
  sh('git push origin main', args);
278
- const tagList = [`v${args.version}`, ...PACKAGES.map((p) => `${p}-v${args.version}`)].join(' ');
279
- sh(`git push origin ${tagList}`, args);
291
+ const perPkgTags = PACKAGES.map((p) => `${p}-v${args.version}`);
292
+ for (const t of perPkgTags) {
293
+ sh(`git push origin ${t}`, args);
294
+ }
295
+ // Umbrella tag last — triggers no workflow (convention only).
296
+ sh(`git push origin v${args.version}`, args);
297
+ console.log(`\n ${perPkgTags.length} per-package tags + 1 umbrella pushed individually.`);
298
+ console.log(' NOTE: a single `git push origin <all tags>` can skip ALL publish-on-tag');
299
+ console.log(' triggers (the GitHub batch-push skip). Step 9 dispatches via');
300
+ console.log(' workflow_dispatch + verifies each triggered — see recovery-paths.md §Scenario 7.');
280
301
  }
281
302
 
282
303
  // ── Step 9 — Publish ─────────────────────────────────────────────
304
+ // Step 8 pushed per-package tags one-at-a-time, so push-on-tag may have fired
305
+ // for some/all packages. --verify-triggered checks which publish-<pkg>.yml runs
306
+ // exist for the <pkg>-vX.Y.Z tags and re-dispatches ONLY the misses — closing the
307
+ // batch-tag-push skip without double-publishing the ones that already fired.
308
+ // Idempotent: if all 9 triggered (or none did), it does the right thing.
283
309
  async function step9Publish(args) {
284
- console.log('\n=== Step 9 — Dispatch publishes ===');
285
- await checkpoint('Ready to dispatch 9 npm publishes', args);
286
- sh(`node ${SCRIPT_DIR}/dispatch-publish.mjs --version ${args.version} --scope ${args.scope}`, args);
310
+ console.log('\n=== Step 9 — Verify-triggered + re-dispatch misses ===');
311
+ await checkpoint('Ready to verify the 9 publish triggers + re-dispatch any misses', args);
312
+ // Give GH a moment to index the runs created by the per-tag push in Step 8.
313
+ if (!args.dry) sh('sleep 6', args);
314
+ sh(`node ${SCRIPT_DIR}/dispatch-publish.mjs --version ${args.version} --scope ${args.scope} --verify-triggered`, args);
287
315
  console.log('\n Waiting for publishes to settle...');
288
316
  sh(`until [ "$(gh run list --workflow=publish-a2ui-validator.yml --limit 1 --json status -q '.[0].status')" = "completed" ]; do sleep 5; done`, args);
289
317
  console.log(' ✓ publishes settled');
@@ -312,15 +340,9 @@ async function step10GhAndSite(args) {
312
340
  }
313
341
  sh('npm run build:site', args);
314
342
  sh(`rsync -az --delete dist/ ${args.host}:/srv/adia-ui/dist/`, args);
315
- sh(`curl -s -o /dev/null -w "EXE gen-ui: HTTP %{http_code}\\n" https://${args.host}/site/gen-ui/`, args);
316
- }
317
-
318
- // ── Step 11 — Ledger ─────────────────────────────────────────────
319
- function step11Ledger(args) {
320
- console.log('\n=== Step 11 — Audit-history ledger ===');
321
- const cmd = `node ${SCRIPT_DIR}/make-ledger.mjs --version ${args.version} --date ${args.date} --summary "<TODO: paste from commit message>" --note "Shipped via release-pack.mjs mode=${args.mode}"`;
322
- sh(cmd, args);
323
- console.log('\n Edit the generated ledger to fill in scope/verification details, then commit + push.');
343
+ // Verify a deployed CONTENT FILE, never an SPA route — the docs site
344
+ // returns HTTP 200 + a blank shell for ANY unmatched route (v0.7.4 trap).
345
+ sh(`curl -s -o /dev/null -w "EXE host.css: HTTP %{http_code}\\n" https://${args.host}/packages/web-components/styles/host.css`, args);
324
346
  }
325
347
 
326
348
  // ── Main ─────────────────────────────────────────────────────────
@@ -328,7 +350,7 @@ async function main() {
328
350
  const args = parseArgs(process.argv.slice(2));
329
351
  // Fail-fast guard: refuse to git/npm/gh/curl against a non-monorepo directory.
330
352
  assertMonorepoRoot(REPO);
331
- console.log(`adia-ui-release release-pack — v${args.version} (${args.mode})${args.dry ? ' [DRY]' : ''}`);
353
+ console.log(`adia-release release-pack — v${args.version} (${args.mode})${args.dry ? ' [DRY]' : ''}`);
332
354
  step1ReBaseline(args);
333
355
  step3PreFlight(args);
334
356
  step4PromoteAndBump(args);
@@ -338,9 +360,8 @@ async function main() {
338
360
  await step8Push(args);
339
361
  await step9Publish(args);
340
362
  await step10GhAndSite(args);
341
- step11Ledger(args);
342
363
  rl.close();
343
- console.log('\n✓ release-pack complete. Don\'t forget to commit + push the ledger.');
364
+ console.log('\n✓ release-pack complete. Verify against the npm registry + GH releases, then author the release notes.');
344
365
  }
345
366
 
346
367
  main().catch((e) => {