@adia-ai/adia-ui-forge 0.8.62 → 0.8.64

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 (143) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/.codex-plugin/plugin.json +3 -3
  3. package/CHANGELOG.md +317 -275
  4. package/README.md +54 -18
  5. package/__init__.py +8 -8
  6. package/agents/a2ui-maintenance-agent.corpus.json +7 -7
  7. package/agents/a2ui-maintenance-agent.md +6 -6
  8. package/agents/a2ui-planner-agent.md +11 -11
  9. package/agents/demo-audit-agent.corpus.json +4 -4
  10. package/agents/demo-audit-agent.md +8 -9
  11. package/agents/framework-checker.md +10 -11
  12. package/agents/framework-planner-agent.md +9 -10
  13. package/agents/gen-ui-review-agent.corpus.json +4 -4
  14. package/agents/gen-ui-review-agent.md +8 -8
  15. package/agents/package-release-agent.corpus.json +4 -4
  16. package/agents/package-release-agent.md +9 -10
  17. package/agents/primitive-authoring-agent.corpus.json +1 -1
  18. package/agents/primitive-authoring-agent.md +5 -5
  19. package/agents/routing-corpus.json +46 -46
  20. package/codex.interface.json +1 -1
  21. package/commands/gen-ui-review.md +1 -1
  22. package/commands/package-release.md +2 -2
  23. package/package.json +1 -1
  24. package/plugin.yaml +2 -2
  25. package/prompts/gen-ui-review.md +1 -1
  26. package/prompts/package-release.md +2 -2
  27. package/references/contracts/a2ui-mcp-surface.md +7 -7
  28. package/references/contracts/migration-guide-format.md +6 -7
  29. package/references/shared/content-trust.md +3 -5
  30. package/skills/a2ui-maintenance/SKILL.md +13 -16
  31. package/skills/a2ui-maintenance/evals/evals.json +2 -2
  32. package/skills/a2ui-maintenance/evals/routing-corpus.json +1 -1
  33. package/skills/a2ui-maintenance/references/anti-patterns.md +10 -10
  34. package/skills/a2ui-maintenance/references/chunk-authoring.md +11 -12
  35. package/skills/a2ui-maintenance/references/corpus-discipline.md +13 -14
  36. package/skills/a2ui-maintenance/references/data-model-reactivity.md +22 -25
  37. package/skills/a2ui-maintenance/references/eval-diagnostics.md +21 -21
  38. package/skills/a2ui-maintenance/references/format-extension-decisions.md +12 -13
  39. package/skills/a2ui-maintenance/references/leverage-rules.md +6 -6
  40. package/skills/a2ui-maintenance/references/mcp-pipeline-ops.md +9 -10
  41. package/skills/a2ui-maintenance/references/mcp-tool-reference.md +11 -12
  42. package/skills/a2ui-maintenance/references/pipeline-overview.md +33 -38
  43. package/skills/a2ui-maintenance/references/semantic-fail-lifting.md +13 -14
  44. package/skills/a2ui-maintenance/references/strategy-engines.md +14 -16
  45. package/skills/a2ui-maintenance/references/surface-lifecycle.md +10 -11
  46. package/skills/a2ui-maintenance/references/zettel-calibration.md +14 -16
  47. package/skills/component-md-authoring/SKILL.md +22 -22
  48. package/skills/component-md-authoring/agents/openai.yaml +1 -1
  49. package/skills/cross-harness-authoring-standards/SKILL.md +19 -21
  50. package/skills/cross-harness-authoring-standards/agents/openai.yaml +1 -1
  51. package/skills/cross-harness-compatibility-standards/SKILL.md +35 -34
  52. package/skills/cross-harness-compatibility-standards/agents/openai.yaml +1 -1
  53. package/skills/demo-audit/SKILL.md +38 -38
  54. package/skills/demo-audit/evals/routing-corpus.json +1 -1
  55. package/skills/demo-audit/references/admin-shell-anatomy.md +21 -25
  56. package/skills/demo-audit/references/app-shell-pitfalls.md +11 -13
  57. package/skills/demo-audit/references/auto-fix-allowlist.md +3 -3
  58. package/skills/demo-audit/references/card-anatomy-sweep.md +10 -11
  59. package/skills/demo-audit/references/chat-shell-anatomy.md +25 -26
  60. package/skills/demo-audit/references/editor-shell-anatomy.md +25 -28
  61. package/skills/demo-audit/references/html-attr-sweep.md +12 -13
  62. package/skills/demo-audit/references/mode7-status-battery.md +4 -4
  63. package/skills/demo-audit/references/native-leak-annotations.md +8 -8
  64. package/skills/demo-audit/references/output-contract-worked-example.md +8 -8
  65. package/skills/demo-audit/references/probe-discipline-and-escalation.md +2 -2
  66. package/skills/demo-audit/references/visual-probe-triage.md +21 -23
  67. package/skills/gen-ui-review/SKILL.md +28 -28
  68. package/skills/gen-ui-review/agents/openai.yaml +1 -1
  69. package/skills/gen-ui-review/evals/evals.json +1 -1
  70. package/skills/gen-ui-review/evals/routing-corpus.json +1 -1
  71. package/skills/gen-ui-review/references/corpus-html-patterns.md +28 -29
  72. package/skills/gen-ui-review/references/exit-gate-mechanics.md +5 -5
  73. package/skills/gen-ui-review/references/lookup-maintenance.md +2 -2
  74. package/skills/gen-ui-review/references/loop-protocol.md +25 -25
  75. package/skills/gen-ui-review/references/rubric-cosmetic.md +10 -10
  76. package/skills/gen-ui-review/references/rubric-decompose.md +15 -16
  77. package/skills/gen-ui-review/references/rubric-score.md +23 -24
  78. package/skills/gen-ui-review/references/scorecard-worked-examples.md +12 -12
  79. package/skills/gen-ui-review/references/scores.schema.json +2 -2
  80. package/skills/llm-client-maintenance/SKILL.md +12 -12
  81. package/skills/llm-client-maintenance/evals/routing-corpus.json +1 -1
  82. package/skills/llm-client-maintenance/references/adapter-contract.md +27 -27
  83. package/skills/llm-client-maintenance/references/add-a-provider.md +25 -25
  84. package/skills/llm-client-maintenance/references/bridge-facade.md +31 -31
  85. package/skills/llm-client-maintenance/references/browser-proxy-boundary.md +17 -17
  86. package/skills/llm-client-maintenance/references/model-registry.md +14 -14
  87. package/skills/llm-client-maintenance/references/streaming-sse.md +21 -21
  88. package/skills/package-release/SKILL.md +21 -23
  89. package/skills/package-release/evals/evals.json +2 -2
  90. package/skills/package-release/evals/routing-corpus.json +2 -2
  91. package/skills/package-release/references/authorization-model.md +6 -8
  92. package/skills/package-release/references/changelog-discipline.md +25 -27
  93. package/skills/package-release/references/cut-procedure.md +129 -136
  94. package/skills/package-release/references/gates-catalog.md +42 -43
  95. package/skills/package-release/references/independent-package-release.md +14 -15
  96. package/skills/package-release/references/invariants-detail.md +9 -9
  97. package/skills/package-release/references/mechanization.md +11 -12
  98. package/skills/package-release/references/migration-guide-authoring.md +20 -20
  99. package/skills/package-release/references/notes-authoring.md +20 -20
  100. package/skills/package-release/references/recovery-paths.md +28 -28
  101. package/skills/primitive-authoring/SKILL.md +19 -19
  102. package/skills/primitive-authoring/agents/openai.yaml +1 -1
  103. package/skills/primitive-authoring/evals/evals.json +2 -2
  104. package/skills/primitive-authoring/evals/routing-corpus.json +2 -2
  105. package/skills/primitive-authoring/references/INDEX.md +22 -22
  106. package/skills/primitive-authoring/references/anti-patterns.md +76 -77
  107. package/skills/primitive-authoring/references/api-contract.md +63 -65
  108. package/skills/primitive-authoring/references/authoring-cycle.md +47 -50
  109. package/skills/primitive-authoring/references/canonical-pattern-index.md +118 -118
  110. package/skills/primitive-authoring/references/code-style.md +50 -52
  111. package/skills/primitive-authoring/references/common-gotchas.md +23 -23
  112. package/skills/primitive-authoring/references/composite-demo-protocol.md +42 -45
  113. package/skills/primitive-authoring/references/css-patterns.md +97 -97
  114. package/skills/primitive-authoring/references/for-attribute-event-contract.md +12 -12
  115. package/skills/primitive-authoring/references/form-control-sizing.md +16 -18
  116. package/skills/primitive-authoring/references/lifecycle-patterns.md +42 -42
  117. package/skills/primitive-authoring/references/llm-bridge.md +21 -21
  118. package/skills/primitive-authoring/references/module-promotion.md +51 -51
  119. package/skills/primitive-authoring/references/primitive-audit.md +16 -16
  120. package/skills/primitive-authoring/references/shell-patterns.md +81 -81
  121. package/skills/primitive-authoring/references/svg-authoring.md +62 -66
  122. package/skills/primitive-authoring/references/token-contract.md +45 -49
  123. package/skills/primitive-authoring/references/trait-pages.md +8 -8
  124. package/skills/primitive-authoring/references/worked-example.md +39 -39
  125. package/skills/primitive-authoring/references/yaml-contract.md +139 -143
  126. package/skills/site-deployment/SKILL.md +25 -26
  127. package/skills/site-deployment/agents/openai.yaml +1 -1
  128. package/skills/site-deployment/evals/routing-corpus.json +1 -1
  129. package/skills/site-deployment/references/deploy-playbooks.md +51 -53
  130. package/skills/site-docs-authoring/SKILL.md +14 -14
  131. package/skills/site-docs-authoring/agents/openai.yaml +1 -1
  132. package/skills/site-docs-authoring/evals/evals.json +1 -1
  133. package/skills/site-docs-authoring/evals/routing-corpus.json +1 -1
  134. package/skills/site-docs-authoring/intent.md +18 -18
  135. package/skills/ssr-compatibility/SKILL.md +24 -25
  136. package/skills/ssr-compatibility/agents/openai.yaml +1 -1
  137. package/skills/ssr-compatibility/evals/evals.json +1 -1
  138. package/skills/ssr-compatibility/evals/routing-corpus.json +1 -1
  139. package/skills/ssr-compatibility/references/consumer-workarounds.md +22 -23
  140. package/skills/ssr-compatibility/references/failure-shapes.md +84 -91
  141. package/skills/ssr-compatibility/references/guard-patterns.md +54 -58
  142. package/skills/ssr-compatibility/references/status-ledger.md +17 -17
  143. package/skills/ssr-compatibility/references/test-without-linkedom.md +26 -27
