@bongos/core 1.19.544 → 1.19.546

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 (45) hide show
  1. package/.bongos-core.json +87 -42
  2. package/docs/adr/0227-a-task-network-map-is-refused-the-goal-graph-is-the-map.md +115 -0
  3. package/docs/copy-inventory.md +42 -18
  4. package/docs/copy-registry.json +271 -25
  5. package/docs/module-api-changelog.md +4 -0
  6. package/docs/research/task-network-map/real-a-every-task.png +0 -0
  7. package/docs/research/task-network-map/real-b-dependency-forest.png +0 -0
  8. package/docs/research/task-network-map/real-c-goals.png +0 -0
  9. package/docs/research/task-network-map/real-d-joined-by-goal.png +0 -0
  10. package/modules/hall-ui/public/hall-kit.js +76 -41
  11. package/modules/hall-ui/public/profile-charts.js +8 -2
  12. package/modules/hall-ui/public/shell.js +30 -1
  13. package/modules/hall-ui/public/studio.css +268 -0
  14. package/modules/hall-ui/public/studio.html +74 -0
  15. package/modules/hall-ui/public/studio.js +213 -0
  16. package/modules/hall-ui/public/studio.states.json +54 -0
  17. package/modules/hall-ui/public/style.css +9 -0
  18. package/modules/ui-design/skills/brandkit/SKILL.md +5 -1
  19. package/modules/ui-design/skills/design-taste-frontend/SKILL.md +5 -1
  20. package/modules/ui-design/skills/design-taste-frontend-v1/SKILL.md +5 -1
  21. package/modules/ui-design/skills/gpt-taste/SKILL.md +5 -1
  22. package/modules/ui-design/skills/high-end-visual-design/SKILL.md +5 -1
  23. package/modules/ui-design/skills/image-to-code/SKILL.md +5 -1
  24. package/modules/ui-design/skills/imagegen-frontend-mobile/SKILL.md +5 -1
  25. package/modules/ui-design/skills/imagegen-frontend-web/SKILL.md +5 -1
  26. package/modules/ui-design/skills/impeccable/SKILL.md +5 -1
  27. package/modules/ui-design/skills/industrial-brutalist-ui/SKILL.md +5 -1
  28. package/modules/ui-design/skills/minimalist-ui/SKILL.md +5 -1
  29. package/modules/ui-design/skills/redesign-existing-projects/SKILL.md +5 -1
  30. package/modules/ui-design/skills/stitch-design-taste/SKILL.md +5 -1
  31. package/modules/ui-design/skills/style/SKILL.md +5 -1
  32. package/package-lock.json +2 -2
  33. package/package.json +1 -1
  34. package/scripts/gds/fitness.js +10 -3
  35. package/scripts/gds/skill-lint.js +43 -9
  36. package/scripts/gds/spike-task-map.js +178 -9
  37. package/src/bongos/serve-internal.js +1 -1
  38. package/src/module-api.js +1 -1
  39. package/tests/hall_kit_charts.mjs +59 -0
  40. package/tests/hall_nav.mjs +6 -1
  41. package/tests/hall_page_gate_map.mjs +5 -0
  42. package/tests/hall_studio_world.mjs +143 -0
  43. package/tests/skill_lint.mjs +51 -2
  44. package/tests/spike_task_map.mjs +0 -0
  45. package/tests/ui_design_skills.mjs +12 -2
