@bongos/core 1.19.396 → 1.19.398

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 (38) hide show
  1. package/.bongos-core.json +149 -19
  2. package/.claude/skills/design/SKILL.md +9 -1
  3. package/docs/adr/0198-third-party-skill-vendoring-policy.md +43 -0
  4. package/docs/adr/README.md +1 -0
  5. package/docs/file-map.md +1 -1
  6. package/docs/recipes/ui-look-before-you-ship.md +113 -0
  7. package/modules/hall-ui/public/ranks.states.json +15 -0
  8. package/modules/ui-design/kit/README.md +12 -2
  9. package/modules/ui-design/kit/check-mock.js +147 -0
  10. package/modules/ui-design/kit/contrast.js +156 -0
  11. package/modules/ui-design/kit/fixtures/cost-summary.json +1 -0
  12. package/modules/ui-design/kit/fixtures/featured.json +1 -0
  13. package/modules/ui-design/kit/fixtures/leaderboard.json +1 -0
  14. package/modules/ui-design/kit/fixtures/me.json +9 -0
  15. package/modules/ui-design/kit/fixtures/my-projects.json +1 -0
  16. package/modules/ui-design/kit/fixtures/progress.json +1 -0
  17. package/modules/ui-design/kit/fixtures/recent-shipped.json +1 -0
  18. package/modules/ui-design/kit/lib.js +389 -0
  19. package/modules/ui-design/kit/png.js +51 -0
  20. package/modules/ui-design/kit/probe.js +100 -0
  21. package/modules/ui-design/kit/render.js +137 -0
  22. package/modules/ui-design/kit/serve.js +178 -0
  23. package/modules/ui-design/skills/PREAMBLE.md +13 -0
  24. package/modules/ui-design/skills/README.md +36 -2
  25. package/modules/ui-design/skills/policy.json +12 -0
  26. package/package-lock.json +2 -2
  27. package/package.json +1 -1
  28. package/public-landing/index.probes.json +37 -0
  29. package/public-landing/index.states.json +16 -0
  30. package/public-landing/projects.probes.json +79 -0
  31. package/public-landing/projects.states.json +25 -0
  32. package/scripts/gds/claude-materialize.js +122 -13
  33. package/scripts/gds/ui-kit-transform.js +43 -0
  34. package/src/bongos/module-scope-map.js +1 -0
  35. package/src/module-api.js +5 -1
  36. package/tests/claude_materialize.mjs +83 -0
  37. package/tests/ui_design_kit.mjs +217 -0
  38. package/tests/ui_design_skills.mjs +168 -0
