@bongos/core 1.20.9 → 1.20.11
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/.bongos-core.json +101 -96
- package/.claude/skills/ask-for-help/SKILL.md +6 -0
- package/.claude/skills/backlog-review/SKILL.md +6 -0
- package/.claude/skills/blocker-review/SKILL.md +6 -0
- package/.claude/skills/blocker-solve/SKILL.md +6 -0
- package/.claude/skills/bug-triage/SKILL.md +6 -0
- package/.claude/skills/builder-backup/SKILL.md +6 -0
- package/.claude/skills/builder-claim/SKILL.md +6 -0
- package/.claude/skills/builder-cost/SKILL.md +6 -0
- package/.claude/skills/builder-end/SKILL.md +6 -0
- package/.claude/skills/builder-exit/SKILL.md +6 -0
- package/.claude/skills/builder-key/SKILL.md +6 -0
- package/.claude/skills/builder-reauth/SKILL.md +6 -0
- package/.claude/skills/builder-redteam/SKILL.md +6 -0
- package/.claude/skills/builder-release/SKILL.md +6 -0
- package/.claude/skills/builder-sequence/SKILL.md +6 -0
- package/.claude/skills/builder-setup/SKILL.md +6 -0
- package/.claude/skills/builder-ship/SKILL.md +6 -0
- package/.claude/skills/builder-stage/SKILL.md +6 -0
- package/.claude/skills/builder-start/SKILL.md +6 -0
- package/.claude/skills/collab-review/SKILL.md +6 -0
- package/.claude/skills/design/SKILL.md +6 -0
- package/.claude/skills/design-sync/SKILL.md +6 -0
- package/.claude/skills/feedback/SKILL.md +6 -0
- package/.claude/skills/figma-design-sync/SKILL.md +6 -0
- package/.claude/skills/fix-task/SKILL.md +6 -0
- package/.claude/skills/goal-create/SKILL.md +6 -0
- package/.claude/skills/goal-review/SKILL.md +6 -0
- package/.claude/skills/goal-uat/SKILL.md +6 -0
- package/.claude/skills/grade-audit/SKILL.md +6 -0
- package/.claude/skills/grade-recover/SKILL.md +6 -0
- package/.claude/skills/grader-health/SKILL.md +6 -0
- package/.claude/skills/idea-triage/SKILL.md +6 -0
- package/.claude/skills/merge-mode/SKILL.md +6 -0
- package/.claude/skills/new-project/SKILL.md +6 -0
- package/.claude/skills/otb-character-review/SKILL.md +6 -0
- package/.claude/skills/otb-design-review/SKILL.md +6 -0
- package/.claude/skills/otb-feedback-capture/SKILL.md +6 -0
- package/.claude/skills/otb-figma-sync/SKILL.md +6 -0
- package/.claude/skills/otb-tile-generate/SKILL.md +6 -0
- package/.claude/skills/planning-session/SKILL.md +6 -0
- package/.claude/skills/priority-session/SKILL.md +6 -0
- package/.claude/skills/read-session-export/SKILL.md +6 -0
- package/.claude/skills/recall/SKILL.md +6 -0
- package/.claude/skills/scan-before-install/SKILL.md +6 -0
- package/.claude/skills/session-handoff/SKILL.md +6 -0
- package/.claude/skills/ship-check/SKILL.md +6 -0
- package/.claude/skills/status/SKILL.md +6 -0
- package/.claude/skills/strand-fix/SKILL.md +6 -0
- package/.claude/skills/tweak/SKILL.md +6 -0
- package/.claude/skills/worktree-clean/SKILL.md +6 -0
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +2 -0
- package/clients/bongos-client/index.cjs +2 -0
- package/clients/bongos-client/index.d.ts +3 -0
- package/clients/bongos-client/index.mjs +2 -0
- package/docs/api/openapi.json +50 -3
- package/docs/api-reference.md +3 -2
- package/docs/copy-inventory.md +60 -52
- package/docs/copy-registry.json +126 -54
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +2 -1
- package/docs/page-readings.json +131 -119
- package/modules/hall-ui/public/panel.css +4 -4
- package/modules/hall-ui/public/settings-specialities.js +139 -2
- package/modules/hall-ui/public/settings.css +26 -0
- package/modules/hall-ui/public/settings.html +4 -0
- package/modules/hall-ui/public/settings.states.json +1 -0
- package/modules/hall-ui/records/panel-family.md +2 -0
- package/modules/specialities/routes/specialities.js +16 -0
- package/modules/ui-design/skills/brandkit/SKILL.md +6 -0
- package/modules/ui-design/skills/design-taste-frontend/SKILL.md +6 -0
- package/modules/ui-design/skills/design-taste-frontend-v1/SKILL.md +6 -0
- package/modules/ui-design/skills/gpt-taste/SKILL.md +6 -0
- package/modules/ui-design/skills/high-end-visual-design/SKILL.md +6 -0
- package/modules/ui-design/skills/image-to-code/SKILL.md +6 -0
- package/modules/ui-design/skills/imagegen-frontend-mobile/SKILL.md +6 -0
- package/modules/ui-design/skills/imagegen-frontend-web/SKILL.md +6 -0
- package/modules/ui-design/skills/impeccable/SKILL.md +6 -0
- package/modules/ui-design/skills/industrial-brutalist-ui/SKILL.md +6 -0
- package/modules/ui-design/skills/minimalist-ui/SKILL.md +6 -0
- package/modules/ui-design/skills/redesign-existing-projects/SKILL.md +6 -0
- package/modules/ui-design/skills/stitch-design-taste/SKILL.md +6 -0
- package/modules/ui-design/skills/style/SKILL.md +6 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/scripts/hall-preview/server.js +4 -0
- package/src/module-api.js +1 -1
- package/tests/speciality_offer_ui.mjs +185 -0
- package/tests/speciality_skills.mjs +51 -0
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
|
|
27
27
|
// `walk` is the skills walkthrough in progress, or null (task 1004073):
|
|
28
28
|
// { id, data, step, decisions, saving }. See walkHtml below.
|
|
29
|
-
|
|
29
|
+
// `offerId` / `offerChoices`: the owner's "what it offers" editor that is open,
|
|
30
|
+
// and its rendered choices once the instance's skills have loaded (task 1004404).
|
|
31
|
+
const state = { rows: [], suggestions: [], openId: null, busy: false, walk: null, offerId: null, offerChoices: null };
|
|
30
32
|
|
|
31
33
|
const el = (id) => document.getElementById(id);
|
|
32
34
|
const statusLine = (msg) => { const s = el('specialities-status'); if (s) s.textContent = msg || ''; };
|
|
@@ -211,12 +213,112 @@
|
|
|
211
213
|
focusWalk();
|
|
212
214
|
}
|
|
213
215
|
|
|
216
|
+
// ── WHAT A SPECIALITY OFFERS (task 1004404; ADR 0310 §1) ──────────────────
|
|
217
|
+
//
|
|
218
|
+
// The OWNER's half: which of this instance's skills the speciality puts on
|
|
219
|
+
// its menu. Until this, only the API could set it, so nothing made in the hall
|
|
220
|
+
// offered anything and the walkthrough above never appeared. Each choice shows
|
|
221
|
+
// the skill's plain description — the same words the walkthrough will show the
|
|
222
|
+
// people who adopt it — so the owner is choosing what to teach, not ticking
|
|
223
|
+
// names. Offering turns nothing on for anyone: every adopter still starts with
|
|
224
|
+
// all of them off and decides one at a time.
|
|
225
|
+
//
|
|
226
|
+
// The choices come from GET /specialities/installed-skills, so only skills
|
|
227
|
+
// this instance has are offered; the server's unknown_skill check on POST/PATCH
|
|
228
|
+
// stays the rule for anything a stale page sends.
|
|
229
|
+
|
|
230
|
+
let installedList = null; // fetched once per page, on first need
|
|
231
|
+
async function installedSkillList() {
|
|
232
|
+
if (!installedList) {
|
|
233
|
+
const data = await call('GET', '/specialities/installed-skills');
|
|
234
|
+
installedList = (data && data.skills) || [];
|
|
235
|
+
}
|
|
236
|
+
return installedList;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function offerChoicesHtml(all, chosen) {
|
|
240
|
+
const picked = new Set(chosen || []);
|
|
241
|
+
const gone = (chosen || []).filter((n) => !all.some((k) => k.name === n));
|
|
242
|
+
return `
|
|
243
|
+
<p class="prow__hint">
|
|
244
|
+
Offering a skill turns nothing on. Everyone who uses this speciality is shown
|
|
245
|
+
each one and chooses for themselves — all start off.
|
|
246
|
+
</p>
|
|
247
|
+
<label class="speciality-field">
|
|
248
|
+
<span>Find a skill</span>
|
|
249
|
+
<input type="search" class="speciality-offer__filter" placeholder="Type to narrow the list" />
|
|
250
|
+
</label>
|
|
251
|
+
<ul class="speciality-offer__list">
|
|
252
|
+
${all.map((k) => `
|
|
253
|
+
<li data-find="${escapeHtml(`${k.name} ${k.does || ''}`.toLowerCase())}">
|
|
254
|
+
<label class="speciality-field speciality-field--check">
|
|
255
|
+
<input type="checkbox" name="skill" value="${escapeHtml(k.name)}" ${picked.has(k.name) ? 'checked' : ''} />
|
|
256
|
+
<span>
|
|
257
|
+
<strong>${escapeHtml(k.name)}</strong> — ${escapeHtml(k.does || 'Its author gave no description.')}
|
|
258
|
+
<small>${escapeHtml(k.cost || '')}</small>
|
|
259
|
+
</span>
|
|
260
|
+
</label>
|
|
261
|
+
</li>`).join('')}
|
|
262
|
+
</ul>
|
|
263
|
+
${gone.length ? `<p class="prow__hint">No longer on this project, so saving drops it from the offer: ${gone.map((n) => `<code>${escapeHtml(n)}</code>`).join(', ')}.</p>` : ''}`;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const chosenSkills = (form) => [...form.querySelectorAll('input[name="skill"]:checked')].map((i) => i.value);
|
|
267
|
+
|
|
268
|
+
function narrowOffer(input) {
|
|
269
|
+
const q = input.value.trim().toLowerCase();
|
|
270
|
+
const list = input.closest('form') && input.closest('form').querySelector('.speciality-offer__list');
|
|
271
|
+
if (!list) return;
|
|
272
|
+
for (const li of list.children) li.hidden = q !== '' && !String(li.dataset.find || '').includes(q);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function offerFormHtml(s) {
|
|
276
|
+
return `
|
|
277
|
+
<form class="speciality-offer" data-id="${escapeHtml(String(s.id))}">
|
|
278
|
+
<h4 class="speciality-create__h">What ${escapeHtml(s.name)} offers</h4>
|
|
279
|
+
<div class="speciality-offer__choices">${state.offerChoices || '<p class="prow__hint">Loading this project’s skills…</p>'}</div>
|
|
280
|
+
<div class="speciality-actions">
|
|
281
|
+
<button type="submit" class="btn" ${state.offerChoices ? '' : 'disabled'}>Save what it offers</button>
|
|
282
|
+
</div>
|
|
283
|
+
</form>`;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async function openOffer(id) {
|
|
287
|
+
if (String(state.offerId) === String(id)) { state.offerId = null; render(); return; }
|
|
288
|
+
state.offerId = String(id);
|
|
289
|
+
state.offerChoices = null;
|
|
290
|
+
state.openId = null;
|
|
291
|
+
state.walk = null;
|
|
292
|
+
render();
|
|
293
|
+
try {
|
|
294
|
+
const all = await installedSkillList();
|
|
295
|
+
const row = state.rows.find((r) => String(r.id) === String(id));
|
|
296
|
+
if (state.offerId !== String(id) || !row) return;
|
|
297
|
+
state.offerChoices = offerChoicesHtml(all, row.skills || []);
|
|
298
|
+
render();
|
|
299
|
+
} catch (err) { statusLine(`Could not load this project’s skills — ${err.message}`); }
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// The create form's picker fills the first time it is opened, so a builder
|
|
303
|
+
// who never offers a skill never pays for the list.
|
|
304
|
+
async function fillCreateOffer(host) {
|
|
305
|
+
if (host.dataset.filled) return;
|
|
306
|
+
host.dataset.filled = '1';
|
|
307
|
+
try {
|
|
308
|
+
host.innerHTML = offerChoicesHtml(await installedSkillList(), []);
|
|
309
|
+
} catch (err) {
|
|
310
|
+
delete host.dataset.filled;
|
|
311
|
+
host.innerHTML = `<p class="prow__hint">Could not load this project’s skills — ${escapeHtml(err.message)}</p>`;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
214
315
|
function cardHtml(s) {
|
|
215
316
|
const trained = Number(s.documentCount || 0);
|
|
216
317
|
const learned = Number(s.learningCount || 0);
|
|
217
318
|
const open = String(state.openId) === String(s.id);
|
|
218
319
|
const offered = Array.isArray(s.skills) ? s.skills.length : 0;
|
|
219
320
|
const walking = state.walk && state.walk.id === String(s.id);
|
|
321
|
+
const offering = s.mine && String(state.offerId) === String(s.id);
|
|
220
322
|
return `
|
|
221
323
|
<li class="prow speciality-card" data-id="${escapeHtml(String(s.id))}">
|
|
222
324
|
<div class="prow__main">
|
|
@@ -237,11 +339,13 @@
|
|
|
237
339
|
<div class="prow__actions">
|
|
238
340
|
${s.active && offered && !walking ? `<button type="button" class="btn btn--quiet" data-act="walk" data-id="${escapeHtml(String(s.id))}">Choose its skills</button>` : ''}
|
|
239
341
|
${s.mine ? `<button type="button" class="btn btn--quiet" data-act="train" data-id="${escapeHtml(String(s.id))}">${open ? 'Close' : 'Train'}</button>` : ''}
|
|
342
|
+
${s.mine ? `<button type="button" class="btn btn--quiet" data-act="offer" data-id="${escapeHtml(String(s.id))}" aria-expanded="${offering}">${offering ? 'Close' : 'Offered skills'}</button>` : ''}
|
|
240
343
|
${s.active
|
|
241
344
|
? `<button type="button" class="btn btn--quiet" data-act="abandon" data-id="${escapeHtml(String(s.id))}">Stop using</button>`
|
|
242
345
|
: `<button type="button" class="btn" data-act="adopt" data-id="${escapeHtml(String(s.id))}" ${s.adoptable ? '' : 'disabled'}>Use this</button>`}
|
|
243
346
|
</div>
|
|
244
347
|
${open ? trainHtml(s) : ''}
|
|
348
|
+
${offering ? offerFormHtml(s) : ''}
|
|
245
349
|
${walking ? walkHtml() : ''}
|
|
246
350
|
</li>`;
|
|
247
351
|
}
|
|
@@ -388,11 +492,28 @@
|
|
|
388
492
|
if (!btn) return;
|
|
389
493
|
const { act: action, id } = btn.dataset;
|
|
390
494
|
if (action === 'walk') { openWalk(id); return; }
|
|
391
|
-
if (action === '
|
|
495
|
+
if (action === 'offer') { openOffer(id); return; }
|
|
496
|
+
if (action === 'train') { state.openId = String(state.openId) === String(id) ? null : id; state.walk = null; state.offerId = null; render(); return; }
|
|
392
497
|
act(action, id);
|
|
393
498
|
});
|
|
394
499
|
|
|
500
|
+
host.addEventListener('input', (e) => {
|
|
501
|
+
if (e.target.matches('.speciality-offer__filter')) narrowOffer(e.target);
|
|
502
|
+
});
|
|
503
|
+
|
|
395
504
|
host.addEventListener('submit', async (e) => {
|
|
505
|
+
const offer = e.target.closest('form.speciality-offer');
|
|
506
|
+
if (offer) {
|
|
507
|
+
e.preventDefault();
|
|
508
|
+
const skills = chosenSkills(offer);
|
|
509
|
+
try {
|
|
510
|
+
await call('PATCH', `/specialities/${offer.dataset.id}`, { skills });
|
|
511
|
+
state.offerId = null;
|
|
512
|
+
toast(skills.length ? `Saved — it offers ${skills.length} skill${skills.length === 1 ? '' : 's'}.` : 'Saved — it offers no skills.');
|
|
513
|
+
await load();
|
|
514
|
+
} catch (err) { statusLine(`Could not save what it offers — ${err.message}`); }
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
396
517
|
const form = e.target.closest('form.speciality-train');
|
|
397
518
|
if (!form) return;
|
|
398
519
|
e.preventDefault();
|
|
@@ -426,8 +547,10 @@
|
|
|
426
547
|
contract_md: create.elements.contract_md.value.trim(),
|
|
427
548
|
visibility: create.elements.visibility.value,
|
|
428
549
|
sellable: create.elements.sellable.checked,
|
|
550
|
+
skills: chosenSkills(create),
|
|
429
551
|
});
|
|
430
552
|
create.reset();
|
|
553
|
+
for (const li of create.querySelectorAll('.speciality-offer__list > li')) li.hidden = false;
|
|
431
554
|
toast('Speciality created.');
|
|
432
555
|
await load();
|
|
433
556
|
} catch (err) { statusLine(err.message); }
|
|
@@ -445,7 +568,21 @@
|
|
|
445
568
|
};
|
|
446
569
|
vis.addEventListener('change', sync);
|
|
447
570
|
sync();
|
|
571
|
+
|
|
572
|
+
// The skills picker (task 1004404): filled on first open, narrowed as you type.
|
|
573
|
+
const offerBox = el('speciality-create-offer');
|
|
574
|
+
const offerHost = el('speciality-create-skills');
|
|
575
|
+
if (offerBox && offerHost) offerBox.addEventListener('toggle', () => { if (offerBox.open) fillCreateOffer(offerHost); });
|
|
576
|
+
create.addEventListener('input', (e) => {
|
|
577
|
+
if (e.target.matches('.speciality-offer__filter')) narrowOffer(e.target);
|
|
578
|
+
});
|
|
448
579
|
}
|
|
580
|
+
|
|
581
|
+
// Enter in a "Find a skill" box narrows the list; it must not submit the
|
|
582
|
+
// form around it half-chosen.
|
|
583
|
+
document.addEventListener('keydown', (e) => {
|
|
584
|
+
if (e.key === 'Enter' && e.target && e.target.matches && e.target.matches('.speciality-offer__filter')) e.preventDefault();
|
|
585
|
+
});
|
|
449
586
|
}
|
|
450
587
|
|
|
451
588
|
function boot() {
|
|
@@ -199,6 +199,32 @@ select.voices__select { min-width: 140px; }
|
|
|
199
199
|
.speciality-field--check { flex-direction: row; align-items: flex-start; gap: 0.5rem; }
|
|
200
200
|
.speciality-field--check input { margin-top: 0.2rem; }
|
|
201
201
|
.speciality-actions { display: flex; gap: 0.5rem; }
|
|
202
|
+
/* What a speciality OFFERS (task 1004404): the owner's pick-list of this
|
|
203
|
+
instance's skills, each with its plain line. Sixty-odd rows, so the list
|
|
204
|
+
scrolls inside its own box rather than pushing the form's buttons off-screen;
|
|
205
|
+
the reading lines keep the measure. */
|
|
206
|
+
.speciality-field input[type='search'] { width: 100%; box-sizing: border-box; }
|
|
207
|
+
.speciality-offer,
|
|
208
|
+
.speciality-offer-box { flex-basis: 100%; display: flex; flex-direction: column; gap: 0.6rem; }
|
|
209
|
+
.speciality-offer { margin-top: 0.75rem; padding-top: 0.75rem; border-top: 1px solid var(--rule); }
|
|
210
|
+
.speciality-offer > *, .speciality-offer-box > * { max-width: var(--measure); }
|
|
211
|
+
.speciality-offer-box > summary { cursor: pointer; font-size: 0.85rem; min-height: 24px; padding-block: 2px; box-sizing: border-box; }
|
|
212
|
+
.speciality-offer-box > summary small { opacity: 0.7; }
|
|
213
|
+
.speciality-offer__choices, #speciality-create-skills { display: flex; flex-direction: column; gap: 0.6rem; }
|
|
214
|
+
.speciality-offer__list {
|
|
215
|
+
list-style: none;
|
|
216
|
+
margin: 0;
|
|
217
|
+
padding: 0 0.25rem 0 0;
|
|
218
|
+
display: flex;
|
|
219
|
+
flex-direction: column;
|
|
220
|
+
gap: 0.5rem;
|
|
221
|
+
max-height: 24rem;
|
|
222
|
+
overflow-y: auto;
|
|
223
|
+
overflow-x: hidden;
|
|
224
|
+
}
|
|
225
|
+
.speciality-offer__list li[hidden] { display: none; }
|
|
226
|
+
.speciality-offer__list strong { font-family: var(--font-mono); font-weight: 600; }
|
|
227
|
+
.speciality-offer__list .speciality-field--check > span { font-size: 0.85rem; line-height: 1.45; }
|
|
202
228
|
/* The adopted contract, shown in full before "Use this" — another builder's prose
|
|
203
229
|
reaching your sessions is the one thing here that must never be folded away. */
|
|
204
230
|
.speciality-contract { flex-basis: 100%; margin-top: 0.5rem; font-size: 0.85rem; }
|
|
@@ -259,6 +259,10 @@
|
|
|
259
259
|
<small>Records your decision only — there is no marketplace yet, and nothing is listed anywhere.</small>
|
|
260
260
|
</span>
|
|
261
261
|
</label>
|
|
262
|
+
<details id="speciality-create-offer" class="speciality-offer-box">
|
|
263
|
+
<summary>Skills it offers <small>(optional)</small></summary>
|
|
264
|
+
<div id="speciality-create-skills"><p class="prow__hint">Loading this project’s skills…</p></div>
|
|
265
|
+
</details>
|
|
262
266
|
<div class="speciality-actions">
|
|
263
267
|
<button type="submit" class="btn">Create speciality</button>
|
|
264
268
|
</div>
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"access": { "auth": true, "url": "/settings#access", "actions": [["wait", 900]], "expect": { "visible": ["#cli-reissue-btn", "#sessions-list", "#artkey-card"] } },
|
|
12
12
|
"preferences": { "auth": true, "url": "/settings#preferences", "actions": [["wait", 900]], "expect": { "visible": ["#wander-rows", "#render-rows", "#specialities-rows"] } },
|
|
13
13
|
"skills-walkthrough": { "auth": true, "url": "/settings#preferences", "actions": [["wait", 900], ["click", "#specialities-rows button[data-act=\"adopt\"]"], ["wait", 700]], "expect": { "visible": [".skill-walk", ".skill-walk__facts", "button[data-walk=\"on\"]", "button[data-walk=\"off\"]"] } },
|
|
14
|
+
"offered-skills": { "auth": true, "url": "/settings#preferences", "actions": [["wait", 900], ["click", "#specialities-rows button[data-act=\"offer\"]"], ["wait", 700]], "expect": { "visible": ["form.speciality-offer", ".speciality-offer__list", ".speciality-offer__filter"] } },
|
|
14
15
|
"sound": { "auth": true, "url": "/settings#sound", "actions": [["wait", 900]], "expect": { "visible": ["#voices-table", "#sound-rows", "#event-sound-rows"] } },
|
|
15
16
|
"update": { "auth": true, "url": "/settings#software-update", "actions": [["wait", 1200]], "expect": { "visible": ["#software-update-h", "#su-body", "#su-update", ".su-versions"] } }
|
|
16
17
|
},
|
|
@@ -28,3 +28,5 @@ Also fixed here, as the board task handed forward: the two sub-24px links on Hom
|
|
|
28
28
|
- **Kit:** `settings.states.json` `update`, ALL CLEAN at 1440/390/320 × dark/light against the harness with the fixture `software-update.json` (a live reading for 1.19.1033). Tests: `tests/software_update.mjs`.
|
|
29
29
|
|
|
30
30
|
**Settings: adopting a speciality walks through its skills (task 1004073, ADR 0310 §3)** — the owner ruled an adopted speciality's skills start OFF "and you should walk through what comes with each". So adopting one that offers skills opens `.skill-walk` inside its card: one skill per step (what it does, when to reach for it, what it costs, what it needs, who can run it), "Leave it off" / "Turn it on" at EQUAL weight (plain `.pbtn`, no accent — the default is off, so the page must not lean toward on), Back, then a recap restating each skill in a line, then ONE `PUT` of the whole set. "Finish later" saves nothing. Focus moves to each new step heading. Two things found on the way: the specialities panel shipped `hidden` and nothing ever revealed it, so adoption was unreachable from the hall; and its contract `<summary>` measured 21px once visible (now 24). **Kit:** `settings.states.json` `skills-walkthrough` + `preferences`, ALL CLEAN at 1440/390/320 × dark/light against the harness, with fixtures `specialities*.json` built from the real skills on disk and canned answers for adopt and save. Tests: `tests/speciality_walkthrough_ui.mjs` (runs the real page script).
|
|
31
|
+
|
|
32
|
+
**Settings: a speciality's owner chooses what it offers (task 1004404, ADR 0310 §1)** — until this only the API could set an offer, so the walkthrough above never appeared for a speciality made in the hall. The create form gains a `<details>` "Skills it offers" picker (filled on first open, so a builder who never offers one never pays for the list), and an owned card gains "Offered skills", an inline `form.speciality-offer` like Train. Both list `GET /specialities/installed-skills` — only what this instance has — each row a checkbox with the skill's plain line and cost, the words adopters will see, under a line saying offering turns nothing on. A "Find a skill" box narrows the sixty-odd rows (Enter never submits), the list scrolls in its own box, an offered skill removed since is named and dropped on save rather than silently kept, and saving is ONE `PATCH { skills }`. **The checkboxes take the shared 24px rule in `panel.css`** (the `.chip-ctl input` / `.craft-pref` block, one selector added) rather than a fourth copy of the `::before` box; without it 128 targets measured 13px. **Kit:** `settings.states.json` `offered-skills` + `preferences`, ALL CLEAN at 1440/390/320 × dark/light, with fixtures `specialities__installed-skills.json` (the real 64 skills, explained) and an owned speciality in `specialities.json`, plus canned POST/PATCH answers. Tests: `tests/speciality_offer_ui.mjs` (runs the real page script).
|
|
@@ -285,6 +285,22 @@ module.exports = function buildSpecialitiesRouter() {
|
|
|
285
285
|
res.json({ suggestions });
|
|
286
286
|
}));
|
|
287
287
|
|
|
288
|
+
// GET /specialities/installed-skills — every skill THIS instance has, each
|
|
289
|
+
// explained the way the adoption walkthrough explains it, so a speciality's
|
|
290
|
+
// owner can choose what it offers from the hall (task 1004404). The CHOICES
|
|
291
|
+
// only: the POST/PATCH `skills` check (unknown_skill) is still the rule, so a
|
|
292
|
+
// stale page that sends a name removed since is refused, not trusted. Nothing
|
|
293
|
+
// here is per-builder and the list is public knowledge of the install — every
|
|
294
|
+
// name is already materialised into each checkout's .claude/skills/.
|
|
295
|
+
// Mounted before '/specialities/:id' would capture it as an id. Read-only;
|
|
296
|
+
// rank: any builder.
|
|
297
|
+
router.get('/specialities/installed-skills', api.requireBuilder, asyncHandler('GET /specialities/installed-skills', async (_req, res) => {
|
|
298
|
+
const skills = [...installedSkills()]
|
|
299
|
+
.map(([name, entry]) => skillsLib.explainSkill({ name, ...entry }))
|
|
300
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
301
|
+
res.json({ skills });
|
|
302
|
+
}));
|
|
303
|
+
|
|
288
304
|
router.get('/specialities/:id', api.requireBuilder, asyncHandler('GET /specialities/:id', async (req, res) => {
|
|
289
305
|
const id = parseId(req, res, { code: 'bad_id' });
|
|
290
306
|
if (id === null) return;
|
|
@@ -5,6 +5,12 @@ description: >-
|
|
|
5
5
|
first, holds the logo standard and the five concept methods, and lands the system as the instance's own
|
|
6
6
|
materials — the mark as SVG, the colours as the pack, the type as its faces. Triggers on "brand kit",
|
|
7
7
|
"identity system", "logo system". Never generates a logo, wordmark or UI mockup.
|
|
8
|
+
plain: >-
|
|
9
|
+
Builds the project's visual identity, its logo, colours and typefaces, as the project's own files.
|
|
10
|
+
reach-for: >-
|
|
11
|
+
When the project needs a brand identity, or its identity needs pulling together.
|
|
12
|
+
cost: >-
|
|
13
|
+
Uses your session. It adds brand files to the project and never generates a logo image.
|
|
8
14
|
---
|
|
9
15
|
|
|
10
16
|
<!-- 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 -->
|
|
@@ -5,6 +5,12 @@ description: >-
|
|
|
5
5
|
Says the design read in one line, sets three dials (variance, motion, density), locks one world for the
|
|
6
6
|
whole page, and ends on a pre-flight the kit measures. Triggers on "design a landing page", "make this not
|
|
7
7
|
look templated", "taste pass", "anti-slop". Not for dashboards or native mobile.
|
|
8
|
+
plain: >-
|
|
9
|
+
The default way to give a landing page or marketing page a strong, considered look that does not feel like a template.
|
|
10
|
+
reach-for: >-
|
|
11
|
+
When a landing or marketing page looks generic.
|
|
12
|
+
cost: >-
|
|
13
|
+
Uses your session. It changes the page, and you check it on a preview.
|
|
8
14
|
---
|
|
9
15
|
|
|
10
16
|
<!-- 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 -->
|
|
@@ -5,6 +5,12 @@ description: >-
|
|
|
5
5
|
gets exactly this behaviour while the default (design-taste-frontend) evolves separately. A fixed triple of
|
|
6
6
|
dials, the layout bans, full interaction cycles, and a named vocabulary of premium patterns. Triggers on
|
|
7
7
|
"design-taste-frontend-v1", "the fixed dials", "the pattern vocabulary".
|
|
8
|
+
plain: >-
|
|
9
|
+
The earlier version of the landing-page style guide, kept so work that asked for it gets exactly the same results.
|
|
10
|
+
reach-for: >-
|
|
11
|
+
Only when a task asks for this older version by name.
|
|
12
|
+
cost: >-
|
|
13
|
+
Uses your session. It changes the page, and you check it on a preview.
|
|
8
14
|
---
|
|
9
15
|
|
|
10
16
|
<!-- 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 -->
|
|
@@ -5,6 +5,12 @@ description: >-
|
|
|
5
5
|
every other one. A written plan block before any markup, derived deterministically from the route; the
|
|
6
6
|
four-stage attention-interest-desire-action structure; an ultra-wide hero; a dense tile grid with zero empty
|
|
7
7
|
cells. Triggers on "it looks generic", "award-tier", "anti-default".
|
|
8
|
+
plain: >-
|
|
9
|
+
A stricter style pass for a long, persuasive page that keeps coming out looking like every other one.
|
|
10
|
+
reach-for: >-
|
|
11
|
+
When a scrolling sales or landing page still looks generic after a first try.
|
|
12
|
+
cost: >-
|
|
13
|
+
Uses your session. It changes the page, and you check it on a preview.
|
|
8
14
|
---
|
|
9
15
|
|
|
10
16
|
<!-- 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 -->
|
|
@@ -5,6 +5,12 @@ description: >-
|
|
|
5
5
|
one layout archetype per project, the container anatomy, the pill button, the spatial rhythm, the motion
|
|
6
6
|
choreography inside the Kill Switch, the mobile collapse, then a pre-output checklist. Triggers on "make it
|
|
7
7
|
feel premium", "agency-grade", "this looks cheap", "the polish pass".
|
|
8
|
+
plain: >-
|
|
9
|
+
Makes a page look expensive and professionally made, while staying in the project's own style.
|
|
10
|
+
reach-for: >-
|
|
11
|
+
When a page looks cheap or unfinished.
|
|
12
|
+
cost: >-
|
|
13
|
+
Uses your session. It changes the page, and you check it on a preview.
|
|
8
14
|
---
|
|
9
15
|
|
|
10
16
|
<!-- 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 -->
|
|
@@ -5,6 +5,12 @@ description: >-
|
|
|
5
5
|
design source of truth is the owner's reference images and the instance's world, read per image and
|
|
6
6
|
extracted into a full spec before a line is written. Triggers on "build this from the reference", "implement
|
|
7
7
|
the mock faithfully". Never generates a section, comp or screen.
|
|
8
|
+
plain: >-
|
|
9
|
+
Builds a page faithfully from reference pictures you provide, studying each one first.
|
|
10
|
+
reach-for: >-
|
|
11
|
+
When you have images of how a page should look and want it built to match.
|
|
12
|
+
cost: >-
|
|
13
|
+
Uses your session. It builds the page and never generates images itself.
|
|
8
14
|
---
|
|
9
15
|
|
|
10
16
|
<!-- 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 -->
|
|
@@ -5,6 +5,12 @@ description: >-
|
|
|
5
5
|
portrait plates and hero objects for the 390 and 320 compositions the kit renders, sized for the fold and
|
|
6
6
|
clear of what a phone reserves, with a sidecar beside every file. Triggers on "a plate for the mobile hero",
|
|
7
7
|
"the masthead at 390". Never a screen, a phone mockup or code.
|
|
8
|
+
plain: >-
|
|
9
|
+
Makes the large picture at the top of a page, sized for phones, with no text in it.
|
|
10
|
+
reach-for: >-
|
|
11
|
+
When a page's phone layout needs its main picture.
|
|
12
|
+
cost: >-
|
|
13
|
+
Paid: each image is made on the project's image key. It adds the picture files to the project.
|
|
8
14
|
---
|
|
9
15
|
|
|
10
16
|
<!-- 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 -->
|
|
@@ -5,6 +5,12 @@ description: >-
|
|
|
5
5
|
objects and atmosphere backdrops for the slots a composition already has — never one per section, never a
|
|
6
6
|
screen, never a UI comp, and no page code. One material, light and camera across the family, landed with a
|
|
7
7
|
provenance sidecar. Triggers on "make a hero plate", "an atmosphere backdrop".
|
|
8
|
+
plain: >-
|
|
9
|
+
Makes the large background or hero pictures for a web page, with no text in them.
|
|
10
|
+
reach-for: >-
|
|
11
|
+
When a page needs its main picture or a background image.
|
|
12
|
+
cost: >-
|
|
13
|
+
Paid: each image is made on the project's image key. It adds the picture files to the project.
|
|
8
14
|
---
|
|
9
15
|
|
|
10
16
|
<!-- 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 -->
|
|
@@ -5,6 +5,12 @@ description: >-
|
|
|
5
5
|
("impeccable audit /projects"). Sixteen playbooks — shape, audit, critique, polish, harden, animate,
|
|
6
6
|
typeset, colorize, layout, adapt, distill, clarify, onboard, optimize, document, extract — one per request.
|
|
7
7
|
Triggers on "impeccable", "critique this page", "audit the UI". Web surfaces only.
|
|
8
|
+
plain: >-
|
|
9
|
+
A toolbox of design checks and fixes for a web page, such as a review, a polish, better type or better layout, one job at a time.
|
|
10
|
+
reach-for: >-
|
|
11
|
+
When you want a page reviewed or improved in one specific way.
|
|
12
|
+
cost: >-
|
|
13
|
+
Uses your session. A review changes nothing; a fix changes the page.
|
|
8
14
|
---
|
|
9
15
|
|
|
10
16
|
<!-- 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 -->
|
|
@@ -5,6 +5,12 @@ description: >-
|
|
|
5
5
|
per project (Swiss industrial print or dark tactical terminal, never mixed), massive uppercase display
|
|
6
6
|
against tracked-out mono metadata, strict grid anchoring, zero corner rounding, halftone and scanline
|
|
7
7
|
degradation inside the Kill Switch. Triggers on "brutalist", "industrial", "make it raw".
|
|
8
|
+
plain: >-
|
|
9
|
+
Gives a page a raw, mechanical look, like a technical document or a control terminal.
|
|
10
|
+
reach-for: >-
|
|
11
|
+
When you want a bold, stark look.
|
|
12
|
+
cost: >-
|
|
13
|
+
Uses your session. It changes the page, and you check it on a preview.
|
|
8
14
|
---
|
|
9
15
|
|
|
10
16
|
<!-- 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 -->
|
|
@@ -5,6 +5,12 @@ description: >-
|
|
|
5
5
|
whitespace first, a capped measure, one display job and one body job, exactly one hairline weight for every
|
|
6
6
|
border, flat tile cards with no shadow, entry motion inside the Kill Switch. Triggers on "make it minimal",
|
|
7
7
|
"clean and editorial", "this feels busy". Declines dense surfaces.
|
|
8
|
+
plain: >-
|
|
9
|
+
Gives a page a calm, clean look built from text and white space.
|
|
10
|
+
reach-for: >-
|
|
11
|
+
When a page feels too busy.
|
|
12
|
+
cost: >-
|
|
13
|
+
Uses your session. It changes the page, and you check it on a preview.
|
|
8
14
|
---
|
|
9
15
|
|
|
10
16
|
<!-- 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 -->
|
|
@@ -5,6 +5,12 @@ description: >-
|
|
|
5
5
|
breaking it. Three fixed steps — scan the stack, diagnose against a written audit, apply targeted fixes —
|
|
6
6
|
with the instance's world as the answer key and a fixed impact-per-risk fix order. Triggers on "redesign
|
|
7
7
|
this", "upgrade the existing page", "audit this UI".
|
|
8
|
+
plain: >-
|
|
9
|
+
Improves the look of an existing site in place, without rebuilding it or breaking what works.
|
|
10
|
+
reach-for: >-
|
|
11
|
+
When an existing page or app needs a visual upgrade.
|
|
12
|
+
cost: >-
|
|
13
|
+
Uses your session. It changes the existing pages, and you check them on a preview.
|
|
8
14
|
---
|
|
9
15
|
|
|
10
16
|
<!-- 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 -->
|
|
@@ -5,6 +5,12 @@ description: >-
|
|
|
5
5
|
platform's own frontmatter shape. Four dials with named bands and stated defaults, then eight fixed sections
|
|
6
6
|
pairing descriptive language with exact values. The thirteen colours are transcribed from the instance's
|
|
7
7
|
pack, never invented. Triggers on "a design brief for Stitch".
|
|
8
|
+
plain: >-
|
|
9
|
+
Writes a design brief in the project's style for an outside screen-design tool such as Google Stitch.
|
|
10
|
+
reach-for: >-
|
|
11
|
+
When you want to design screens in Stitch using the project's look.
|
|
12
|
+
cost: >-
|
|
13
|
+
Uses your session. It writes one brief document and changes no pages.
|
|
8
14
|
---
|
|
9
15
|
|
|
10
16
|
<!-- 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 -->
|
|
@@ -5,6 +5,12 @@ description: >-
|
|
|
5
5
|
the fifteen-token contract, and proves it clean before it lands. Four steps and no more — SEARCH, CREATE,
|
|
6
6
|
PROVE, ADOPT. Triggers on "/style", "make a look", "author a style", "I want our own theme", "add a style to
|
|
7
7
|
the library".
|
|
8
|
+
plain: >-
|
|
9
|
+
A short guided session for creating your own look for the project, checked before it is used.
|
|
10
|
+
reach-for: >-
|
|
11
|
+
When you want the project to have its own theme.
|
|
12
|
+
cost: >-
|
|
13
|
+
Uses your session. The new look is only used once you choose to adopt it.
|
|
8
14
|
---
|
|
9
15
|
|
|
10
16
|
<!-- 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.20.
|
|
3
|
+
"version": "1.20.11",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.20.
|
|
9
|
+
"version": "1.20.11",
|
|
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.20.
|
|
3
|
+
"version": "1.20.11",
|
|
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",
|
package/release-notes.json
CHANGED
|
@@ -8106,5 +8106,17 @@
|
|
|
8106
8106
|
"id": "1003268",
|
|
8107
8107
|
"text": "Added three database lookups so the 'who have I worked near' view stays fast as the project grows. Nothing changes on screen today."
|
|
8108
8108
|
}
|
|
8109
|
+
],
|
|
8110
|
+
"1.20.10": [
|
|
8111
|
+
{
|
|
8112
|
+
"id": "1004403",
|
|
8113
|
+
"text": "When you adopt a speciality, each skill it offers is now explained in plain words: what it does, when you would use it, and what it costs, including which ones spend money. Before, those explanations were guessed from technica"
|
|
8114
|
+
}
|
|
8115
|
+
],
|
|
8116
|
+
"1.20.11": [
|
|
8117
|
+
{
|
|
8118
|
+
"id": "1004404",
|
|
8119
|
+
"text": "You can now choose which skills your speciality offers from Settings, not just through the API. You can do it when you create the speciality or later from its card. Each skill is listed with a plain description and what it cos"
|
|
8120
|
+
}
|
|
8109
8121
|
]
|
|
8110
8122
|
}
|
|
@@ -164,6 +164,10 @@ const CANNED_WRITES = [
|
|
|
164
164
|
// opens the skills walkthrough and its recap can save in the harness.
|
|
165
165
|
['POST', /^specialities\/\d+\/adopt$/, 'specialities__9__adopt.post'],
|
|
166
166
|
['PUT', /^specialities\/\d+\/skills$/, 'specialities__9__skills.put'],
|
|
167
|
+
// the owner choosing what a speciality offers (task 1004404): on create, and
|
|
168
|
+
// afterwards from the card's "Offered skills" editor.
|
|
169
|
+
['POST', /^specialities$/, 'specialities.post'],
|
|
170
|
+
['PATCH', /^specialities\/\d+$/, 'specialities__12.patch'],
|
|
167
171
|
];
|
|
168
172
|
|
|
169
173
|
// A DATE THAT ROTS CANNOT DEMO AN AGE SIGNAL (task 1004041). The help-request
|
package/src/module-api.js
CHANGED
|
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
75
75
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
76
76
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
77
77
|
|
|
78
|
-
const CORE_VERSION = '1.20.
|
|
78
|
+
const CORE_VERSION = '1.20.11'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
79
79
|
|
|
80
80
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
81
81
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|