@@ -0,0 +1,213 @@
1
+ // modules/hall-ui/public/studio.js — the Studio (artist mode), /builders/studio.
2
+ // R06 of goal 1000074 (the artist's loop), task 1003117.
3
+ //
4
+ // The criterion (artist-mode): "a place an artist wants to be, not a form. A
5
+ // focus environment on the Cloud Bongos screen — a lofi-girl-style zen garden of
6
+ // focus and creativity — rather than another table of rows."
7
+ //
8
+ // So this is NOT a queue table. It aggregates the artist's real work — the two
9
+ // surfaces the rest of goal 1000074 built — and presents it ONE PIECE AT A TIME,
10
+ // a stone to tend in a calm garden, with the rest waiting quietly:
11
+ // 1. REVIEW REQUESTS (R04, cascade.js) — a copy/visual ship files a two-step
12
+ // artist review at `backlog`, discipline 'artist', source 'cascade'. The
13
+ // two steps (coach the grader, then remake/tweak) live on the task record;
14
+ // this room names them and opens the door.
15
+ // 2. FLAGGED COPY (R02, copy-desk) — open flags over the words the product
16
+ // shows people. Best-effort: an instance without the copy-desk module has
17
+ // no queue, and the room degrades to reviews alone rather than erroring.
18
+ //
19
+ // It writes NOTHING and gates NOTHING (ADR 0162 / the goal's no-live-CMS rule):
20
+ // "open this" routes to where the craft is actually done (the task record, the
21
+ // copy desk), and "let it be for now" only advances the reader to the next
22
+ // piece. The full tables it replaces stay one link away in the footer.
23
+
24
+ (() => {
25
+ 'use strict';
26
+
27
+ const API = '/api/bongos';
28
+ const api = window.BongosClient.createClient({ baseUrl: '', credentials: 'same-origin', throwOnError: false });
29
+ const { escapeHtml, $ } = window.OTB;
30
+
31
+ let viewer = null;
32
+ let pieces = []; // the combined garden: [{ type:'review'|'flag', … }]
33
+ let idx = 0; // which stone is in focus
34
+
35
+ // Bound the reviews read. cascade.js files a review per qualifying ship and they
36
+ // sit at backlog until claimed or dismissed ("dismissable rather than nagging"),
37
+ // so this queue grows without bound over the project's life — the unbounded-read
38
+ // shape modules/hall-ui/CLAUDE.md flags as a repeat bug (the Work board's backlog,
39
+ // Home's next-up card, the Goals rollup all had it). 50 is far more than a focus
40
+ // queue tends at once; the footer links to the copy desk and board are the
41
+ // whole-list path. listTasks orders `priority NULLS LAST, created_at`, so the cap
42
+ // keeps the most-prioritised waiting reviews.
43
+ const REVIEW_LIMIT = 50;
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // helpers
47
+ // ---------------------------------------------------------------------------
48
+
49
+ function greeting() {
50
+ const h = new Date().getHours();
51
+ if (h < 5) return 'Still up';
52
+ if (h < 12) return 'Good morning';
53
+ if (h < 18) return 'Good afternoon';
54
+ return 'Good evening';
55
+ }
56
+
57
+ // The shipped task a review is ABOUT — cascade.js stamps
58
+ // source_ref = 'cascade-artist-review-on-ship-<taskId>'.
59
+ function reviewShippedId(t) {
60
+ const m = /-on-ship-(\d+)/.exec(t.source_ref || '');
61
+ return m ? m[1] : null;
62
+ }
63
+
64
+ // The product's own words, shown as evidence — untrusted source-tree text, so
65
+ // escaped, and clipped so one runaway string cannot fill the room.
66
+ function quote(text, max = 300) {
67
+ const s = String(text == null ? '' : text);
68
+ return escapeHtml(s.length > max ? `${s.slice(0, max)}…` : s);
69
+ }
70
+
71
+ // Clip a plain field (a name, a reason) so one very long value cannot stretch
72
+ // the card — the mirror of quote()'s clip, for text that is not the evidence.
73
+ // Escaping still happens at render; this only bounds length.
74
+ function clip(text, max) {
75
+ const s = String(text == null ? '' : text);
76
+ return s.length > max ? `${s.slice(0, max)}…` : s;
77
+ }
78
+
79
+ // ---------------------------------------------------------------------------
80
+ // the focus card, one shape per kind of work
81
+ // ---------------------------------------------------------------------------
82
+
83
+ function reviewCard(p, n) {
84
+ const title = p.shippedId
85
+ ? `The copy and visuals that shipped in task #${escapeHtml(p.shippedId)}`
86
+ : escapeHtml(p.title || 'A review of what shipped');
87
+ return `
88
+ <p class="studio-card__eyebrow">to tend now · ${escapeHtml(n)}</p>
89
+ <h2 class="studio-card__title">${title}</h2>
90
+ <p class="studio-card__context">It shipped and it is live. Your review coaches the grader on how it judged the work, then remakes or tweaks the words or art only if they need it — it never blocks what shipped.</p>
91
+ <ol class="studio-steps">
92
+ <li class="studio-step"><span class="studio-step__n">01</span><span>Coach the grader<small>Review how it judged this work, and teach it what good looked like.</small></span></li>
93
+ <li class="studio-step"><span class="studio-step__n">02</span><span>Remake or tweak<small>Only if it needs it — with the AI at your side, or on your own.</small></span></li>
94
+ </ol>
95
+ <div class="studio-card__actions">
96
+ <a class="btn-accent" href="/builders/task/${escapeHtml(String(p.taskId))}">Open this review</a>
97
+ <button type="button" class="btn-ghost" data-skip>Let it be for now</button>
98
+ </div>`;
99
+ }
100
+
101
+ function flagCard(p, n) {
102
+ const who = p.who ? `${escapeHtml(p.who)} flagged this` : 'Someone flagged this';
103
+ const reason = p.reason ? ` — ${escapeHtml(p.reason)}` : '';
104
+ return `
105
+ <p class="studio-card__eyebrow">to tend now · ${escapeHtml(n)}</p>
106
+ <blockquote class="studio-card__quote">${quote(p.text)}</blockquote>
107
+ <p class="studio-card__context">${who}${reason}</p>
108
+ <div class="studio-card__actions">
109
+ <a class="btn-accent" href="/builders/copy-desk">Open in the copy desk</a>
110
+ <button type="button" class="btn-ghost" data-skip>Let it be for now</button>
111
+ </div>`;
112
+ }
113
+
114
+ function emptyCard(done) {
115
+ const heading = done ? 'You have tended everything waiting' : 'Nothing waits right now';
116
+ const body = done
117
+ ? 'That is the whole garden. Come back when work ships, or start again from the top.'
118
+ : 'A quiet moment — either good news or an unread queue. When work with copy or visuals ships, a review appears here for you.';
119
+ const restart = done
120
+ ? '<div class="studio-card__actions studio-empty__actions"><button type="button" class="btn-ghost" data-restart>Start over</button></div>'
121
+ : '';
122
+ return `<div class="studio-empty"><h2>${heading}</h2><p>${body}</p>${restart}</div>`;
123
+ }
124
+
125
+ // ---------------------------------------------------------------------------
126
+ // paint
127
+ // ---------------------------------------------------------------------------
128
+
129
+ function paintHead() {
130
+ const name = viewer && (viewer.display_name || viewer.github_login);
131
+ $('#studio-greet').textContent = name ? `${greeting()}, ${name}` : greeting();
132
+ const n = pieces.length;
133
+ $('#studio-sub').textContent = n
134
+ ? `${n} ${n === 1 ? 'piece' : 'pieces'} of the product's voice ${n === 1 ? 'waits' : 'wait'} for you. Tend them one at a time — or let them be.`
135
+ : 'Nothing waits right now. Enjoy the quiet.';
136
+ }
137
+
138
+ function paint() {
139
+ const focus = $('#studio-focus');
140
+ const rest = $('#studio-rest');
141
+
142
+ if (!pieces.length) { focus.innerHTML = emptyCard(false); rest.hidden = true; return; }
143
+ if (idx >= pieces.length) { focus.innerHTML = emptyCard(true); rest.hidden = true; return; }
144
+
145
+ const p = pieces[idx];
146
+ focus.innerHTML = p.type === 'review' ? reviewCard(p, `${idx + 1} of ${pieces.length}`) : flagCard(p, `${idx + 1} of ${pieces.length}`);
147
+
148
+ // The rest of the garden: one stone per remaining piece, quiet and
149
+ // non-interactive — the room asks the reader to tend one thing, not to scan.
150
+ const remaining = pieces.length - (idx + 1);
151
+ if (remaining > 0) {
152
+ const shown = Math.min(remaining, 14);
153
+ const stones = new Array(shown).fill('<span class="studio-stone"></span>').join('');
154
+ const extra = remaining > shown ? ` +${remaining - shown}` : '';
155
+ rest.innerHTML = `<span class="studio-stones" aria-hidden="true">${stones}</span>`
156
+ + `<span class="studio-rest__line">and ${remaining} more waiting${extra}</span>`;
157
+ rest.hidden = false;
158
+ } else {
159
+ rest.hidden = true;
160
+ }
161
+ }
162
+
163
+ // ---------------------------------------------------------------------------
164
+ // load
165
+ // ---------------------------------------------------------------------------
166
+
167
+ // The waiting review requests: backlog is where cascade.js files them and where
168
+ // they sit until claimed or dismissed ("dismissable rather than nagging"), so
169
+ // status=backlog is the bounded, correct read for what is waiting to be tended.
170
+ async function loadReviews() {
171
+ const res = await api.request('GET', `${API}/tasks?discipline=artist&status=backlog&limit=${REVIEW_LIMIT}`);
172
+ if (!res.ok) return [];
173
+ const tasks = (res.data && res.data.tasks) || [];
174
+ return tasks
175
+ .filter((t) => t.source === 'cascade' || /^artist review\b/i.test(t.title || ''))
176
+ .sort((a, b) => new Date(b.created_at || 0) - new Date(a.created_at || 0))
177
+ .map((t) => ({ type: 'review', taskId: t.id, title: t.title, shippedId: reviewShippedId(t) }));
178
+ }
179
+
180
+ // The open flags. Best-effort: no copy-desk module → 404 → no flags, and the
181
+ // room is reviews alone rather than an error.
182
+ async function loadFlags() {
183
+ const res = await api.request('GET', `${API}/copy-desk/queue?status=open`);
184
+ if (!res.ok) return [];
185
+ // The server already filters to open flags (?status=open); no client re-filter.
186
+ const flags = (res.data && res.data.flags) || [];
187
+ return flags.map((f) => ({
188
+ type: 'flag',
189
+ text: f.scope === 'surface' ? f.surface : (f.current ? f.current.text : f.text_at_flag),
190
+ reason: clip(f.reason, 240),
191
+ who: clip(f.flagged_by_name || f.flagged_by_login || '', 60) || null,
192
+ }));
193
+ }
194
+
195
+ // ---------------------------------------------------------------------------
196
+ // wire + boot
197
+ // ---------------------------------------------------------------------------
198
+
199
+ document.addEventListener('click', (ev) => {
200
+ if (ev.target.closest('[data-skip]')) { idx += 1; paint(); window.scrollTo({ top: 0 }); return; }
201
+ if (ev.target.closest('[data-restart]')) { idx = 0; paint(); window.scrollTo({ top: 0 }); }
202
+ });
203
+
204
+ (async () => {
205
+ const me = await api.request('GET', `${API}/me`);
206
+ if (me.ok) viewer = me.data.builder || me.data;
207
+ // Reviews first (the richer craft), then flags — each newest-first within.
208
+ const [reviews, flags] = await Promise.all([loadReviews(), loadFlags()]);
209
+ pieces = [...reviews, ...flags];
210
+ paintHead();
211
+ paint();
212
+ })();
213
+ })();
@@ -0,0 +1,54 @@
1
+ {
2
+ "_": "The Studio (artist mode), R06 of goal 1000074 (task 1003117). A calm room that surfaces the artist's real work — backlog review requests (cascade, R04) and open copy flags (R02) — one piece at a time, never a table. Against the kit's own stub the page renders its EMPTY state (no /tasks or /copy-desk feed), which is a state the floors must hold in; against the hall-preview harness with the studio__reviews / copy-desk__queue fixtures it renders the focus card and the garden's stones.",
3
+ "page": "/builders/studio",
4
+ "surface": "hall-ui",
5
+ "stub": {
6
+ "prefix": "/builders/"
7
+ },
8
+ "modeQuery": false,
9
+ "ignoreRequests": [
10
+ "/api/(gds|bongos)/me$",
11
+ "/api/(gds|bongos)/me/",
12
+ "/api/(gds|bongos)/tasks",
13
+ "/api/(gds|bongos)/copy-desk/"
14
+ ],
15
+ "ignoreConsole": [
16
+ "/api/(gds|bongos)/",
17
+ "\\b401\\b",
18
+ "\\b403\\b",
19
+ "\\b404\\b",
20
+ "\\b501\\b",
21
+ "Unauthorized",
22
+ "Forbidden",
23
+ "Not Found",
24
+ "load failed"
25
+ ],
26
+ "states": {
27
+ "focus": {
28
+ "auth": true,
29
+ "actions": [
30
+ [
31
+ "wait",
32
+ 700
33
+ ]
34
+ ],
35
+ "expect": {
36
+ "visible": [
37
+ "#studio-focus"
38
+ ]
39
+ }
40
+ },
41
+ "out": {
42
+ "auth": false
43
+ }
44
+ },
45
+ "reducedMotion": {
46
+ "auth": true,
47
+ "actions": [
48
+ [
49
+ "wait",
50
+ 600
51
+ ]
52
+ ]
53
+ }
54
+ }
@@ -658,6 +658,15 @@ main:focus { outline: none; }
658
658
  .nav-item__label { display: none; font-size: 14px; }