@@ -0,0 +1,168 @@
1
+ // tests/ui_design_skills.mjs — the ui-design module's vendored-skill policy (task 1003321,
2
+ // ADR 0198) is a pinned contract, not a README: every skill under skills/vendor/ carries a
3
+ // PROVENANCE.md sidecar with an allowlisted licence and a landable scan verdict; its SKILL.md
4
+ // opens with the PLATFORM PREAMBLE byte-identical to PREAMBLE.md; every name the manifest
5
+ // contributes resolves to a real skill dir; a module-owned skill reaches a builder's
6
+ // materialised .claude/skills with its sidecar (and not when the module is off); /design
7
+ // lists every vendored skill by name; the policy ADR is indexed. DB-free.
8
+ import assert from 'node:assert/strict';
9
+ import { test } from 'node:test';
10
+ import { createRequire } from 'node:module';
11
+ import fs from 'node:fs';
12
+ import os from 'node:os';
13
+ import path from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+
16
+ const require = createRequire(import.meta.url);
17
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
18
+ const MODULE = path.join(ROOT, 'modules', 'ui-design');
19
+ const SKILLS = path.join(MODULE, 'skills');
20
+ const VENDOR = path.join(SKILLS, 'vendor');
21
+ const read = (p) => fs.readFileSync(p, 'utf8').replace(/\r\n/g, '\n');
22
+ const readJson = (p) => JSON.parse(fs.readFileSync(p, 'utf8'));
23
+ const policy = readJson(path.join(SKILLS, 'policy.json'));
24
+ const manifest = readJson(path.join(MODULE, 'module.json'));
25
+ const materialize = require(path.join(ROOT, 'scripts', 'gds', 'claude-materialize.js'));
26
+
27
+ // Minimal YAML-frontmatter reader: `key: value` lines between the first two `---` fences.
28
+ function frontmatter(text) {
29
+ const m = text.match(/^---\n([\s\S]*?)\n---\n/);
30
+ const out = {};
31
+ if (!m) return out;
32
+ for (const line of m[1].split('\n')) {
33
+ const kv = line.match(/^([A-Za-z][A-Za-z0-9_-]*):\s*(.*)$/);
34
+ if (kv) out[kv[1]] = kv[2].replace(/^["']|["']$/g, '').trim();
35
+ }
36
+ return out;
37
+ }
38
+
39
+ const vendoredDirs = fs.existsSync(VENDOR)
40
+ ? fs.readdirSync(VENDOR).filter((n) => fs.statSync(path.join(VENDOR, n)).isDirectory()).sort()
41
+ : [];
42
+ const ownDirs = fs.readdirSync(SKILLS)
43
+ .filter((n) => n !== 'vendor' && fs.statSync(path.join(SKILLS, n)).isDirectory() && fs.existsSync(path.join(SKILLS, n, 'SKILL.md')))
44
+ .sort();
45
+
46
+ // ── the policy itself ────────────────────────────────────────────────────────
47
+ test('the policy file pins the allowlist, the landable verdicts, the sidecar and the preamble markers', () => {
48
+ assert.deepEqual(policy.licenceAllowlist, ['Apache-2.0', 'MIT', 'BSD-2-Clause', 'BSD-3-Clause', 'CC-BY-4.0']);
49
+ assert.deepEqual(policy.verdictsThatMayLand, ['clean', 'caution'], 'a dangerous verdict never lands a copy');
50
+ assert.equal(policy.sidecar, 'PROVENANCE.md');
51
+ assert.equal(policy.preamble.source, 'PREAMBLE.md');
52
+ assert.ok(policy.sidecarRequiredFields.includes('licence') && policy.sidecarRequiredFields.includes('scanVerdict') && policy.sidecarRequiredFields.includes('commit'));
53
+ });
54
+
55
+ test('PREAMBLE.md is one fenced block that reads the world before any taste rule', () => {
56
+ const pre = read(path.join(SKILLS, 'PREAMBLE.md'));
57
+ assert.ok(pre.startsWith(policy.preamble.begin), 'opens with the BEGIN marker');
58
+ assert.ok(pre.trimEnd().endsWith(policy.preamble.end), 'closes with the END marker');
59
+ assert.equal(pre.split(policy.preamble.begin).length, 2, 'exactly one BEGIN marker');
60
+ assert.equal(pre.split(policy.preamble.end).length, 2, 'exactly one END marker');
61
+ for (const must of ['config/branding.json', 'config/branding.neutral.json', 'config/design-tokens.json', 'DESIGN.md', 'fifteen', 'prefers-reduced-motion', '24px', '4.5:1', 'hero plates', 'ui-look-before-you-ship']) {
62
+ assert.ok(pre.includes(must), `preamble names ${must}`);
63
+ }
64
+ });
65
+
66
+ // ── every vendored skill: sidecar, licence, verdict, preamble ───────────────
67
+ test('every vendored skill carries a PROVENANCE.md with an allowlisted licence, a landable verdict and every required field', () => {
68
+ for (const name of vendoredDirs) {
69
+ const dir = path.join(VENDOR, name);
70
+ assert.ok(fs.existsSync(path.join(dir, 'SKILL.md')), `${name}: SKILL.md`);
71
+ const side = path.join(dir, policy.sidecar);
72
+ assert.ok(fs.existsSync(side), `${name}: ${policy.sidecar} sidecar`);
73
+ const fm = frontmatter(read(side));
74
+ for (const f of policy.sidecarRequiredFields) assert.ok(fm[f], `${name}: sidecar field ${f}`);
75
+ assert.equal(fm.name, name, `${name}: sidecar names its own dir`);
76
+ assert.ok(policy.licenceAllowlist.includes(fm.licence), `${name}: licence ${fm.licence} is on the allowlist`);
77
+ assert.ok(policy.verdictsThatMayLand.includes(fm.scanVerdict), `${name}: verdict ${fm.scanVerdict} may land`);
78
+ assert.match(fm.commit, /^[0-9a-f]{40}$/, `${name}: the pinned commit is a full sha`);
79
+ assert.match(fm.origin, /^https:\/\//, `${name}: origin is an https url`);
80
+ assert.ok(fs.existsSync(path.join(dir, 'LICENSE')), `${name}: the upstream licence text travels with the copy`);
81
+ }
82
+ });
83
+
84
+ test('every vendored SKILL.md opens with the PLATFORM PREAMBLE, byte-identical to PREAMBLE.md, right after its frontmatter', () => {
85
+ const pre = read(path.join(SKILLS, 'PREAMBLE.md')).trimEnd();
86
+ for (const name of vendoredDirs) {
87
+ const text = read(path.join(VENDOR, name, 'SKILL.md'));
88
+ const fm = text.match(/^---\n[\s\S]*?\n---\n/);
89
+ assert.ok(fm, `${name}: SKILL.md keeps its frontmatter first (the harness parses it)`);
90
+ const body = text.slice(fm[0].length).replace(/^\n+/, '');
91
+ assert.ok(body.startsWith(pre), `${name}: the preamble block is the first thing after the frontmatter and matches PREAMBLE.md byte for byte`);
92
+ assert.equal(text.split(policy.preamble.begin).length, 2, `${name}: the preamble appears exactly once`);
93
+ }
94
+ });
95
+
96
+ // ── the manifest declares what is on disk, and nothing else ─────────────────
97
+ test('every skill the manifest contributes resolves to a skill dir (core .claude/skills or the module), and every module skill dir is declared', () => {
98
+ const declared = manifest.contributes.skills;
99
+ assert.ok(Array.isArray(declared) && declared.length >= 3);
100
+ for (const name of declared) {
101
+ const homes = [path.join(ROOT, '.claude', 'skills', name), path.join(SKILLS, name), path.join(VENDOR, name)];
102
+ assert.ok(homes.some((h) => fs.existsSync(path.join(h, 'SKILL.md'))), `${name}: declared but no SKILL.md in any home`);
103
+ }
104
+ for (const name of [...ownDirs, ...vendoredDirs]) assert.ok(declared.includes(name), `${name}: on disk under the module but not declared in contributes.skills`);
105
+ const sources = materialize.moduleSkillSources({ coreRoot: ROOT, instanceDir: ROOT });
106
+ assert.deepEqual(sources.map((s) => s.name).sort(), [...ownDirs, ...vendoredDirs].sort(), 'the materialiser resolves exactly the module skill dirs on disk');
107
+ });
108
+
109
+ // ── materialisation: the builder's .claude/skills carries the set, sidecar included ─
110
+ test('a module-owned skill lands in a materialised .claude/skills with its sidecar when the module is on, and not when it is off', () => {
111
+ const core = fs.mkdtempSync(path.join(os.tmpdir(), 'ui-skills-core-'));
112
+ const mod = path.join(core, 'modules', 'ui-design');
113
+ fs.mkdirSync(path.join(mod, 'skills', 'vendor', 'sample'), { recursive: true });
114
+ fs.mkdirSync(path.join(core, '.claude', 'skills'), { recursive: true });
115
+ fs.writeFileSync(path.join(mod, 'module.json'), JSON.stringify({ key: 'ui-design', default: true, contributes: { skills: ['sample'] } }));
116
+ const pre = read(path.join(SKILLS, 'PREAMBLE.md'));
117
+ fs.writeFileSync(path.join(mod, 'skills', 'vendor', 'sample', 'SKILL.md'), `---\nname: sample\n---\n\n${pre}\nupstream body\n`);
118
+ fs.writeFileSync(path.join(mod, 'skills', 'vendor', 'sample', 'PROVENANCE.md'), '---\nname: sample\nlicence: MIT\nscanVerdict: clean\n---\n');
119
+ fs.writeFileSync(path.join(mod, 'skills', 'vendor', 'sample', 'LICENSE'), 'MIT\n');
120
+
121
+ const on = fs.mkdtempSync(path.join(os.tmpdir(), 'ui-skills-inst-'));
122
+ const resOn = materialize.materializeClaude({ coreRoot: core, instanceDir: on, dryRun: false });
123
+ assert.equal(resOn.moduleSkills, 3);
124
+ for (const f of ['SKILL.md', 'PROVENANCE.md', 'LICENSE']) assert.ok(fs.existsSync(path.join(on, '.claude', 'skills', 'sample', f)), `materialised ${f}`);
125
+ assert.ok(read(path.join(on, '.claude', 'skills', 'sample', 'SKILL.md')).includes(policy.preamble.begin), 'the preamble survives materialisation');
126
+
127
+ const off = fs.mkdtempSync(path.join(os.tmpdir(), 'ui-skills-inst-'));
128
+ fs.mkdirSync(path.join(off, 'config'), { recursive: true });
129
+ fs.writeFileSync(path.join(off, 'config', 'modules.json'), JSON.stringify({ modules: { 'ui-design': false } }));
130
+ const resOff = materialize.materializeClaude({ coreRoot: core, instanceDir: off, dryRun: false });
131
+ assert.equal(resOff.moduleSkills, 0);
132
+ assert.ok(!fs.existsSync(path.join(off, '.claude', 'skills', 'sample')), 'disabling the module withdraws the skill');
133
+ assert.deepEqual(resOff.excludedSkills, ['sample']);
134
+
135
+ // the policy holds at COPY time too, read from this module's own policy.json: a
136
+ // dangerous verdict never lands, whatever the CI test would have said
137
+ fs.copyFileSync(path.join(SKILLS, 'policy.json'), path.join(mod, 'skills', 'policy.json'));
138
+ fs.writeFileSync(path.join(mod, 'skills', 'vendor', 'sample', 'PROVENANCE.md'), '---\nname: sample\nlicence: MIT\nscanVerdict: dangerous\n---\n');
139
+ const refusedInst = fs.mkdtempSync(path.join(os.tmpdir(), 'ui-skills-inst-'));
140
+ const resRefused = materialize.materializeClaude({ coreRoot: core, instanceDir: refusedInst, dryRun: false });
141
+ assert.equal(resRefused.moduleSkills, 0);
142
+ assert.ok(!fs.existsSync(path.join(refusedInst, '.claude', 'skills', 'sample')), 'a dangerous verdict is refused at copy time');
143
+ assert.ok(resRefused.notes.some((n) => n.includes('refused') && n.includes('may not land')));
144
+ for (const d of [core, on, off, refusedInst]) fs.rmSync(d, { recursive: true, force: true });
145
+ });
146
+
147
+ // ── the doors: /design lists them, the README rosters them, the ADR is indexed ──
148
+ test('/design lists every vendored skill by name, one line each, and names the playwright plugin as a prerequisite', () => {
149
+ const design = read(path.join(ROOT, '.claude', 'skills', 'design', 'SKILL.md'));
150
+ for (const name of vendoredDirs) {
151
+ const line = design.split('\n').find((l) => l.includes(`\`/${name}\``) || l.includes(`**${name}**`));
152
+ assert.ok(line, `/design names ${name} on a line of its own`);
153
+ }
154
+ assert.match(design, /playwright/i, '/design names the playwright plugin');
155
+ });
156
+
157
+ test('the skills README rosters every vendored skill and every rebuild spec by name', () => {
158
+ const readme = read(path.join(SKILLS, 'README.md'));
159
+ for (const name of vendoredDirs) assert.ok(readme.includes(`\`${name}\``), `README rosters ${name}`);
160
+ assert.ok(readme.includes('policy.json') && readme.includes('PREAMBLE.md') && readme.includes('PROVENANCE.md'));
161
+ });
162
+
163
+ test('the vendoring policy ADR exists and is indexed', () => {
164
+ const adr = fs.readdirSync(path.join(ROOT, 'docs', 'adr')).find((f) => /^0198-/.test(f));
165
+ assert.ok(adr, 'docs/adr/0198-*.md exists');
166
+ const index = read(path.join(ROOT, 'docs', 'adr', 'README.md'));
167
+ assert.ok(index.includes(`](${adr})`), 'docs/adr/README.md links the ADR');
168
+ });