@adia-ai/adia-ui-forge 0.8.58 → 0.8.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/CHANGELOG.md +61 -0
- package/__init__.py +2 -2
- package/package.json +1 -1
- package/plugin.yaml +1 -1
- package/references/contracts/a2ui-mcp-surface.md +1 -1
- package/scripts/lint-rules.generated.mjs +11554 -1120
- package/skills/a2ui-maintenance/references/pipeline-overview.md +31 -1
- package/skills/component-md-authoring/SKILL.md +18 -14
- package/skills/demo-audit/SKILL.md +3 -3
- package/skills/demo-audit/agents/openai.yaml +1 -1
- package/skills/demo-audit/references/app-shell-pitfalls.md +6 -1
- package/skills/demo-audit/references/chat-shell-anatomy.md +98 -0
- package/skills/demo-audit/references/editor-shell-anatomy.md +109 -0
- package/skills/gen-ui-review/scripts/gen-review-decompose.mjs +27 -34
- package/skills/gen-ui-review/scripts/overflow-detect.generated.mjs +101 -0
- package/skills/package-release/references/cut-procedure.md +90 -5
- package/skills/package-release/references/recovery-paths.md +20 -0
- package/skills/package-release/scripts/bump.mjs +77 -8
- package/skills/package-release/scripts/gate-roster.mjs +27 -0
- package/skills/package-release/scripts/release-pack.mjs +481 -45
- package/skills/primitive-authoring/references/yaml-contract.md +106 -9
|
@@ -23,6 +23,7 @@ Two entry variants, converging at Step 5:
|
|
|
23
23
|
| 1 | Re-baseline (branch check + status + log + fetch) | No |
|
|
24
24
|
| 2 | Classify uncommitted files; stash strays | Stash only |
|
|
25
25
|
| 3 | Pre-flight gates (+ harvest preamble if source content changed) | No |
|
|
26
|
+
| 4a-pre | Assemble `changes/<pr>.md` fragments into the right `[Unreleased]` (REQ-W11-06) | Yes |
|
|
26
27
|
| 4 | (Variant B) Promote `[Unreleased]`; bump; lockfile | Yes |
|
|
27
28
|
| 4f | Pre-tag coverage `--fix` — authoritative F-N1 matcher, pre-PR | CHANGELOGs |
|
|
28
29
|
| 5 | Stage the release allowlist; commit on `release/vX.Y.Z` | Yes |
|
|
@@ -100,8 +101,28 @@ When only source *hashes* move and chunk content does not, `check:embeddings-fre
|
|
|
100
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
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.
|
|
105
|
+
|
|
103
106
|
### 3.1 The full roster — every gate runs; a subset = pre-flight failure
|
|
104
107
|
|
|
108
|
+
**Precondition — tsc build for llm, agent, persona (gh#3342).** Gate 4
|
|
109
|
+
(`test:unit:serial`) runs each package's root `*.test.js` files against its
|
|
110
|
+
BUILT output, not its `src/*.ts`: `persona.test.js` imports `./index.js`
|
|
111
|
+
directly, `agent.test.js`'s own docstring says "run against the BUILT
|
|
112
|
+
output ... `npm run build -w @adia-ai/agent` first", and `llm/core` carries
|
|
113
|
+
a dedicated `dist-check.test.js` that asserts the emitted artifacts exist
|
|
114
|
+
and explicitly does not build them itself. All three packages' emitted
|
|
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
|
|
117
|
+
artifact, not a content-conditional regen, so `release-pack.mjs`'s
|
|
118
|
+
`step3PreFlight()` now runs `npm run build -w @adia-ai/llm -w @adia-ai/agent
|
|
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
|
|
121
|
+
v0.8.59 cut hit this: §3.0's `npm run build -w @adia-ai/llm` line only
|
|
122
|
+
fires when its source-content trigger list matches, and never named
|
|
123
|
+
agent/persona at all, so a cut with no matching trigger reached gate 4 with
|
|
124
|
+
stale or absent dist and failed on it.
|
|
125
|
+
|
|
105
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.
|
|
106
127
|
|
|
107
128
|
```bash
|
|
@@ -140,6 +161,8 @@ npm run check:harness-manifests-fresh # 32 Hermes/Pi plugin.yaml + __in
|
|
|
140
161
|
npm run verify:patterns-index # 33 pattern-index.md (mcp + adia-ui-factory) vs corpus source
|
|
141
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)
|
|
142
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)
|
|
143
166
|
```
|
|
144
167
|
|
|
145
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.
|
|
@@ -157,10 +180,50 @@ Any red → route via [`gates-catalog.md`](gates-catalog.md); fix at the source,
|
|
|
157
180
|
|
|
158
181
|
## §Step 4 — (Variant B, or ANY variant with uncommitted `[Unreleased]` content) Promote, bump, lockfile
|
|
159
182
|
|
|
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
|
|
186
|
+
change and refuses the direct edit outright (gh#3123; the root `CHANGELOG.md` was already
|
|
187
|
+
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
|
|
189
|
+
this step, and it must run before 4a promotes `[Unreleased]` to a versioned heading, or a
|
|
190
|
+
fragment folded in afterward would land under the WRONG (already-promoted) heading.
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
node scripts/release/assemble-changelog-fragments.mjs # writes, deletes consumed fragments
|
|
194
|
+
node scripts/release/assemble-changelog-fragments.mjs --verify # must print PASS afterward
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Since gh#3638 a fragment may also be named `changes/gh-<issue>.md` (the form a builder can use
|
|
198
|
+
before a PR number exists). Assembly handles both: an issue-named fragment is credited to the PR
|
|
199
|
+
that merged it, read off that merge commit's own `(#<pr>)` subject, and falls back to the issue
|
|
200
|
+
number when no such commit is found. Nothing changes in this step's commands.
|
|
201
|
+
|
|
202
|
+
Routing rule (`scripts/release/assemble-changelog-fragments.mjs`'s own header, full detail
|
|
203
|
+
there): a fragment's first line is `- <kind>: <sentence>` (kind in fix|feature|chore|docs,
|
|
204
|
+
unchanged from `changelog_fragments.py`'s schema) or, this repo's own addition, `- <kind>
|
|
205
|
+
(<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
|
|
207
|
+
norm for package-scoped fragments as of gh#3123, not a hypothetical; the plain form routes to
|
|
208
|
+
the repo-root `CHANGELOG.md`, whose own header scopes
|
|
209
|
+
it to exactly that shape of change ("tooling, CI, build scripts, cross-package work, docs").
|
|
210
|
+
`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
|
|
212
|
+
already exist, otherwise appended to the existing one.
|
|
213
|
+
|
|
214
|
+
**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
|
|
216
|
+
runs before this step, in Step 3) would fail on every cut that has any recent chore/fix/feature
|
|
217
|
+
PR queued, which is the common case. The `--verify` invocation above is a post-assembly
|
|
218
|
+
self-check (proves the assemble actually consumed everything it found), not a standing gate;
|
|
219
|
+
`gate-roster.mjs`'s count is unchanged by this ticket.
|
|
220
|
+
|
|
160
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.
|
|
161
222
|
|
|
162
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).
|
|
163
224
|
|
|
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.
|
|
226
|
+
|
|
164
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)).
|
|
165
228
|
|
|
166
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.
|
|
@@ -229,6 +292,7 @@ stub sections exist leaves it nothing to append to, and the gap resurfaces
|
|
|
229
292
|
as F-N1 warns at the push boundary, costing a tag move:
|
|
230
293
|
|
|
231
294
|
```bash
|
|
295
|
+
node scripts/release/assemble-changelog-fragments.mjs # 4a-pre (idempotent — safe to re-run; no-op if already assembled)
|
|
232
296
|
node "<plugin-root>/skills/package-release/scripts/insert-stub.mjs" \
|
|
233
297
|
--version X.Y.Z --date YYYY-MM-DD --previous-version X.Y.Z-1 \
|
|
234
298
|
--substantive "<one-line>" --xref "<anchor>" --packages <missing-stubs> # 4a-stub — FIRST, only the missing ones (hard-errors on existing sections)
|
|
@@ -286,11 +350,20 @@ Drift here means a regen output was left out of the allowlist — stage it and r
|
|
|
286
350
|
fails the cut if any tracked file is still modified-in-the-worktree —
|
|
287
351
|
proof the allowlist covered everything bump.mjs / cut-hygiene touched this
|
|
288
352
|
cut, not just what §Step 5.5's three named freshness gates happen to check.
|
|
289
|
-
|
|
290
|
-
gh#
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
353
|
+
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
|
|
356
|
+
fallback for anything below. **The PINNED_REFS-covered subset of the
|
|
357
|
+
allowlist can no longer drift this way at all** (gh#3361): `release-pack.mjs`
|
|
358
|
+
now derives those specific entries straight from `bump.mjs`'s own
|
|
359
|
+
`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`
|
|
361
|
+
is automatically a new allowlist entry, no second edit needed. Everything
|
|
362
|
+
NOT PINNED_REFS-covered (roster `package.json`/`CHANGELOG.md`, and the Step
|
|
363
|
+
4d.5-4d.8 derived catalog/manifest/dist outputs) is still hand-listed and
|
|
364
|
+
still relies on this guard as the safety net. A manual cut should run the
|
|
365
|
+
equivalent check by hand: `git status --porcelain` after staging must be
|
|
366
|
+
empty of `M`/`D` lines.
|
|
294
367
|
|
|
295
368
|
## §Step 5.7 — Release PR: push the branch, merge, re-baseline
|
|
296
369
|
|
|
@@ -413,6 +486,18 @@ npm view @adia-ai/web-components dist-tags.latest # must equal X.Y.Z
|
|
|
413
486
|
|
|
414
487
|
Zero workflows fired after a tag push → [`recovery-paths.md`](recovery-paths.md) §Scenario 7.
|
|
415
488
|
|
|
489
|
+
**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`,
|
|
492
|
+
default 30, up from the 10-minute window the v0.8.59 cut exceeded with every
|
|
493
|
+
publish run green). A re-dispatch attempted while a package is in that state
|
|
494
|
+
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
|
|
496
|
+
failure; release-pack's Step 9 detects it (the failing run's own log) and polls
|
|
497
|
+
longer instead of hard-failing. See [`recovery-paths.md`](recovery-paths.md)
|
|
498
|
+
§Scenario 9 for manual recovery, including resuming at Step 10 only
|
|
499
|
+
(`--from-step10`) without re-running the pre-flight or re-tagging.
|
|
500
|
+
|
|
416
501
|
## §Step 10 — GH releases + site deploy dispatch
|
|
417
502
|
|
|
418
503
|
```bash
|
|
@@ -97,6 +97,26 @@ For a batch, preserve npm-latest ordering (`--after <prev>`). Verify against the
|
|
|
97
97
|
|
|
98
98
|
---
|
|
99
99
|
|
|
100
|
+
## §Scenario 9 — Step 9 registry poll times out with every publish run green (npm E409)
|
|
101
|
+
|
|
102
|
+
**Shape:** `release-pack.mjs --mode handoff` exits 1 at Step 9, but `gh run list --workflow=publish-<pkg>.yml` shows every run `success` and `npm view <scope>/<pkg> version` eventually returns the target version — it just took longer than the poll waited. A large tarball can take up to ~25 minutes to become visible on `npm view` after npm accepts it (asynchronous staged publish); the v0.8.59 cut's poll window was 10 minutes.
|
|
103
|
+
|
|
104
|
+
**A re-dispatch made while a package is in this state fails its own `npm publish` step with `npm error code E409` / "Cannot publish over previously staged version".** That error is not a real failure — npm rejected the duplicate publish precisely because the real one already landed server-side. It is the staged-not-lost signal, never grounds to re-dispatch a third time.
|
|
105
|
+
|
|
106
|
+
**Resolution:**
|
|
107
|
+
|
|
108
|
+
- `release-pack.mjs`'s Step 9 (gh#3342) already extends its own poll to `REGISTRY_POLL_MINUTES` (default 30, override via env) and, for any package still stale after that, checks its latest `publish-<pkg>.yml` run for the E409 signature before deciding — an E409-confirmed package gets one more extended, isolated poll instead of an immediate hard-fail. Nothing to do by hand in that case; let it finish.
|
|
109
|
+
- If Step 9 already exited 1 and you've confirmed by hand (registry + `gh run list`) that every package is actually published, don't re-run the full handoff — it would re-run the ~15min pre-flight (Step 3) and re-tag at HEAD (Step 6, wrong if anything merged since the original tag). Resume from Step 10 only:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
node "<plugin-root>/skills/package-release/scripts/release-pack.mjs" \
|
|
113
|
+
--mode handoff --version X.Y.Z --date YYYY-MM-DD --previous-version X.Y.Z-1 \
|
|
114
|
+
--gh-notes-file <path> --from-step10
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`--from-step10` skips Steps 1/3/4/5/6/7/8/9 entirely and runs only Step 10 (GH releases + site deploy) — the exact shape a hand-rolled one-off script (`/tmp/cut-0859-step10.sh`, never checked in) worked around live on the v0.8.59 cut. Before jumping, it hard-verifies the umbrella tag AND every per-package tag (`vX.Y.Z`, `<pkg>-vX.Y.Z`) already exist on origin and resolve to HEAD — without that check, `gh release create` on a tag that was never actually made would silently mint a NEW lightweight tag at whatever HEAD happens to be, attaching the release notes to the wrong commit. A missing or mismatched tag refuses the resume outright, naming which tag and why (either Step 8 never pushed it, or HEAD moved since); re-tag/push for real, or checkout the tagged commit, before retrying.
|
|
118
|
+
- **Never** re-dispatch a package a third time once it shows E409 — a third attempt only 409s again. Wait for the registry; it always converges.
|
|
119
|
+
|
|
100
120
|
## §Decision flowchart
|
|
101
121
|
|
|
102
122
|
```text
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
import fs from 'node:fs';
|
|
23
23
|
import path from 'node:path';
|
|
24
24
|
import process from 'node:process';
|
|
25
|
+
import { fileURLToPath } from 'node:url';
|
|
25
26
|
import { PACKAGE_ROSTER } from './package-paths.mjs';
|
|
26
27
|
|
|
27
28
|
// Single-sourced roster (H3, package-paths.mjs). Each plugin also carries a
|
|
@@ -60,7 +61,14 @@ const SIBLING_MANIFESTS = Object.fromEntries(
|
|
|
60
61
|
// `gen-ui-mcp`) was narrowed back by the shim-deletion follow-up PR, matching
|
|
61
62
|
// lockstep-checks.mjs's MCP_PIN_RE — a retired name in the pin must now fail
|
|
62
63
|
// loudly instead of being bumped along.
|
|
63
|
-
|
|
64
|
+
// gh#3361: exported so release-pack.mjs's Step 5 can derive its
|
|
65
|
+
// PINNED_REFS-covered allowlist entries from this table directly instead of
|
|
66
|
+
// hand-duplicating the file list — the drift class this table itself was
|
|
67
|
+
// born to fix (gh#1198/1899/1954/2473/3342, all "bump.mjs moved a pin,
|
|
68
|
+
// release-pack.mjs's own list didn't know about it") can no longer recur
|
|
69
|
+
// for anything PINNED_REFS already covers: a new entry here is now a new
|
|
70
|
+
// entry there, structurally, not two edits.
|
|
71
|
+
export const A2UI_MCP_PIN = {
|
|
64
72
|
label: 'generation-MCP pin (@adia-ai/mcp)',
|
|
65
73
|
// `@adia-ai/mcp@<from>` (not a prefix of a longer version) → `@<to>`
|
|
66
74
|
// Escape EVERY regex metacharacter in `from`, not just dots: a prerelease or
|
|
@@ -71,7 +79,7 @@ const A2UI_MCP_PIN = {
|
|
|
71
79
|
new RegExp(`(@adia-ai/mcp@)${from.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?![\\d.])`),
|
|
72
80
|
replace: (to) => `$1${to}`,
|
|
73
81
|
};
|
|
74
|
-
const PINNED_REFS = {
|
|
82
|
+
export const PINNED_REFS = {
|
|
75
83
|
'packages/plugins/adia-ui-factory': [
|
|
76
84
|
{ ...A2UI_MCP_PIN, file: '.mcp.json' },
|
|
77
85
|
{ ...A2UI_MCP_PIN, file: 'README.md', label: 'generation-MCP pin (README prose)' },
|
|
@@ -88,6 +96,23 @@ const PINNED_REFS = {
|
|
|
88
96
|
new RegExp(`("@adia-ai/[a-z0-9-]+":\\s*")${from.replace(/\./g, '\\.')}(?![\\d.])`, 'g'),
|
|
89
97
|
replace: (to) => `$1${to}`,
|
|
90
98
|
}],
|
|
99
|
+
// icons-cdn.js's hand-typed PACKAGE_VERSION literal (gh#3228's jsDelivr
|
|
100
|
+
// manifest-fallback URL pin) must move with web-components' OWN version
|
|
101
|
+
// every cut — unlike the file's other literal (PINNED_VERSION, which
|
|
102
|
+
// tracks @phosphor-icons/core's independent release cadence and is
|
|
103
|
+
// updated by hand only when Phosphor cuts), this one pins the very
|
|
104
|
+
// package it lives in. Left unmoved by bump.mjs through the 0.8.58 cut,
|
|
105
|
+
// it would leave MANIFEST_CDN_FALLBACK_URL naming a stale version with
|
|
106
|
+
// check:lockstep staying green — no coherence source watched it until
|
|
107
|
+
// lockstep-checks.mjs's invariant 9 (gh#3240) and this pin closed the
|
|
108
|
+
// loop together.
|
|
109
|
+
'packages/web-components': [{
|
|
110
|
+
file: 'core/icons-cdn.js',
|
|
111
|
+
label: 'icons-cdn.js PACKAGE_VERSION literal (jsDelivr manifest fallback pin)',
|
|
112
|
+
pattern: (from) =>
|
|
113
|
+
new RegExp(`(const PACKAGE_VERSION = ')${from.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?![\\d.])(')`),
|
|
114
|
+
replace: (to) => `$1${to}$2`,
|
|
115
|
+
}],
|
|
91
116
|
};
|
|
92
117
|
|
|
93
118
|
// Repo-level version references (path relative to REPO ROOT, not a package) —
|
|
@@ -96,7 +121,7 @@ const PINNED_REFS = {
|
|
|
96
121
|
// once and the next cut fails the gate — so the bump moves it, like the
|
|
97
122
|
// .mcp.json pin (H2 follow-through, 2026-07-19: found 0.7.13 claimed while
|
|
98
123
|
// 0.8.7 was live — a full minor of README drift shipped to npm).
|
|
99
|
-
const REPO_PINNED_REFS = [
|
|
124
|
+
export const REPO_PINNED_REFS = [
|
|
100
125
|
{
|
|
101
126
|
file: 'README.md',
|
|
102
127
|
label: '"Current version" claim',
|
|
@@ -274,10 +299,14 @@ function main() {
|
|
|
274
299
|
// script never calls fs.writeFileSync by pointing at a path whose directory
|
|
275
300
|
// doesn't exist; a latent write bug would throw ENOENT instead of passing.
|
|
276
301
|
function selftest() {
|
|
302
|
+
// A fixture pkg name deliberately NOT in PINNED_REFS/SIBLING_MANIFESTS —
|
|
303
|
+
// this proof exercises bumpAll's core version-field transform in
|
|
304
|
+
// isolation, so it must not collide with (and pull in the file reads for)
|
|
305
|
+
// any real package's pin config.
|
|
277
306
|
const fake = [{
|
|
278
|
-
pkg: 'packages/
|
|
307
|
+
pkg: 'packages/fixture-pkg',
|
|
279
308
|
path: '/nonexistent-fixture-dir/package.json',
|
|
280
|
-
txt: '{\n "name": "@adia-ai/
|
|
309
|
+
txt: '{\n "name": "@adia-ai/fixture-pkg",\n "version": "0.8.4"\n}\n',
|
|
281
310
|
version: '0.8.4',
|
|
282
311
|
}];
|
|
283
312
|
if (!validateAllAtFrom(fake, '0.8.4')) {
|
|
@@ -341,6 +370,24 @@ function selftest() {
|
|
|
341
370
|
console.error('selftest FAIL: peer-pin pattern prefix-matched a longer version'); process.exit(1);
|
|
342
371
|
}
|
|
343
372
|
|
|
373
|
+
// icons-cdn.js PACKAGE_VERSION literal (gh#3240, invariant 9) — must move
|
|
374
|
+
// with web-components' own version, and must not touch the file's OTHER
|
|
375
|
+
// literal-looking pin (PINNED_VERSION, @phosphor-icons/core's version,
|
|
376
|
+
// updated by hand on its own independent cadence).
|
|
377
|
+
const [iconsCdnCfg] = PINNED_REFS['packages/web-components'];
|
|
378
|
+
const iconsCdnBefore =
|
|
379
|
+
"const PINNED_VERSION = '2.1.1';\n\nconst PACKAGE_VERSION = '0.8.58';\n";
|
|
380
|
+
const iconsCdnAfter = bumpPinnedRef(iconsCdnBefore, iconsCdnCfg, '0.8.58', '0.8.59');
|
|
381
|
+
if (!iconsCdnAfter.includes("PACKAGE_VERSION = '0.8.59'") || iconsCdnAfter.includes("PACKAGE_VERSION = '0.8.58'")) {
|
|
382
|
+
console.error('selftest FAIL: PINNED_REFS did not bump the icons-cdn.js PACKAGE_VERSION literal'); process.exit(1);
|
|
383
|
+
}
|
|
384
|
+
if (!iconsCdnAfter.includes("PINNED_VERSION = '2.1.1'")) {
|
|
385
|
+
console.error('selftest FAIL: icons-cdn.js pin bump touched the unrelated PINNED_VERSION (phosphor) literal'); process.exit(1);
|
|
386
|
+
}
|
|
387
|
+
if (bumpPinnedRef("const PACKAGE_VERSION = '0.8.580';", iconsCdnCfg, '0.8.58', '0.8.59') !== "const PACKAGE_VERSION = '0.8.580';") {
|
|
388
|
+
console.error('selftest FAIL: icons-cdn.js pin pattern prefix-matched a longer version'); process.exit(1);
|
|
389
|
+
}
|
|
390
|
+
|
|
344
391
|
// validatePinsAtFrom — the mutation-free pre-flight (reviewer finding: the
|
|
345
392
|
// mid-loop pin check fired only after two files were already written).
|
|
346
393
|
const goodPin = [{ pkg: 'packages/plugins/adia-ui-factory', cfg: pinCfg, path: '/nonexistent/.mcp.json', txt: pinBefore }];
|
|
@@ -368,6 +415,28 @@ function selftest() {
|
|
|
368
415
|
console.log('selftest OK');
|
|
369
416
|
}
|
|
370
417
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
else main()
|
|
418
|
+
// gh#3361: this module is now imported for its PINNED_REFS/REPO_PINNED_REFS
|
|
419
|
+
// exports (release-pack.mjs's Step 5), not only run as a CLI — a bare
|
|
420
|
+
// `topArgv[0] === 'selftest'` / `else main()` at module scope would fire
|
|
421
|
+
// `main()` (which calls `parseArgs`, which prints "error: --from/--to
|
|
422
|
+
// required" and `process.exit(2)`s) the moment ANYTHING imports this file,
|
|
423
|
+
// killing the importer's own process. Realpath-safe (a naive
|
|
424
|
+
// `argv[1] === fileURLToPath(import.meta.url)` reads false through a
|
|
425
|
+
// symlink, same pitfall scripts/lib/is-entry-point.mjs documents) so this
|
|
426
|
+
// stays correct if bump.mjs is ever ELM'd behind one; inlined rather than
|
|
427
|
+
// importing that top-level helper, keeping this skill's own scripts/
|
|
428
|
+
// self-sufficient.
|
|
429
|
+
function isEntryPoint() {
|
|
430
|
+
if (!process.argv[1]) return false;
|
|
431
|
+
try {
|
|
432
|
+
return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(path.resolve(process.argv[1]));
|
|
433
|
+
} catch {
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (isEntryPoint()) {
|
|
439
|
+
const topArgv = process.argv.slice(2);
|
|
440
|
+
if (topArgv[0] === 'selftest') selftest();
|
|
441
|
+
else main();
|
|
442
|
+
}
|
|
@@ -163,6 +163,33 @@ export const GATE_ROSTER = [
|
|
|
163
163
|
// inserted — same numbering discipline as gate 30/31/32/33.
|
|
164
164
|
{ n: 34, cmd: 'node scripts/release/check-yaml-events-vs-runtime.mjs --strict --strict-details', what: 'yaml events: blocks vs runtime dispatch (no phantom/missing events)' },
|
|
165
165
|
{ n: 35, cmd: 'node scripts/release/check-yaml-impl-coverage.mjs --strict', what: 'yaml schema fields vs implementation coverage' },
|
|
166
|
+
// gh#2912, plan-top-priority-verification.md item 4 (G1): the full
|
|
167
|
+
// 10-component x 2-bundler single-import build matrix — byte budgets +
|
|
168
|
+
// 0-foreign-marker-leak + paired-CSS-purity + whole-lib-delta + docs
|
|
169
|
+
// grep-gate. Too slow/heavy to gate every `npm run check` run (it spins
|
|
170
|
+
// up real esbuild AND rollup builds per cell); a 3-cell fast canary is
|
|
171
|
+
// wired into `npm run check` instead (check:treeshake:canary) as a
|
|
172
|
+
// regression tripwire, with the full matrix reserved for cut time. The
|
|
173
|
+
// matrix surfaced 14 real, pre-existing cross-component marker leaks
|
|
174
|
+
// (tracked as gh#2922, not fixed by this ticket) that are held under a
|
|
175
|
+
// known-red allowlist in check-treeshake.mjs itself — a cell diverging
|
|
176
|
+
// from its allowlisted finding, or any non-allowlisted cell failing,
|
|
177
|
+
// still reds this gate; the script also reports an allowlisted cell that
|
|
178
|
+
// comes back clean as promotable rather than staying silent about it.
|
|
179
|
+
// With the allowlist, this gate is currently all-green. Appended, not
|
|
180
|
+
// inserted — same numbering discipline as gate 30/31/32/33/34/35.
|
|
181
|
+
{ n: 36, cmd: 'npm run check:treeshake', what: 'single-import build matrix (esbuild+rollup) — byte budgets + marker-leak + CSS purity + whole-lib delta + docs grep-gate' },
|
|
182
|
+
// gh#2911 (plan-top-priority-verification.md item 3 G1): the lint rule
|
|
183
|
+
// bank's seeded-violation catch-rate + false-positive golden-set sweep +
|
|
184
|
+
// 5-rule mutation hardening. NOT in `npm run check` — the golden-set FP
|
|
185
|
+
// sweep alone measured ~100s (every rule in the bank re-parses every one
|
|
186
|
+
// of ~440 `*.examples.html`/`*.contents.html` files; the generated
|
|
187
|
+
// composition rules each call `tagTree()` independently rather than
|
|
188
|
+
// sharing one parse per file — a real perf gap, filed as gh#2937,
|
|
189
|
+
// not fixed here), over AGENTS.md's <60s bar for the PR-blocking
|
|
190
|
+
// aggregate. Appended, not inserted — same numbering discipline as gate
|
|
191
|
+
// 30/31/32/33/34/35/36.
|
|
192
|
+
{ n: 37, cmd: 'npm run check:lint-efficacy', what: 'lint rule bank: seeded catch rate 100% + golden-set 0 error FPs + mutation hardening' },
|
|
166
193
|
];
|
|
167
194
|
|
|
168
195
|
// -- CLI ------------------------------------------------------------------
|