659
659
  .nav-icon { width: 16px; height: 16px; flex-shrink: 0; }
660
660
 
661
+ /* task 1003117 (R06 of goal 1000074): the Studio entry is warmed for a builder
662
+ whose craft is artist — the "emphasized entry for artist-craft builders" half
663
+ of artist mode. Distinct from --active (the current page's leading accent bar):
664
+ this warms the GLYPH, so the emphasis reads in the collapsed icon-only rail
665
+ too. accent-ink is the text-safe accent in both modes. shell.js sets the class
666
+ from /me's preferred_disciplines; it is cosmetic (the page is open to all). */
667
+ .nav-item--craft .nav-icon,
668
+ .nav-item--craft .nav-item__label { color: var(--accent-ink); }
669
+
661
670
  .app-sidebar__foot {
662
671
  padding: 10px 0;
663
672
  border-top: 1px solid var(--glass-edge);
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  name: brandkit
3
- description: The identity-system method for ANY Cloud Bongos instance, under the owner's hero-only rule: infers the brand strategy before anything is made (category, audience, function, promise, cultural position, the one symbolic metaphor, what to avoid), holds the logo standard and the five concept methods, and lands the system as the instance's own materials: the mark drawn as SVG in code, the colour system as the branding pack, the type specimen as the pack's faces, the applications as the platform's real surfaces rendered by the kit, and the image direction as a text-free hero-object family with a sidecar beside every plate. The nine-panel board becomes a contact sheet composed from those parts, never a generated image with text in it. Triggers on "brand kit", "identity system", "logo system", "brand guidelines board", "the visual world for a new instance", or when cloudbongos init asks what an instance's mark and material are. Never generates a logo, a wordmark, a board with text, or a mockup with UI. Ported in-house from the taste-skill rebuild spec (tasks 1003325 and 1003328, ADR 0198) with the hero-only rewrite the upstream never had.
3
+ description: >-
4
+ The identity-system method for any instance, under the owner's hero-only rule. Infers the brand strategy
5
+ first, holds the logo standard and the five concept methods, and lands the system as the instance's own
6
+ materials — the mark as SVG, the colours as the pack, the type as its faces. Triggers on "brand kit",
7
+ "identity system", "logo system". Never generates a logo, wordmark or UI mockup.
4
8
  ---
5
9
 
6
10
  <!-- BEGIN PLATFORM PREAMBLE · ui-design module · one block, byte-identical in every skill the module ships (vendored or rebuilt in-house) so an upstream refresh is a three-way merge; the source is modules/ui-design/skills/PREAMBLE.md and tests/ui_design_skills.mjs fails on drift -->
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  name: design-taste-frontend
3
- description: The default taste skill for a landing page, a portfolio, a marketing surface or a redesign of one, on ANY Cloud Bongos instance. Reads the brief and says the design read in one line, sets three dials (variance, motion, density) from named presets, locks one world for the whole page, keeps the hero and the layout disciplined, rations decoration, and ends on a pre-flight the kit measures. Triggers on "design a landing page", "make this not look templated", "taste pass", "anti-slop", "this looks like every AI site", or when /design hands off a marketing surface. Not for dashboards, data tables, wizards, code editors, native mobile or realtime collaboration (those go to /design and the hall's archetypes). Rebuilt in-house from the taste-skill rebuild spec (task 1003325, ADR 0198); the instance's world is the palette and images are hero plates only.
3
+ description: >-
4
+ The default taste skill for a landing page, portfolio or marketing surface on any Cloud Bongos instance.
5
+ Says the design read in one line, sets three dials (variance, motion, density), locks one world for the
6
+ whole page, and ends on a pre-flight the kit measures. Triggers on "design a landing page", "make this not
7
+ look templated", "taste pass", "anti-slop". Not for dashboards or native mobile.
4
8
  ---
5
9
 
6
10
  <!-- BEGIN PLATFORM PREAMBLE · ui-design module · one block, byte-identical in every skill the module ships (vendored or rebuilt in-house) so an upstream refresh is a three-way merge; the source is modules/ui-design/skills/PREAMBLE.md and tests/ui_design_skills.mjs fails on drift -->
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  name: design-taste-frontend-v1
3
- description: The earlier generation of the taste skill, preserved under its own name so a task or a builder that names it gets exactly this behaviour while the default (design-taste-frontend) evolves separately. A fixed baseline triple of dials adapted in conversation, stack conventions for a served surface, typography and colour rules read against the instance's pack, the layout bans, full interaction cycles, motion rules inside the Kill Switch, performance guardrails, a named vocabulary of premium patterns and a tile-grid archetype set. Triggers on "design-taste-frontend-v1", "use the v1 taste skill", "the fixed dials", or "the pattern vocabulary". Rebuilt in-house from the taste-skill rebuild spec (task 1003325, ADR 0198); nothing here auto-updates from v2.
3
+ description: >-
4
+ The earlier generation of the taste skill, kept under its own name so a task or builder that asks for it
5
+ gets exactly this behaviour while the default (design-taste-frontend) evolves separately. A fixed triple of
6
+ dials, the layout bans, full interaction cycles, and a named vocabulary of premium patterns. Triggers on
7
+ "design-taste-frontend-v1", "the fixed dials", "the pattern vocabulary".
4
8
  ---
5
9
 
6
10
  <!-- BEGIN PLATFORM PREAMBLE · ui-design module · one block, byte-identical in every skill the module ships (vendored or rebuilt in-house) so an upstream refresh is a three-way merge; the source is modules/ui-design/skills/PREAMBLE.md and tests/ui_design_skills.mjs fails on drift -->
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  name: gpt-taste
3
- description: Anti-default enforcement for a persuasive scrolling surface when the last attempt came out looking like every other one. A written plan block before any markup whose selection is derived deterministically from the route (a hero architecture, three component architectures, two CSS motion paradigms, checked against what sibling surfaces already used), the four-stage attention-interest-desire-action structure, an ultra-wide hero container that holds the headline to two or three lines, a dense tile grid with zero empty cells, real scroll-driven motion inside the Kill Switch, and a measured contrast sweep. Triggers on "it looks generic", "award-tier", "stop making the same page", "anti-default". Rebuilt in-house from the taste-skill rebuild spec (task 1003327, ADR 0198).
3
+ description: >-
4
+ Anti-default enforcement for a persuasive scrolling surface when the last attempt came out looking like
5
+ every other one. A written plan block before any markup, derived deterministically from the route; the
6
+ four-stage attention-interest-desire-action structure; an ultra-wide hero; a dense tile grid with zero empty
7
+ cells. Triggers on "it looks generic", "award-tier", "anti-default".
4
8
  ---
5
9
 
6
10
  <!-- BEGIN PLATFORM PREAMBLE · ui-design module · one block, byte-identical in every skill the module ships (vendored or rebuilt in-house) so an upstream refresh is a three-way merge; the source is modules/ui-design/skills/PREAMBLE.md and tests/ui_design_skills.mjs fails on drift -->
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  name: high-end-visual-design
3
- description: Makes an interface read as an expensive agency build, inside the instance's world. One texture archetype and one layout archetype per project (never the same pair twice), the container anatomy (a shell and a core with a hairline ring and concentric radii, where the world allows a container at all), the pill button with its nested arrow disc, the spatial rhythm, the motion choreography inside the Kill Switch, the hover physics, the scroll entry, the mobile collapse per archetype and the performance rules, then a pre-output checklist. Triggers on "make it feel premium", "agency-grade", "this looks cheap", "expensive", "high-end", "the polish pass". Rebuilt in-house from the taste-skill rebuild spec (task 1003325, ADR 0198); the fonts are the pack's, the palette is the world's, images are hero plates only.
3
+ description: >-
4
+ Makes an interface read as an expensive agency build, inside the instance's world. One texture archetype and
5
+ one layout archetype per project, the container anatomy, the pill button, the spatial rhythm, the motion
6
+ choreography inside the Kill Switch, the mobile collapse, then a pre-output checklist. Triggers on "make it
7
+ feel premium", "agency-grade", "this looks cheap", "the polish pass".
4
8
  ---
5
9
 
6
10
  <!-- BEGIN PLATFORM PREAMBLE · ui-design module · one block, byte-identical in every skill the module ships (vendored or rebuilt in-house) so an upstream refresh is a three-way merge; the source is modules/ui-design/skills/PREAMBLE.md and tests/ui_design_skills.mjs fails on drift -->
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  name: image-to-code
3
- description: The analyse-then-implement loop for visually important web work on ANY Cloud Bongos instance, under the owner's hero-only rule: the design source of truth for the UI is the owner's reference images and the instance's world, read with a structured per-image analysis and extracted into a full spec before a line is written; the only image this skill generates is the hero plate the composition has a slot for, and the sections are built from the world's templates. Implements as a faithful translation rather than an interpretation, names design drift as the failure mode, holds the hero and clutter rules, and ends on a clarity checklist the kit measures. Triggers on "build this from the reference", "recreate these images in code", "implement the mock faithfully", "the owner sent screenshots to build from", or when a claimed ui task carries reference images. Never generates a section, a comp or a screen. Ported in-house from the taste-skill rebuild spec (tasks 1003325 and 1003328, ADR 0198) with the hero-only rewrite the upstream never had.
3
+ description: >-
4
+ The analyse-then-implement loop for visually important web work, under the owner's hero-only rule. The
5
+ design source of truth is the owner's reference images and the instance's world, read per image and
6
+ extracted into a full spec before a line is written. Triggers on "build this from the reference", "implement
7
+ the mock faithfully". Never generates a section, comp or screen.
4
8
  ---
5
9
 
6
10
  <!-- BEGIN PLATFORM PREAMBLE · ui-design module · one block, byte-identical in every skill the module ships (vendored or rebuilt in-house) so an upstream refresh is a three-way merge; the source is modules/ui-design/skills/PREAMBLE.md and tests/ui_design_skills.mjs fails on drift -->
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  name: imagegen-frontend-mobile
3
- description: The plate-making skill for the narrow end of a web surface on ANY Cloud Bongos instance, under the owner's hero-only rule: it produces text-free portrait plates and hero objects for the 390 and 320 compositions the kit renders, the masthead above the words and the field behind them, sized for the fold and clear of the regions a phone reserves. Never a screen, never a phone mockup, never a flow of screens as images, and never code: a screen concept is the surface's states file rendered by the kit at 390, and a native app target declines to /design. Locks one material, light and camera across the family from the world's DESIGN.md, keeps the first view to one focal point, puts a scrim under any text that sits over a plate, and lands the pair convention with a sidecar beside every file. Triggers on "a plate for the mobile hero", "the masthead at 390", "the hero object for the phone collapse", "a portrait plate", or when adapt or design-taste-frontend names a narrow-width slot with no plate. Ported in-house from the taste-skill rebuild spec (tasks 1003325 and 1003328, ADR 0198) with the hero-only rewrite the upstream never had.
3
+ description: >-
4
+ The plate-making skill for the narrow end of a web surface, under the owner's hero-only rule. Text-free
5
+ portrait plates and hero objects for the 390 and 320 compositions the kit renders, sized for the fold and
6
+ clear of what a phone reserves, with a sidecar beside every file. Triggers on "a plate for the mobile hero",
7
+ "the masthead at 390". Never a screen, a phone mockup or code.
4
8
  ---
5
9
 
6
10
  <!-- BEGIN PLATFORM PREAMBLE · ui-design module · one block, byte-identical in every skill the module ships (vendored or rebuilt in-house) so an upstream refresh is a three-way merge; the source is modules/ui-design/skills/PREAMBLE.md and tests/ui_design_skills.mjs fails on drift -->
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  name: imagegen-frontend-web
3
- description: The plate-making skill for a web surface on ANY Cloud Bongos instance, under the owner's hero-only rule: it produces text-free hero plates, hero objects and atmosphere backdrops for the slots a composition already has, never one image per section, never a screen, never a UI comp, and it writes no page code. Commits to the plate list out loud, chooses a composition anchor per plate that is not the text-left image-right default, locks one material, one light and one camera across the family, matches every plate tonally to the instance's pack, runs the generation through the instance's art pipeline key, and lands each plate as the pair convention (a black-ground plate for dark, an alpha cutout for light) with a provenance sidecar beside every file. Triggers on "make a hero plate", "we need a hero object for", "an atmosphere backdrop for the landing", "the project orbs need a new material", or when design-taste-frontend or /design names a plate slot with no plate. Ported in-house from the taste-skill rebuild spec (tasks 1003325 and 1003328, ADR 0198) with the hero-only rewrite the upstream never had.
3
+ description: >-
4
+ The plate-making skill for a web surface, under the owner's hero-only rule. Text-free hero plates, hero
5
+ objects and atmosphere backdrops for the slots a composition already has — never one per section, never a
6
+ screen, never a UI comp, and no page code. One material, light and camera across the family, landed with a
7
+ provenance sidecar. Triggers on "make a hero plate", "an atmosphere backdrop".
4
8
  ---
5
9
 
6
10
  <!-- BEGIN PLATFORM PREAMBLE · ui-design module · one block, byte-identical in every skill the module ships (vendored or rebuilt in-house) so an upstream refresh is a three-way merge; the source is modules/ui-design/skills/PREAMBLE.md and tests/ui_design_skills.mjs fails on drift -->
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  name: impeccable
3
- description: The design-craft method for ANY surface on ANY Cloud Bongos instance, invoked as one skill plus an optional sub-command and an optional target ("impeccable audit /projects", "impeccable polish the hall's ranks page", "impeccable harden the wizard"). Sixteen sub-command playbooks (shape, audit, critique, polish, harden, animate, typeset, colorize, layout, adapt, distill, clarify, onboard, optimize, document, extract) plus the two directions (bolder, quieter), exactly one loaded per request; a bare description with no sub-command is ordinary design work routed to the closest playbook; no argument prints a menu built from live project signals and never auto-runs anything. Carries the craft floor (loaded right before UI is edited, never recited to the user) and the bounded finish (build fully, one render round through the kit, one batch of fixes, at most one confirming round, stop). Triggers on "impeccable", "critique this page", "audit the UI", "polish the whole path", "make it survive long text", "animate this", "tighten the copy", "document the design system", "extract the repeated pattern". Rebuilt in-house from the impeccable rebuild spec (task 1003326, ADR 0198) with no hooks, no bypass agent, no telemetry and no live server; the live loop is the playwright plugin and the headless loop is the module's kit. Web surfaces only; a native mobile target declines to /design.
3
+ description: >-
4
+ The design-craft method for any surface, invoked as one skill plus an optional sub-command and target
5
+ ("impeccable audit /projects"). Sixteen playbooks — shape, audit, critique, polish, harden, animate,
6
+ typeset, colorize, layout, adapt, distill, clarify, onboard, optimize, document, extract — one per request.
7
+ Triggers on "impeccable", "critique this page", "audit the UI". Web surfaces only.
4
8
  ---
5
9
 
6
10
  <!-- BEGIN PLATFORM PREAMBLE · ui-design module · one block, byte-identical in every skill the module ships (vendored or rebuilt in-house) so an upstream refresh is a three-way merge; the source is modules/ui-design/skills/PREAMBLE.md and tests/ui_design_skills.mjs fails on drift -->
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  name: industrial-brutalist-ui
3
- description: Raw mechanical interfaces that read like a technical document, inside the instance's world. One archetype per project (Swiss industrial print or dark tactical terminal, never mixed), massive uppercase clamped display against fixed tracked-out mono metadata, the pack's terracotta as the single hazard accent and its sea as one terminal green, strict grid anchoring with the hairline as a one-pixel grid gap, zero corner rounding, bracket and crosshair framing glyphs, and static halftone, scanline and noise degradation inside the Kill Switch. Triggers on "brutalist", "industrial", "terminal", "make it raw", "like a technical document". Rebuilt in-house from the taste-skill rebuild spec (task 1003327, ADR 0198).
3
+ description: >-
4
+ Raw mechanical interfaces that read like a technical document, inside the instance's world. One archetype
5
+ per project (Swiss industrial print or dark tactical terminal, never mixed), massive uppercase display
6
+ against tracked-out mono metadata, strict grid anchoring, zero corner rounding, halftone and scanline
7
+ degradation inside the Kill Switch. Triggers on "brutalist", "industrial", "make it raw".
4
8
  ---
5
9
 
6
10
  <!-- BEGIN PLATFORM PREAMBLE · ui-design module · one block, byte-identical in every skill the module ships (vendored or rebuilt in-house) so an upstream refresh is a three-way merge; the source is modules/ui-design/skills/PREAMBLE.md and tests/ui_design_skills.mjs fails on drift -->
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  name: minimalist-ui
3
- description: Quiet editorial product interfaces built out of type and white space, inside the instance's world. Macro whitespace first, a capped measure, one display job and one body job, exactly one hairline weight for every border in the sheet, washed accents measured against their own ink, flat tile cards with no shadow, box-less accordions, keycap shortcuts, one depth treatment per band, entry motion inside the Kill Switch. Triggers on "make it minimal", "clean and editorial", "quiet", "document-like", "this feels busy". Declines dense surfaces like dashboards and ledgers. Rebuilt in-house from the taste-skill rebuild spec (task 1003327, ADR 0198); the palette is the pack and the nearest library look is expedition.
3
+ description: >-
4
+ Quiet editorial product interfaces built out of type and white space, inside the instance's world. Macro
5
+ whitespace first, a capped measure, one display job and one body job, exactly one hairline weight for every
6
+ border, flat tile cards with no shadow, entry motion inside the Kill Switch. Triggers on "make it minimal",
7
+ "clean and editorial", "this feels busy". Declines dense surfaces.
4
8
  ---
5
9
 
6
10
  <!-- BEGIN PLATFORM PREAMBLE · ui-design module · one block, byte-identical in every skill the module ships (vendored or rebuilt in-house) so an upstream refresh is a three-way merge; the source is modules/ui-design/skills/PREAMBLE.md and tests/ui_design_skills.mjs fails on drift -->
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  name: redesign-existing-projects
3
- description: Upgrades an existing site or app in place, in whatever framework and styling method it already uses, without breaking it. A fixed three-step sequence (scan the stack, diagnose against a written audit, apply targeted fixes), the seven-group audit (typography, colour and surface, layout, interaction, content, code and omissions) with the instance's world as the answer key, a catalogue of upgrade techniques marked CSS-only or not available, and a fixed impact-per-risk fix order. Triggers on "redesign this", "upgrade the existing page", "make the current X premium", "audit this UI", "this page looks generic, fix it". Rebuilt in-house from the taste-skill rebuild spec (task 1003325, ADR 0198); keeps the stack, never migrates frameworks, images are hero plates only.
3
+ description: >-
4
+ Upgrades an existing site or app in place, in whatever framework and styling method it already uses, without
5
+ breaking it. Three fixed steps — scan the stack, diagnose against a written audit, apply targeted fixes —
6
+ with the instance's world as the answer key and a fixed impact-per-risk fix order. Triggers on "redesign
7
+ this", "upgrade the existing page", "audit this UI".
4
8
  ---
5
9
 
6
10
  <!-- BEGIN PLATFORM PREAMBLE · ui-design module · one block, byte-identical in every skill the module ships (vendored or rebuilt in-house) so an upstream refresh is a three-way merge; the source is modules/ui-design/skills/PREAMBLE.md and tests/ui_design_skills.mjs fails on drift -->
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  name: stitch-design-taste
3
- description: Writes a design contract document for an external screen-generating tool such as Google Stitch, in the platform's own frontmatter shape. Four dials (creativity, density, variance, motion intent) with named bands and stated defaults, then eight fixed sections (atmosphere, colour roles, typography, components, layout, responsive rules, motion intent, banned patterns) written as descriptive language paired with exact values. The thirteen colours are transcribed from the instance's pack and diffed, never invented; motion is an intent brief inside the Kill Switch; the screens that come back are a reference, not a source of truth. Triggers on "write a design brief for Stitch", "a design contract document", "a prompt-to-screen tool". Rebuilt in-house from the taste-skill rebuild spec (task 1003327, ADR 0198).
3
+ description: >-
4
+ Writes a design contract document for an external screen-generating tool such as Google Stitch, in the
5
+ platform's own frontmatter shape. Four dials with named bands and stated defaults, then eight fixed sections
6
+ pairing descriptive language with exact values. The thirteen colours are transcribed from the instance's
7
+ pack, never invented. Triggers on "a design brief for Stitch".
4
8
  ---
5
9
 
6
10
  <!-- BEGIN PLATFORM PREAMBLE · ui-design module · one block, byte-identical in every skill the module ships (vendored or rebuilt in-house) so an upstream refresh is a three-way merge; the source is modules/ui-design/skills/PREAMBLE.md and tests/ui_design_skills.mjs fails on drift -->
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  name: style
3
- description: Runs a bounded session in which an owner authors their OWN look for the instance, as a branding pack inside the fifteen-token contract, and proves it clean before it lands. Four steps and no more. SEARCH reads the instance's world and the style library, renders the nearest two entries so the owner looks rather than imagines, and asks two or three questions about feel. CREATE drafts modules/ui-design/styles/<name>/ in the library's contract shape (pack.json, DESIGN.md, mock.html), never a fork of a sheet and never a sixteenth token. PROVE retunes the thirteen against contrast.js, the styles suite, check-mock and the kit's renders until every named pair is green, showing the shots each round. ADOPT hands the owner the one-step switch and files the look into the catalogue. Triggers on "/style", "make a look", "author a style", "I want our own theme", "can we have a different look", "add a style to the library".
3
+ description: >-
4
+ Runs a bounded session in which an owner authors their own look for the instance, as a branding pack inside
5
+ the fifteen-token contract, and proves it clean before it lands. Four steps and no more — SEARCH, CREATE,
6
+ PROVE, ADOPT. Triggers on "/style", "make a look", "author a style", "I want our own theme", "add a style to
7
+ the library".
4
8
  ---
5
9
 
6
10
  <!-- BEGIN PLATFORM PREAMBLE · ui-design module · one block, byte-identical in every skill the module ships (vendored or rebuilt in-house) so an upstream refresh is a three-way merge; the source is modules/ui-design/skills/PREAMBLE.md and tests/ui_design_skills.mjs fails on drift -->
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.544",
3
+ "version": "1.19.546",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.544",
9
+ "version": "1.19.546",
10
10
  "license": "AGPL-3.0-or-later",
11
11
  "dependencies": {
12
12
  "express": "^4.21.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.544",
3
+ "version": "1.19.546",
4
4
  "description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "main": "src/platform-server.js",
@@ -1311,7 +1311,11 @@ function checkErrorEnvelope() {
1311
1311
  // truncated EVERY field, the skill matched on its body text, and nothing in CI noticed. A parse
1312
1312
  // error is a hard fail; description length and the listing budget are advisory (the listing is
1313
1313
  // resident in every session, ~1% of context, and gets truncated past ~8k chars). The check reads
1314
- // the instance's .claude/skills/ and is a quiet pass when a checkout carries none (published core).
1314
+ // every skill Claude Code can load — the instance's .claude/skills/ AND the module-owned
1315
+ // modules/*/skills/ that claude-materialize.js copies into it (task 1003587: scanning only the
1316
+ // tracked root meant 14 ui-design skills went unlinted until the untracked copy existed, so four
1317
+ // unparseable frontmatters shipped and fitness only went red on a checkout that had materialized).
1318
+ // A quiet pass when a checkout carries none (published core).
1315
1319
  function checkSkillFrontmatter() {
1316
1320
  const violations = [];
1317
1321
  const warnings = [];
@@ -1329,7 +1333,10 @@ function checkSkillFrontmatter() {
1329
1333
  }
1330
1334
  if (long.length) {
1331
1335
  long.sort((a, b) => b.n - a.n);
1332
- warnings.push(`${long.length} description(s) over ${lint.DESC_WARN_CHARS} chars (longest: ${long.slice(0, 4).map((l) => `${l.file.split('/')[2]} ${l.n}`).join(', ')}) — every char is resident in every session; run node scripts/gds/skill-lint.js.`);
1336
+ // The skill's own name is its PARENT dir — a fixed path index would read "skills"
1337
+ // for a module-owned path (modules/<key>/skills/<name>/SKILL.md).
1338
+ const skillName = (f) => f.split('/').slice(-2, -1)[0] || f;
1339
+ warnings.push(`${long.length} description(s) over ${lint.DESC_WARN_CHARS} chars (longest: ${long.slice(0, 4).map((l) => `${skillName(l.file)} ${l.n}`).join(', ')}) — every char is resident in every session; run node scripts/gds/skill-lint.js.`);
1333
1340
  }
1334
1341
  if (report.overBudget) warnings.push(`the skill listing is ${report.listingChars} chars (~${estTokens(report.listingChars)} tokens) against a ~${report.budgetChars}-char budget — Claude Code truncates entries past it and routing degrades.`);
1335
1342
  return {
@@ -1338,7 +1345,7 @@ function checkSkillFrontmatter() {
1338
1345
  hardFail: violations.length > 0,
1339
1346
  violations,
1340
1347
  warnings,
1341
- note: `${report.results.length} SKILL.md file(s) under .claude/skills/ parse as YAML (name + description present, no ": " / " #" in a plain scalar); length + listing budget advisory.`,
1348
+ note: `${report.results.length} SKILL.md file(s) under .claude/skills/ + modules/*/skills/ parse as YAML (name + description present, no ": " / " #" in a plain scalar); length + listing budget advisory.`,
1342
1349
  };
1343
1350
  }
1344
1351
 
@@ -23,7 +23,9 @@
23
23
  // verdict as authoritative over this one.
24
24
  //
25
25
  // Usage:
26
- // node scripts/gds/skill-lint.js lint every .claude/skills/*/SKILL.md; exit 1 on any error
26
+ // node scripts/gds/skill-lint.js lint every skill Claude Code can load — .claude/skills/*
27
+ // plus the module-owned modules/*/skills/* that materialize
28
+ // into it; exit 1 on any error
27
29
  // node scripts/gds/skill-lint.js <file...> lint specific files
28
30
  // node scripts/gds/skill-lint.js --hook PostToolUse mode: read the hook JSON on stdin, lint the
29
31
  // edited file if it is a SKILL.md, print findings, exit 0
@@ -108,14 +110,40 @@ function lintSkillFile(text, { file = 'SKILL.md' } = {}) {
108
110
  return res;
109
111
  }
110
112
 
111
- // Every SKILL.md under .claude/skills/ (one level deep).
112
- function listSkillFiles(root = REPO_ROOT) {
113
- const dir = path.join(root, '.claude', 'skills');
113
+ // Every SKILL.md in one skills root (one level deep, plus a `vendor/` tier).
114
+ function skillsIn(dir) {
114
115
  if (!fs.existsSync(dir)) return [];
115
- return fs.readdirSync(dir, { withFileTypes: true })
116
- .filter((d) => d.isDirectory())
117
- .map((d) => path.join(dir, d.name, 'SKILL.md'))
118
- .filter((f) => fs.existsSync(f));
116
+ const out = [];
117
+ for (const d of fs.readdirSync(dir, { withFileTypes: true })) {
118
+ if (!d.isDirectory()) continue;
119
+ const own = path.join(dir, d.name, 'SKILL.md');
120
+ if (fs.existsSync(own)) { out.push(own); continue; }
121
+ if (d.name === 'vendor') out.push(...skillsIn(path.join(dir, d.name)));
122
+ }
123
+ return out;
124
+ }
125
+
126
+ // Every skill Claude Code can end up loading — the tracked `.claude/skills/` PLUS the
127
+ // module-owned `modules/<key>/skills/`, because claude-materialize.js copies those into
128
+ // `.claude/skills/` as untracked copies (task 1003330) and a defect in one only shows up
129
+ // after materialization. Scanning only the tracked root left 14 ui-design skills unlinted
130
+ // until the copy existed, which is exactly how four unparseable frontmatters shipped
131
+ // (task 1003587). A name already present in `.claude/skills/` wins and its module copy is
132
+ // skipped — the same precedence the materializer applies — so nothing is counted twice.
133
+ function listSkillFiles(root = REPO_ROOT) {
134
+ const core = skillsIn(path.join(root, '.claude', 'skills'));
135
+ const seen = new Set(core.map((f) => path.basename(path.dirname(f))));
136
+ const modulesDir = path.join(root, 'modules');
137
+ const owned = [];
138
+ if (fs.existsSync(modulesDir)) {
139
+ for (const d of fs.readdirSync(modulesDir, { withFileTypes: true })) {
140
+ if (!d.isDirectory()) continue;
141
+ for (const f of skillsIn(path.join(modulesDir, d.name, 'skills'))) {
142
+ if (!seen.has(path.basename(path.dirname(f)))) owned.push(f);
143
+ }
144
+ }
145
+ }
146
+ return [...core, ...owned];
119
147
  }
120
148
 
121
149
  // Lint a set of files; returns per-file results plus the listing total vs budget.
@@ -125,7 +153,13 @@ function lintSkills(files) {
125
153
  return { results, listingChars, budgetChars: LISTING_BUDGET_CHARS, overBudget: listingChars > LISTING_BUDGET_CHARS };
126
154
  }
127
155
 
128
- function isSkillMd(p) { return /[\\/]\.claude[\\/]skills[\\/][^\\/]+[\\/]SKILL\.md$/.test(String(p || '')); }
156
+ // The PostToolUse hook's gate. It must match a module-owned SKILL.md too — that is the
157
+ // file a builder actually edits; the `.claude/skills/` copy is generated (task 1003587).
158
+ function isSkillMd(p) {
159
+ const s = String(p || '');
160
+ return /[\\/]\.claude[\\/]skills[\\/](?:vendor[\\/])?[^\\/]+[\\/]SKILL\.md$/.test(s)
161
+ || /[\\/]modules[\\/][^\\/]+[\\/]skills[\\/](?:vendor[\\/])?[^\\/]+[\\/]SKILL\.md$/.test(s);
162
+ }
129
163
 
130
164
  function format(report, { relTo = REPO_ROOT } = {}) {
131
165
  const out = [];