@adia-ai/adia-ui-forge 0.8.4 → 0.8.6
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/CHANGELOG.md +27 -2
- package/README.md +13 -4
- package/agents/a2ui-builder.corpus.json +35 -0
- package/agents/a2ui-builder.md +37 -0
- package/agents/component-author.corpus.json +40 -0
- package/agents/component-author.md +10 -1
- package/agents/framework-reviewer.corpus.json +35 -0
- package/agents/framework-reviewer.md +42 -0
- package/agents/release-builder.corpus.json +39 -0
- package/agents/release-builder.md +39 -0
- package/agents/routing-corpus.json +38 -38
- package/bin/release-pretag-docs-gate +226 -0
- package/commands/release.md +5 -3
- package/hooks/hooks.json +10 -0
- package/package.json +1 -1
- package/skills/adia-a2ui/SKILL.md +37 -8
- package/skills/adia-a2ui/evals/routing-corpus.json +36 -29
- package/skills/adia-a2ui/references/pipeline-overview.md +1 -1
- package/skills/adia-a2ui/references/zettel-calibration.md +5 -5
- package/skills/adia-author/SKILL.md +16 -8
- package/skills/adia-author/evals/routing-corpus.json +39 -218
- package/skills/adia-author/references/anti-patterns.md +7 -7
- package/skills/adia-author/references/api-contract.md +9 -9
- package/skills/adia-author/references/authoring-cycle.md +25 -6
- package/skills/adia-author/references/code-style.md +4 -5
- package/skills/adia-author/references/lifecycle-patterns.md +8 -8
- package/skills/adia-author/references/llm-bridge.md +7 -0
- package/skills/adia-author/references/token-contract.md +5 -5
- package/skills/adia-author/references/worked-example.md +34 -18
- package/skills/adia-author/references/yaml-contract.md +1 -1
- package/skills/adia-deploy/SKILL.md +71 -12
- package/skills/adia-deploy/evals/routing-corpus.json +37 -0
- package/skills/adia-deploy/references/deploy-playbooks.md +1 -1
- package/skills/adia-dogfood/SKILL.md +71 -11
- package/skills/adia-dogfood/evals/routing-corpus.json +34 -0
- package/skills/adia-dogfood/references/admin-shell-anatomy.md +1 -1
- package/skills/adia-gen-review/SKILL.md +73 -7
- package/skills/adia-gen-review/evals/routing-corpus.json +34 -0
- package/skills/adia-gen-review/scripts/gen-review-decompose.mjs +5 -0
- package/skills/adia-llm-internals/SKILL.md +10 -6
- package/skills/adia-llm-internals/evals/routing-corpus.json +37 -0
- package/skills/adia-llm-internals/references/bridge-facade.md +2 -2
- package/skills/adia-llm-internals/references/streaming-sse.md +1 -1
- package/skills/adia-release/SKILL.md +43 -13
- package/skills/adia-release/evals/evals.json +24 -0
- package/skills/adia-release/evals/routing-corpus.json +35 -0
- package/skills/adia-release/references/changelog-discipline.md +15 -9
- package/skills/adia-release/references/cut-procedure.md +78 -24
- package/skills/adia-release/references/gates-catalog.md +3 -3
- package/skills/adia-release/references/independent-package-release.md +1 -1
- package/skills/adia-release/references/recovery-paths.md +4 -2
- package/skills/adia-release/scripts/bump.mjs +32 -1
- package/skills/adia-release/scripts/dispatch-publish.mjs +49 -11
- package/skills/adia-release/scripts/gate-roster.mjs +71 -0
- package/skills/adia-release/scripts/insert-stub.mjs +36 -2
- package/skills/adia-release/scripts/promote-unreleased.mjs +35 -1
- package/skills/adia-release/scripts/release-pack.mjs +315 -56
- package/skills/adia-release/scripts/tag-lockstep.mjs +29 -7
- package/skills/adia-site-docs/SKILL.md +6 -1
- package/skills/adia-site-docs/evals/routing-corpus.json +35 -173
- package/skills/adia-ssr/SKILL.md +128 -0
- package/skills/adia-ssr/evals/audit-report.md +63 -0
- package/skills/adia-ssr/evals/routing-corpus.json +34 -0
- package/skills/adia-ssr/references/consumer-workarounds.md +81 -0
- package/skills/adia-ssr/references/failure-shapes.md +164 -0
- package/skills/adia-ssr/references/guard-patterns.md +183 -0
- package/skills/adia-ssr/references/status-ledger.md +33 -0
- package/skills/adia-ssr/references/test-without-linkedom.md +129 -0
- package/agents/a2ui-engineer.md +0 -26
- package/agents/framework-verifier.md +0 -26
- package/agents/release-engineer.md +0 -26
|
@@ -140,4 +140,35 @@ function main() {
|
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
|
|
143
|
+
// Fixture-locked proof of the core transform, entirely in-memory (no real
|
|
144
|
+
// package.json files touched) — the `dry: true` path below asserts the
|
|
145
|
+
// script never calls fs.writeFileSync by pointing at a path whose directory
|
|
146
|
+
// doesn't exist; a latent write bug would throw ENOENT instead of passing.
|
|
147
|
+
function selftest() {
|
|
148
|
+
const fake = [{
|
|
149
|
+
pkg: 'packages/web-components',
|
|
150
|
+
path: '/nonexistent-fixture-dir/package.json',
|
|
151
|
+
txt: '{\n "name": "@adia-ai/web-components",\n "version": "0.8.4"\n}\n',
|
|
152
|
+
version: '0.8.4',
|
|
153
|
+
}];
|
|
154
|
+
if (!validateAllAtFrom(fake, '0.8.4')) {
|
|
155
|
+
console.error('selftest FAIL: validateAllAtFrom rejected a matching version'); process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
if (validateAllAtFrom(fake, '0.8.3')) {
|
|
158
|
+
console.error('selftest FAIL: validateAllAtFrom accepted a mismatched version'); process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
const bumped = bumpAll(fake, '0.8.4', '0.8.5', true /* dry */);
|
|
161
|
+
if (bumped !== fake.length) {
|
|
162
|
+
console.error('selftest FAIL: bumpAll(dry) did not report all packages bumped'); process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
// Corrupted package.json (regex has nothing to match) must be caught, not silently skipped.
|
|
165
|
+
const corrupted = [{ pkg: 'packages/x', path: '/nonexistent-fixture-dir/x/package.json', txt: '{}', version: '0.8.4' }];
|
|
166
|
+
if (bumpAll(corrupted, '0.8.4', '0.8.5', true) !== -1) {
|
|
167
|
+
console.error('selftest FAIL: corrupted package.json (no version match) not caught'); process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
console.log('selftest OK');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const topArgv = process.argv.slice(2);
|
|
173
|
+
if (topArgv[0] === 'selftest') selftest();
|
|
174
|
+
else main();
|
|
@@ -199,19 +199,57 @@ async function main() {
|
|
|
199
199
|
}
|
|
200
200
|
console.log(`\n[dispatch] ${succeeded}/${PACKAGES.length} dispatched ${args.dry ? '(dry)' : ''}`);
|
|
201
201
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
202
|
+
// defect-2 (forge-campaign gh#268 audit): this used to print a "[next] wait
|
|
203
|
+
// for / verify" block phrased as a to-do EVEN THOUGH the dispatch above
|
|
204
|
+
// already fired — an operator's agent read it as "publish hasn't happened
|
|
205
|
+
// yet" and went back to refine CHANGELOGs post-publish. State what WAS
|
|
206
|
+
// done; offer monitoring as optional, not required.
|
|
207
|
+
if (!args.dry && succeeded > 0) {
|
|
208
|
+
// CodeRabbit (PR #289): "RUNNING" overclaimed — `gh workflow run` only
|
|
209
|
+
// confirms the dispatch was SUBMITTED; the run may still queue, or fail
|
|
210
|
+
// to materialize (bad ref, disabled workflow). "dispatched" is what we
|
|
211
|
+
// actually know happened; no further action is still true either way —
|
|
212
|
+
// the point stands, the verb doesn't overreach.
|
|
213
|
+
console.log(`\n✓ ${succeeded} publish workflow(s) dispatched above — no further action required.`);
|
|
214
|
+
console.log(' Optional — monitor until they settle:');
|
|
215
|
+
console.log(` until [ "$(gh run list --workflow=publish-a2ui-validator.yml --limit 1 --json status -q '.[0].status')" = "completed" ]; do sleep 5; done`);
|
|
216
|
+
console.log(' Optional — check each conclusion once settled:');
|
|
217
|
+
console.log(` for pkg in ${PACKAGES.join(' ')}; do`);
|
|
218
|
+
console.log(` gh run list --workflow=publish-$pkg.yml --limit 1 --json conclusion -q '.[0].conclusion'`);
|
|
219
|
+
console.log(` done`);
|
|
209
220
|
}
|
|
210
221
|
|
|
211
222
|
if (succeeded !== PACKAGES.length) process.exit(1);
|
|
212
223
|
}
|
|
213
224
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
225
|
+
// dry mode never shells out (run()/dispatch() short-circuit before
|
|
226
|
+
// execSync), so this exercises the real functions without touching gh/npm.
|
|
227
|
+
function selftest() {
|
|
228
|
+
let allOk = true;
|
|
229
|
+
for (const pkg of PACKAGES) {
|
|
230
|
+
if (!dispatch(pkg, '9.9.9', true /* dry */)) allOk = false;
|
|
231
|
+
}
|
|
232
|
+
if (!allOk) { console.error('selftest FAIL: dry dispatch() reported a failure for at least one package'); process.exit(1); }
|
|
233
|
+
|
|
234
|
+
// triggeredForTag() in dry mode must conservatively report "not yet
|
|
235
|
+
// triggered" — verifyTriggeredAndRedispatch's whole point is to re-dispatch
|
|
236
|
+
// misses; silently assuming "triggered" would let a real miss through.
|
|
237
|
+
if (triggeredForTag('web-components', '9.9.9', true) !== false) {
|
|
238
|
+
console.error('selftest FAIL: dry triggeredForTag() must report false (conservative default)'); process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const rc = verifyTriggeredAndRedispatch('9.9.9', true);
|
|
242
|
+
if (rc !== 0) { console.error(`selftest FAIL: dry verifyTriggeredAndRedispatch() should report rc=0, got ${rc}`); process.exit(1); }
|
|
243
|
+
|
|
244
|
+
console.log('selftest OK');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const topArgv = process.argv.slice(2);
|
|
248
|
+
if (topArgv[0] === 'selftest') {
|
|
249
|
+
selftest();
|
|
250
|
+
} else {
|
|
251
|
+
main().catch((e) => {
|
|
252
|
+
console.error(`error: ${e.message}`);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
});
|
|
255
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// gate-roster.mjs — the ONE ordered list of the pre-cut gate roster.
|
|
3
|
+
//
|
|
4
|
+
// H3 (forge-campaign gh#268 audit): release-pack.mjs's Step 3 pre-flight ran
|
|
5
|
+
// only 12 of the 17 gates cut-procedure.md §3.1 declares as "every gate runs;
|
|
6
|
+
// a subset = pre-flight failure" — the roster had drifted into two places
|
|
7
|
+
// (the script's own hardcoded array + the doc's table) and only one of them
|
|
8
|
+
// got the memo when gates were added. This module is the single source: the
|
|
9
|
+
// doc's §3.1 table transcribes it, release-pack.mjs's step3PreFlight()
|
|
10
|
+
// imports and runs it in full, and nothing else declares its own subset — a
|
|
11
|
+
// gate can only be dropped by editing this file, which is the change the
|
|
12
|
+
// doc and the script both need anyway.
|
|
13
|
+
//
|
|
14
|
+
// Usage:
|
|
15
|
+
// import { GATE_ROSTER } from './gate-roster.mjs';
|
|
16
|
+
// node gate-roster.mjs --list # human-readable numbered roster (docs/CI use)
|
|
17
|
+
// node gate-roster.mjs --count # just the count (drift check)
|
|
18
|
+
|
|
19
|
+
import { fileURLToPath } from 'node:url';
|
|
20
|
+
import { realpathSync } from 'node:fs';
|
|
21
|
+
|
|
22
|
+
export const GATE_ROSTER = [
|
|
23
|
+
{ n: 1, cmd: 'node scripts/build/components.mjs --verify', what: 'yaml ↔ sidecar ↔ .d.ts' },
|
|
24
|
+
{ n: 2, cmd: 'npm run verify:traits', what: 'trait coverage' },
|
|
25
|
+
{ n: 3, cmd: 'npm run check:lockstep', what: 'version coherence' },
|
|
26
|
+
{ n: 4, cmd: 'npm run test:unit', what: 'vitest' },
|
|
27
|
+
{ n: 5, cmd: 'npm run typecheck', what: 'tsc --noEmit' },
|
|
28
|
+
{ n: 6, cmd: 'npm run check:demo-shells', what: 'demo imports cover composes:' },
|
|
29
|
+
{ n: 7, cmd: 'npm run check:lightningcss-build', what: 'CSS minifies' },
|
|
30
|
+
{ n: 8, cmd: 'npm run check:css-bundles-fresh', what: 'dist CSS matches source' },
|
|
31
|
+
{ n: 9, cmd: 'npm run check:js-bundles-fresh', what: 'dist JS matches source' },
|
|
32
|
+
{ n: 10, cmd: 'npm run smoke:engines', what: 'gen-UI engines' },
|
|
33
|
+
{ n: 11, cmd: 'npm run smoke:register-engine', what: 'register-engine 11/11' },
|
|
34
|
+
{ n: 12, cmd: 'npm run verify:corpus', what: 'corpus 0 errors' },
|
|
35
|
+
{ n: 13, cmd: 'npm run check:chunks-fresh', what: 'chunk index vs sources' },
|
|
36
|
+
{ n: 14, cmd: 'npm run check:embeddings-fresh', what: 'embeddings vs chunk index' },
|
|
37
|
+
{ n: 15, cmd: 'npm run check:links', what: 'intra-repo links' },
|
|
38
|
+
{ n: 16, cmd: 'npm run eval:diff -- --engine zettel', what: 'eval floors' },
|
|
39
|
+
{ n: 17, cmd: 'npm run dogfood:status', what: 'P0/P1 dogfood floor' },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
// -- CLI ------------------------------------------------------------------
|
|
43
|
+
// Only runs when invoked directly (not when imported by release-pack.mjs).
|
|
44
|
+
// realpathSync BOTH sides (CodeRabbit, PR #289): a naive string/URL compare
|
|
45
|
+
// breaks under a symlinked invocation — this skill is normally reached
|
|
46
|
+
// through `.claude/skills/adia-release` -> `packages/plugins/adia-ui-forge/
|
|
47
|
+
// skills/adia-release`, and Node resolves `import.meta.url` through that
|
|
48
|
+
// symlink to the real file while `process.argv[1]` keeps the as-invoked
|
|
49
|
+
// path, so an unresolved compare never matches even when this IS the entry
|
|
50
|
+
// point. Verified directly (see embeddings-chunks.mjs's identical guard).
|
|
51
|
+
const isMain = (() => {
|
|
52
|
+
if (!process.argv[1]) return false;
|
|
53
|
+
try {
|
|
54
|
+
return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1]);
|
|
55
|
+
} catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
})();
|
|
59
|
+
if (isMain) {
|
|
60
|
+
const args = new Set(process.argv.slice(2));
|
|
61
|
+
if (args.has('--count')) {
|
|
62
|
+
console.log(GATE_ROSTER.length);
|
|
63
|
+
} else if (args.has('--list') || args.size === 0) {
|
|
64
|
+
console.log(`${GATE_ROSTER.length}-gate pre-cut roster (source of truth — cut-procedure.md §3.1 transcribes this):\n`);
|
|
65
|
+
for (const g of GATE_ROSTER) {
|
|
66
|
+
console.log(` ${String(g.n).padStart(2)}. ${g.cmd.padEnd(46)} # ${g.what}`);
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
console.log('Usage: node gate-roster.mjs [--list|--count]');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
// cycles.
|
|
17
17
|
|
|
18
18
|
import fs from 'node:fs';
|
|
19
|
+
import os from 'node:os';
|
|
20
|
+
import path from 'node:path';
|
|
19
21
|
import process from 'node:process';
|
|
20
22
|
import { resolvePackageChangelog } from './package-paths.mjs';
|
|
21
23
|
|
|
@@ -66,7 +68,7 @@ function buildStub(version, date, substantive, xref) {
|
|
|
66
68
|
return `## [${version}] — ${date}
|
|
67
69
|
|
|
68
70
|
### Maintenance
|
|
69
|
-
- **Lockstep version bump only.** No source changes in this package; bumped to maintain the
|
|
71
|
+
- **Lockstep version bump only.** No source changes in this package; bumped to maintain the 11-package version coherence enforced by \`scripts/release/check-lockstep.mjs\`. Substantive v${version} work shipped in ${substantive}. See \`${xref}\` for details.
|
|
70
72
|
|
|
71
73
|
`;
|
|
72
74
|
}
|
|
@@ -126,4 +128,36 @@ function main() {
|
|
|
126
128
|
}
|
|
127
129
|
}
|
|
128
130
|
|
|
129
|
-
|
|
131
|
+
// Fixture-locked proof against a real (throwaway) temp directory.
|
|
132
|
+
function selftest() {
|
|
133
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'insert-stub-selftest-'));
|
|
134
|
+
try {
|
|
135
|
+
const dir = path.join(tmp, 'packages', 'llm');
|
|
136
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
137
|
+
const clPath = path.join(dir, 'CHANGELOG.md');
|
|
138
|
+
const anchor = '## [0.1.0] — 2026-01-01';
|
|
139
|
+
fs.writeFileSync(clPath, `# C\n\n${anchor}\n\n- prior\n`);
|
|
140
|
+
|
|
141
|
+
const stub = buildStub('0.2.0', '2026-02-02', '@adia-ai/web-components', 'packages/web-components/CHANGELOG.md#020--2026-02-02');
|
|
142
|
+
const r1 = insertStub(tmp, 'llm', anchor, stub, '0.2.0', false);
|
|
143
|
+
if (r1 !== 'inserted') { console.error(`selftest FAIL: expected 'inserted', got '${r1}'`); process.exit(1); }
|
|
144
|
+
const out = fs.readFileSync(clPath, 'utf8');
|
|
145
|
+
if (!out.includes('## [0.2.0]')) { console.error('selftest FAIL: stub heading not written'); process.exit(1); }
|
|
146
|
+
if (out.indexOf('## [0.2.0]') > out.indexOf(anchor)) {
|
|
147
|
+
console.error('selftest FAIL: stub must land ABOVE the previous-version anchor, not below'); process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Idempotency: re-inserting against an already-stubbed target must fail
|
|
151
|
+
// loudly, not create a duplicate [0.2.0] block.
|
|
152
|
+
const r2 = insertStub(tmp, 'llm', anchor, stub, '0.2.0', false);
|
|
153
|
+
if (r2 !== 'already-present') { console.error(`selftest FAIL: expected 'already-present' on re-run, got '${r2}'`); process.exit(1); }
|
|
154
|
+
|
|
155
|
+
console.log('selftest OK');
|
|
156
|
+
} finally {
|
|
157
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const topArgv = process.argv.slice(2);
|
|
162
|
+
if (topArgv[0] === 'selftest') selftest();
|
|
163
|
+
else main();
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
// 2 — bad args / file I/O error
|
|
21
21
|
|
|
22
22
|
import fs from 'node:fs';
|
|
23
|
+
import os from 'node:os';
|
|
24
|
+
import path from 'node:path';
|
|
23
25
|
import process from 'node:process';
|
|
24
26
|
import { resolvePackageChangelog } from './package-paths.mjs';
|
|
25
27
|
|
|
@@ -86,4 +88,36 @@ function main() {
|
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
90
|
|
|
89
|
-
|
|
91
|
+
// Fixture-locked proof against a real (throwaway) temp directory — promote()
|
|
92
|
+
// does real fs.readFileSync/writeFileSync via resolvePackageChangelog, so a
|
|
93
|
+
// pure in-memory test can't exercise it; a temp dir keeps this <1s and
|
|
94
|
+
// touches nothing in the real repo.
|
|
95
|
+
function selftest() {
|
|
96
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'promote-unreleased-selftest-'));
|
|
97
|
+
try {
|
|
98
|
+
const dir = path.join(tmp, 'packages', 'web-components');
|
|
99
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
100
|
+
const clPath = path.join(dir, 'CHANGELOG.md');
|
|
101
|
+
fs.writeFileSync(clPath, '# C\n\n## [Unreleased]\n\n### Fixed\n- x\n\n## [0.1.0] — 2026-01-01\n\n- y\n');
|
|
102
|
+
|
|
103
|
+
const r1 = promote(tmp, 'web-components', '0.2.0', '2026-02-02', false);
|
|
104
|
+
if (r1 !== 'promoted') { console.error(`selftest FAIL: expected 'promoted', got '${r1}'`); process.exit(1); }
|
|
105
|
+
const out = fs.readFileSync(clPath, 'utf8');
|
|
106
|
+
if (!out.includes('## [0.2.0] — 2026-02-02')) { console.error('selftest FAIL: new heading not written'); process.exit(1); }
|
|
107
|
+
if (out.includes('## [Unreleased]')) { console.error('selftest FAIL: [Unreleased] heading should have been renamed, not left behind'); process.exit(1); }
|
|
108
|
+
if (!out.includes('### Fixed\n- x')) { console.error('selftest FAIL: content under the heading must survive the rename'); process.exit(1); }
|
|
109
|
+
|
|
110
|
+
// Idempotency: re-running against an already-promoted target must fail
|
|
111
|
+
// loudly (a silent no-op or a duplicate block would both be wrong).
|
|
112
|
+
const r2 = promote(tmp, 'web-components', '0.2.0', '2026-02-02', false);
|
|
113
|
+
if (r2 !== 'already-promoted') { console.error(`selftest FAIL: expected 'already-promoted' on re-run, got '${r2}'`); process.exit(1); }
|
|
114
|
+
|
|
115
|
+
console.log('selftest OK');
|
|
116
|
+
} finally {
|
|
117
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const topArgv = process.argv.slice(2);
|
|
122
|
+
if (topArgv[0] === 'selftest') selftest();
|
|
123
|
+
else main();
|