@@ -1,4 +1,4 @@
1
- # `cut-procedure.md` the standard lockstep cut
1
+ # `cut-procedure.md`, the standard lockstep cut
2
2
 
3
3
  `<plugin-root>` below is `$CLAUDE_PLUGIN_ROOT` in Claude Code; the plugin's installed directory
4
4
  in Codex.
@@ -13,10 +13,10 @@ in Codex.
13
13
 
14
14
  Two entry variants, converging at Step 5:
15
15
 
16
- - **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).
17
- - **Variant B author from scratch:** source landed under `## [Unreleased]` with no bump. Do Step 4 (promotion + bump + lockfile), then the full tail.
16
+ - **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).
17
+ - **Variant B, author from scratch:** source landed under `## [Unreleased]` with no bump. Do Step 4 (promotion + bump + lockfile), then the full tail.
18
18
 
19
- `` `<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.
19
+ `` `<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.
20
20
 
21
21
  | Step | Action | Mutates? |
22
22
  | --- | --- | --- |
@@ -25,12 +25,12 @@ Two entry variants, converging at Step 5:
25
25
  | 3 | Pre-flight gates (+ harvest preamble if source content changed) | No |
26
26
  | 4a-pre | Assemble `changes/<pr>.md` fragments into the right `[Unreleased]` (REQ-W11-06) | Yes |
27
27
  | 4 | (Variant B) Promote `[Unreleased]`; bump; lockfile | Yes |
28
- | 4f | Pre-tag coverage `--fix` authoritative F-N1 matcher, pre-PR | CHANGELOGs |
28
+ | 4f | Pre-tag coverage `--fix`, authoritative F-N1 matcher, pre-PR | CHANGELOGs |
29
29
  | 5 | Stage the release allowlist; commit on `release/vX.Y.Z` | Yes |
30
30
  | 5.5 | Pre-commit freshness trip-wire | No |
31
31
  | 5.7 | Release PR: push branch → CI → merge → re-baseline on `main` | PR + merge |
32
32
  | 6 | Tag umbrella + per-package at post-merge HEAD | Yes |
33
- | 7 | F-N1 release trip-wire (expected clean 4f already ran) | No |
33
+ | 7 | F-N1 release trip-wire (expected clean, 4f already ran) | No |
34
34
  | 8 | Push tags (one per push; `main` already merged) | Push |
35
35
  | 9 | Dispatch publish workflows; wait; verify registry | Publish |
36
36
  | 10 | GH releases + site deploy dispatch | Deploy |
@@ -39,7 +39,7 @@ Two entry variants, converging at Step 5:
39
39
 
40
40
  ---
41
41
 
42
- ## §Step 1 Re-baseline
42
+ ## §Step 1, Re-baseline
43
43
 
44
44
  Context is stale at the start of every turn; run unconditionally:
45
45
 
@@ -49,35 +49,35 @@ git -C "$REPO" status --short
49
49
  git -C "$REPO" log --oneline -8
50
50
  git -C "$REPO" fetch && git -C "$REPO" log HEAD..origin/main --oneline # must be empty
51
51
  git -C "$REPO" tag --list 'vX.Y.Z' '*-vX.Y.Z' # must NOT exist yet
52
- [ -d "$REPO/node_modules/.pnpm" ] && echo "STOP: pnpm-shaped node_modules run npm ci first" # gate 9 precondition, see §3.1 layout note
52
+ [ -d "$REPO/node_modules/.pnpm" ] && echo "STOP: pnpm-shaped node_modules, run npm ci first" # gate 9 precondition, see §3.1 layout note
53
53
  ```
54
54
 
55
55
  **Check the `node_modules` layout here, not at gate 9.** The §3.1 layout note (gh#1359)
56
56
  already says gate 9 (`check:js-bundles-fresh`) needs the npm-ci layout, but it sits AFTER
57
- the gate roster and is easy to read past the v0.8.55 cut (2026-08-28) ran the full
57
+ the gate roster and is easy to read past, the v0.8.55 cut (2026-08-28) ran the full
58
58
  33-gate pre-flight under a pnpm-shaped tree, lost ~12 minutes to gates 1-8 and 10-33
59
59
  passing, then failed gate 9 with phantom bundle drift (1.24 MB on disk vs 1.52 MB fresh
60
- on every JS entry the Phosphor icon glob resolving differently, not a real source
60
+ on every JS entry, the Phosphor icon glob resolving differently, not a real source
61
61
  change). The one-line check above fails in under a second instead. `npm ci` to recover;
62
62
  `npm install` alone does NOT reshape an existing pnpm tree.
63
63
 
64
64
  `branch --show-current` ≠ `main` → stop; cutting on a feature branch pushes a stale `main` ref while the tags point at the feature tip. Recovery: [`recovery-paths.md`](recovery-paths.md) §Scenario 8. If multiple unpushed `release(*):` commits exist → this is a batch push, [`recovery-paths.md`](recovery-paths.md) §Scenario 2.
65
65
 
66
- ## §Step 2 Classify uncommitted files; stash strays
66
+ ## §Step 2, Classify uncommitted files; stash strays
67
67
 
68
- Modified/untracked files you didn't author are classification decisions diff each one. A **stray** = uncommitted + undocumented (no CHANGELOG entry or commit explains it) + behavior-visible + contradicting a release artifact. Stash strays; never revert (revert destroys the work):
68
+ Modified/untracked files you didn't author are classification decisions, diff each one. A **stray** = uncommitted + undocumented (no CHANGELOG entry or commit explains it) + behavior-visible + contradicting a release artifact. Stash strays; never revert (revert destroys the work):
69
69
 
70
70
  ```bash
71
- git -C "$REPO" stash push <file1> <file2> -m "vX.Y.Z-cycle: <reason> parked"
71
+ git -C "$REPO" stash push <file1> <file2> -m "vX.Y.Z-cycle: <reason>, parked"
72
72
  ```
73
73
 
74
- Keep the stash held through the site deploy (Step 10) the tarball builds from the **tag** but the site builds from the **working tree**; an uncommitted stray leaks into the deployed site and not the packages, silently. Pop after the cycle; if pop reports "kept the stash", see [`recovery-paths.md`](recovery-paths.md) §Scenario 6. Record any exclusion in the release-commit message (`Excluded in-flight: <file> (<reason>)`).
74
+ Keep the stash held through the site deploy (Step 10), the tarball builds from the **tag** but the site builds from the **working tree**; an uncommitted stray leaks into the deployed site and not the packages, silently. Pop after the cycle; if pop reports "kept the stash", see [`recovery-paths.md`](recovery-paths.md) §Scenario 6. Record any exclusion in the release-commit message (`Excluded, in-flight: <file> (<reason>)`).
75
75
 
76
- ## §Step 3 Pre-flight gates
76
+ ## §Step 3, Pre-flight gates
77
77
 
78
78
  ### 3.0 Harvest preamble (only when source content changed in the window)
79
79
 
80
- If `git diff <prev-tag>..HEAD --name-only` touches component yaml / `.a2ui.json` sources, **`*.examples.html`**, `*.contents.html`, **any file under `site/`, `apps/`, `playgrounds/`, or `catalog/`** annotated with `data-chunk-*` or not or any `styles/colors/*` token source, regenerate downstream artifacts proactively (otherwise the freshness gates force a tag-move recovery later):
80
+ If `git diff <prev-tag>..HEAD --name-only` touches component yaml / `.a2ui.json` sources, **`*.examples.html`**, `*.contents.html`, **any file under `site/`, `apps/`, `playgrounds/`, or `catalog/`**, annotated with `data-chunk-*` or not, or any `styles/colors/*` token source, regenerate downstream artifacts proactively (otherwise the freshness gates force a tag-move recovery later):
81
81
 
82
82
  ```bash
83
83
  node scripts/build/components.mjs # catalog + per-component sidecars
@@ -92,20 +92,20 @@ npm run build:bundles # dist CSS+JS bundles
92
92
 
93
93
  **The `data-chunk-*` qualifier was the trap** (gh#421). Two separate cuts lost a CI round-trip to it:
94
94
 
95
- - **v0.8.14** edited two components' `*.examples.html` not yaml, not `.contents.html`, no annotation so it read as out of scope. It isn't: `.examples.html` feeds **two** generators (`.examples.md` and the chunk harvest's source hashes; a third, the retired site-a2ui converted rows, applied historically). Every freshness gate this doc named came back clean, and CI still failed on `🔴 stale /site/components/{menu,popover}` plus `2 source file(s) changed since harvest`.
96
- - **The 0.8.16 cycle** then added a brand-new `site/pages/guides/theming.html` with *no* annotations, ran `check:links`, `verify:llms`, and `verify:patterns-index` all clean and CI failed anyway: `1 source file(s) NEW since harvest`.
95
+ - **v0.8.14** edited two components' `*.examples.html`, not yaml, not `.contents.html`, no annotation, so it read as out of scope. It isn't: `.examples.html` feeds **two** generators (`.examples.md` and the chunk harvest's source hashes; a third, the retired site-a2ui converted rows, applied historically). Every freshness gate this doc named came back clean, and CI still failed on `🔴 stale /site/components/{menu,popover}` plus `2 source file(s) changed since harvest`.
96
+ - **The 0.8.16 cycle** then added a brand-new `site/pages/guides/theming.html` with *no* annotations, ran `check:links`, `verify:llms`, and `verify:patterns-index`, all clean, and CI failed anyway: `1 source file(s) NEW since harvest`.
97
97
 
98
98
  Root cause of both: `check-chunks-fresh` hashes **every** source file under the harvest globs and compares the set against the corpus record, so a new or changed file trips it whether or not the harvester extracts a chunk from it. The annotation governs what gets *harvested*, never what gets *hashed*. Hence the trigger list above names the directories, not the annotation.
99
99
 
100
- When only source *hashes* move and chunk content does not, `check:embeddings-fresh` stays green on its content hash no embedding rebuild, no `OPENAI_API_KEY` needed. Both incidents above were that case, which is why the block above runs that check *before* `build:embeddings:chunks` and skips the rebuild when it passes: an unconditional rebuild makes an ordinary `.examples.html` or new-page edit fail pre-flight on any machine without the key, for no reason. Only a change that alters chunk **content** needs the rebuild.
100
+ When only source *hashes* move and chunk content does not, `check:embeddings-fresh` stays green on its content hash, no embedding rebuild, no `OPENAI_API_KEY` needed. Both incidents above were that case, which is why the block above runs that check *before* `build:embeddings:chunks` and skips the rebuild when it passes: an unconditional rebuild makes an ordinary `.examples.html` or new-page edit fail pre-flight on any machine without the key, for no reason. Only a change that alters chunk **content** needs the rebuild.
101
101
 
102
- **Regen output supersedes working-tree state.** These outputs land in the release commit unconditionally, even when the same paths are also dirty from a peer the fresh regen is authoritative; divergent uncommitted work rebases on top afterwards.
102
+ **Regen output supersedes working-tree state.** These outputs land in the release commit unconditionally, even when the same paths are also dirty from a peer: the fresh regen is authoritative; divergent uncommitted work rebases on top afterwards.
103
103
 
104
- **Staging a read-only report without cutting** (gh#3063): `node scripts/release/preflight-dry-run.mjs --version X.Y.Z` runs this same roster (`gate-roster.mjs` SoT) inside a throwaway `npm ci` clone and prints per-gate PASS/FAIL plus a tail-of-log on failure no bump, no tag, no CHANGELOG promotion, nothing lands on the real repo. This mechanizes the ad-hoc procedure the 0.8.59 staging pre-flight hand-drove (gh#2870 comments 5526330460 + addendum); use it whenever a "how healthy is main right now" report is wanted ahead of an actual cut. `--dry-run` lists the roster with no clone/npm ci; `--keep` preserves the throwaway clone for inspection.
104
+ **Staging a read-only report without cutting** (gh#3063): `node scripts/release/preflight-dry-run.mjs --version X.Y.Z` runs this same roster (`gate-roster.mjs` SoT) inside a throwaway `npm ci` clone and prints per-gate PASS/FAIL plus a tail-of-log on failure, no bump, no tag, no CHANGELOG promotion, nothing lands on the real repo. This mechanizes the ad-hoc procedure the 0.8.59 staging pre-flight hand-drove (gh#2870 comments 5526330460 + addendum); use it whenever a "how healthy is main right now" report is wanted ahead of an actual cut. `--dry-run` lists the roster with no clone/npm ci; `--keep` preserves the throwaway clone for inspection.
105
105
 
106
- ### 3.1 The full roster every gate runs; a subset = pre-flight failure
106
+ ### 3.1 The full roster, every gate runs; a subset = pre-flight failure
107
107
 
108
- **Precondition tsc build for llm, agent, persona (gh#3342).** Gate 4
108
+ **Precondition, tsc build for llm, agent, persona (gh#3342).** Gate 4
109
109
  (`test:unit:serial`) runs each package's root `*.test.js` files against its
110
110
  BUILT output, not its `src/*.ts`: `persona.test.js` imports `./index.js`
111
111
  directly, `agent.test.js`'s own docstring says "run against the BUILT
@@ -113,23 +113,23 @@ output ... `npm run build -w @adia-ai/agent` first", and `llm/core` carries
113
113
  a dedicated `dist-check.test.js` that asserts the emitted artifacts exist
114
114
  and explicitly does not build them itself. All three packages' emitted
115
115
  `.js`/`.d.ts` are gitignored, so a fresh cut clone has none of them until
116
- something builds them unlike §3.0's regen outputs, this is a plain build
116
+ something builds them, unlike §3.0's regen outputs, this is a plain build
117
117
  artifact, not a content-conditional regen, so `release-pack.mjs`'s
118
118
  `step3PreFlight()` now runs `npm run build -w @adia-ai/llm -w @adia-ai/agent
119
119
  -w @adia-ai/persona` unconditionally, every cut, immediately before gate 4
120
- (mechanized fix a manual cut should run the same command first). The
120
+ (mechanized fix, a manual cut should run the same command first). The
121
121
  v0.8.59 cut hit this: §3.0's `npm run build -w @adia-ai/llm` line only
122
122
  fires when its source-content trigger list matches, and never named
123
123
  agent/persona at all, so a cut with no matching trigger reached gate 4 with
124
124
  stale or absent dist and failed on it.
125
125
 
126
- **Execution model (gh#2006): three phases, not one serial walk.** `step3PreFlight()` runs gate 4 solo first (see its own note below), then gates 16 → 27 → 28 strictly in order (the eval-health write-then-read dependency gate 28 reads whichever `evals/mcp/runs/` directory sorts lexically LAST, so nothing else may write there between 27 and 28), concurrently with a bounded pool running every other gate at once (`PREFLIGHT_CONCURRENCY`, default 4 override for a dedicated/idle host). Every gate still resolves the same command, still fails the whole pre-flight on a red result, and still reports its own number only the WALL-CLOCK schedule changed, never the roster below or its numbering. `--dry` previews stay the original flat serial walk unchanged.
126
+ **Execution model (gh#2006): three phases, not one serial walk.** `step3PreFlight()` runs gate 4 solo first (see its own note below), then gates 16 → 27 → 28 strictly in order (the eval-health write-then-read dependency, gate 28 reads whichever `evals/mcp/runs/` directory sorts lexically LAST, so nothing else may write there between 27 and 28), concurrently with a bounded pool running every other gate at once (`PREFLIGHT_CONCURRENCY`, default 4, override for a dedicated/idle host). Every gate still resolves the same command, still fails the whole pre-flight on a red result, and still reports its own number, only the WALL-CLOCK schedule changed, never the roster below or its numbering. `--dry` previews stay the original flat serial walk unchanged.
127
127
 
128
128
  ```bash
129
129
  node scripts/build/components.mjs --verify # 1 yaml ↔ sidecar ↔ .d.ts
130
130
  npm run verify:traits # 2 trait coverage
131
131
  npm run check:lockstep # 3 version coherence (+ factory .mcp.json @adia-ai/mcp pin, invariant 8)
132
- npm run test:unit:serial # 4 vitest, serial the source of truth (§Cat 8; parallel flakes under load)
132
+ npm run test:unit:serial # 4 vitest, serial, the source of truth (§Cat 8; parallel flakes under load)
133
133
  npm run typecheck # 5 tsc --noEmit
134
134
  npm run check:demo-shells # 6 demo imports cover composes:
135
135
  npm run check:lightningcss-build # 7 CSS minifies
@@ -144,48 +144,47 @@ npm run check:links # 15 intra-repo links
144
144
  npm run eval:diff -- --engine zettel # 16 eval floors
145
145
  npm run dogfood:status # 17 P0/P1 dogfood floor (static-only under npm-ci; run once more under bootstrap layout for full coverage, gh#1359)
146
146
  npm run check:examples-md-fresh # 18 .examples.md vs .examples.html
147
- # gate 19 (verify:site-a2ui) retired with the site-a2ui mechanism ADR-0072 Decision 2 / gh#2410
148
- npm run verify:contrast # 20 WCAG AA canvas-text AND text-on-fill
147
+ # gate 19 (verify:site-a2ui) retired with the site-a2ui mechanism, ADR-0072 Decision 2 / gh#2410
148
+ npm run verify:contrast # 20 WCAG AA, canvas-text AND text-on-fill
149
149
  npm run check:token-semantics-sync # 21 token-selection generated refs vs token sources
150
150
  npm run check:demo-routes # 22 demo surfaces routed + patterns indexed
151
151
  npm run check:brand-assets # 23 brand mark token-driven, not baked raster
152
- 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)
152
+ 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)
153
153
  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)
154
- 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)
154
+ 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)
155
155
  npm run eval:diff -- --engine free-form # 27 fresh free-form eval run (evals/health input, gh#1135)
156
156
  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)
157
- node scripts/release/check-estate-split-latch.mjs # 29 no lockstep cut mid estate-split (ADR-0048 / gh#1192) see the note below
158
- npm run check:catalog-tiers # 30 tier-index.json vs committed catalog (gh#1494 ADR-0069 moved its PR-blocking half to derived-resync; the pre-cut roster re-asserts Class-R freshness before a tag. Gate 13 can't catch this: the harvester hashes tier-index.json as a SOURCE)
157
+ node scripts/release/check-estate-split-latch.mjs # 29 no lockstep cut mid estate-split (ADR-0048 / gh#1192), see the note below
158
+ npm run check:catalog-tiers # 30 tier-index.json vs committed catalog (gh#1494, ADR-0069 moved its PR-blocking half to derived-resync; the pre-cut roster re-asserts Class-R freshness before a tag. Gate 13 can't catch this: the harvester hashes tier-index.json as a SOURCE)
159
159
  npm run check:codex-manifests-fresh # 31 Codex plugin.json + openai.yaml vs .claude-plugin/plugin.json SoT (gh#1888)
160
160
  npm run check:harness-manifests-fresh # 32 Hermes/Pi plugin.yaml + __init__.py + prompts vs .claude-plugin/plugin.json + commands SoT (gh#1954)
161
161
  npm run verify:patterns-index # 33 pattern-index.md (mcp + adia-ui-factory) vs corpus source
162
- node scripts/release/check-yaml-events-vs-runtime.mjs --strict --strict-details # 34 yaml events: blocks vs runtime dispatch no phantom/missing events (gh#2829)
162
+ node scripts/release/check-yaml-events-vs-runtime.mjs --strict --strict-details # 34 yaml events: blocks vs runtime dispatch, no phantom/missing events (gh#2829)
163
163
  node scripts/release/check-yaml-impl-coverage.mjs --strict # 35 yaml schema fields vs implementation coverage (gh#2829)
164
- npm run check:treeshake # 36 single-import build matrix (esbuild+rollup) byte budgets + marker-leak + CSS purity + whole-lib delta + docs grep-gate (gh#2912)
165
- npm run check:lint-efficacy # 37 lint rule bank: seeded catch rate 100% + golden-set 0 error FPs + mutation hardening (gh#2911 not in `npm run check`, ~100s over the <60s bar)
164
+ npm run check:treeshake # 36 single-import build matrix (esbuild+rollup), byte budgets + marker-leak + CSS purity + whole-lib delta + docs grep-gate (gh#2912)
165
+ npm run check:lint-efficacy # 37 lint rule bank: seeded catch rate 100% + golden-set 0 error FPs + mutation hardening (gh#2911, not in `npm run check`, ~100s over the <60s bar)
166
166
  ```
167
167
 
168
- **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.
168
+ **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.
169
169
 
170
- 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.
170
+ 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.
171
171
 
172
172
  **Layout note (gh#1359): the roster does not run under one uniform `node_modules` shape.** Every gate above is layout-agnostic except two, which pull in opposite directions:
173
173
 
174
- - **Gate 9** (`check:js-bundles-fresh`) needs the **npm-ci layout** the committed `dist/` bundles are npm-shaped (an entry-relative Phosphor icon glob resolves differently under pnpm's non-hoisted layout, producing a materially larger fresh build that fails the diff). Run pre-flight under `npm ci`.
175
- - **Gate 17** (`npm run dogfood:status`) needs the **pnpm/bootstrapped layout** for its two live-probe legs (`empty-instantiation`, `padded-route-gutter`) to render icons correctly (gh#340) but auto-degrades to a static-only run (every other audit it aggregates has no dev-server dependency) rather than hard-failing when it detects an npm-shaped layout, so it no longer blocks a straight npm-ci roster pass. **A static-only pass is not full coverage.** Run `node scripts/dev/bootstrap-worktree.mjs && npm run dogfood:status` once under that layout before or after the main npm-ci roster pass to actually exercise the two live-probe legs before tagging.
174
+ - **Gate 9** (`check:js-bundles-fresh`) needs the **npm-ci layout**: the committed `dist/` bundles are npm-shaped (an entry-relative Phosphor icon glob resolves differently under pnpm's non-hoisted layout, producing a materially larger fresh build that fails the diff). Run pre-flight under `npm ci`.
175
+ - **Gate 17** (`npm run dogfood:status`) needs the **pnpm/bootstrapped layout** for its two live-probe legs (`empty-instantiation`, `padded-route-gutter`) to render icons correctly (gh#340), but auto-degrades to a static-only run (every other audit it aggregates has no dev-server dependency) rather than hard-failing when it detects an npm-shaped layout, so it no longer blocks a straight npm-ci roster pass. **A static-only pass is not full coverage.** Run `node scripts/dev/bootstrap-worktree.mjs && npm run dogfood:status` once under that layout, before or after the main npm-ci roster pass, to actually exercise the two live-probe legs before tagging.
176
176
 
177
177
  No other gate in the roster cares which layout produced `node_modules`.
178
178
 
179
179
  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.
180
180
 
181
- ## §Step 4 (Variant B, or ANY variant with uncommitted `[Unreleased]` content) Promote, bump, lockfile
181
+ ## §Step 4, (Variant B, or ANY variant with uncommitted `[Unreleased]` content) Promote, bump, lockfile
182
182
 
183
183
  **4a-pre. Assemble `changes/<pr>.md` fragments (REQ-W11-06, gh#2931), BEFORE promotion.** This
184
- repo's PRs land a `changes/<pr>.md` fragment instead of hand-editing a CHANGELOG directly
185
- `check-changelog-pr-gate.mjs`'s own PR-time gate now REQUIRES a fragment for a roster-package
184
+ repo's PRs land a `changes/<pr>.md` fragment instead of hand-editing a CHANGELOG directly, `check-changelog-pr-gate.mjs`'s own PR-time gate now REQUIRES a fragment for a roster-package
186
185
  change and refuses the direct edit outright (gh#3123; the root `CHANGELOG.md` was already
187
186
  fragment-only in practice before that ticket). Those fragments accumulate
188
- unreleased until something folds them into the right CHANGELOG's `[Unreleased]` section that's
187
+ unreleased until something folds them into the right CHANGELOG's `[Unreleased]` section, that's
189
188
  this step, and it must run before 4a promotes `[Unreleased]` to a versioned heading, or a
190
189
  fragment folded in afterward would land under the WRONG (already-promoted) heading.
191
190
 
@@ -203,44 +202,43 @@ Routing rule (`scripts/release/assemble-changelog-fragments.mjs`'s own header, f
203
202
  there): a fragment's first line is `- <kind>: <sentence>` (kind in fix|feature|chore|docs,
204
203
  unchanged from `changelog_fragments.py`'s schema) or, this repo's own addition, `- <kind>
205
204
  (<package>): <sentence>` naming a `PACKAGE_ROSTER` (`package-paths.mjs`) entry. The
206
- parenthetical-package form routes to that package's own `packages/<dir>/CHANGELOG.md` the
205
+ parenthetical-package form routes to that package's own `packages/<dir>/CHANGELOG.md`, the
207
206
  norm for package-scoped fragments as of gh#3123, not a hypothetical; the plain form routes to
208
207
  the repo-root `CHANGELOG.md`, whose own header scopes
209
208
  it to exactly that shape of change ("tooling, CI, build scripts, cross-package work, docs").
210
209
  `kind` maps to a Keep-a-Changelog subsection: `feature`→Added, `fix`→Fixed, `chore`→Changed,
211
- `docs`→Docs created under `## [Unreleased]` in that canonical order if the subsection doesn't
210
+ `docs`→Docs, created under `## [Unreleased]` in that canonical order if the subsection doesn't
212
211
  already exist, otherwise appended to the existing one.
213
212
 
214
213
  **Deliberately NOT a pre-flight roster gate (§3.1).** Pending fragments are a NORMAL state
215
- between PRs, not a defect a `--verify`-shaped freshness gate added to the pre-cut roster (which
214
+ between PRs, not a defect, a `--verify`-shaped freshness gate added to the pre-cut roster (which
216
215
  runs before this step, in Step 3) would fail on every cut that has any recent chore/fix/feature
217
216
  PR queued, which is the common case. The `--verify` invocation above is a post-assembly
218
217
  self-check (proves the assemble actually consumed everything it found), not a standing gate;
219
218
  `gate-roster.mjs`'s count is unchanged by this ticket.
220
219
 
221
- **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.
220
+ **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.
222
221
 
223
- **4a. Promote** `## [Unreleased]` → `## [vX.Y.Z] YYYY-MM-DD` per package (`` `<plugin-root>/skills/package-release/scripts/promote-unreleased.mjs` ``); author fresh blocks for changed-but-unlogged packages; stub the pure ride-alongs (`` `<plugin-root>/skills/package-release/scripts/insert-stub.mjs` ``). Classification recipe + shapes: [`changelog-discipline.md`](changelog-discipline.md).
222
+ **4a. Promote** `## [Unreleased]` → `## [vX.Y.Z], YYYY-MM-DD` per package (`` `<plugin-root>/skills/package-release/scripts/promote-unreleased.mjs` ``); author fresh blocks for changed-but-unlogged packages; stub the pure ride-alongs (`` `<plugin-root>/skills/package-release/scripts/insert-stub.mjs` ``). Classification recipe + shapes: [`changelog-discipline.md`](changelog-discipline.md).
224
223
 
225
- `release-pack.mjs` (both `--mode cut` and `--mode from-scratch`) now rejects any `--substantive-packages`/`--stub-packages` name whose entry in `scripts/package-paths.mjs`'s `PACKAGE_ROSTER` is unknown or `lockstep: false`, at parse time, before Step 1 runs (gh#2894) the 0.8.58 cut passed `adia-plugins` (lockstep:false) in `--substantive-packages` and let `promote-unreleased.mjs` rewrite its `[Unreleased]` header to a version that package never ships, caught only at Step 5.6 after the full pre-flight had already run.
224
+ `release-pack.mjs` (both `--mode cut` and `--mode from-scratch`) now rejects any `--substantive-packages`/`--stub-packages` name whose entry in `scripts/package-paths.mjs`'s `PACKAGE_ROSTER` is unknown or `lockstep: false`, at parse time, before Step 1 runs (gh#2894), the 0.8.58 cut passed `adia-plugins` (lockstep:false) in `--substantive-packages` and let `promote-unreleased.mjs` rewrite its `[Unreleased]` header to a version that package never ships, caught only at Step 5.6 after the full pre-flight had already run.
226
225
 
227
- **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 "<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)).
226
+ **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 "<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)).
228
227
 
229
- **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.
228
+ **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.
230
229
 
231
- **4d.** `npm run check:lockstep` → `OK all packages at X.Y.Z, all internal ranges at ^X.Y.0`.
230
+ **4d.** `npm run check:lockstep` → `OK, all packages at X.Y.Z, all internal ranges at ^X.Y.0`.
232
231
 
233
- **4d.5. Regenerate the derived genui catalog** (gh#617): `node scripts/build/derive-genui-catalog.mjs`. Its `catalogId` embeds the lockstep version (`adia.base@X.Y.Z`), so the 4b bump just invalidated `packages/genui/adia-catalog/{base,adia-pack}.json` regenerated pre-bump (§3.0) or not. The v0.8.26 cut skipped this and `check:genui-catalog` failed in CI one push later. Both files ride the release commit (Step 5 stages them).
232
+ **4d.5. Regenerate the derived genui catalog** (gh#617): `node scripts/build/derive-genui-catalog.mjs`. Its `catalogId` embeds the lockstep version (`adia.base@X.Y.Z`), so the 4b bump just invalidated `packages/genui/adia-catalog/{base,adia-pack}.json`, regenerated pre-bump (§3.0) or not. The v0.8.26 cut skipped this and `check:genui-catalog` failed in CI one push later. Both files ride the release commit (Step 5 stages them).
234
233
 
235
- **4d.5b. Regenerate catalog tiers + re-harvest the chunk corpus if it goes stale** (gh#1361, automated this was a recurring manual rider before): `npm run build:catalog-tiers`, then `npm run check:chunks-fresh`; if that probe goes stale, `npm run harvest:chunks`. `tier-index.json` derives from the same post-bump catalog 4d.5 just refreshed, and the chunk harvester hashes `tier-index.json` as a harvest SOURCE regenerating tiers without re-harvesting left `check:chunks-fresh` red on the next run, needing a manual rider commit both cuts it happened live: v0.8.39 (`72417beff`) and v0.8.40/CUT-0840-B (`d9ca8b323`, "Ran `npm run build:catalog-tiers` ... That regen staled `check:chunks-fresh` ... so re-harvested"). The re-harvest is CONDITIONAL on the freshness probe, not unconditional a tier regen that produces a byte-identical index owes no re-harvest. `tier-index.json`, `packages/gen-ui/engine/corpus/manifest.json`, and `packages/gen-ui/engine/corpus/chunks/` all ride the release commit (Step 5 stages them).
234
+ **4d.5b. Regenerate catalog tiers + re-harvest the chunk corpus if it goes stale** (gh#1361, automated: this was a recurring manual rider before): `npm run build:catalog-tiers`, then `npm run check:chunks-fresh`; if that probe goes stale, `npm run harvest:chunks`. `tier-index.json` derives from the same post-bump catalog 4d.5 just refreshed, and the chunk harvester hashes `tier-index.json` as a harvest SOURCE, regenerating tiers without re-harvesting left `check:chunks-fresh` red on the next run, needing a manual rider commit both cuts it happened live: v0.8.39 (`72417beff`) and v0.8.40/CUT-0840-B (`d9ca8b323`, "Ran `npm run build:catalog-tiers` ... That regen staled `check:chunks-fresh` ... so re-harvested"). The re-harvest is CONDITIONAL on the freshness probe, not unconditional, a tier regen that produces a byte-identical index owes no re-harvest. `tier-index.json`, `packages/gen-ui/engine/corpus/manifest.json`, and `packages/gen-ui/engine/corpus/chunks/` all ride the release commit (Step 5 stages them).
236
235
 
237
- **4d.6. Regenerate the Codex plugin manifests** (gh#1888, gh#1899): `node scripts/build/codex-manifests.mjs`, then `npm run check:codex-manifests-fresh` to confirm. Both plugins' `.codex-plugin/plugin.json` embed `version` from `.claude-plugin/plugin.json`, which the 4b bump just moved same "derived artifact carries the lockstep version" class as 4d.5's genui catalog. Gate 31 runs pre-bump in Step 3 and only proves freshness against the PREVIOUS version; nothing re-ran the generator post-bump before this line existed the v0.8.48 release PR (#1897) shipped stale manifests as a result, caught by `check:codex-manifests-fresh` in CI and fixed by hand on the release branch. `packages/plugins/*/.codex-plugin/plugin.json` and `packages/plugins/*/skills/*/agents/openai.yaml` ride the release commit (Step 5 stages them).
236
+ **4d.6. Regenerate the Codex plugin manifests** (gh#1888, gh#1899): `node scripts/build/codex-manifests.mjs`, then `npm run check:codex-manifests-fresh` to confirm. Both plugins' `.codex-plugin/plugin.json` embed `version` from `.claude-plugin/plugin.json`, which the 4b bump just moved, same "derived artifact carries the lockstep version" class as 4d.5's genui catalog. Gate 31 runs pre-bump in Step 3 and only proves freshness against the PREVIOUS version; nothing re-ran the generator post-bump before this line existed, the v0.8.48 release PR (#1897) shipped stale manifests as a result, caught by `check:codex-manifests-fresh` in CI and fixed by hand on the release branch. `packages/plugins/*/.codex-plugin/plugin.json` and `packages/plugins/*/skills/*/agents/openai.yaml` ride the release commit (Step 5 stages them).
238
237
 
239
- **4d.7. Regenerate the Hermes/Pi plugin manifests** (gh#1954): `node scripts/build/harness-manifests.mjs`, then `npm run check:harness-manifests-fresh` to confirm. Same hazard as 4d.6, same fix both plugins' `plugin.yaml` embed `version` from `.claude-plugin/plugin.json`. Gate 32 runs pre-bump in Step 3 and only proves freshness against the PREVIOUS version; run this post-bump every cut, not just when a skill/command changed. `packages/plugins/*/plugin.yaml`, `packages/plugins/*/__init__.py`, `packages/plugins/*/hermes-mcp.yaml` (factory only), and `packages/plugins/*/prompts/*.md` ride the release commit (Step 5 stages them).
238
+ **4d.7. Regenerate the Hermes/Pi plugin manifests** (gh#1954): `node scripts/build/harness-manifests.mjs`, then `npm run check:harness-manifests-fresh` to confirm. Same hazard as 4d.6, same fix, both plugins' `plugin.yaml` embed `version` from `.claude-plugin/plugin.json`. Gate 32 runs pre-bump in Step 3 and only proves freshness against the PREVIOUS version; run this post-bump every cut, not just when a skill/command changed. `packages/plugins/*/plugin.yaml`, `packages/plugins/*/__init__.py`, `packages/plugins/*/hermes-mcp.yaml` (factory only), and `packages/plugins/*/prompts/*.md` ride the release commit (Step 5 stages them).
240
239
 
241
240
  **4e. Release docs + team notes** (gated, not optional): review the entry
242
- files the release touches (root README/CHANGELOG, per-package READMEs
243
- content currency is YOUR judgment; the gate only proves presence), then
241
+ files the release touches (root README/CHANGELOG, per-package READMEs, content currency is YOUR judgment; the gate only proves presence), then
244
242
  generate the team notes and land them IN the release commit:
245
243
 
246
244
  ```bash
@@ -248,7 +246,7 @@ node scripts/release/generate-release-notes.mjs --version X.Y.Z --write # →
248
246
  node scripts/release/check-release-docs.mjs --version X.Y.Z # must print OK
249
247
  ```
250
248
 
251
- `check-release-docs` is enforced twice downstream the
249
+ `check-release-docs` is enforced twice downstream, the
252
250
  `release-pretag-docs-gate` Claude Code hook (adia-forge plugin) denies
253
251
  tag-creation/tag-push/publish-dispatch commands until it passes, and
254
252
  `.githooks/pre-push` blocks release-tag pushes the same way (v0.8.4
@@ -256,27 +254,27 @@ shipped tarballs with unpromoted `[Unreleased]` CHANGELOG headers because
256
254
  this class of check ran only after tagging).
257
255
 
258
256
  **4-resume. Resuming a cut that died mid-Step-4** (v0.8.10 and v0.8.29 hit
259
- this live). Two shapes, told apart by the lockstep versions `release-pack.mjs`
257
+ this live). Two shapes, told apart by the lockstep versions, `release-pack.mjs`
260
258
  checks them itself at startup (gh#765):
261
259
 
262
260
  - **Complete bump (all 13 `package.json` versions at the cut version):**
263
261
  just RE-RUN the same `release-pack.mjs` command. It detects the half-cut
264
262
  tree before the pre-flight, prints a `[resume]` banner, validates the
265
263
  hygiene gates at the CUT version (no more gate-24 false-fail at the
266
- previous version), and skips promote/stub/bump in Step 4 re-running only
264
+ previous version), and skips promote/stub/bump in Step 4, re-running only
267
265
  the idempotent substeps (lockfile, catalog, 4e notes, 4f coverage, 4g
268
266
  hygiene) and continuing to Step 5. The `promote-unreleased.mjs`
269
267
  "already has ## [X.Y.Z]" hard-error can no longer be reached on this path.
270
- Do NOT hand-edit CHANGELOGs back to `[Unreleased]` first the resumed run
268
+ Do NOT hand-edit CHANGELOGs back to `[Unreleased]` first, the resumed run
271
269
  expects the promoted state and re-proves it with the
272
270
  unpromoted-`[Unreleased]` guard.
273
- - **Mixed versions (some packages bumped, some not a mid-bump abort):**
271
+ - **Mixed versions (some packages bumped, some not, a mid-bump abort):**
274
272
  the orchestrator hard-stops listing the stragglers and will neither resume
275
273
  nor roll back. Recover by hand: restore the tree (`git status` /
276
274
  `git checkout -- <files>`, or finish the bump with `bump.mjs` directly),
277
275
  then re-run. To verify the promotion state along the way, the
278
276
  authoritative gate is `node scripts/release/check-release-docs.mjs
279
- --version X.Y.Z` it covers every lockstep package (including the nested
277
+ --version X.Y.Z`, it covers every lockstep package (including the nested
280
278
  `a2ui/*` and `plugins/*` paths a shallow `packages/*` glob misses) and
281
279
  fails on any leftover `[Unreleased]` content or missing `[X.Y.Z]` heading;
282
280
  it will still flag the not-yet-generated `docs/ops/releases/vX.Y.Z.md`, which
@@ -285,36 +283,35 @@ checks them itself at startup (gh#765):
285
283
  The fully manual fallback (both shapes, if the orchestrator itself is
286
284
  suspect) remains the standalone pieces the Mechanization section names, in
287
285
  this order. **Stubs complete BEFORE 4f** (the v0.8.32 resume proved the
288
- ordering): the bump-complete marker cannot see whether insert-stub ran a
286
+ ordering): the bump-complete marker cannot see whether insert-stub ran, a
289
287
  stub package's `[Unreleased]` is empty, so every guard passes with its
290
288
  `[X.Y.Z]` section entirely absent; running the coverage `--fix` before the
291
289
  stub sections exist leaves it nothing to append to, and the gap resurfaces
292
290
  as F-N1 warns at the push boundary, costing a tag move:
293
291
 
294
292
  ```bash
295
- node scripts/release/assemble-changelog-fragments.mjs # 4a-pre (idempotent safe to re-run; no-op if already assembled)
293
+ node scripts/release/assemble-changelog-fragments.mjs # 4a-pre (idempotent, safe to re-run; no-op if already assembled)
296
294
  node "<plugin-root>/skills/package-release/scripts/insert-stub.mjs" \
297
295
  --version X.Y.Z --date YYYY-MM-DD --previous-version X.Y.Z-1 \
298
- --substantive "<one-line>" --xref "<anchor>" --packages <missing-stubs> # 4a-stub FIRST, only the missing ones (hard-errors on existing sections)
296
+ --substantive "<one-line>" --xref "<anchor>" --packages <missing-stubs> # 4a-stub, FIRST, only the missing ones (hard-errors on existing sections)
299
297
  node "<plugin-root>/skills/package-release/scripts/bump.mjs" --from X.Y.Z-1 --to X.Y.Z # 4b (skip if versions already moved)
300
298
  npm install --package-lock-only --no-audit --no-fund # 4c
301
299
  npm run check:lockstep # 4d
302
- node scripts/build/derive-genui-catalog.mjs # 4d.5 catalogId carries the bumped version (gh#617)
303
- node scripts/build/codex-manifests.mjs # 4d.6 Codex manifest version carries the bumped version (gh#1899)
304
- node scripts/build/harness-manifests.mjs # 4d.7 Hermes/Pi manifest version carries the bumped version (gh#1954)
305
- node scripts/release/check-release.mjs --pending-version X.Y.Z --fix # 4f AFTER the stubs exist
300
+ node scripts/build/derive-genui-catalog.mjs # 4d.5, catalogId carries the bumped version (gh#617)
301
+ node scripts/build/codex-manifests.mjs # 4d.6, Codex manifest version carries the bumped version (gh#1899)
302
+ node scripts/build/harness-manifests.mjs # 4d.7, Hermes/Pi manifest version carries the bumped version (gh#1954)
303
+ node scripts/release/check-release.mjs --pending-version X.Y.Z --fix # 4f, AFTER the stubs exist
306
304
  node scripts/release/generate-release-notes.mjs --version X.Y.Z --write # 4e
307
305
  node scripts/release/check-release-docs.mjs --version X.Y.Z # 4e gate
308
- node scripts/release/check-cut-hygiene.mjs --version X.Y.Z # 4g post-bump proof
309
- # then Step 5 by hand (branch, stage, commit) Step 5.5's freshness
310
- # trip-wire runs on the staged set exactly as on a normal cut, don't skip it
311
- # and Step 5.7 via pr-bridge.mjs
306
+ node scripts/release/check-cut-hygiene.mjs --version X.Y.Z # 4g, post-bump proof
307
+ # then Step 5 by hand (branch, stage, commit), Step 5.5's freshness
308
+ # trip-wire runs on the staged set exactly as on a normal cut, don't skip it, # and Step 5.7 via pr-bridge.mjs
312
309
  ```
313
310
 
314
- ## §Step 5 Stage and commit (on a release branch)
311
+ ## §Step 5, Stage and commit (on a release branch)
315
312
 
316
313
  The release commit lands via PR, never a direct push to `main` (repo
317
- policy, operator ruling 2026-07-12 everything ships PR-first). Branch
314
+ policy, operator ruling 2026-07-12, everything ships PR-first). Branch
318
315
  FIRST, then stage. Defensively clear the index, then stage by explicit
319
316
  allowlist (never `git add -A`; if peers may have pre-staged files,
320
317
  `git commit -o <paths>` also bypasses a polluted index):
@@ -331,9 +328,9 @@ git -C "$REPO" add package-lock.json packages/*/package.json packages/*/CHANGELO
331
328
  git -C "$REPO" diff --cached --stat | tail -3 # count must match the allowlist
332
329
  ```
333
330
 
334
- Commit shape: `chore(release): vX.Y.Z lockstep <summary>` with substantive scope per package, ride-along stub list, any `Excluded in-flight:` lines, and the pasted gate summary.
331
+ Commit shape: `chore(release): vX.Y.Z lockstep, <summary>` with substantive scope per package, ride-along stub list, any `Excluded, in-flight:` lines, and the pasted gate summary.
335
332
 
336
- ## §Step 5.5 Pre-commit freshness trip-wire
333
+ ## §Step 5.5, Pre-commit freshness trip-wire
337
334
 
338
335
  After `git add`, before `git commit`, re-run the three freshness gates against staged state:
339
336
 
@@ -341,23 +338,22 @@ After `git add`, before `git commit`, re-run the three freshness gates against s
341
338
  node scripts/build/components.mjs --verify && npm run check:chunks-fresh && npm run check:embeddings-fresh
342
339
  ```
343
340
 
344
- Drift here means a regen output was left out of the allowlist stage it and re-run. <2s now vs ~5min of tag-move recovery after CI catches it. Do not proceed to tag with drift.
341
+ Drift here means a regen output was left out of the allowlist, stage it and re-run. <2s now vs ~5min of tag-move recovery after CI catches it. Do not proceed to tag with drift.
345
342
 
346
- ## §Step 5.6 Unstaged-tracked-files guard (`release-pack.mjs`, gh#2473)
343
+ ## §Step 5.6, Unstaged-tracked-files guard (`release-pack.mjs`, gh#2473)
347
344
 
348
345
  `release-pack.mjs`'s Step 5 mechanizes this: right after its own `git add`
349
346
  (the allowlist above, as automated), it runs `git status --porcelain` and
350
- fails the cut if any tracked file is still modified-in-the-worktree
351
- proof the allowlist covered everything bump.mjs / cut-hygiene touched this
347
+ fails the cut if any tracked file is still modified-in-the-worktree, proof the allowlist covered everything bump.mjs / cut-hygiene touched this
352
348
  cut, not just what §Step 5.5's three named freshness gates happen to check.
353
349
  The allowlist itself went stale five times (gh#1198, gh#1899, gh#1954,
354
- gh#2473, gh#3342 most recently `icons-cdn.js`'s PACKAGE_VERSION pin), so
355
- this guard is generic rather than another named file it stays the
350
+ gh#2473, gh#3342, most recently `icons-cdn.js`'s PACKAGE_VERSION pin), so
351
+ this guard is generic rather than another named file, it stays the
356
352
  fallback for anything below. **The PINNED_REFS-covered subset of the
357
353
  allowlist can no longer drift this way at all** (gh#3361): `release-pack.mjs`
358
354
  now derives those specific entries straight from `bump.mjs`'s own
359
355
  `PINNED_REFS`/`REPO_PINNED_REFS` tables (`pinnedRefFiles()`) instead of
360
- hand-listing the same paths a second time a new pinned file in `bump.mjs`
356
+ hand-listing the same paths a second time, a new pinned file in `bump.mjs`
361
357
  is automatically a new allowlist entry, no second edit needed. Everything
362
358
  NOT PINNED_REFS-covered (roster `package.json`/`CHANGELOG.md`, and the Step
363
359
  4d.5-4d.8 derived catalog/manifest/dist outputs) is still hand-listed and
@@ -365,10 +361,10 @@ still relies on this guard as the safety net. A manual cut should run the
365
361
  equivalent check by hand: `git status --porcelain` after staging must be
366
362
  empty of `M`/`D` lines.
367
363
 
368
- ## §Step 5.7 Release PR: push the branch, merge, re-baseline
364
+ ## §Step 5.7, Release PR: push the branch, merge, re-baseline
369
365
 
370
366
  The release commit reaches `main` through the standard PR flow. Mechanized
371
- form (preferred `pr-bridge.mjs` pushes, opens the PR, polls, merges ONLY
367
+ form (preferred, `pr-bridge.mjs` pushes, opens the PR, polls, merges ONLY
372
368
  when every non-fail-soft check is green AND zero review threads are
373
369
  unresolved AND no review requests changes; any other state stops with the
374
370
  evidence, never force-merges):
@@ -389,78 +385,77 @@ git -C "$REPO" checkout main && git -C "$REPO" pull
389
385
  ```
390
386
 
391
387
  Post-merge fixes land as follow-up commits to the SAME release PR (or a
392
- second PR merged before tagging) the tag point below is always `main`'s
388
+ second PR merged before tagging): the tag point below is always `main`'s
393
389
  post-merge HEAD, so anything merged before tagging ships in the tarball.
394
390
  If unrelated PRs merged between yours and the tag step, that is fine: tag
395
391
  at HEAD is the invariant, and the window closes at the tag.
396
392
 
397
393
  ## §Single authorization + evidence log (operator ruling 2026-07-17)
398
394
 
399
- The operator's initiating instruction covers the whole cycle no per-step re-confirmation (this replaced the 4-checkpoint sign-off model after the v0.8.5 cut spent ~40 minutes on approval relays while every real protection fired deterministically). The evidence blocks the checkpoints used to gate on still PRINT, as a running log the audit trail is unchanged; only the waiting is gone:
395
+ The operator's initiating instruction covers the whole cycle, no per-step re-confirmation (this replaced the 4-checkpoint sign-off model after the v0.8.5 cut spent ~40 minutes on approval relays while every real protection fired deterministically). The evidence blocks the checkpoints used to gate on still PRINT, as a running log: the audit trail is unchanged; only the waiting is gone:
400
396
 
401
397
  | Evidence logged before | Content | Why it's still printed |
402
398
  | --- | --- | --- |
403
399
  | Tagging (Step 6) | The planned tag list (umbrella + 10 per-package) | The log line a recovery diagnoses from |
404
- | 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 |
400
+ | 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 |
405
401
  | Publishing (Step 9) | The current registry snapshot (versions + `dist-tags.latest`) | Ordering is verified against what's LIVE, mechanically |
406
402
  | Deploying (Step 10) | The `deploy-site.yml` dispatch (never a raw rsync) | The workflow carries its own GitHub-environment human gate |
407
403
 
408
- `release-pack.mjs --go` auto-confirms all of these; the granular `--yes` / `--push` / `--publish` flags remain for cautious manual runs and prompt interactively when absent. **Two rules no flag or instruction wording skips:** an F-N1 *error* hard-stops before the push step exists at all, and a cosmetic F-N1 *warn* refuses auto-confirmation at the push boundary (with Step 4f mechanized, a warn appearing at Step 7 means something novel investigate, don't loop enrichment PRs).
404
+ `release-pack.mjs --go` auto-confirms all of these; the granular `--yes` / `--push` / `--publish` flags remain for cautious manual runs and prompt interactively when absent. **Two rules no flag or instruction wording skips:** an F-N1 *error* hard-stops before the push step exists at all, and a cosmetic F-N1 *warn* refuses auto-confirmation at the push boundary (with Step 4f mechanized, a warn appearing at Step 7 means something novel, investigate, don't loop enrichment PRs).
409
405
 
410
- ## §Step 4f Pre-tag coverage `--fix` (the retag-loop killer)
406
+ ## §Step 4f, Pre-tag coverage `--fix` (the retag-loop killer)
411
407
 
412
- After the bump + lockfile, run the AUTHORITATIVE F-N1 matcher against the working tree same code, same `changelogMentions` patterns, every roster package and let it append verified Maintenance bullets for any changed-but-unmentioned directory:
408
+ After the bump + lockfile, run the AUTHORITATIVE F-N1 matcher against the working tree, same code, same `changelogMentions` patterns, every roster package, and let it append verified Maintenance bullets for any changed-but-unmentioned directory:
413
409
 
414
410
  ```bash
415
411
  node scripts/release/check-release.mjs --pending-version X.Y.Z --fix
416
412
  ```
417
413
 
418
- Stage its CHANGELOG edits into the release commit (the Step-5 allowlist already covers `CHANGELOG.md`). It re-verifies its own output with the same matcher before writing a `--fix` that doesn't satisfy the checker is a hard error, not a silent pass. (The former `check:changelog-coverage` gate a different matcher and a 9-package roster missing the 2 plugins cost the v0.8.5 cut 3 enrichment PRs and 3 tag rewrites after "coverage clean" at cut time; it was deleted 2026-07-19, so `check-release.mjs --pending-version` is now the only pre-tag coverage check.)
414
+ Stage its CHANGELOG edits into the release commit (the Step-5 allowlist already covers `CHANGELOG.md`). It re-verifies its own output with the same matcher before writing, a `--fix` that doesn't satisfy the checker is a hard error, not a silent pass. (The former `check:changelog-coverage` gate, a different matcher and a 9-package roster missing the 2 plugins, cost the v0.8.5 cut 3 enrichment PRs and 3 tag rewrites after "coverage clean" at cut time; it was deleted 2026-07-19, so `check-release.mjs --pending-version` is now the only pre-tag coverage check.)
419
415
 
420
- ## §Step 6 Tag
416
+ ## §Step 6, Tag
421
417
 
422
- 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):
418
+ 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):
423
419
 
424
420
  ```bash
425
421
  node "<plugin-root>/skills/package-release/scripts/tag-lockstep.mjs" \
426
422
  --version X.Y.Z # umbrella vX.Y.Z + 10 <pkg>-vX.Y.Z (8 npm + 2 plugins)
427
423
  ```
428
424
 
429
- ## §Step 7 F-N1 release trip-wire
425
+ ## §Step 7, F-N1 release trip-wire
430
426
 
431
427
  ```bash
432
428
  node scripts/release/check-release.mjs --all-pending
433
429
  ```
434
430
 
435
- Per-package tags must be `✓ clean`; the umbrella-tag error is expected noise. **Expected outcome: clean Step 4f already ran the same matcher pre-PR.** A warn here means something changed between 4f and the tag (an interleaved merge, a 4f skip) investigate the cause, then recover via [`changelog-discipline.md`](changelog-discipline.md) §F-N1: fix the entry, land it as a follow-up commit through the PR flow (`--amend` is not possible the release commit is already merged), delete + re-create the tags at the new post-merge SHA, re-run.
431
+ Per-package tags must be `✓ clean`; the umbrella-tag error is expected noise. **Expected outcome: clean, Step 4f already ran the same matcher pre-PR.** A warn here means something changed between 4f and the tag (an interleaved merge, a 4f skip), investigate the cause, then recover via [`changelog-discipline.md`](changelog-discipline.md) §F-N1: fix the entry, land it as a follow-up commit through the PR flow (`--amend` is not possible: the release commit is already merged), delete + re-create the tags at the new post-merge SHA, re-run.
436
432
 
437
- ## §Step 8 Push tags
433
+ ## §Step 8, Push tags
438
434
 
439
- `main` is already on the remote (the release PR merged in Step 5.7)
440
- only the tags push here. Log the Step 7 F-N1 results + the tag list
435
+ `main` is already on the remote (the release PR merged in Step 5.7), only the tags push here. Log the Step 7 F-N1 results + the tag list
441
436
  (evidence table above) + confirm `git rev-list --count origin/main..HEAD`
442
- is 0 (a non-zero count means local commits bypassed the PR flow stop
437
+ is 0 (a non-zero count means local commits bypassed the PR flow, stop
443
438
  and route them through a PR first); then:
444
439
 
445
440
  The package list comes from the roster, never a hand list. This block used to
446
- enumerate the names inline and silently went stale it pushed 11 tags against
441
+ enumerate the names inline and silently went stale, it pushed 11 tags against
447
442
  a 14-package roster (`agent`, `persona` and `a2ui-protocol-mcp` missing), which
448
443
  is three packages that would simply never publish.
449
444
 
450
445
  ```bash
451
446
  PKGS=$(node -e "import('<plugin-root>/skills/package-release/scripts/package-paths.mjs')
452
447
  .then(m => console.log(m.PACKAGE_ROSTER.filter(p => p.lockstep !== false).map(p => p.name).join(' ')))")
453
- echo "$PKGS" # log it this IS the tag list evidence
448
+ echo "$PKGS" # log it: this IS the tag list evidence
454
449
 
455
450
  for p in $PKGS; do
456
- git -C "$REPO" push origin "${p}-vX.Y.Z" # ONE tag per push batched multi-tag
451
+ git -C "$REPO" push origin "${p}-vX.Y.Z" # ONE tag per push, batched multi-tag
457
452
  done # pushes drop the create event (Scenario 7)
458
453
  git -C "$REPO" push origin vX.Y.Z # umbrella last; triggers nothing
459
454
  ```
460
455
 
461
- ## §Step 9 Publish
456
+ ## §Step 9, Publish
462
457
 
463
- 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:
458
+ 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:
464
459
 
465
460
  ```bash
466
461
  node "<plugin-root>/skills/package-release/scripts/dispatch-publish.mjs" \
@@ -470,7 +465,7 @@ node "<plugin-root>/skills/package-release/scripts/dispatch-publish.mjs" \
470
465
  Wait for the workflows to settle, then verify against the **registry**, never the workflow's green check:
471
466
 
472
467
  ```bash
473
- # The name list comes from PACKAGE_ROSTER, never a hand-typed loop this loop
468
+ # The name list comes from PACKAGE_ROSTER, never a hand-typed loop: this loop
474
469
  # WAS hand-typed and went stale the moment ADR-0048 changed the roster (it still
475
470
  # named the six pre-split a2ui packages and omitted a2ui-protocol-mcp, so a
476
471
  # "verified" cut would have skipped checking the package most likely to be
@@ -487,18 +482,17 @@ npm view @adia-ai/web-components dist-tags.latest # must equal X.Y.Z
487
482
  Zero workflows fired after a tag push → [`recovery-paths.md`](recovery-paths.md) §Scenario 7.
488
483
 
489
484
  **npm's async staged-publish path (gh#3342):** a large tarball can take up to
490
- ~25 minutes to become visible on `npm view` after npm accepts it
491
- `release-pack.mjs`'s own Step 9 poll accounts for this (`REGISTRY_POLL_MINUTES`,
485
+ ~25 minutes to become visible on `npm view` after npm accepts it, `release-pack.mjs`'s own Step 9 poll accounts for this (`REGISTRY_POLL_MINUTES`,
492
486
  default 30, up from the 10-minute window the v0.8.59 cut exceeded with every
493
487
  publish run green). A re-dispatch attempted while a package is in that state
494
488
  fails its `npm publish` step with `npm error code E409` ("Cannot publish over
495
- previously staged version") that is the staged-not-lost signal, never a real
489
+ previously staged version"): that is the staged-not-lost signal, never a real
496
490
  failure; release-pack's Step 9 detects it (the failing run's own log) and polls
497
491
  longer instead of hard-failing. See [`recovery-paths.md`](recovery-paths.md)
498
492
  §Scenario 9 for manual recovery, including resuming at Step 10 only
499
493
  (`--from-step10`) without re-running the pre-flight or re-tagging.
500
494
 
501
- ## §Step 10 GH releases + site deploy dispatch
495
+ ## §Step 10, GH releases + site deploy dispatch
502
496
 
503
497
  ```bash
504
498
  for pkg in $(node -e '
@@ -515,7 +509,7 @@ gh workflow run "Deploy site (ui-kit.exe.xyz)" --repo adiahealth/gen-ui-kit --re
515
509
 
516
510
  Deploy discipline (the release tenant of the demo-site host; VM/service ops belong to the deploy skill, `site-deployment`):
517
511
 
518
- - `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).
512
+ - `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).
519
513
  - **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:
520
514
 
521
515
  ```bash
@@ -523,7 +517,7 @@ Deploy discipline (the release tenant of the demo-site host; VM/service ops belo
523
517
  curl -s https://ui-kit.exe.xyz/<this-cycle's-content-file> | grep -q "<unique string>" && echo OK
524
518
  ```
525
519
 
526
- - Any docs **route** cited to the operator or in notes must exist in `site/sitemap.json` (`grep '"path":'`) a plausible-looking route that isn't in the sitemap renders blank.
520
+ - Any docs **route** cited to the operator or in notes must exist in `site/sitemap.json` (`grep '"path":'`), a plausible-looking route that isn't in the sitemap renders blank.
527
521
 
528
522
  **Milestone close (ADR-0103, gh#2729, dated addendum 2026-09-01).** Every
529
523
  lockstep cut has an open GitHub Milestone named `vX.Y.Z` (one per cut,
@@ -537,7 +531,7 @@ gh api -X PATCH "repos/adiahealth/gen-ui-kit/milestones/$number" -f state=closed
537
531
  ```
538
532
 
539
533
  Any issue still open in that milestone at close time either ships anyway
540
- (re-tag it into the milestone that actually shipped it never leave an
534
+ (re-tag it into the milestone that actually shipped it, never leave an
541
535
  already-shipped issue's milestone wrong) or slips to the next milestone
542
536
  (re-tag now, don't leave the closed milestone showing open issues). Create
543
537
  the NEXT cut's milestone here too, so scheduling work for it doesn't wait on
@@ -549,11 +543,11 @@ gh api repos/adiahealth/gen-ui-kit/milestones -f title="vNEXT.Y.Z" -f state=open
549
543
 
550
544
  Any Step-2 stashes: `git stash pop`; flag conflicts to the operator.
551
545
 
552
- ## §Step 11 Author release notes (default)
546
+ ## §Step 11, Author release notes (default)
553
547
 
554
- 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".
548
+ 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".
555
549
 
556
- ## §Step 12 Restore the dev layout
550
+ ## §Step 12, Restore the dev layout
557
551
 
558
552
  The cut runs under `npm ci` (Step 1's layout check, gate 9), and that leaves
559
553
  `node_modules` npm-shaped. The dev server refuses to start on that tree:
@@ -572,18 +566,17 @@ gitignored). Live cost of skipping this: the v0.8.55 cut (2026-08-28) left the
572
566
  operator's primary checkout unable to run `npm run dev` until the layout was
573
567
  restored by hand.
574
568
 
575
- ## §The 0.8.38 cut (the clean-world cut delete this section after it lands)
569
+ ## §The 0.8.38 cut (the clean-world cut, delete this section after it lands)
576
570
 
577
571
  The first cut AFTER the estate-split bridge. Pre-conditions, all verifiable
578
572
  before Step 1:
579
573
 
580
- 1. **The shim-deletion follow-up PR has MERGED** (the §0.8.37 follow-up that
574
+ 1. **The shim-deletion follow-up PR has MERGED** (the §0.8.37 follow-up, that
581
575
  section deleted itself with the same PR, as designed:
582
576
  `packages/shims/` gone, roster shim rows gone, the six `publish-a2ui-*.yml`
583
577
  workflows gone, `check:shims`/`check-shim-bridges` retired). The roster file
584
- is the member count never transcribe it.
585
- 2. **Deprecation pointers have been live for the full inter-cut window**
586
- `npm info <old-name> deprecated` returns the pointer text for all six names.
578
+ is the member count, never transcribe it.
579
+ 2. **Deprecation pointers have been live for the full inter-cut window**, `npm info <old-name> deprecated` returns the pointer text for all six names.
587
580
  This is the stated policy for the one-cycle bridge: caret-floating consumers
588
581
  landed on the shims at 0.8.37, were pointed at the successors the entire
589
582
  window, and hard-stop only now. If the window was shorter than ~a week of
@@ -591,37 +584,37 @@ before Step 1:
591
584
  deleting the bridge out from under them.
592
585
  3. **`fix-old-names` has swept the known consumer repos** against the shim
593
586
  names (the closeout half of the rename wave + split).
594
- 4. Whatever landed between cuts rides along normally as of authoring, the
587
+ 4. Whatever landed between cuts rides along normally, as of authoring, the
595
588
  queued candidates are the directory wave (gh#1244, npm-invisible), the
596
589
  catalog tiers (gh#1243, new `@adia-ai/a2ui` subpath `./catalog`), and the
597
- factory MCP server (gh#1241, a third `adia-mcp` subcommand NOT a new
590
+ factory MCP server (gh#1241, a third `adia-mcp` subcommand, NOT a new
598
591
  package). None changes the roster; if one does by then, the roster file
599
592
  already reflects it and the gates enforce it.
600
593
 
601
- **In the release commit:** nothing estate-split-specific remains this is a
594
+ **In the release commit:** nothing estate-split-specific remains: this is a
602
595
  normal cut. If gh#1241 landed, the factory `.mcp.json` MAY additionally
603
- register `adia-factory` (`["-y", "@adia-ai/mcp@0.8.38", "factory"]`) an
596
+ register `adia-factory` (`["-y", "@adia-ai/mcp@0.8.38", "factory"]`), an
604
597
  addition, not a flip.
605
598
 
606
599
  **After publish:** the standard registry verify, plus one split-closure check:
607
600
  `npm view @adia-ai/a2ui-compose versions` should END at 0.8.37 (the shim's one
608
- and only publish) a 0.8.38 appearing under any retired name means a workflow
601
+ and only publish), a 0.8.38 appearing under any retired name means a workflow
609
602
  survived the follow-up PR; kill the tag and investigate before anything else.
610
603
 
611
604
  Delete this section in the same PR that closes the split's tracking record.
612
605
 
613
606
  ## §Variant A shortcut
614
607
 
615
- 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.
608
+ 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.
616
609
 
617
- ## §Plugin cache content between cuts is invisible until the next bump
610
+ ## §Plugin cache, content between cuts is invisible until the next bump
618
611
 
619
- The installed Claude Code plugin cache is keyed by the version string in `.claude-plugin/plugin.json` `/reload-plugins` only refreshes when that string CHANGES. Lockstep forbids a solo plugin bump, so any skill/agent/doc content merged to `main` between cuts does NOT reach the installed plugin until the next lockstep cut (or a deliberate manual `rsync` of the plugin dir over `~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/`). Consequences:
612
+ The installed Claude Code plugin cache is keyed by the version string in `.claude-plugin/plugin.json`, `/reload-plugins` only refreshes when that string CHANGES. Lockstep forbids a solo plugin bump, so any skill/agent/doc content merged to `main` between cuts does NOT reach the installed plugin until the next lockstep cut (or a deliberate manual `rsync` of the plugin dir over `~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/`). Consequences:
620
613
 
621
- - Shipping plugin-content changes mid-cycle is fine just know they're repo-only until the next cut; don't report them as "live in the harness".
614
+ - Shipping plugin-content changes mid-cycle is fine, just know they're repo-only until the next cut; don't report them as "live in the harness".
622
615
  - The v0.8.5 case (PR #304): a full release-process rewrite merged with no bump, and the installed plugin silently kept executing the RETIRED procedure until a manual cache sync. If the merged content changes operational behavior an active session depends on, do the manual sync immediately and say so.
623
- - At cut time nothing extra is needed the lockstep bump itself is what invalidates the cache.
616
+ - At cut time nothing extra is needed: the lockstep bump itself is what invalidates the cache.
624
617
 
625
618
  ## §When to abort
626
619
 
627
- Stop and surface to the operator when: a gate fails outside the documented recoveries; F-N1 reports >1 warn per package tag or any non-umbrella error; the release-commit candidate fails `check:demo-shells` / `check:lockstep` / `check:embeddings-fresh` (→ [`recovery-paths.md`](recovery-paths.md) §Scenario 4); uncommitted files stay unclassifiable after diffing; or a publish workflow fails E404/E401 (npm-token rotation operator-owned).
620
+ Stop and surface to the operator when: a gate fails outside the documented recoveries; F-N1 reports >1 warn per package tag or any non-umbrella error; the release-commit candidate fails `check:demo-shells` / `check:lockstep` / `check:embeddings-fresh` (→ [`recovery-paths.md`](recovery-paths.md) §Scenario 4); uncommitted files stay unclassifiable after diffing; or a publish workflow fails E404/E401 (npm-token rotation, operator-owned).