@bongos/core 1.19.460 → 1.19.461
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 +45 -20
- package/.claude/skills/design/SKILL.md +10 -3
- package/docs/adr/0220-an-in-house-rebuilt-skill-is-a-first-party-skill.md +46 -0
- package/docs/adr/README.md +1 -0
- package/docs/file-map.md +1 -1
- package/docs/module-api-changelog.md +2 -0
- package/modules/ui-design/module.json +1 -1
- package/modules/ui-design/skills/PREAMBLE.md +3 -3
- package/modules/ui-design/skills/README.md +13 -0
- package/modules/ui-design/skills/design-taste-frontend/SKILL.md +136 -0
- package/modules/ui-design/skills/design-taste-frontend-v1/SKILL.md +125 -0
- package/modules/ui-design/skills/high-end-visual-design/SKILL.md +128 -0
- package/modules/ui-design/skills/redesign-existing-projects/SKILL.md +120 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/docs-entropy.js +5 -1
- package/src/module-api.js +1 -1
- package/tests/docs_entropy.mjs +29 -1
- package/tests/ui_design_skills.mjs +99 -9
package/.bongos-core.json
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.19.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.461",
|
|
6
|
+
"core_contract": "1.19.461",
|
|
7
|
+
"source_commit": "c90f2d3b84966cebe03459eff83dddb8f458c871",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-01T23:
|
|
9
|
+
"built_at": "2026-09-01T23:39:44.285Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 380,
|
|
13
13
|
"agent_docs_stubbed": 24,
|
|
14
14
|
"functional_verbatim": 1816,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2220,
|
|
20
|
+
"tree_sha256": "4c595d2e8e84883925a96367ebdcb500c59ca83987e4b0ef21fc52bfcf20ef08",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
{
|
|
128
128
|
"path": ".claude/skills/design/SKILL.md",
|
|
129
129
|
"mode": "0000644",
|
|
130
|
-
"sha256": "
|
|
130
|
+
"sha256": "ced60ba6ce3e6b8295e3fa9986234f6bb9be8cd4a941ce801af47591928cb270"
|
|
131
131
|
},
|
|
132
132
|
{
|
|
133
133
|
"path": ".claude/skills/dev/SKILL.md",
|
|
@@ -1604,10 +1604,15 @@
|
|
|
1604
1604
|
"mode": "0000644",
|
|
1605
1605
|
"sha256": "9ae416852dc8112a2cea4837e4a25b29338dbddecee0903830bcf5132645274d"
|
|
1606
1606
|
},
|
|
1607
|
+
{
|
|
1608
|
+
"path": "docs/adr/0220-an-in-house-rebuilt-skill-is-a-first-party-skill.md",
|
|
1609
|
+
"mode": "0000644",
|
|
1610
|
+
"sha256": "8e121001f8ff3f854c5efe461aabb17dc4f81e49ca262b2cadf4717c49b11bb0"
|
|
1611
|
+
},
|
|
1607
1612
|
{
|
|
1608
1613
|
"path": "docs/adr/README.md",
|
|
1609
1614
|
"mode": "0000644",
|
|
1610
|
-
"sha256": "
|
|
1615
|
+
"sha256": "441c2773abced552c433f5c54a52fd4924e674c304360c9c3c6d962fedb33fc3"
|
|
1611
1616
|
},
|
|
1612
1617
|
{
|
|
1613
1618
|
"path": "docs/api-reference.md",
|
|
@@ -2087,7 +2092,7 @@
|
|
|
2087
2092
|
{
|
|
2088
2093
|
"path": "docs/file-map.md",
|
|
2089
2094
|
"mode": "0000644",
|
|
2090
|
-
"sha256": "
|
|
2095
|
+
"sha256": "2164f82effe472df69ccf7c05787060ea6d108c4dfa3e74064a6f23623eb018b"
|
|
2091
2096
|
},
|
|
2092
2097
|
{
|
|
2093
2098
|
"path": "docs/handoff-template.md",
|
|
@@ -2097,7 +2102,7 @@
|
|
|
2097
2102
|
{
|
|
2098
2103
|
"path": "docs/module-api-changelog.md",
|
|
2099
2104
|
"mode": "0000644",
|
|
2100
|
-
"sha256": "
|
|
2105
|
+
"sha256": "0ebc5de06d0b5454d8509fbd60ad81f7107414610a1afb8cc363c3cf7a0473be"
|
|
2101
2106
|
},
|
|
2102
2107
|
{
|
|
2103
2108
|
"path": "docs/modules-contract.md",
|
|
@@ -6337,7 +6342,7 @@
|
|
|
6337
6342
|
{
|
|
6338
6343
|
"path": "modules/ui-design/module.json",
|
|
6339
6344
|
"mode": "0000644",
|
|
6340
|
-
"sha256": "
|
|
6345
|
+
"sha256": "31e80de5f689a2bdaf5eb0ebbb31cc96de99b2e1fe31b0eba9d7f9515d07f0a5"
|
|
6341
6346
|
},
|
|
6342
6347
|
{
|
|
6343
6348
|
"path": "modules/ui-design/scripts/design-sync.js",
|
|
@@ -6357,18 +6362,38 @@
|
|
|
6357
6362
|
{
|
|
6358
6363
|
"path": "modules/ui-design/skills/PREAMBLE.md",
|
|
6359
6364
|
"mode": "0000644",
|
|
6360
|
-
"sha256": "
|
|
6365
|
+
"sha256": "ecb2539b28bb7dfce1554d6b2416afc91cb1d548c9aca6cd5f7e86e0cd1c7549"
|
|
6361
6366
|
},
|
|
6362
6367
|
{
|
|
6363
6368
|
"path": "modules/ui-design/skills/README.md",
|
|
6364
6369
|
"mode": "0000644",
|
|
6365
|
-
"sha256": "
|
|
6370
|
+
"sha256": "018332aa1b1dae06abfecdd96b64cdd0f99132be1c925b2a77484e69865c83f3"
|
|
6371
|
+
},
|
|
6372
|
+
{
|
|
6373
|
+
"path": "modules/ui-design/skills/design-taste-frontend-v1/SKILL.md",
|
|
6374
|
+
"mode": "0000644",
|
|
6375
|
+
"sha256": "5e461056c3e6fa4775cc6734b55bf6d5c050f01e0ee7a52378a9f4ab5501da28"
|
|
6376
|
+
},
|
|
6377
|
+
{
|
|
6378
|
+
"path": "modules/ui-design/skills/design-taste-frontend/SKILL.md",
|
|
6379
|
+
"mode": "0000644",
|
|
6380
|
+
"sha256": "ee07bdde8e388b2863431673390d4bff74ee1296920ebcb07b529060eb7f8959"
|
|
6381
|
+
},
|
|
6382
|
+
{
|
|
6383
|
+
"path": "modules/ui-design/skills/high-end-visual-design/SKILL.md",
|
|
6384
|
+
"mode": "0000644",
|
|
6385
|
+
"sha256": "eeb33e0a2d0a227d23b67c5e6d32c5757d8d5d7249a1a6d0fe3c67d8bd95fc91"
|
|
6366
6386
|
},
|
|
6367
6387
|
{
|
|
6368
6388
|
"path": "modules/ui-design/skills/policy.json",
|
|
6369
6389
|
"mode": "0000644",
|
|
6370
6390
|
"sha256": "f73cb4ce230ffbd940a4aa00742f5d09e736a8f70402c155d1933618e70392d7"
|
|
6371
6391
|
},
|
|
6392
|
+
{
|
|
6393
|
+
"path": "modules/ui-design/skills/redesign-existing-projects/SKILL.md",
|
|
6394
|
+
"mode": "0000644",
|
|
6395
|
+
"sha256": "22e8f7ea0d206b3f4b2243b986a4dcd10df4ba0198aa063e76ef4d2788fec55d"
|
|
6396
|
+
},
|
|
6372
6397
|
{
|
|
6373
6398
|
"path": "modules/ui-design/styles/README.md",
|
|
6374
6399
|
"mode": "0000644",
|
|
@@ -6437,12 +6462,12 @@
|
|
|
6437
6462
|
{
|
|
6438
6463
|
"path": "package-lock.json",
|
|
6439
6464
|
"mode": "0000644",
|
|
6440
|
-
"sha256": "
|
|
6465
|
+
"sha256": "3ca799f0609f1ad2e4bfea78e7bba57f976b6df4df4cefad5e0229e3f5e2a8ab"
|
|
6441
6466
|
},
|
|
6442
6467
|
{
|
|
6443
6468
|
"path": "package.json",
|
|
6444
6469
|
"mode": "0000644",
|
|
6445
|
-
"sha256": "
|
|
6470
|
+
"sha256": "8ea4badf28d81545fe9d546df94c49da9980c78650f8a7fdc13a8b907168787c"
|
|
6446
6471
|
},
|
|
6447
6472
|
{
|
|
6448
6473
|
"path": "public-docs/index.html",
|
|
@@ -6817,7 +6842,7 @@
|
|
|
6817
6842
|
{
|
|
6818
6843
|
"path": "scripts/gds/docs-entropy.js",
|
|
6819
6844
|
"mode": "0000644",
|
|
6820
|
-
"sha256": "
|
|
6845
|
+
"sha256": "a4357b36e7e005895021f55db27f83d40e7fded3534ed9ee3d50162945e2cea7"
|
|
6821
6846
|
},
|
|
6822
6847
|
{
|
|
6823
6848
|
"path": "scripts/gds/doctor.js",
|
|
@@ -8092,7 +8117,7 @@
|
|
|
8092
8117
|
{
|
|
8093
8118
|
"path": "src/module-api.js",
|
|
8094
8119
|
"mode": "0000644",
|
|
8095
|
-
"sha256": "
|
|
8120
|
+
"sha256": "2f2268d16d20bc3ae5c531c9bab8faae7a323a0517c4b9692dc7d10fd3fd5d26"
|
|
8096
8121
|
},
|
|
8097
8122
|
{
|
|
8098
8123
|
"path": "src/module-loader/catalog.js",
|
|
@@ -8952,7 +8977,7 @@
|
|
|
8952
8977
|
{
|
|
8953
8978
|
"path": "tests/docs_entropy.mjs",
|
|
8954
8979
|
"mode": "0000644",
|
|
8955
|
-
"sha256": "
|
|
8980
|
+
"sha256": "0dda5513bb550dbcf24c8016e01db07100eefec3abe14fc65d5bc06a1e57e026"
|
|
8956
8981
|
},
|
|
8957
8982
|
{
|
|
8958
8983
|
"path": "tests/docs_openapi_route.mjs",
|
|
@@ -11007,7 +11032,7 @@
|
|
|
11007
11032
|
{
|
|
11008
11033
|
"path": "tests/ui_design_skills.mjs",
|
|
11009
11034
|
"mode": "0000644",
|
|
11010
|
-
"sha256": "
|
|
11035
|
+
"sha256": "73a4c7e6b29f2703b866f3241a459fd0c4a57b7b606f0058905306b624134b1b"
|
|
11011
11036
|
},
|
|
11012
11037
|
{
|
|
11013
11038
|
"path": "tests/ui_design_styles.mjs",
|
|
@@ -55,11 +55,18 @@ Generated code uses the `--dt-*` variables; adapters write only `src/ui/<surface
|
|
|
55
55
|
- **Figma** (`/figma-design-sync`): push the current state so a designer never opens a stale file; land their edit back as committed code. Canvas art that won't translate becomes an asset, never forced into code.
|
|
56
56
|
- Both are adapters against `modules/ui-design/docs/design-contract.md`; a third tool is a third adapter, not a new integration.
|
|
57
57
|
|
|
58
|
-
## The vetted skills (the ui-design module's `skills/`)
|
|
58
|
+
## The vetted skills and the in-house rebuilds (the ui-design module's `skills/`)
|
|
59
59
|
|
|
60
|
-
Third-party design skills reach a builder only through the module's vendoring policy ([`modules/ui-design/skills/README.md`](../../../modules/ui-design/skills/README.md), ADR 0198): scanned with `/scan-before-install` and reduced to `clean` or `caution`, a licence on the allowlist, a `PROVENANCE.md` sidecar, and the PLATFORM PREAMBLE that reads the instance's world before any taste rule. A
|
|
60
|
+
Third-party design skills reach a builder only through the module's vendoring policy ([`modules/ui-design/skills/README.md`](../../../modules/ui-design/skills/README.md), ADR 0198): scanned with `/scan-before-install` and reduced to `clean` or `caution`, a licence on the allowlist, a `PROVENANCE.md` sidecar, and the PLATFORM PREAMBLE that reads the instance's world before any taste rule. A skill the module ships is listed here by name, one line each, whether vendored or rebuilt in-house; a name that is not listed is not the module's, whatever a builder's own `~/.claude/skills` carries.
|
|
61
61
|
|
|
62
|
-
- *None vendored (2026-08-28).* Both origins scanned for the scoped set — `pbakaus/impeccable`, and the eleven `Leonxlnx/taste-skill` skills (`
|
|
62
|
+
- *None vendored (2026-08-28).* Both origins scanned for the scoped set — `pbakaus/impeccable`, and the eleven `Leonxlnx/taste-skill` skills (`minimalist-ui`, `industrial-brutalist-ui`, `gpt-taste`, `stitch-design-taste`, `image-to-code`, `imagegen-frontend-web`, `imagegen-frontend-mobile`, `brandkit`, and the four below) — reduced to `dangerous` at the scanner's deterministic floor and landed as rebuild specs (GDS tasks). The roster, the verdicts and the way to land one are in the README; vendoring one anyway is an owner override recorded in an ADR, not a session's call.
|
|
63
|
+
|
|
64
|
+
**Rebuilt in-house from the specs** ([ADR 0220](../../../docs/adr/<redacted>.md), task 1003325): first-party skills at `modules/ui-design/skills/<name>/`, each opening with the same preamble, each naming its spec and what changed in translation. They run INSIDE the loop above: the world first, the style library as the palette source for a variant, images as hero plates only, every motion rule inside the Kill Switch, the kit as the pre-flight.
|
|
65
|
+
|
|
66
|
+
- **design-taste-frontend** — the default taste skill for a landing page, a portfolio or a marketing surface: the brief read in one line, three dials from presets, one world locked for the page, hero and layout discipline, rationed decoration, a pre-flight the kit measures. Declines dashboards, tables, wizards and editors.
|
|
67
|
+
- **design-taste-frontend-v1** — the earlier generation, kept distinct: a fixed baseline triple adapted in conversation, stack conventions for a served surface, the layout bans, full interaction cycles, the named vocabulary of premium patterns and the tile-grid archetypes.
|
|
68
|
+
- **high-end-visual-design** — the agency-build polish pass: one texture archetype (the library's looks) and one layout archetype per project, the nested container anatomy where the world allows a container, the pill with its nested arrow disc, the spatial rhythm, choreography and scroll entry inside the Kill Switch.
|
|
69
|
+
- **redesign-existing-projects** — the in-place upgrade: scan the stack, diagnose against the seven-group audit with the instance's world as the answer key, fix in impact-per-risk order, one group per commit; keeps the stack, never migrates frameworks, `probe.js` green before and after.
|
|
63
70
|
|
|
64
71
|
**Prerequisite, not vendored:** the **playwright plugin** (`claude plugin install playwright@claude-plugins-official`) is the LIVE browser-driving loop, where the kit in Step 2 is the headless one. Install it on the machine that runs a design session; the module never installs anything.
|
|
65
72
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# ADR 0220 — An in-house rebuilt skill is a first-party skill: it lands at `skills/<name>/`, opens with the same preamble, carries a "Rebuilt from" section instead of a sidecar, and is counted
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted (2026-09-01)
|
|
4
|
+
- **Task:** [task 1003325](https://cloudbongos.com/builders#/task/1003325) (goal 1000083 — the ui-design module as a platform capability; the first of the rebuild-spec tasks [ADR 0198](<redacted>.md) filed)
|
|
5
|
+
- **Owner intent (2026-08-28):** every builder, on every instance, can produce good-looking, functional UIs in line with that instance's world. The taste skills that produced Cloud Bongos's world were to become part of the module; the scan said they could not be copied, so they are rebuilt.
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
ADR 0198 landed both scanned origins as rebuild specs and left the roster empty. Task 1003325 is the first rebuild: the **taste core** — `design-taste-frontend` (the default), `design-taste-frontend-v1` (the preserved earlier generation), `high-end-visual-design` and `redesign-existing-projects` — from the REBUILD-SPEC in the task body, which was derived from the origin's documentation only. Three things the policy did not say:
|
|
10
|
+
|
|
11
|
+
1. **Where a rebuild lives and what it carries.** ADR 0198 §5 said a skill the module *authors* lives at `skills/<name>/` and is counted; it did not say what replaces the `PROVENANCE.md` sidecar (a rebuild has no pinned commit of its own bytes, no upstream licence text, and no scan verdict that applies to it), nor how the materialiser treats a dir with no sidecar.
|
|
12
|
+
2. **Whether the preamble fits.** `PREAMBLE.md` was written for vendored copies: it said "this skill is vendored" and "the rules below are the upstream skill's, unchanged", both false for a rebuild. The test pins every copy byte-identical to the source, so the fix had to be in the source.
|
|
13
|
+
3. **Whether five byte-identical copies are debt.** `docs-entropy.js` counts a paragraph of 120+ characters that appears verbatim in two or more tracked files, and `fitness-ratchets.js` fails the build when that count exceeds its baseline (6). The preamble is one paragraph, intentionally identical in every skill and in `PREAMBLE.md`; four rebuilds would have tripped the ratchet on the policy working as designed.
|
|
14
|
+
|
|
15
|
+
And a fourth, which is the substance: what "inside the world" means when a taste skill written for any site is rebuilt for a platform whose surfaces read only the fifteen-token contract, whose motion must die under a media query, and whose owner confines image generation to hero plates.
|
|
16
|
+
|
|
17
|
+
## Decision
|
|
18
|
+
|
|
19
|
+
1. **Home, declaration, materialisation.** A rebuilt skill is `modules/ui-design/skills/<name>/SKILL.md`, declared in `module.json` `contributes.skills`, and reaches a builder through `claude-materialize.js` step 1b exactly as a vendored one does: `moduleSkillSources` resolves the own dir first and applies `vendoredSkillGate` only to `vendor/`, so a rebuild needs no sidecar to land and cannot be refused by one. It is **first-party code**: no `vendor/` segment, so the line, duplication and doc ratchets count it. A `rebuilt/` segment exempt like `vendor/` was rejected because the whole point is that these are ours.
|
|
20
|
+
2. **One preamble, worded for both origins; provenance in a section, not a sidecar.** `PREAMBLE.md` now says the skill "ships with the module (vendored from a scanned origin, or rebuilt in-house from a spec)" and closes with "a vendored copy carries a `PROVENANCE.md` … an in-house rebuild names its rebuild spec and its ADR in the section right after this block". The block stays byte-identical everywhere (`tests/ui_design_skills.mjs` pins every own and vendored copy against the source). A rebuild's provenance is its **`## Rebuilt from`** section, first after the block: the origin repository and commit the *spec* was derived from (documentation only), the licence, the scan verdict and date, the spec task, the statement that **no upstream bytes were consulted or copied**, this ADR, and **what changed in translation** — so a reader can see where the rebuild deliberately departs from the documented behaviour. No `PROVENANCE.md`, no `LICENSE`: an in-house skill claims no upstream, and the test refuses both on an own dir.
|
|
21
|
+
3. **The translation rules** (what every rebuilt taste skill does with the upstream's opinions):
|
|
22
|
+
- The palette is the pack; **variants come from the style library** (`modules/ui-design/styles/`, ADR 0219), rendered with `--style <name>` on the kit; a look is adopted by the owner through `config/branding.json`, never by a page.
|
|
23
|
+
- The "foundation" or "design system" is the instance's `DESIGN.md`; a third-party design system named in a brief becomes an approximation labelled as such in a comment, and this skill installs nothing — a dependency is a task decision.
|
|
24
|
+
- **Imagery is hero plates and hero objects by token, and never a step.** The upstream's "generate assets first, placeholder photography second, labelled empty slots last" becomes "a slot with no plate is not a slot"; a screenshot faked from divs stays banned.
|
|
25
|
+
- **Every motion rule lives inside the Kill Switch.** Springs, magnetic hover, shared-element transitions and pointer-tracked borders need script and are named **not available** rather than kept; staggers come from an `--i` custom property in the markup; scroll entry prefers `animation-timeline` behind `@supports` and otherwise one observer-added class over a rest frame that is complete without it.
|
|
26
|
+
- The upstream's bans (the long dash, decorative labels, three equal cards, the purple-blue gradient) are **defaults an instance's `DESIGN.md` overrides**; where the world calls for something the audit would flag (the chrome world's pure-black dark, its flat hairline bands), **the world is the answer key** and the row is not a defect.
|
|
27
|
+
- Stack conventions are restated for a served platform surface (static markup, one sheet, `createElement` DOM, script only for live data), and the kit's renders are every skill's pre-flight.
|
|
28
|
+
4. **`docs-entropy.js` skips a paragraph that opens with the preamble's BEGIN marker**, beside the generated-block skip it already had, for the same reason: the duplication is the policy, not drift. The skip is narrow (the marker string) and calibrated in `tests/docs_entropy.mjs` against a duplicated plain paragraph that is still flagged.
|
|
29
|
+
5. **v1 is a distinct skill, not a version stamp.** The spec preserves the earlier generation "under its own install name so projects pinned to its exact behaviour keep working". Rebuilt, that means two files that differ in content — v1 carries the fixed baseline triple, the stack conventions, the pattern vocabulary and the tile-grid archetypes; v2 carries the brief read, the presets and the pre-flight — and a task that names neither gets the default.
|
|
30
|
+
6. **The pins** (`tests/ui_design_skills.mjs`): every own skill's frontmatter names its dir and carries a description; the preamble is first after the frontmatter, byte-identical, exactly once; the `## Rebuilt from` section names a task, ADR 0198 and "no upstream bytes"; the body names the style library and "hero plates" and contains no image mandate; no sidecar or licence text; `/design`, the skills README and the module `CLAUDE.md` list every own skill; the four skills of this task exist and are declared.
|
|
31
|
+
|
|
32
|
+
## Consequences
|
|
33
|
+
|
|
34
|
+
- **The roster is no longer empty.** Four in-house skills ship with the module and land in every instance's `.claude/skills/` where it is on. The core checkout itself still does not see module-owned skills (the materialiser's self-copy guard); task 1003330 is that step.
|
|
35
|
+
- **A builder whose `~/.claude/skills` carries the upstream under the same names** has a collision by design: the module's names are the roster's, and the materialised project-level copy is the one an instance ships. The upstream copies are that builder's own and are not read by any task here.
|
|
36
|
+
- **The scan verdict stands.** Nothing here vendors, overrides or re-scans; the rebuild specs for the impeccable method (task 1003326), the image family (1003328), the detector (1003329) and the four looks (1003327) follow this shape.
|
|
37
|
+
- **The doc ratchet now has a second intentional-duplication marker.** A third would be a sign to generalise the skip; two is not.
|
|
38
|
+
|
|
39
|
+
## Rejected
|
|
40
|
+
|
|
41
|
+
- **A `PROVENANCE.md` on a rebuild** with the spec's origin as its commit: the sidecar's fields assert a copy of those bytes under that licence and verdict, which is exactly what a rebuild is not; `vendoredSkillGate` would also have to learn a third verdict.
|
|
42
|
+
- **A second preamble for rebuilds.** Two blocks means two things to keep byte-identical and a test that has to know which is which; one block worded for both is one pin.
|
|
43
|
+
- **A `rebuilt/` dir segment exempt from the ratchets like `vendor/`.** These are our lines; counting them is the point.
|
|
44
|
+
- **Vendoring the four anyway** (ADR 0198 "what the owner can decide next"): an owner override recorded in an ADR, not a session's call, and not taken.
|
|
45
|
+
- **Folding v1 into v2 with a "v1 mode" flag.** The spec's reason for v1 is that it does not move when v2 does.
|
|
46
|
+
- **Raising `doc_duplicate_prose`'s baseline** to absorb the preamble copies: raising a baseline to pass CI is out of policy, and the duplication is not debt.
|
package/docs/adr/README.md
CHANGED
|
@@ -306,5 +306,6 @@ This keeps the decision history honest and traceable.
|
|
|
306
306
|
| 0217 | [**Rank gates INVITING, not VIEWING: the owner’s answers to the account-security review’s five blockers** ([task 1003358](https://cloudbongos.com/builders#/task/1003358) · goal 1000045; answers blockers 1000121/1000122/1000123/1000124/1000126). The 2026-08-29 account-security batch shipped fourteen tasks and deliberately refused five audience/consent questions. Four turned out to be the same question, and one answer reframed the rest. Decisions: **D1** the governing line — *rank gates the ability to INVITE; it does not gate the ability to VIEW*. Seeing what someone built across projects is not privileged (the T&C already promises that data is public by consent, to search engines included); reaching out to them costs them something, and that is where a floor belongs. So `membership_kind` is **NOT** widened — `membershipKind()` stays FIRST_ADMIN-only, the federated wire contract is unchanged, the My Projects owned/building split keeps its meaning, the cross-version window never opens, and task 1003350 is **abandoned**. A **per-project INVITE rank floor defaulting to archon** replaces it (task 1003362), set by that project’s own archons — strictly better than a founder requesting a platform-side `project.curate` grant per teammate. Viewing gains no gate, on a new cross-project achievements page that per the owner **lives on cloudbongos.com, not the hall variant** (task 1003363) — so `public-landing/` inside the apex-only region, not `modules/hall-ui/`. The risk to hold as it is built: public consented profile data and a PRIVATE account’s recruiter sliver are different material, and the sliver must not ride along — not as a row, a facet count, or a search-result absence that reveals presence. **D2** the sliver keeps its **archon floor** (blocker 1000126), and ADR 0215 D4’s defensive hold becomes a positive agreement: the invite floor also defaults to archon, and the C5 tension dissolves because C5 was about browsing. Whoever builds 1003362 must check whether it IS the console wave’s `project.recruit` atom before shipping a rival knob. **D3** the consent pair — `/terms` said acceptance is recorded “by signing in here” but the stamp is reachable only via `/sso/authorize`, which a CHILD PROJECT reaches, so a hub-only builder was never stamped and (NULL `terms_accepted_at` → `isAccountActive` false) was invisible everywhere, permanently, under no-backfill. Owner took **both halves**: correct the sentence now (task 1003353) and **capture at the hub’s own sign-in** next (task 1003361), through the `platform-identity.hub` seam so child instances see no change. On grandfathering the owner chose **B — grandfather** (task 1003360) against the recommendation, having been shown that it records a consent which did not happen; the ledger does not absorb that silently, so a grandfathered acceptance is **marked** (queryably distinct from a sign-in, via a recorded source not a magic timestamp) and `/terms` **says so**. ADR 0213’s honest property is not “every acceptance was a sign-in” but **the record never claims more than what happened**, and that survives. **D4** `/access-requests/status` **collapses** to `admitted` + one `status` of `member`|`invited`|`not_admitted` (task 1003359, the fix ADR 0209 already named). It does **not** close the oracle and must not claim to — `admitted:true` still means member-or-invited; the 1003339 limiter is what reduces enumeration, and collapsing narrows what leaks about **applicants**. The cost is paid in the same change: `login.js` is the ONLY place telling a declined applicant they were declined, so its 30-minute timeout copy is rewritten to be honest rather than promising an approval that is never coming. ⚠ Also: report 1000027 (the live SSO check-in identity hole) marked **fixed**, held open until now so the ledger would not claim a fix while the hole still served; `verifyHubAssertion`’s purpose-confusion weakness filed as report **1000028** (medium, latent — zero call sites, becomes an admission bypass the moment ADR 0201 D2.1’s vouch is minted, so the fix must land BEFORE the feature that arms it). **New invariant:** any gate added to a viewing surface, or any recruiting/invite surface below the archon floor, contradicts this ADR and needs one that supersedes it.](<redacted>.md) | platform identity / privacy / permissions |
|
|
307
307
|
| 0218 | [**The apex-root web surface: a module may own the front door, and says so with a flag, not an empty prefix** ([task 1003322](https://cloudbongos.com/builders#/task/1003322) · goal 1000080 — the last of the five tasks scoped with the owner on 2026-08-28). Carves the repo-root `public-landing/` into `modules/public-landing/`, the THIRD web-surface module after status-UI (BV1.R84) and hall-UI (BV1.R85) and the first that is not a subdomain. The obstacle: `moduleWebSurfaces()`'s whole routing vocabulary was a Host-header PREFIX, and the apex root is not a prefix of anything — it is whatever is left once the subdomains are taken. So a surface now names where it answers in exactly one of two ways, `{ host: "status.", dir }` or `{ apexRoot: true, dir }`, never both and never neither. **`host: ""` is rejected on purpose:** the routers match with `host.startsWith(s.host)` and every string starts with `""`, so an empty prefix would answer on `builders.`/`status.` too AND make `isModuleSurfaceHost()` true for every request — the predicate eleven `/builders` guards use to step aside — silently un-serving the hall. A boolean cannot be mistaken for a prefix. The landing keeps its DEDICATED serving block (the hall precedent): the apex-only strip, the branding stamp, the `?v=` content stamp and the six routes are untouched; only `LANDING_DIR` changes, from a hardcoded path to a lookup through the loader, resolved by the `apexRoot` PREDICATE so neither `platform-server.js` nor `serve-internal.js` names a module key (ADR 0083 §Decision #4). Disabling the module removes the front door outright and the apex answers the game-less 404 tail — the module contract's "disable without breaking anything" in its most literal form. At most one apex-root surface: the first discovered wins and a second is logged. Also: the BV1.R76 host-identity gate learned that an **apex-only region is exempt** — the server deletes it on every non-hub instance, so it cannot make a child render the hub's brand, which is the failure that gate names. Evidence: the five apex pages hashed as served, before and after, differ by exactly the same 22 bytes — `"public-landing":true,`, the module's own `window.__MODULES__` entry — and are otherwise identical; the 106 moved files hash identically on disk.](<redacted>.md) |
|
|
308
308
|
| 0219 | [**A look is a branding pack: the style library ships looks as `theme.ui` overlays, never as sheets** ([task 1003323](https://cloudbongos.com/builders#/task/1003323) · goal 1000083). The owner scoped "varying ui styles" for the ui-design module; the shape was decided by three facts: every served sheet already reads only the contract and is measured by a suite that resolves its own expressions under the pack (ADR 0196), the pack is one-mode (light values; dark is each sheet's black tier), and the world round's candidate token layers were sheets, not packs. Decisions: **D1** a look is `modules/ui-design/styles/<name>/` = `pack.json` (the `theme.ui` overlay, exactly the thirteen colours + the two font stacks and their links) + `DESIGN.md` (the derived recipes in the root DESIGN.md's frontmatter shape, thirteen in lockstep with the pack) + `mock.html` (a specimen with no `:root`, served through the stub with the pack spliced in), never a fork of a sheet; a sixteenth token is a contract change. **D2** `chrome-world` is canonical and pinned byte-for-byte to the neutral pack; its DESIGN.md points at the root one. **D3** the alternates `expedition` (natgeo-editorial) and `grove` (organic-cards) are ported by retuning the thirteen against the three sheets' own pair lists in both modes until every pair clears AA (the grove's sage lifted and its inks deepened for the hub's status vocabulary and the hall's chips); dark under both is the sheets' black. **D4** the picker rides `GDS_BRANDING_FILE`, the fixed knob `src/branding.js` already has: `STYLE=<name>` on the stub, `--style` on render/probe, `--pack <name>` on contrast/check-mock; no core change. **D5** the three token suites take `UI_DESIGN_PACK` and their drift tests keep measuring against the neutral pack; `tests/ui_design_styles.mjs` runs them once per alternate and proves the knob live with a pack that must fail. **D6** an instance adopts a look by copying `theme` into `config/branding.json`; what stays the platform's whichever look is on: the dark ground, the hall's verification vocabulary, every sheet's derived tier, the floors. Rejected: sheets per look beside world.css; a dark tier in the contract; a picker that writes the host boundary.](<redacted>.md) | ui-design / branding contract |
|
|
309
|
+
| 0220 | [**An in-house rebuilt skill is a first-party skill: it lands at `skills/<name>/`, opens with the same preamble, carries a "Rebuilt from" section instead of a sidecar, and is counted** ([task 1003325](https://cloudbongos.com/builders#/task/1003325) · goal 1000083). ADR 0198 landed both scanned origins as rebuild specs and left the roster empty; this is the first rebuild — the taste core: `design-taste-frontend`, `design-taste-frontend-v1`, `high-end-visual-design`, `redesign-existing-projects` — and the policy had not said where a rebuild lives, what replaces `PROVENANCE.md`, whether a preamble written for vendored copies still fits, or whether five byte-identical copies of it trip the doc-entropy ratchet (they would have: baseline 6). Decisions: **D1** a rebuild is `modules/ui-design/skills/<name>/SKILL.md`, declared in `module.json`, materialised by step 1b's own-dir branch (no sidecar gate), first-party and counted by every ratchet — no `rebuilt/` exemption like `vendor/`, because these are our lines. **D2** one preamble worded for both origins ("vendored from a scanned origin, or rebuilt in-house from a spec"), still byte-identical everywhere; a rebuild's provenance is its `## Rebuilt from` section first after the block: the origin the SPEC was derived from (documentation only), the verdict, the spec task, "no upstream bytes consulted or copied", this ADR, and what changed in translation; no `PROVENANCE.md`, no `LICENSE`. **D3** the translation rules for every rebuilt taste skill: the palette is the pack and variants come from the style library (ADR 0219); the design system is the instance's `DESIGN.md`; imagery is hero plates and hero objects by token and never a step; every motion rule lives inside the Kill Switch (springs, magnetic hover, pointer-tracked borders are named *not available*); the upstream's bans are defaults an instance's `DESIGN.md` overrides, and where the world calls for what an audit would flag, the world is the answer key. **D4** `docs-entropy.js` skips a paragraph opening with the preamble marker beside its generated-block skip, calibrated against a plain duplicate that is still flagged. **D5** v1 is a distinct skill differing in content (the fixed triple, the stack conventions, the pattern vocabulary, the tile-grid archetypes), not a version stamp. **D6** the pins in `tests/ui_design_skills.mjs`: frontmatter name = dir, preamble first and once, the section's task link + ADR 0198 + "no upstream bytes", the style library and "hero plates" named, no image mandate, no sidecar, every door lists the four. Rejected: a sidecar with the spec's commit; a second preamble; a `rebuilt/` segment; vendoring anyway (an owner override, not taken); folding v1 into v2; raising the doc baseline.](<redacted>.md) | ui-design / skills |
|
|
309
310
|
|
|
310
311
|
> ⚠️ **Numbering collisions are now machine-enforced, not narrated.** Twenty numbers are shared by two ADRs each — assigned in parallel sessions before anything checked. The files keep their filenames (renumbering would break every existing citation, and a number once assigned is never reused), and this table disambiguates each pair as `NNNN-a` / `NNNN-b`. The authoritative list is `LEGACY_DUPLICATE_ADRS` in [`scripts/gds/adr-namespace.js`](../../scripts/gds/adr-namespace.js), frozen by exact filename: a **new** collision — or a third file joining a legacy number, or a rename of either half — hard-fails the `unit` CI gate, as does an index row that points at no file or an ADR with no row. The list may only shrink. This paragraph used to narrate the collisions one by one and had fallen eight behind reality; see [ADR 0195](<redacted>.md) for why the check exists and why the pairs are grandfathered rather than renumbered.
|
package/docs/file-map.md
CHANGED
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
│ ├── hall-ui/ ← CORE-DOMAIN WEB-SURFACE carve (BV1.R85, default-on): the authenticated builders' hall at builders.<apex>, moved from public-builders/ (~13k LOC, the largest). NO routes/db/port — declares contributes.webSurfaces:[{host:"builders.",dir:"public"}] for discovery, but UNLIKE status-ui keeps its DEDICATED serving block in serve-internal (the /builders shim + canonical redirects + citizen/Archon page gates + asset-stamping + hall-widget injection), partitioned out of the generic web-surface loop and served from BUILDERS_DIR. CLAUDE.md, module.json, public/{index.html,builders.js,style.css,work,watch,harbor,gate,pair,settings,ranks,sessions,primer,diagrams,atlas,...}.{html,js},cursors/,chime.wav. Three ARCHETYPE sheets sit beside the per-page ones: `record.css` (the task + idea records, task 1003305), `room.css` (the READING ROOMS — the primer + the diagrams index, task 1003306: the reading measure and rhythm, the table of contents, the progress hairline and the framed figure diagram-viewer.js upgrades on both pages) and `oversight.css` (the OVERSIGHT family — government, watch, harbor, gate, task 1003469: the row, the figure band inside a panel, the group head, the inline form, the grant toggle and the shared confirm modal; named oversight because the vocabulary wall bans "governance" on the government surface). A page sheet then holds only what one page has and the others do not — `primer.css` the acknowledge gate, `diagrams.css` the legend strip, `gate.css` the hold reasons and file chips, `watch.css` the severities, the report reader and the grade trend.
|
|
131
131
|
│ ├── lifecycle/ ← CORE-DOMAIN carve (BV1.R86, default-on): the build state machine — tasks · claims · versions · done-when · goals · dependencies · claim→ship→grade→credit · github-push + merge-lock + conflict-resolve + publish-reconciler. The LAST, most-coupled carve; registers the `lifecycle` kernel port (createTask, classifyTaskKind, tallyPeerVotes, work-tracking reads). CLAUDE.md, module.json, routes/{tasks,claims,versions,done-when,goals,dependencies,gate-approvals,analytics}.js, lifecycle.js, github-push.js, merge-lock.js, conflict-resolve.js, publish-reconciler.js, task-visuals.js + task-visual-db.js + routes/visuals.js (the optional ship-time visual, task 1003109 — the route file is NOT named task-*.js on purpose; see the module CLAUDE.md).
|
|
132
132
|
│ ├── copy-desk/ ← CORE-DOMAIN module (task 1003113 / R02 of goal 1000074, default-on): the COPY half of the design contract — ADR 0081 gave design TOKENS a repo-owned source of truth, text had none. Owns the FLAG (anyone in any role marks a string or a whole surface as needing an artist's attention, with a reason required in BOTH the route and a CHECK constraint) and the QUEUE an artist reads it from. READS R01's committed registry (docs/copy-registry.json, generated by scripts/gds/copy-inventory.js) and never writes it. NO LIVE CMS is the hard non-goal: no column here could hold replacement text, and copy_no_cms.mjs pins that. Key is `copy-desk`, not `copy` — the branding contract already owns a `copy` block, so the bare word reds the "core carries no module code" fitness check. CLAUDE.md, module.json, registry.js, flags.js, routes/copy-desk.js, migrations/copy_desk_001_flags.sql, tests/{copy_flags,copy_queue,copy_no_cms}.mjs. Its hall surface lives with hall-ui (public/copy-desk.{html,js,css}), per the one-key-per-web-surface precedent.
|
|
133
|
-
│ ├── ui-design/ ← CORE-DOMAIN module (task 1003324 / ADR 0197, default-on): the design capability every builder on every instance gets — ships METHOD, never a world. Absorbs ADR 0081's layer: adapters/{claude-design,figma}/index.js (moved from src/ui/adapters/), scripts/{design-sync,figma-design-sync,validate-design}.js (moved from scripts/gds/, which keeps thin shims), docs/design-contract.md (moved from docs/). Declaration-only: contributes.skills design (the /design playbook, new) + design-sync + figma-design-sync; no routes/port/migrations, so disabling it touches no served surface. CLAUDE.md separates the platform FLOORS (the Fifteen Rule, the dark twins, no page :root, the 24px floor, AA in both modes, the kill switch) from the instance's WORLD (its packs + DESIGN.md). kit/ (task 1003320) is the look-before-you-ship kit: lib.js (the floors, the states/probes contracts, the action + expectation vocabularies, the WCAG maths, the stub spawner), serve.js (any surface through the doorway's real transforms + fixtures/), render.js (states × 1440/390/320 × dark/light, audited), probe.js (PASS/FAIL contracts), check-mock.js (platform rules + the instance's DESIGN.md taste bans), contrast.js + png.js; a page's <page>.states.json / <page>.probes.json sit BESIDE the surface (modules/public-landing/public/, modules/hall-ui/public/). Recipe: docs/recipes/ui-look-before-you-ship.md. skills/ (task 1003321 / ADR 0198) is the vendoring policy: policy.json (licence allowlist, verdicts that may land, sidecar fields, preamble markers), PREAMBLE.md (the platform preamble every vendored SKILL.md opens with), README.md (the roster — both origins scanned dangerous on 2026-08-28, landed as rebuild specs; nothing vendored — and the landing recipe); a landable copy goes under skills/vendor/<name>/ with PROVENANCE.md + LICENSE and is materialised into an instance's .claude/skills/ by claude-materialize.js step 1b. styles/ (task 1003323 / ADR 0219) is the style library: <name>/pack.json (the theme.ui overlay, exactly the fifteen) + DESIGN.md (the look's derived recipes, thirteen in lockstep with the pack) + mock.html (the specimen); chrome-world (pinned to the neutral pack), expedition, grove; STYLE=<name> on the kit lays a look over the instance's pack through GDS_BRANDING_FILE; tests/ui_design_styles.mjs runs the world/cosmos/hall token suites once per alternate (UI_DESIGN_PACK). config/design-tokens.* + design-sources.json STAY in config/ as the host boundary; public-landing keeps its own key (first customer, not owner).
|
|
133
|
+
│ ├── ui-design/ ← CORE-DOMAIN module (task 1003324 / ADR 0197, default-on): the design capability every builder on every instance gets — ships METHOD, never a world. Absorbs ADR 0081's layer: adapters/{claude-design,figma}/index.js (moved from src/ui/adapters/), scripts/{design-sync,figma-design-sync,validate-design}.js (moved from scripts/gds/, which keeps thin shims), docs/design-contract.md (moved from docs/). Declaration-only: contributes.skills design (the /design playbook, new) + design-sync + figma-design-sync; no routes/port/migrations, so disabling it touches no served surface. CLAUDE.md separates the platform FLOORS (the Fifteen Rule, the dark twins, no page :root, the 24px floor, AA in both modes, the kill switch) from the instance's WORLD (its packs + DESIGN.md). kit/ (task 1003320) is the look-before-you-ship kit: lib.js (the floors, the states/probes contracts, the action + expectation vocabularies, the WCAG maths, the stub spawner), serve.js (any surface through the doorway's real transforms + fixtures/), render.js (states × 1440/390/320 × dark/light, audited), probe.js (PASS/FAIL contracts), check-mock.js (platform rules + the instance's DESIGN.md taste bans), contrast.js + png.js; a page's <page>.states.json / <page>.probes.json sit BESIDE the surface (modules/public-landing/public/, modules/hall-ui/public/). Recipe: docs/recipes/ui-look-before-you-ship.md. skills/ (task 1003321 / ADR 0198) is the vendoring policy: policy.json (licence allowlist, verdicts that may land, sidecar fields, preamble markers), PREAMBLE.md (the platform preamble every vendored SKILL.md opens with), README.md (the roster — both origins scanned dangerous on 2026-08-28, landed as rebuild specs; nothing vendored — and the landing recipe); a landable copy goes under skills/vendor/<name>/ with PROVENANCE.md + LICENSE and is materialised into an instance's .claude/skills/ by claude-materialize.js step 1b. skills/<name>/ (task 1003325 / ADR 0220) are the in-house rebuilds from the specs, first-party and counted: design-taste-frontend (the default taste skill), design-taste-frontend-v1 (the earlier generation, kept distinct), high-end-visual-design (the polish pass), redesign-existing-projects (the in-place upgrade); each opens with the same preamble and carries a "Rebuilt from" section instead of a sidecar. styles/ (task 1003323 / ADR 0219) is the style library: <name>/pack.json (the theme.ui overlay, exactly the fifteen) + DESIGN.md (the look's derived recipes, thirteen in lockstep with the pack) + mock.html (the specimen); chrome-world (pinned to the neutral pack), expedition, grove; STYLE=<name> on the kit lays a look over the instance's pack through GDS_BRANDING_FILE; tests/ui_design_styles.mjs runs the world/cosmos/hall token suites once per alternate (UI_DESIGN_PACK). config/design-tokens.* + design-sources.json STAY in config/ as the host boundary; public-landing keeps its own key (first customer, not owner).
|
|
134
134
|
│ ├── character-anim/ ← consistent-character generative animation (ADR 0088) — graded painterly frame sets + manifest, the otb-character-review skill. CLAUDE.md, module.json.
|
|
135
135
|
│ ├── provisioning/ ← stands up + operates a Cloud Bongos instance's own infra (server/DB/domain/DNS/TLS) via the API (ADR 0111); web-tier request+state only, no cloud tokens — a separate control-plane runner drains the intents. Distinct from the `hosting` module (ADR 0092), which brokers compute workloads. CLAUDE.md, module.json.
|
|
136
136
|
│ └── demo/ ← BV1.R52 acceptance-proof module: one rank-gated write route + one namespaced migration proving the loader mounts a scaffolded module with zero core edits. CLAUDE.md, module.json.
|
|
@@ -1357,5 +1357,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1357
1357
|
landed since 1.19.458 with no explicit bump. run 33568609735. (task 1002620)
|
|
1358
1358
|
1.19.460 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1359
1359
|
landed since 1.19.459 with no explicit bump. run 33571137958. (task 1002620)
|
|
1360
|
+
1.19.461 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1361
|
+
landed since 1.19.460 with no explicit bump. run 33571982271. (task 1002620)
|
|
1360
1362
|
---------------------------------------------------------------------------
|
|
1361
1363
|
```
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"coreVersion": "^1.19.0",
|
|
6
6
|
"default": true,
|
|
7
7
|
"maintenance": { "status": "core-maintained" },
|
|
8
|
-
"contributes": { "skills": ["design", "design-sync", "figma-design-sync"] },
|
|
8
|
+
"contributes": { "skills": ["design", "design-sync", "figma-design-sync", "design-taste-frontend", "design-taste-frontend-v1", "high-end-visual-design", "redesign-existing-projects"] },
|
|
9
9
|
"provides": [],
|
|
10
10
|
"consumes": []
|
|
11
11
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
<!-- BEGIN PLATFORM PREAMBLE · ui-design module · one block, byte-identical in every vendored
|
|
2
|
-
> **Platform preamble — read before any rule below.** This skill
|
|
1
|
+
<!-- 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 -->
|
|
2
|
+
> **Platform preamble — read before any rule below.** This skill ships with the `ui-design` module (vendored from a scanned origin, or rebuilt in-house from a spec) and runs INSIDE an instance's world, which it reads first and never overrides.
|
|
3
3
|
>
|
|
4
4
|
> 1. **Load the world before any taste rule.** `config/branding.json` over `config/branding.neutral.json` (`theme.ui` is the fifteen tokens: <redacted> colours and two font stacks), `config/design-tokens.json` over `config/design-tokens.neutral.json`, and `DESIGN.md` at the repo root: the instance's palette and derived tiers, faces, materials, motion grammar, components, and its Do's and Don'ts. Where an instance has no `DESIGN.md`, the neutral pack is the world and the platform floors in item 6 are the whole rulebook; say so and build to them. Never invent a palette.
|
|
5
5
|
> 2. **The fifteen-token contract is the only colour source.** Every colour is a `var()` or a `color-mix()` of the fifteen; the only legal literal is pure black or white with alpha as a scrim, mask or halo. No page `:root` (page tokens live on `body`), and every dark rule is written twice: `:root[data-mode="dark"]` and its `prefers-color-scheme` twin.
|
|
@@ -9,5 +9,5 @@
|
|
|
9
9
|
> 6. **The platform floors hold on every instance** and a `DESIGN.md` may tighten them, never loosen them: every control clears 24px on both sides (primary pills 46–48px); AA in both modes (4.5:1 body text, 3:1 large text and non-text) measured from the real stylesheet; and the Kill Switch, `@media (prefers-reduced-motion: reduce)` at the foot of the token layer killing every animation and transition, no motion authored in JavaScript, and a rest frame that is a complete composition on its own.
|
|
10
10
|
> 7. **Look before you ship.** Render the real surface through the module's kit before and after (`/design` Step 2 and `docs/recipes/ui-look-before-you-ship.md`: every state at 1440 / 390 / 320 in dark and light, audited against the floors); a page with no states file gets one first, and new interactive code ships with a pin in the page's own test file.
|
|
11
11
|
>
|
|
12
|
-
> The rules below are the
|
|
12
|
+
> The rules below are the skill's own. A vendored copy carries a `PROVENANCE.md` beside this file naming the origin, the pinned commit, the licence and the scan verdict it was vendored under; an in-house rebuild names its rebuild spec and its ADR in the section right after this block.
|
|
13
13
|
<!-- END PLATFORM PREAMBLE -->
|
|
@@ -25,6 +25,19 @@ Two origins were scanned for the set scoped with the owner (impeccable; the elev
|
|
|
25
25
|
|
|
26
26
|
The eleven taste skills are single-file prose; the floor's one critical is a false positive in the human sense and a true positive in the scanner's — and the rule that makes the scanner worth having is that a session does not get to tell the difference. Vendoring any of them anyway is an **owner override recorded in an ADR**, not a judgement a task makes.
|
|
27
27
|
|
|
28
|
+
## The in-house rebuilds (2026-09-01)
|
|
29
|
+
|
|
30
|
+
A rebuild spec becomes a **first-party skill** at `<name>/` — no `vendor/` segment, so the ratchets count it; no `PROVENANCE.md` or `LICENSE`, because an in-house skill claims no upstream; the same PLATFORM PREAMBLE first after the frontmatter, byte-identical; then a **`## Rebuilt from`** section naming the origin the *spec* was derived from (documentation only), the scan verdict, the spec task, the statement that no upstream bytes were consulted or copied, and what changed in translation ([ADR 0220](../../../docs/adr/<redacted>.md)). Declared in `module.json` like any other; `claude-materialize.js` step 1b lands it through its own-dir branch, which no sidecar gate touches. `tests/ui_design_skills.mjs` pins all of it.
|
|
31
|
+
|
|
32
|
+
| Skill | Rebuilt from (spec) | Task | What it is |
|
|
33
|
+
|---|---|---|---|
|
|
34
|
+
| `design-taste-frontend` | `Leonxlnx/taste-skill` `taste-skill` (v2) | [task 1003325](https://cloudbongos.com/builders#/task/1003325) | the default taste skill: the brief read in one line, three dials from presets, one world locked, hero and layout discipline, rationed decoration, a pre-flight the kit measures; declines dashboards, tables, wizards, editors |
|
|
35
|
+
| `design-taste-frontend-v1` | `Leonxlnx/taste-skill` `taste-skill-v1` | [task 1003325](https://cloudbongos.com/builders#/task/1003325) | the earlier generation, kept distinct: a fixed baseline triple adapted in conversation, stack conventions for a served surface, the layout bans, full interaction cycles, the pattern vocabulary, the tile-grid archetypes |
|
|
36
|
+
| `high-end-visual-design` | `Leonxlnx/taste-skill` `soft-skill` | [task 1003325](https://cloudbongos.com/builders#/task/1003325) | the agency-build polish pass: one texture archetype (the style library's looks) and one layout archetype per project, the nested container anatomy where the world allows a container, the pill with its nested arrow disc, rhythm, choreography and scroll entry inside the Kill Switch |
|
|
37
|
+
| `redesign-existing-projects` | `Leonxlnx/taste-skill` `redesign-skill` | [task 1003325](https://cloudbongos.com/builders#/task/1003325) | the in-place upgrade: scan the stack, diagnose against the seven-group audit with the instance's world as the answer key, fix in impact-per-risk order, one group per commit; keeps the stack, never migrates frameworks |
|
|
38
|
+
|
|
39
|
+
What every rebuild does with the upstream's opinions, in the same words: the palette is the pack and a variant is a look from [`../styles/`](../styles/README.md); the design system is the instance's `DESIGN.md`; images are hero plates and hero objects by token and never a step; every motion rule lives inside the Kill Switch (springs, magnetic hover, pointer-tracked borders are named *not available*); the upstream's bans are defaults an instance's `DESIGN.md` overrides; the kit's renders are the pre-flight. Still specs, not skills: the impeccable method (task 1003326), the image family (1003328), the detector (1003329), the four looks (1003327).
|
|
40
|
+
|
|
28
41
|
**The playwright plugin** (`claude-plugins-official`, an MCP plugin) is the LIVE browser-driving loop, where the kit is the headless one. It is a prerequisite, documented — `claude plugin install playwright@claude-plugins-official` — never vendored.
|
|
29
42
|
|
|
30
43
|
## How to land one (when a scan says it may)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
---
|
|
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.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- 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 -->
|
|
7
|
+
> **Platform preamble — read before any rule below.** This skill ships with the `ui-design` module (vendored from a scanned origin, or rebuilt in-house from a spec) and runs INSIDE an instance's world, which it reads first and never overrides.
|
|
8
|
+
>
|
|
9
|
+
> 1. **Load the world before any taste rule.** `config/branding.json` over `config/branding.neutral.json` (`theme.ui` is the fifteen tokens: <redacted> colours and two font stacks), `config/design-tokens.json` over `config/design-tokens.neutral.json`, and `DESIGN.md` at the repo root: the instance's palette and derived tiers, faces, materials, motion grammar, components, and its Do's and Don'ts. Where an instance has no `DESIGN.md`, the neutral pack is the world and the platform floors in item 6 are the whole rulebook; say so and build to them. Never invent a palette.
|
|
10
|
+
> 2. **The fifteen-token contract is the only colour source.** Every colour is a `var()` or a `color-mix()` of the fifteen; the only legal literal is pure black or white with alpha as a scrim, mask or halo. No page `:root` (page tokens live on `body`), and every dark rule is written twice: `:root[data-mode="dark"]` and its `prefers-color-scheme` twin.
|
|
11
|
+
> 3. **A style skill produces variants INSIDE that world, never a second world.** The look this skill carries (its palette, faces, materials, mood boards) is a reference for composition and craft; the instance's tokens and faces replace it. Changing the world is the owner's decision and an ADR, not a session's taste.
|
|
12
|
+
> 4. **Images are for hero plates and hero objects only.** UI is built from templates and the instance's world, not painted; any image-generation step below applies to text-free plates and hero objects, never to controls, cards, text, or a screenshot of a screen.
|
|
13
|
+
> 5. **The instance's taste bans apply and win.** Read them from its `DESIGN.md`. Cloud Bongos's own (no em dash or en dash in visible copy, no emoji, no gradient text, no three-equal-card row, no fake screenshot built from divs) are that instance's, not the module's; where this skill's rules conflict with an instance's bans, the instance wins.
|
|
14
|
+
> 6. **The platform floors hold on every instance** and a `DESIGN.md` may tighten them, never loosen them: every control clears 24px on both sides (primary pills 46–48px); AA in both modes (4.5:1 body text, 3:1 large text and non-text) measured from the real stylesheet; and the Kill Switch, `@media (prefers-reduced-motion: reduce)` at the foot of the token layer killing every animation and transition, no motion authored in JavaScript, and a rest frame that is a complete composition on its own.
|
|
15
|
+
> 7. **Look before you ship.** Render the real surface through the module's kit before and after (`/design` Step 2 and `docs/recipes/ui-look-before-you-ship.md`: every state at 1440 / 390 / 320 in dark and light, audited against the floors); a page with no states file gets one first, and new interactive code ships with a pin in the page's own test file.
|
|
16
|
+
>
|
|
17
|
+
> The rules below are the skill's own. A vendored copy carries a `PROVENANCE.md` beside this file naming the origin, the pinned commit, the licence and the scan verdict it was vendored under; an in-house rebuild names its rebuild spec and its ADR in the section right after this block.
|
|
18
|
+
<!-- END PLATFORM PREAMBLE -->
|
|
19
|
+
|
|
20
|
+
## Rebuilt from
|
|
21
|
+
|
|
22
|
+
This skill is an **in-house rebuild**, not a vendored copy. Its documented functionality comes from `Leonxlnx/taste-skill` @ `<redacted>` (MIT; the `taste-skill` entry, "design-taste-frontend v2"), which `/scan-before-install` reduced to `dangerous` at the default tier on 2026-08-28, so under [ADR 0198](../../../../docs/adr/<redacted>.md) it could land only as a REBUILD-SPEC. The spec is the body of [task 1003325](https://cloudbongos.com/builders#/task/1003325); this file implements its requirements from scratch, **no upstream bytes consulted or copied**, and the landing shape (the in-house `skills/<name>/` home, this section in place of a `PROVENANCE.md`) is [ADR 0220](../../../../docs/adr/<redacted>.md).
|
|
23
|
+
|
|
24
|
+
What changed in translation, on purpose: the upstream skill mandated image generation as a first step and accepted seeded placeholder photography; here **imagery is hero plates and hero objects only, by token, and never a step**. Its "foundation" step installed a vendor's design-system package; here **the foundation is the instance's world** (`DESIGN.md` and its pack), and the style library is where variants come from. Its motion rules assumed a script-driven animation stack; here **every rule lives inside the Kill Switch** and nothing moves in JavaScript. Its bans (the long dash, decorative labels) are kept as defaults that an instance's own `DESIGN.md` bans override.
|
|
25
|
+
|
|
26
|
+
## Scope: when this skill fires, and when it declines
|
|
27
|
+
|
|
28
|
+
**Fires** on a landing page, a portfolio, a marketing site, a product story page, a pricing page, and on a redesign of any of those (for the audit-first version of a redesign, `redesign-existing-projects` runs first and hands back here).
|
|
29
|
+
|
|
30
|
+
**Declines, and says so instead of trying anyway:** dashboards and ledgers, data tables, multi-step wizards, editors, native mobile, realtime collaboration surfaces. Those have their own archetypes on this platform (the hall's row and panel archetypes, the hub's card) and the plain `/design` loop; a marketing taste applied to a working surface is how a ledger gets a hero.
|
|
31
|
+
|
|
32
|
+
## Step 1: read the brief, then say the read in one line
|
|
33
|
+
|
|
34
|
+
Read, in this order, before any opinion: the page kind; the audience; the vibe words (verbatim, they are the brief's only taste signal); linked references (open them; extract composition, rhythm, density, never a palette); existing brand assets the instance already ships (its plates under `assets/world/`, its mark); the hard constraints (the world file is one: everything the instance's `DESIGN.md` names is a constraint, not a suggestion).
|
|
35
|
+
|
|
36
|
+
Then write **one line**: `Design read: <page kind> for <audience>, <the one word the vibe reduces to>, <variance/motion/density>.` That line is the contract for the session and goes in the ship notes.
|
|
37
|
+
|
|
38
|
+
Ask **at most one** clarifying question, and only when the read genuinely forks (two page kinds, two audiences, a vibe word that contradicts the world). A brief that does not fork gets the read and a first render, not a questionnaire.
|
|
39
|
+
|
|
40
|
+
## Step 2: set the three dials from a preset, never silently
|
|
41
|
+
|
|
42
|
+
Three numbers, 0 to 10, stated in the read line and restated in a comment at the top of the page's sheet:
|
|
43
|
+
|
|
44
|
+
| Preset | Variance | Motion | Density | What it means |
|
|
45
|
+
|---|---|---|---|---|
|
|
46
|
+
| Landing / marketing | 6 | 4 | 3 | asymmetric composition, one authored arrival plus slow ambient loops, a lot of air |
|
|
47
|
+
| Portfolio | 7 | 3 | 2 | the work is the composition; the chrome recedes |
|
|
48
|
+
| Product story / pricing | 5 | 3 | 5 | more content per band; still one accent, still hairlines |
|
|
49
|
+
| Reading page (docs, terms) | 2 | 1 | 6 | a measure, a rhythm, almost no motion |
|
|
50
|
+
| Redesign in place | inherit | inherit | inherit | the existing page's numbers, read from the audit, then moved one step at most |
|
|
51
|
+
|
|
52
|
+
- **Variance** is how far the layout may leave the centred single column: at 0 everything is centred and stacked; from 5 the hero is asymmetric and sections may offset; at 8+ elements may overlap and break the container.
|
|
53
|
+
- **Motion** is capped by the world's grammar before it is capped by the dial: under the chrome world an ambient loop is 20s or slower and there is exactly one authored arrival; a dial above the world's ceiling is clamped to it. Motion 0 is what every page becomes under `prefers-reduced-motion`, so the rest frame at motion 0 is designed first.
|
|
54
|
+
- **Density** is content per band, not padding: it changes how many facts a band carries, never whether the 24px floor holds.
|
|
55
|
+
|
|
56
|
+
Never run a baseline silently. If the brief gives no signal, pick the preset that matches the page kind, say so in the read line, and move on.
|
|
57
|
+
|
|
58
|
+
## Step 3: the foundation is the world
|
|
59
|
+
|
|
60
|
+
The design system for every surface on an instance is that instance's `DESIGN.md` plus its branding pack. Map the brief onto it:
|
|
61
|
+
|
|
62
|
+
- **The palette** is the fifteen. A variant of the look comes from the style library, `modules/ui-design/styles/<name>/` (render the surface under one with `--style <name>` on the kit to see it); the palette source for a variant is a look's `pack.json`, never a hex chosen for this page. If the brief's vibe wants a colour the world does not have, the answer is a look from the library or an owner decision, not a private token.
|
|
63
|
+
- **The faces** are the pack's two stacks. A brief that names a typeface it wants gets the nearest face the pack already carries, and the ship notes say so.
|
|
64
|
+
- **When the brief names a third-party design system** ("make it feel like <vendor>"), do not install anything: this skill adds no dependency, and a dependency is a task decision the claim's owner takes. Build the direction from web standards inside the world, and label the approximation in a comment in the sheet (`/* an approximation of <vendor>'s rhythm inside the chrome world; not official material */`), never as official.
|
|
65
|
+
- **Where the instance has no `DESIGN.md`**, the neutral pack is the world and the platform floors are the rulebook. Say so in the read line.
|
|
66
|
+
|
|
67
|
+
## Step 4: lock consistency for the whole page
|
|
68
|
+
|
|
69
|
+
One world, one accent, one radius scale (the world's `rounded` tier: pill, panel, tile), one mode pair (the pack's light and the sheet's dark twin), one label face. Locked at the top of the sheet in a comment, then held:
|
|
70
|
+
|
|
71
|
+
- No mid-page inversion. A dark band on a light page, or a light card on a dark page, is the copy-paste accident the redesign audit flags; the chrome world bans a black panel on a light page by name.
|
|
72
|
+
- No accent swap. The accent is the pack's `accent`; a second voice takes `sea` before anything is invented; the hall's fixed verification vocabulary (green, amber, red) is not a palette this skill may borrow.
|
|
73
|
+
- No radius drift. Every rounded thing on the page is one of the three tokens.
|
|
74
|
+
- No face drift. Display, body, label: three roles, two stacks plus the world's label face.
|
|
75
|
+
|
|
76
|
+
## Step 5: hero discipline
|
|
77
|
+
|
|
78
|
+
The hero is where a page becomes generic. Hold these:
|
|
79
|
+
|
|
80
|
+
- **The headline is at most two lines** at 1440 and at 390 (the chrome world's Two-Line Rule). If it wraps to three, the copy is too long or the container is too narrow; widen the container first, then shorten the copy with `copy-desk` (the copy half is theirs; flag, do not rewrite the voice).
|
|
81
|
+
- **The subtext is capped**: two lines, about twenty-four words.
|
|
82
|
+
- **The primary action is visible without scrolling** at 1440x900 and at 390x844. The kit's first shot of every render is that test.
|
|
83
|
+
- **Top padding is capped** at the world's band clamp; a hero that starts below the fold is not a hero.
|
|
84
|
+
- **At most four text elements** in the hero: eyebrow if the world allows one (the chrome world does not, above the h1), headline, subtext, the action pair. Nothing else. A stat, a badge, a pill row and a scroll cue are all the fifth element.
|
|
85
|
+
- **Trust logos leave the hero** and get their own band below it, and only where the instance's `DESIGN.md` does not ban a trust strip outright (the chrome world does).
|
|
86
|
+
|
|
87
|
+
## Step 6: layout discipline
|
|
88
|
+
|
|
89
|
+
- **No layout family reused across adjacent sections.** Two-column split, full-bleed band, hairline ledger, tile grid, single measure: each appears once before any repeats, and never twice in a row.
|
|
90
|
+
- **At most two consecutive image-plus-text zigzags.** A third is a template.
|
|
91
|
+
- **A bento grid has exactly as many cells as it has items.** No empty cell, no filler card, no "coming soon" tile. Count them in the sheet comment.
|
|
92
|
+
- **One-line desktop navigation under a height cap**: the world's bar height (64 to 72px in the chrome world; 60 under its breakpoint) and never a second row on desktop.
|
|
93
|
+
- **The container is the world's** (`spacing.container`); the breakpoint is the world's (one, at 900px in the chrome world); the widths the kit renders are the truth (1440 / 390 / 320).
|
|
94
|
+
|
|
95
|
+
## Step 7: ration the decoration
|
|
96
|
+
|
|
97
|
+
Small uppercase labels are seasoning. **One eyebrow per three sections**, at most, in the world's label face, and never above the h1 where the world bans it.
|
|
98
|
+
|
|
99
|
+
Banned outright, as defaults an instance's `DESIGN.md` may only tighten: enumerated section labels ("01 / Features"), scroll cues, locale and time strips, version stamps, decorative status dots, floating badges over hero text, pill tags under the hero, raw statistics inside the hero. The chrome world bans most of these by name; where a world is silent, this list holds.
|
|
100
|
+
|
|
101
|
+
## Step 8: copy hygiene inside the taste
|
|
102
|
+
|
|
103
|
+
- **The long dash and its separator variant do not appear in visible copy** where the instance bans them (Cloud Bongos does): not in headlines, captions, buttons, quotes or alt text. The plain hyphen and the minus sign survive. The ledger's quoted titles are normalised at render time rather than edited.
|
|
104
|
+
- No emoji, no exclamation mark in success copy, no placeholder names, no fake round numbers, no invented brands. Fixtures come from `modules/ui-design/kit/fixtures/`, which is a neutral builder and real feed shapes.
|
|
105
|
+
- The voice belongs to `copy-desk`. Flag a string in the ship notes; do not rewrite the product's voice in passing.
|
|
106
|
+
|
|
107
|
+
## Step 9: imagery, under the owner's rule
|
|
108
|
+
|
|
109
|
+
Imagery is optional and, where it exists, it is **hero plates and hero objects by token**: the world's rendered plates (`--plate-*` in the chrome world), an owner-supplied `art_url`, a project's own cover. This skill never generates an image as a step, never proposes placeholder photography, and never asks for an image budget.
|
|
110
|
+
|
|
111
|
+
- A slot with no plate is not a slot: the composition is redesigned without it. An empty labelled slot ("image here") is the generic tell this skill exists to remove.
|
|
112
|
+
- **No product screenshot faked out of styled divs** (the chrome world's ban, and the module's default). A real screenshot is content an owner supplies; a fake one is decoration.
|
|
113
|
+
- No hand-drawn icon paths, no icon set introduced for one page. Where an icon is needed, the world's inline mark or a hairline glyph.
|
|
114
|
+
- A plate is the one place the world allows atmosphere; nothing else is painted.
|
|
115
|
+
|
|
116
|
+
## Step 10: motion honesty
|
|
117
|
+
|
|
118
|
+
Every animation on the page is **justifiable in one sentence, written as a CSS comment above it**. If the sentence is "it looks nice", the animation goes.
|
|
119
|
+
|
|
120
|
+
- Everything moves in CSS, on `transform` and `opacity` (and `filter` on plates where the world does), on the world's curves: an exponential ease-out for entrances, the state curve at 200 to 400ms for hover and press. No spring library, no scroll listener, no component state for a continuous value: motion authored in JavaScript escapes the Kill Switch and is banned on every instance.
|
|
121
|
+
- The two scroll skeletons, where a dial above 5 asks for them, are CSS: a **pinned stack** is `position: sticky` panels in a tall band, and a **horizontal pan** is a sticky band with `animation-timeline: scroll()` behind `@supports`, falling back to a static row. Both are complete compositions at rest.
|
|
122
|
+
- Above motion 2 the page honours `prefers-reduced-motion` by design, not by exception: the Kill Switch at the foot of the token layer kills every animation and transition, and the rest frame you designed at Step 2 is what remains.
|
|
123
|
+
- Ambient loops respect the world's period floor (20s in the chrome world) and move only the plates.
|
|
124
|
+
|
|
125
|
+
## Pre-flight (a single unticked box means the work is not done)
|
|
126
|
+
|
|
127
|
+
Run the kit, not your eyes: `node modules/ui-design/kit/render.js --page <path>` (every state, 1440 / 390 / 320, the modes the states file names, audited against the floors, a reduced-motion pass) and `node modules/ui-design/kit/probe.js --page <path>` for the interaction contract. Then tick:
|
|
128
|
+
|
|
129
|
+
- [ ] **Contrast** measured, not eyeballed: ALL CLEAN in every mode the page serves; the pack floor via `contrast.js`.
|
|
130
|
+
- [ ] **Copy self-audit**: no long dash in visible copy where banned, no emoji, no placeholder names or fake numbers, two-line headline at 1440 and 390.
|
|
131
|
+
- [ ] **Theme and shape locks** held: one accent, one radius scale, one mode pair, no inverted band, no private hex, no page `:root`.
|
|
132
|
+
- [ ] **Imagery sourced by token**: every image is a plate token, an owner asset or absent; no faked screenshot, no labelled empty slot.
|
|
133
|
+
- [ ] **Motion honesty**: every animation has its one-sentence comment; nothing moves in JavaScript; the reduced-motion render is a complete composition.
|
|
134
|
+
- [ ] **Mobile collapse** looked at: the 390 and 320 shots first (the fold, the bar, the bands); nothing scrolls sideways; the 24px floor holds on every control.
|
|
135
|
+
- [ ] **Web vitals by construction**: the LCP element is a plate or the headline, never a script-built node; fonts load with `font-display: swap` and a sized fallback so nothing shifts; live DOM is `createElement` and `textContent`; no layout property animates.
|
|
136
|
+
- [ ] **The read line** from Step 1 and the dials from Step 2 are in the ship notes with the shots you looked at, in which modes.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
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.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- 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 -->
|
|
7
|
+
> **Platform preamble — read before any rule below.** This skill ships with the `ui-design` module (vendored from a scanned origin, or rebuilt in-house from a spec) and runs INSIDE an instance's world, which it reads first and never overrides.
|
|
8
|
+
>
|
|
9
|
+
> 1. **Load the world before any taste rule.** `config/branding.json` over `config/branding.neutral.json` (`theme.ui` is the fifteen tokens: <redacted> colours and two font stacks), `config/design-tokens.json` over `config/design-tokens.neutral.json`, and `DESIGN.md` at the repo root: the instance's palette and derived tiers, faces, materials, motion grammar, components, and its Do's and Don'ts. Where an instance has no `DESIGN.md`, the neutral pack is the world and the platform floors in item 6 are the whole rulebook; say so and build to them. Never invent a palette.
|
|
10
|
+
> 2. **The fifteen-token contract is the only colour source.** Every colour is a `var()` or a `color-mix()` of the fifteen; the only legal literal is pure black or white with alpha as a scrim, mask or halo. No page `:root` (page tokens live on `body`), and every dark rule is written twice: `:root[data-mode="dark"]` and its `prefers-color-scheme` twin.
|
|
11
|
+
> 3. **A style skill produces variants INSIDE that world, never a second world.** The look this skill carries (its palette, faces, materials, mood boards) is a reference for composition and craft; the instance's tokens and faces replace it. Changing the world is the owner's decision and an ADR, not a session's taste.
|
|
12
|
+
> 4. **Images are for hero plates and hero objects only.** UI is built from templates and the instance's world, not painted; any image-generation step below applies to text-free plates and hero objects, never to controls, cards, text, or a screenshot of a screen.
|
|
13
|
+
> 5. **The instance's taste bans apply and win.** Read them from its `DESIGN.md`. Cloud Bongos's own (no em dash or en dash in visible copy, no emoji, no gradient text, no three-equal-card row, no fake screenshot built from divs) are that instance's, not the module's; where this skill's rules conflict with an instance's bans, the instance wins.
|
|
14
|
+
> 6. **The platform floors hold on every instance** and a `DESIGN.md` may tighten them, never loosen them: every control clears 24px on both sides (primary pills 46–48px); AA in both modes (4.5:1 body text, 3:1 large text and non-text) measured from the real stylesheet; and the Kill Switch, `@media (prefers-reduced-motion: reduce)` at the foot of the token layer killing every animation and transition, no motion authored in JavaScript, and a rest frame that is a complete composition on its own.
|
|
15
|
+
> 7. **Look before you ship.** Render the real surface through the module's kit before and after (`/design` Step 2 and `docs/recipes/ui-look-before-you-ship.md`: every state at 1440 / 390 / 320 in dark and light, audited against the floors); a page with no states file gets one first, and new interactive code ships with a pin in the page's own test file.
|
|
16
|
+
>
|
|
17
|
+
> The rules below are the skill's own. A vendored copy carries a `PROVENANCE.md` beside this file naming the origin, the pinned commit, the licence and the scan verdict it was vendored under; an in-house rebuild names its rebuild spec and its ADR in the section right after this block.
|
|
18
|
+
<!-- END PLATFORM PREAMBLE -->
|
|
19
|
+
|
|
20
|
+
## Rebuilt from
|
|
21
|
+
|
|
22
|
+
This skill is an **in-house rebuild**, not a vendored copy. Its documented functionality comes from `Leonxlnx/taste-skill` @ `<redacted>` (MIT; the `taste-skill-v1` entry), which `/scan-before-install` reduced to `dangerous` at the default tier on 2026-08-28, so under [ADR 0198](../../../../docs/adr/<redacted>.md) it could land only as a REBUILD-SPEC. The spec is the body of [task 1003325](https://cloudbongos.com/builders#/task/1003325); this file implements its requirements from scratch, **no upstream bytes consulted or copied**; the landing shape is [ADR 0220](../../../../docs/adr/<redacted>.md).
|
|
23
|
+
|
|
24
|
+
What changed in translation, on purpose: the upstream generation was written for a component framework with a motion library; a platform surface here is static markup, one sheet and the instance's transforms, so the stack conventions are restated for that and **every motion rule lives inside the Kill Switch** (the spring, the magnetic hover and the shared-element transition, which need script, are named as not available rather than quietly kept). The typography and colour rules are read **against the instance's pack**: the pack's faces are the faces and the pack's accent is the accent, whatever this skill would have preferred. Imagery follows the owner's rule (hero plates and hero objects only).
|
|
25
|
+
|
|
26
|
+
## Why v1 is kept
|
|
27
|
+
|
|
28
|
+
The default skill (`design-taste-frontend`) reads the brief, states a design read and sets its dials from presets. This generation does something narrower and more predictable: it starts from a **fixed baseline** and adapts it in conversation, and it carries a **vocabulary** the default folded into its steps. A task that says "v1" means this file, and this file does not change when the default does. If both are in front of you and the task names neither, use the default.
|
|
29
|
+
|
|
30
|
+
## The baseline triple
|
|
31
|
+
|
|
32
|
+
Variance **6**, motion **4**, density **5**. Fixed. Adapt them **conversationally**: when the person you are working with says "calmer", motion drops one; "busier" raises density one; "more editorial" raises variance one. Say the new triple each time it moves. Do not ask anyone to edit this file to change a dial, and do not edit it yourself for a session: the triple is the skill's baseline, the conversation is the override.
|
|
33
|
+
|
|
34
|
+
The motion value is clamped by the world before the dial applies (the chrome world: ambient loops at 20s or slower, one authored arrival, everything dead under reduced motion), and motion 0 is what every page becomes under `prefers-reduced-motion`, so the rest frame is designed first.
|
|
35
|
+
|
|
36
|
+
## Stack conventions for a served surface
|
|
37
|
+
|
|
38
|
+
- **Check the surface's stack before importing anything**: the instance's `package.json`, the directory's nested `CLAUDE.md`, the sheet the page already loads. A platform surface is static HTML served through the instance's transforms, one sheet, and script only for live data (`createElement` / `textContent`, never `innerHTML`). This skill introduces no framework and no library.
|
|
39
|
+
- **If a package is missing, print the install command and stop.** Adding a dependency is a task decision, not a taste decision; the ship notes carry the command, the claim's owner takes it.
|
|
40
|
+
- **Static markup by default; script isolated to leaf behaviour** (a disclosure, a live refresh) and never to motion, because script motion escapes the Kill Switch.
|
|
41
|
+
- **Utility CSS is not assumed.** If a surface already uses one, read its version from the manifest before writing a class; if it does not, write the world's tokens straight.
|
|
42
|
+
- **Breakpoints are the world's** (one, at 900px in the chrome world) and the kit's three widths (1440 / 390 / 320) are the proof; the container is `spacing.container`; a full-height section is the world's floored stage or `100dvh`, never `100vh`.
|
|
43
|
+
|
|
44
|
+
## Typography rules
|
|
45
|
+
|
|
46
|
+
- **Display type is large and tight-tracked**: the pack's display face at the world's display weight and tracking (the chrome world: Manrope 300, `-0.042em`, lowercase). A display line is drawing, not a slogan.
|
|
47
|
+
- **Body measure is capped near 65 characters** (`max-width: 62ch` on prose; the chrome world's bands cap at 56 to 62ch).
|
|
48
|
+
- **The pack's body face is the UI face.** A default UI sans is discouraged for premium work only where the pack does not name it; where the pack does, the pack wins.
|
|
49
|
+
- **Serif is not a UI face** on a working surface (a ledger, a settings page, a form). A look from `modules/ui-design/styles/` that carries a serif carries it for display, and the body stack decides the rest.
|
|
50
|
+
- Every real number is `font-variant-numeric: tabular-nums`; a figure that refreshes must not reflow the composition.
|
|
51
|
+
|
|
52
|
+
## Colour rules
|
|
53
|
+
|
|
54
|
+
- **One accent, the pack's,** spent only on the pressable and the live (the chrome world's One Warm Thing Rule). Under 80% saturation is the pack author's business, not this page's: the accent is not adjusted per page.
|
|
55
|
+
- **No purple-to-blue glow aesthetic**, no neon-on-near-black HUD, no gradient text. These are bans whichever pack is on.
|
|
56
|
+
- **One palette per project** means the pack, and the pack's neutral ramp is the only ramp: no warm grey on one band and cool grey on the next, because there is only `bg`, `bg-card`, `bg-deep`, `bg-frame` and the inks.
|
|
57
|
+
- Every colour is a `var()` or a `color-mix()` of the fifteen; the dark twin is written twice; a literal is pure black or white with alpha as a scrim, mask or halo, and nothing else.
|
|
58
|
+
|
|
59
|
+
## Emoji and icons
|
|
60
|
+
|
|
61
|
+
- **No emoji** in code, markup, copy or alt text. Not as a bullet, not as a status glyph, not in a button label. Cloud Bongos's `DESIGN.md` bans it by name; the default holds on every instance.
|
|
62
|
+
- **Icons come from one small allowed set of families, at one standardised stroke width.** The allowed set on a platform surface is: the world's inline mark (the chrome world's bongo-pair SVG), and one hairline glyph family drawn at a single stroke (`1.5px`, `stroke-linecap: round`) for the few glyphs a page needs (a chevron, an arrow, a magnifier, a close mark). Never two families on one page, never a thick default icon set, never a hand-drawn path introduced for one page. Where a world names its own family, that family is the set.
|
|
63
|
+
- An icon is never the only carrier of meaning: the label is the text, the glyph is the pointer.
|
|
64
|
+
|
|
65
|
+
## Layout bans
|
|
66
|
+
|
|
67
|
+
- **No centred hero above variance 5.** At the baseline the hero is asymmetric: the type on one side, the object (a plate, by token) or air on the other.
|
|
68
|
+
- **No three equal feature cards.** Three unequal objects at three sizes read as a constellation; three equal boxes read as a rank, and the chrome world bans the row by name.
|
|
69
|
+
- **No boxed card containers above density 6.** Content is separated by hairlines (`rule-soft`) and negative space; a card is for a surface that carries its own elevation vocabulary, and the chrome world has none.
|
|
70
|
+
|
|
71
|
+
## Full interaction cycles, not the successful state only
|
|
72
|
+
|
|
73
|
+
Every interactive component ships all of its states, and the page's states file (`<page>.states.json`) names them so the kit renders each:
|
|
74
|
+
|
|
75
|
+
- **Loading**: a skeleton shaped like the layout it replaces (the same columns, the same row heights, the ground tone `bg-deep`), never a spinner.
|
|
76
|
+
- **Empty**: a composed empty state that says what will be here and offers the one next action; a feed with nothing collapses its rows, its meter and its sub-line rather than promising six rows that never come.
|
|
77
|
+
- **Error**: inline, beside the thing that failed, in the world's `err` state colour on text, never a modal for a field.
|
|
78
|
+
- **Press**: tactile feedback on `:active` (`transform: scale(.98)` on the state curve); **focus** with the world's two-tone ring; **hover** on anything pressable.
|
|
79
|
+
- Every control clears the 24px floor in both axes, footer links included; primary pills sit at 46 to 48px.
|
|
80
|
+
|
|
81
|
+
## Motion rules
|
|
82
|
+
|
|
83
|
+
- **The world's curves are the easing.** Where a world names an entrance curve (the chrome world's exponential ease-out, `cubic-bezier(.16, 1, .3, 1)`) and a state curve, use them; where it names none, a curve with a little overshoot for entrances and a plain ease-out for states. No spring library: a spring is script, and script motion is banned.
|
|
84
|
+
- **Staggered list reveals** are CSS: `animation-delay` from an `--i` custom property set in the markup (`style="--i: 3"`), never a script loop.
|
|
85
|
+
- **Shared-element layout transitions and magnetic hover are not available on this platform.** Both need script driving continuous values; the first is replaced by an instant state change under a 200ms crossfade, the second by the world's hover lift (a 6 to 8px `translateY` over 400ms).
|
|
86
|
+
- Everything is dead under the Kill Switch and the rest frame is the composition.
|
|
87
|
+
|
|
88
|
+
## Performance guardrails
|
|
89
|
+
|
|
90
|
+
- Animate **`transform` and `opacity` only** (and `filter` on a plate where the world does). Never a layout property.
|
|
91
|
+
- **Grain and noise overlays** exist only where the world has that material (the expedition look's halftone screen is a look's material, not a default); when they do, they sit on a fixed, `pointer-events: none` pseudo-element.
|
|
92
|
+
- **No arbitrary `z-index`.** A named scale on `body` (`--z-bar`, `--z-band`, `--z-dialog`), reserved for systemic layers.
|
|
93
|
+
- **Full-height sections use dynamic viewport height** (`100dvh`, or the world's floored stage `max(100dvh, 900px)`), never `100vh`.
|
|
94
|
+
- `backdrop-filter` only on a fixed or sticky layer, and only where the world's glass has a blur (light glass over paper is a tint, blur 0).
|
|
95
|
+
|
|
96
|
+
## The vocabulary of premium patterns
|
|
97
|
+
|
|
98
|
+
Named so a brief can ask for one and a review can name what is missing. Each is a pattern inside the world, not a component to import:
|
|
99
|
+
|
|
100
|
+
- **The two-line display**: the h1 as two lines, the second indented, lowercase where the world's display voice is.
|
|
101
|
+
- **The tabular figure**: a real number from the ledger at display weight, tabular, with a caption.
|
|
102
|
+
- **The hairline ledger**: rows built from 1px top rules, baseline-aligned columns, no borders, no boxes.
|
|
103
|
+
- **The door pill**: two equal actions joined in one glass bar, the primary carrying the accent with the world's `accent-on` ink.
|
|
104
|
+
- **The lit rail step**: one step of a pipeline lit in the accent (hairline, dot, name), the rest in ink; exactly one is live.
|
|
105
|
+
- **The leader-line callout**: a 1px diagonal line, a figure, a caption; real numbers only, at most two per surface.
|
|
106
|
+
- **The feathered patch**: a soft patch of the ground behind glyphs that sit on art, so a ground never cuts a hard-edged box out of a plate.
|
|
107
|
+
- **The contact shadow**: the one shadow in a world, and it belongs to the object, not to a component.
|
|
108
|
+
- **The hairline band menu**: a disclosure that unfolds as a full-width band under the bar, over the page, closed by one hairline.
|
|
109
|
+
- **The poster card**: a modal on the card ground closed by one hairline, its regions flat children in the brief's order, every region hiding on null.
|
|
110
|
+
- **Pill or nothing**: every control a full pill (999px), and the one exception the world names.
|
|
111
|
+
|
|
112
|
+
## The tile-grid archetypes
|
|
113
|
+
|
|
114
|
+
Motion-first tile grids, each with its collapse under the world's breakpoint:
|
|
115
|
+
|
|
116
|
+
- **The constellation**: three different objects at deliberately unequal sizes and baseline drops; hover lifts one. Collapses to a vertical stack at unequal sizes, never to three equal circles.
|
|
117
|
+
- **The dense auto-flow**: `grid-auto-flow: dense` with interlocking spans and **zero empty cells**, three to five intentional tiles over eight cluttered ones. Collapses to one column; spans reset.
|
|
118
|
+
- **The hairline ledger grid**: fixed columns (`88px 1fr 152px 96px` in the chrome world), 1px top rules, tabular ids. Collapses to one column per row, the id and the title first.
|
|
119
|
+
- **The five-column rail**: five beats on one rail, one lit; collapses to one column with the hairlines kept.
|
|
120
|
+
|
|
121
|
+
## Pre-flight (short)
|
|
122
|
+
|
|
123
|
+
- [ ] **Mobile collapse** looked at in the 390 and 320 shots; nothing scrolls sideways; every archetype collapsed the way its entry says.
|
|
124
|
+
- [ ] **Effect cleanup**: nothing to clean, because nothing moves in script; any live-refresh timer is cleared on navigation.
|
|
125
|
+
- [ ] **State coverage**: every state in the page's states file rendered by `node modules/ui-design/kit/render.js --page <path>` and ALL CLEAN; the interaction contract PASS under `probe.js`.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
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.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- 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 -->
|
|
7
|
+
> **Platform preamble — read before any rule below.** This skill ships with the `ui-design` module (vendored from a scanned origin, or rebuilt in-house from a spec) and runs INSIDE an instance's world, which it reads first and never overrides.
|
|
8
|
+
>
|
|
9
|
+
> 1. **Load the world before any taste rule.** `config/branding.json` over `config/branding.neutral.json` (`theme.ui` is the fifteen tokens: <redacted> colours and two font stacks), `config/design-tokens.json` over `config/design-tokens.neutral.json`, and `DESIGN.md` at the repo root: the instance's palette and derived tiers, faces, materials, motion grammar, components, and its Do's and Don'ts. Where an instance has no `DESIGN.md`, the neutral pack is the world and the platform floors in item 6 are the whole rulebook; say so and build to them. Never invent a palette.
|
|
10
|
+
> 2. **The fifteen-token contract is the only colour source.** Every colour is a `var()` or a `color-mix()` of the fifteen; the only legal literal is pure black or white with alpha as a scrim, mask or halo. No page `:root` (page tokens live on `body`), and every dark rule is written twice: `:root[data-mode="dark"]` and its `prefers-color-scheme` twin.
|
|
11
|
+
> 3. **A style skill produces variants INSIDE that world, never a second world.** The look this skill carries (its palette, faces, materials, mood boards) is a reference for composition and craft; the instance's tokens and faces replace it. Changing the world is the owner's decision and an ADR, not a session's taste.
|
|
12
|
+
> 4. **Images are for hero plates and hero objects only.** UI is built from templates and the instance's world, not painted; any image-generation step below applies to text-free plates and hero objects, never to controls, cards, text, or a screenshot of a screen.
|
|
13
|
+
> 5. **The instance's taste bans apply and win.** Read them from its `DESIGN.md`. Cloud Bongos's own (no em dash or en dash in visible copy, no emoji, no gradient text, no three-equal-card row, no fake screenshot built from divs) are that instance's, not the module's; where this skill's rules conflict with an instance's bans, the instance wins.
|
|
14
|
+
> 6. **The platform floors hold on every instance** and a `DESIGN.md` may tighten them, never loosen them: every control clears 24px on both sides (primary pills 46–48px); AA in both modes (4.5:1 body text, 3:1 large text and non-text) measured from the real stylesheet; and the Kill Switch, `@media (prefers-reduced-motion: reduce)` at the foot of the token layer killing every animation and transition, no motion authored in JavaScript, and a rest frame that is a complete composition on its own.
|
|
15
|
+
> 7. **Look before you ship.** Render the real surface through the module's kit before and after (`/design` Step 2 and `docs/recipes/ui-look-before-you-ship.md`: every state at 1440 / 390 / 320 in dark and light, audited against the floors); a page with no states file gets one first, and new interactive code ships with a pin in the page's own test file.
|
|
16
|
+
>
|
|
17
|
+
> The rules below are the skill's own. A vendored copy carries a `PROVENANCE.md` beside this file naming the origin, the pinned commit, the licence and the scan verdict it was vendored under; an in-house rebuild names its rebuild spec and its ADR in the section right after this block.
|
|
18
|
+
<!-- END PLATFORM PREAMBLE -->
|
|
19
|
+
|
|
20
|
+
## Rebuilt from
|
|
21
|
+
|
|
22
|
+
This skill is an **in-house rebuild**, not a vendored copy. Its documented functionality comes from `Leonxlnx/taste-skill` @ `<redacted>` (MIT; the `soft-skill` entry, "high-end-visual-design"), which `/scan-before-install` reduced to `dangerous` at the default tier on 2026-08-28, so under [ADR 0198](../../../../docs/adr/<redacted>.md) it could land only as a REBUILD-SPEC. The spec is the body of [task 1003325](https://cloudbongos.com/builders#/task/1003325); this file implements its requirements from scratch, **no upstream bytes consulted or copied**; the landing shape is [ADR 0220](../../../../docs/adr/<redacted>.md).
|
|
23
|
+
|
|
24
|
+
What changed in translation, on purpose: the upstream skill specified **the** fonts and shadows that make a site feel expensive; here the fonts are the pack's two stacks and a shadow exists only where the instance's world has one (the chrome world has exactly one, and it belongs to the sculpture). Its texture archetypes become the looks in the style library; its layout archetypes stay free. Its motion (a hamburger morph, a blurred full-screen menu, an 800ms blurred fade-up on scroll) is kept as CSS inside the Kill Switch, and the scroll entry is honest about what a static page can do. Imagery follows the owner's rule: hero plates and hero objects by token, never a step.
|
|
25
|
+
|
|
26
|
+
## One texture archetype, one layout archetype, per project
|
|
27
|
+
|
|
28
|
+
Pick **one of each** before anything is drawn, write the pair in the sheet's header comment, and record it in the ship notes. The same pair is never used twice across sibling surfaces of one instance: the second surface that needs "premium" takes a different layout archetype under the same texture, because the texture is the instance's and the layout is yours.
|
|
29
|
+
|
|
30
|
+
**Texture archetypes** map to the style library, `modules/ui-design/styles/`, and to the world; the pack in force fixes which one you are in:
|
|
31
|
+
|
|
32
|
+
| Archetype | In this library | What carries it |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| Dark glass tech | `chrome-world` (the neutral pack) | the void, the glass tokens, one rendered chrome plate, a black type halo |
|
|
35
|
+
| Warm editorial luxury | `expedition` | warm paper, a halftone screen, hairline frames, heavy uppercase display, one lamp yellow as a fill |
|
|
36
|
+
| Soft light structuralism | `grove` | pale sage, soft corners on what holds content, tinted shadows, a sea slab under a band |
|
|
37
|
+
|
|
38
|
+
A page never switches texture. To see a surface under another texture, render it with `--style <name>` on the kit; adopting one is the owner's `config/branding.json`, not a page.
|
|
39
|
+
|
|
40
|
+
**Layout archetypes** (the free choice):
|
|
41
|
+
|
|
42
|
+
- **The asymmetric tile grid**: interlocking spans, zero empty cells, one tile that breaks the container by a gutter.
|
|
43
|
+
- **The depth cascade**: bands that overlap by a step (a negative top margin of `spacing.lg`), the object (a plate, by token) crossing two of them.
|
|
44
|
+
- **The editorial split**: a hard vertical split at the golden-ish 5:7, type on the narrow side, the plate or the air on the wide side, a hairline down the seam.
|
|
45
|
+
|
|
46
|
+
## The bans
|
|
47
|
+
|
|
48
|
+
Defaults an instance's `DESIGN.md` may only tighten:
|
|
49
|
+
|
|
50
|
+
- **The default sans stack** as a display face, unless it is the pack's face (then it is the face; the pack wins).
|
|
51
|
+
- **Thick default icon sets.** One hairline stroke width, the world's inline mark, or no icon.
|
|
52
|
+
- **Plain grey hairline borders** as decoration. A hairline is structure (`rule` / `rule-soft`, tokenised); it separates, it does not box.
|
|
53
|
+
- **Harsh dark drop shadows.** Where the world has a shadow at all, it is tinted with the ink (`color-mix(in srgb, var(--ink) 20%, transparent)`) and it belongs to an object; the chrome world's UI has none.
|
|
54
|
+
- **Edge-glued navbars.** The bar keeps the world's gutter on both sides and one hairline under it.
|
|
55
|
+
- **Symmetric three-column grids** (the three-equal-card row the chrome world bans by name).
|
|
56
|
+
- **Default easing** (`ease`, `linear`, `ease-in-out` on an entrance). The world's curves, or none.
|
|
57
|
+
|
|
58
|
+
## The container anatomy, where the world allows a container
|
|
59
|
+
|
|
60
|
+
Where a surface carries a panel (a dialog, the poster card, a settings sheet), a major container is **two concentric shapes**: an outer shell and an inner core.
|
|
61
|
+
|
|
62
|
+
- The shell: the ground one step above the page (`bg-card`), the world's panel radius (`rounded.panel`), a **hairline ring** in `glass-edge` (dark) / `rule-soft` (light).
|
|
63
|
+
- The core: inset by one spacing step (`spacing.sm`), an **inner highlight** as a 1px inset pure-white line at low alpha in dark (the one legal literal), and an **inner radius mathematically concentric**: `calc(var(--radius-panel) - var(--space-sm))`, never the same radius as the shell.
|
|
64
|
+
- The chrome world has no card vocabulary below the fold (hairlines only); there the anatomy applies to the one thing that is allowed to be a panel, and the sheet says so in a comment. A world that says "no containers" is not overruled by this section.
|
|
65
|
+
|
|
66
|
+
## The button anatomy
|
|
67
|
+
|
|
68
|
+
A primary call to action is a **pill** (999px) at 46 to 48px, the accent as the fill and the world's `accent-on` as the ink, and its trailing arrow sits **inside its own circular wrapper**, flush with the pill's inner padding, never a bare glyph beside the label:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
[ Open the hall (→) ] the disc: accent-on at 12% over the fill (color-mix), 28px, the glyph centred
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The secondary action is the same pill, transparent at rest, glass on hover, no disc. Both clear the 24px floor by construction. A world that joins the two into one bar (the chrome world's door pill) is that world's signature; keep it.
|
|
75
|
+
|
|
76
|
+
## Spatial rhythm
|
|
77
|
+
|
|
78
|
+
- **Section padding roughly doubles** the body's rhythm: the world's band clamp is the floor and twice it is the ceiling (`clamp(58px, 8.4vh, 104px)` doubled to `clamp(116px, 16.8vh, 208px)` on a hero-adjacent band in the chrome world); heavy breathing room between chapters, tight rhythm inside one.
|
|
79
|
+
- **Tiny uppercase eyebrows** above major headings, in the world's label face, as a hairline pill or bare: at most one per three sections, and never above the h1 where the world bans it (the chrome world does).
|
|
80
|
+
- The container is `spacing.container`; the gutter is the world's; nothing scrolls sideways at any width.
|
|
81
|
+
|
|
82
|
+
## Motion choreography, inside the Kill Switch
|
|
83
|
+
|
|
84
|
+
All CSS, on the world's curves; nothing moves in script; everything dead under `prefers-reduced-motion`, where the rest frame is the complete composition.
|
|
85
|
+
|
|
86
|
+
- **Easing that simulates mass**: the world's entrance curve (the chrome world's `cubic-bezier(.16, 1, .3, 1)`) for arrivals, the state curve for hover and press. Where a world names none, an exponential ease-out.
|
|
87
|
+
- **The hamburger that morphs into a close mark**: three bars as three pseudo-elements; the `aria-expanded` toggle (state, in script) flips a class; the bars rotate and collapse in CSS over 300ms. Under the switch they swap instantly.
|
|
88
|
+
- **The full-screen blurred menu**: a fixed layer with `backdrop-filter` (blur is allowed here because the layer is fixed), links revealed in a **staggered slide-up** via `animation-delay` from `--i` in the markup. Under the switch the menu is simply open and complete. On the chrome world's apex the menu is a hairline band, not a full-screen layer; the world's component wins.
|
|
89
|
+
|
|
90
|
+
## Hover physics
|
|
91
|
+
|
|
92
|
+
Internal kinetic tension instead of a colour swap: on `:hover` the button holds; on `:active` the button **scales down** (`scale(.98)`) while the nested arrow disc **translates diagonally and scales up** (`translate(2px, -2px) scale(1.1)`), both on `transform` only, over 200 to 400ms on the state curve. Focus restates the world's ring. Nothing here needs the pointer's position, so nothing here needs script.
|
|
93
|
+
|
|
94
|
+
## Scroll entry
|
|
95
|
+
|
|
96
|
+
Elements arrive with a **slow blurred fade-up over roughly 800ms**: `opacity: 0; translate: 0 16px; filter: blur(6px)` to rest, on the entrance curve.
|
|
97
|
+
|
|
98
|
+
- Prefer **CSS scroll-driven animation** (`animation-timeline: view()`) behind `@supports`, which needs no script.
|
|
99
|
+
- Where that is unsupported, an `IntersectionObserver` may add **one class**; the motion stays in CSS. The entry state is applied only once the observer is live (a `.js` class on `body`), so a page without script, or with the observer not yet run, shows the composed rest frame and never a hidden element.
|
|
100
|
+
- Never a raw scroll listener, never a continuous value in script.
|
|
101
|
+
- Under the switch there is no entry: every element is at rest.
|
|
102
|
+
|
|
103
|
+
## Mobile collapse, per archetype
|
|
104
|
+
|
|
105
|
+
Below the world's breakpoint (900px in the chrome world; the tablet breakpoint elsewhere):
|
|
106
|
+
|
|
107
|
+
- **Asymmetric tile grid**: spans reset, one column, full width, tiles in reading order.
|
|
108
|
+
- **Depth cascade**: overlaps and negative margins removed; bands stack with the hairline between them; the plate becomes a masthead above the words.
|
|
109
|
+
- **Editorial split**: the seam becomes a hairline between two stacked bands; the narrow side first.
|
|
110
|
+
- Rotations, offsets and overlaps go; a full-height section is `100dvh` or the world's floored stage, never `100vh`; the bar holds one row down to 320px.
|
|
111
|
+
|
|
112
|
+
## Performance rules
|
|
113
|
+
|
|
114
|
+
- Animate **`transform` and `opacity` only** (and `filter` on a plate where the world does).
|
|
115
|
+
- **Backdrop blur only on fixed or sticky layers**; light glass over paper is a tint at blur 0, as the world's glass token says.
|
|
116
|
+
- **Noise and grain** only where the world has that material, on a fixed, `pointer-events: none` pseudo-element.
|
|
117
|
+
- **`z-index` reserved for systemic layers** on a named scale on `body`; no arbitrary values.
|
|
118
|
+
- The LCP element is the plate or the headline; fonts have a sized fallback; no layout property animates.
|
|
119
|
+
|
|
120
|
+
## Pre-output checklist
|
|
121
|
+
|
|
122
|
+
- [ ] The **archetype pair** is named in the sheet header and the ship notes, and it is not the pair a sibling surface already used.
|
|
123
|
+
- [ ] **Nested shells** on every major container the world allows one on; concentric inner radius; hairline ring; inner highlight in dark.
|
|
124
|
+
- [ ] **Section padding** at or above the world's band clamp; eyebrows rationed; the h1 has none where banned.
|
|
125
|
+
- [ ] **The world's easing** on every transition; no default curve.
|
|
126
|
+
- [ ] **Scroll entry** honest: CSS timeline or one observer class; rest frame complete without script and under the switch.
|
|
127
|
+
- [ ] **Mobile collapse** per archetype, looked at in the 390 and 320 shots.
|
|
128
|
+
- [ ] **The premium impression** judged on the kit's shots (`node modules/ui-design/kit/render.js --page <path>`, ALL CLEAN in every mode the page serves), not on the source; the button's disc and the ring measured, not eyeballed.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
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.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- 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 -->
|
|
7
|
+
> **Platform preamble — read before any rule below.** This skill ships with the `ui-design` module (vendored from a scanned origin, or rebuilt in-house from a spec) and runs INSIDE an instance's world, which it reads first and never overrides.
|
|
8
|
+
>
|
|
9
|
+
> 1. **Load the world before any taste rule.** `config/branding.json` over `config/branding.neutral.json` (`theme.ui` is the fifteen tokens: <redacted> colours and two font stacks), `config/design-tokens.json` over `config/design-tokens.neutral.json`, and `DESIGN.md` at the repo root: the instance's palette and derived tiers, faces, materials, motion grammar, components, and its Do's and Don'ts. Where an instance has no `DESIGN.md`, the neutral pack is the world and the platform floors in item 6 are the whole rulebook; say so and build to them. Never invent a palette.
|
|
10
|
+
> 2. **The fifteen-token contract is the only colour source.** Every colour is a `var()` or a `color-mix()` of the fifteen; the only legal literal is pure black or white with alpha as a scrim, mask or halo. No page `:root` (page tokens live on `body`), and every dark rule is written twice: `:root[data-mode="dark"]` and its `prefers-color-scheme` twin.
|
|
11
|
+
> 3. **A style skill produces variants INSIDE that world, never a second world.** The look this skill carries (its palette, faces, materials, mood boards) is a reference for composition and craft; the instance's tokens and faces replace it. Changing the world is the owner's decision and an ADR, not a session's taste.
|
|
12
|
+
> 4. **Images are for hero plates and hero objects only.** UI is built from templates and the instance's world, not painted; any image-generation step below applies to text-free plates and hero objects, never to controls, cards, text, or a screenshot of a screen.
|
|
13
|
+
> 5. **The instance's taste bans apply and win.** Read them from its `DESIGN.md`. Cloud Bongos's own (no em dash or en dash in visible copy, no emoji, no gradient text, no three-equal-card row, no fake screenshot built from divs) are that instance's, not the module's; where this skill's rules conflict with an instance's bans, the instance wins.
|
|
14
|
+
> 6. **The platform floors hold on every instance** and a `DESIGN.md` may tighten them, never loosen them: every control clears 24px on both sides (primary pills 46–48px); AA in both modes (4.5:1 body text, 3:1 large text and non-text) measured from the real stylesheet; and the Kill Switch, `@media (prefers-reduced-motion: reduce)` at the foot of the token layer killing every animation and transition, no motion authored in JavaScript, and a rest frame that is a complete composition on its own.
|
|
15
|
+
> 7. **Look before you ship.** Render the real surface through the module's kit before and after (`/design` Step 2 and `docs/recipes/ui-look-before-you-ship.md`: every state at 1440 / 390 / 320 in dark and light, audited against the floors); a page with no states file gets one first, and new interactive code ships with a pin in the page's own test file.
|
|
16
|
+
>
|
|
17
|
+
> The rules below are the skill's own. A vendored copy carries a `PROVENANCE.md` beside this file naming the origin, the pinned commit, the licence and the scan verdict it was vendored under; an in-house rebuild names its rebuild spec and its ADR in the section right after this block.
|
|
18
|
+
<!-- END PLATFORM PREAMBLE -->
|
|
19
|
+
|
|
20
|
+
## Rebuilt from
|
|
21
|
+
|
|
22
|
+
This skill is an **in-house rebuild**, not a vendored copy. Its documented functionality comes from `Leonxlnx/taste-skill` @ `<redacted>` (MIT; the `redesign-skill` entry), which `/scan-before-install` reduced to `dangerous` at the default tier on 2026-08-28, so under [ADR 0198](../../../../docs/adr/<redacted>.md) it could land only as a REBUILD-SPEC. The spec is the body of [task 1003325](https://cloudbongos.com/builders#/task/1003325); this file implements its requirements from scratch, **no upstream bytes consulted or copied**; the landing shape is [ADR 0220](../../../../docs/adr/<redacted>.md).
|
|
23
|
+
|
|
24
|
+
What changed in translation, on purpose: the upstream audit carried its own opinions (pure black backgrounds are a defect, flat sections are unfinished); here **the instance's world is the answer key**, so an audit row is a defect only where the world does not call for it (the chrome world's dark ground is pure black by the owner's decision, and its bands are flat by design). The upgrade catalogue marks each technique **CSS-only or not available** on a platform where nothing moves in script. Imagery follows the owner's rule.
|
|
25
|
+
|
|
26
|
+
## The three steps, in order
|
|
27
|
+
|
|
28
|
+
1. **Scan.** Identify the framework and the styling method from the tree, not from memory: the instance's `package.json`, the directory's nested `CLAUDE.md`, the sheet the page loads, `config/design-sources.json` (which tool authored the surface). On a platform surface that is static HTML, one sheet and the instance's transforms. Then load the world (the preamble's order) and the page's states file. Render **before** with the kit and keep the shots: `node modules/ui-design/kit/render.js --page <path> --out <dir>/before`.
|
|
29
|
+
2. **Diagnose** against the written audit below. Every row is a yes or a no with a line reference; the rows that are "yes" and not called for by the world are the work. Write the list in the ship notes before touching anything.
|
|
30
|
+
3. **Fix**, in the impact-per-risk order below, one group per commit, and render **after** with the same states file. The diff names which audit rows it closed.
|
|
31
|
+
|
|
32
|
+
## The audit (the world is the answer key)
|
|
33
|
+
|
|
34
|
+
### Typography
|
|
35
|
+
|
|
36
|
+
- Default faces where the pack names others: the two stacks are the faces.
|
|
37
|
+
- Weak headline presence: display size, weight and tracking are the world's (the chrome world: 300, `-0.042em`, two lines).
|
|
38
|
+
- Over-wide measure: prose above 65ch.
|
|
39
|
+
- Only two weights in use: the world's hierarchy has display, body and a term weight; a page that uses two is flat.
|
|
40
|
+
- Proportional numerals in data: every real number is `tabular-nums`.
|
|
41
|
+
- Missing tracking adjustments on display sizes; orphaned last words on headlines (`text-wrap: balance` on headings, `pretty` on prose).
|
|
42
|
+
|
|
43
|
+
### Colour and surface
|
|
44
|
+
|
|
45
|
+
- **Pure black backgrounds** where the world does not call for one (the chrome world does; a page inside it is not in violation).
|
|
46
|
+
- Oversaturated or multiple accents: the accent is the pack's `accent`, once; a second voice is `sea`.
|
|
47
|
+
- Mixed warm and cool greys: only the pack's neutral ramp.
|
|
48
|
+
- The purple-blue generated gradient signature; gradient text; a neon glow: banned on every pack.
|
|
49
|
+
- Untinted shadows: a shadow, where the world has one, is tinted with `ink`.
|
|
50
|
+
- Zero texture: a defect only where the world has a material (a plate, a screen); the chrome world's texture is its plate, and nothing else is painted.
|
|
51
|
+
- Inconsistent light direction across plates, shadows and highlights.
|
|
52
|
+
- **A lone inverted section** inside an otherwise light or dark page: the copy-paste accident; the chrome world bans a black panel on a light page.
|
|
53
|
+
- **Flat text-on-plain-ground sections** read as unfinished **where the world builds depth**; where the world is hairlines by design, a flat band is the design.
|
|
54
|
+
|
|
55
|
+
### Layout
|
|
56
|
+
|
|
57
|
+
- Total symmetry; three equal feature columns; every card the same size.
|
|
58
|
+
- Fixed `100vh` sections: the world's floored stage or `100dvh`.
|
|
59
|
+
- Flexbox percentage maths for a grid: `grid` with named tracks.
|
|
60
|
+
- Missing `max-width`: the container is `spacing.container`.
|
|
61
|
+
- Uniform radii everywhere: the world's three-step radius tier (pill, panel, tile), and nothing between.
|
|
62
|
+
- No overlap or depth where the world has a cascade; misaligned baselines across side-by-side cards.
|
|
63
|
+
|
|
64
|
+
### Interaction
|
|
65
|
+
|
|
66
|
+
- Missing hover, press, focus, loading, empty or error states: every one is a state the page's states file names and the kit renders.
|
|
67
|
+
- Dead placeholder links (`href="#"`): a link goes somewhere or is not a link.
|
|
68
|
+
- No active-page indicator in navigation (the world's lit hairline, `aria-current`).
|
|
69
|
+
- Animation of layout-triggering properties; motion in script; anything that escapes the Kill Switch.
|
|
70
|
+
|
|
71
|
+
### Content
|
|
72
|
+
|
|
73
|
+
Text is `copy-desk`'s; this audit **flags** and the ship notes carry the flags. Placeholder person names, fake round numbers, invented startup brand names, cliche marketing verbs, exclamation marks in success copy, passive voice, identical dates, repeated avatars. The fixtures under `modules/ui-design/kit/fixtures/` are the neutral builder and the real feed shapes; a page rendered with them shows whether the layout survives real data.
|
|
74
|
+
|
|
75
|
+
### Code and omissions
|
|
76
|
+
|
|
77
|
+
- Div soup where the world requires structure: one `<main>`, one `<header>`, one `<footer>`, one `<h1>`, a skip link that targets `<main>`.
|
|
78
|
+
- Inline styles; arbitrary `z-index` values (a named scale on `body`); missing `alt` text; missing social meta tags; imports that do not exist.
|
|
79
|
+
- Missing legal links (the footer row: privacy, terms, contact), a 404 that renders the world (the platform's game-less 404 tail), field validation, the skip link (a floor, not a nicety), a consent banner where the instance needs one.
|
|
80
|
+
|
|
81
|
+
## The upgrade catalogue
|
|
82
|
+
|
|
83
|
+
A replacement for each generic pattern, each marked for this platform:
|
|
84
|
+
|
|
85
|
+
| Generic pattern | Upgrade | On this platform |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| A static headline | **Variable-font animation** on `font-variation-settings`, on the state curve | CSS-only, where the pack's face is variable |
|
|
88
|
+
| A fade-in title | **Text-mask reveal**: a clip-path wipe on the entrance curve | CSS-only; a gradient mask is banned where the world bans gradient text |
|
|
89
|
+
| A symmetric grid | **The broken grid**: asymmetric spans, one tile past the container by a gutter, zero empty cells | CSS-only |
|
|
90
|
+
| A card row | **The parallax card stack**: sticky panels in a tall band, each one step lower | CSS-only (`position: sticky`) |
|
|
91
|
+
| Linear easing | **Spring-like motion**: the world's entrance curve, a little overshoot where the world names none | CSS-only; no spring library |
|
|
92
|
+
| A flat overlay | **True layered glass**: the world's glass tokens, blur only on a fixed layer | CSS-only |
|
|
93
|
+
| A hover border | **Spotlight border** following the pointer | **Not available** (needs pointer tracking in script); use a hairline that lights to the accent on hover |
|
|
94
|
+
| A grey drop shadow | **Tinted shadow**: `color-mix(in srgb, var(--ink) 20%, transparent)` | CSS-only, where the world has a shadow |
|
|
95
|
+
| A flat ground | **Grain** on a fixed pointer-inert pseudo-element | Only where the world has that material |
|
|
96
|
+
| An empty hero | **The plate**: the world's hero object by token, or the air | Hero plates and hero objects only; never a generated image as a step |
|
|
97
|
+
|
|
98
|
+
## The fix order (impact per risk)
|
|
99
|
+
|
|
100
|
+
Apply in this order, one group per commit, and stop when the audit list is empty:
|
|
101
|
+
|
|
102
|
+
1. **Fonts**: the pack's two stacks through the world's roles; `font-display: swap` with a sized fallback.
|
|
103
|
+
2. **Palette cleanup**: every literal becomes a `var()` or a `color-mix()` of the fifteen; the dark twin written twice; no page `:root`; the accent spent only on the pressable and the live. A page that "needs a different palette" needs a look: render it under one from `modules/ui-design/styles/` with `--style <name>` on the kit to compare, and leave adopting it to the owner's `config/branding.json`; a redesign never carries a private palette.
|
|
104
|
+
3. **Hover, press and focus states**: the state curve, the world's ring, the 24px floor on every control.
|
|
105
|
+
4. **Layout and spacing**: the world's rhythm, the container, the breakpoint, the radius tier.
|
|
106
|
+
5. **Component replacement**: the world's components for the page's generic ones (its bar, its door, its ledger rows, its card).
|
|
107
|
+
6. **Loading, empty and error states**: designed, named in the states file, rendered.
|
|
108
|
+
7. **Typographic polish**: tracking, measure, tabular figures, balanced headings.
|
|
109
|
+
|
|
110
|
+
## Hard rules
|
|
111
|
+
|
|
112
|
+
- **Keep the existing stack. Never migrate frameworks.** A redesign that changes the build tool is a different task and an ADR.
|
|
113
|
+
- **Do not break functionality.** `node modules/ui-design/kit/probe.js --page <path>` is PASS before the first commit and after the last; a probe that was green and goes red reverts the group that broke it.
|
|
114
|
+
- **Check the dependency manifest and the styling-library version before adding anything**, and adding is a task decision: print the command in the ship notes, do not run it inside a taste pass.
|
|
115
|
+
- **Small, reviewable diffs**: one audit group per commit, each naming its rows. The grader reads the real diff.
|
|
116
|
+
- **The world wins.** An audit row the instance's `DESIGN.md` calls for by name is not a defect; say so in the notes and move on.
|
|
117
|
+
|
|
118
|
+
## Proof
|
|
119
|
+
|
|
120
|
+
The before and after shots from the same states file, side by side, in the modes the page serves and at 1440 / 390 / 320; `render.js` ALL CLEAN after; `probe.js` PASS before and after; the contrast pairs measured; the audit list in the ship notes with every closed row struck and every world-sanctioned row marked as such.
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.461",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.461",
|
|
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.
|
|
3
|
+
"version": "1.19.461",
|
|
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",
|
|
@@ -259,8 +259,12 @@ function scanDuplicateProse(relFiles) {
|
|
|
259
259
|
for (const { text, startLine } of paragraphs) {
|
|
260
260
|
const trimmed = text.trim();
|
|
261
261
|
if (trimmed.length < MIN_DUPE_CHARS) continue;
|
|
262
|
-
// Skip content that is intentionally generated/duplicated across files
|
|
262
|
+
// Skip content that is intentionally generated/duplicated across files:
|
|
263
|
+
// generated blocks, and the ui-design module's PLATFORM PREAMBLE, which
|
|
264
|
+
// its policy requires byte-identical in every skill it ships (ADR 0198 §4;
|
|
265
|
+
// tests/ui_design_skills.mjs pins the copies against PREAMBLE.md).
|
|
263
266
|
if (trimmed.startsWith('<!-- BEGIN GENERATED')) continue;
|
|
267
|
+
if (trimmed.startsWith('<!-- BEGIN PLATFORM PREAMBLE')) continue;
|
|
264
268
|
// Normalize internal whitespace for comparison
|
|
265
269
|
const key = trimmed.replace(/\s+/g, ' ');
|
|
266
270
|
if (!byText.has(key)) byText.set(key, []);
|
package/src/module-api.js
CHANGED
|
@@ -33,7 +33,7 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
33
33
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
34
34
|
// the entry to that file. Look for a version's history there, not here.
|
|
35
35
|
// ---------------------------------------------------------------------------
|
|
36
|
-
const CORE_VERSION = '1.19.
|
|
36
|
+
const CORE_VERSION = '1.19.461'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
37
37
|
|
|
38
38
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
39
39
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
package/tests/docs_entropy.mjs
CHANGED
|
@@ -19,7 +19,7 @@ import path from 'node:path';
|
|
|
19
19
|
const require = createRequire(import.meta.url);
|
|
20
20
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
21
21
|
const SCANNER = path.join(ROOT, 'scripts', 'gds', 'docs-entropy.js');
|
|
22
|
-
const { collectMarkdownFiles, parseFile } = require(SCANNER); // require.main-guarded → no scan on import
|
|
22
|
+
const { collectMarkdownFiles, parseFile, scanDuplicateProse } = require(SCANNER); // require.main-guarded → no scan on import
|
|
23
23
|
|
|
24
24
|
test('collectMarkdownFiles passes the glob as an argv array, never a shell string', () => {
|
|
25
25
|
let seen = null;
|
|
@@ -138,3 +138,31 @@ test('the real scan on THIS checkout matches the CI baselines (the platform-pari
|
|
|
138
138
|
+ 'on a CRLF checkout this read 21, because every heading in every CRLF file went unparsed',
|
|
139
139
|
);
|
|
140
140
|
});
|
|
141
|
+
|
|
142
|
+
// The duplicate-prose scan skips content that is DUPLICATED BY POLICY: generated blocks,
|
|
143
|
+
// and the ui-design module's PLATFORM PREAMBLE, which ADR 0198 §4 requires byte-identical
|
|
144
|
+
// in every skill the module ships (task 1003325 / ADR 0220 — four in-house copies would
|
|
145
|
+
// have tripped the doc_duplicate_prose ratchet on the policy working as designed). The
|
|
146
|
+
// skip is a marker prefix, so a plain paragraph beside it in the same two files is still
|
|
147
|
+
// flagged: the calibration that proves the skip is narrow.
|
|
148
|
+
test('scanDuplicateProse skips a paragraph opening with the PLATFORM PREAMBLE marker, and still flags a plain duplicate beside it', () => {
|
|
149
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'docs-entropy-preamble-'));
|
|
150
|
+
const plain = 'A plain paragraph long enough to clear the scanner minimum of one hundred and twenty characters, written into two files so the scan has a real duplicate to report.';
|
|
151
|
+
const preamble = [
|
|
152
|
+
'<!-- BEGIN PLATFORM PREAMBLE · a fixture block, long enough to clear the minimum on its own, duplicated on purpose -->',
|
|
153
|
+
'> **Platform preamble.** Read the world first; this block is byte-identical in every skill the module ships.',
|
|
154
|
+
'<!-- END PLATFORM PREAMBLE -->',
|
|
155
|
+
].join('\n');
|
|
156
|
+
const generated = '<!-- BEGIN GENERATED fixture --> a generated block long enough to clear the minimum, also duplicated on purpose, also skipped <!-- END GENERATED -->';
|
|
157
|
+
for (const name of ['a.md', 'b.md']) {
|
|
158
|
+
fs.writeFileSync(path.join(tmp, name), `# ${name}\n\n${plain}\n\n${preamble}\n\n${generated}\n`);
|
|
159
|
+
}
|
|
160
|
+
// scanDuplicateProse resolves each rel against the repo root, so a temp file is
|
|
161
|
+
// addressed by its relative path from there (path.join normalises the `..` hops).
|
|
162
|
+
const rel = (f) => path.relative(ROOT, path.join(tmp, f)).split(path.sep).join('/');
|
|
163
|
+
const dupes = scanDuplicateProse([rel('a.md'), rel('b.md')]);
|
|
164
|
+
assert.equal(dupes.length, 1, `exactly the plain paragraph is flagged, got ${JSON.stringify(dupes.map((d) => d.snippet.slice(0, 40)))}`);
|
|
165
|
+
assert.ok(dupes[0].snippet.startsWith('A plain paragraph'), 'the flagged block is the plain paragraph');
|
|
166
|
+
assert.equal(dupes[0].locations.length, 2, 'seen in both files');
|
|
167
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
168
|
+
});
|
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
// opens with the PLATFORM PREAMBLE byte-identical to PREAMBLE.md; every name the manifest
|
|
5
5
|
// contributes resolves to a real skill dir; a module-owned skill reaches a builder's
|
|
6
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.
|
|
7
|
+
// lists every vendored skill by name; the policy ADR is indexed. Since task 1003325 (ADR
|
|
8
|
+
// 0220) the same file pins the IN-HOUSE rebuilds under skills/<name>/: the same preamble
|
|
9
|
+
// first and once, a `## Rebuilt from` section instead of a sidecar (the spec task, ADR
|
|
10
|
+
// 0198, "no upstream bytes"), the style library named as the palette source, no image
|
|
11
|
+
// mandate, and the preamble copies proven not to be doc-entropy debt. DB-free.
|
|
8
12
|
import assert from 'node:assert/strict';
|
|
9
13
|
import { test } from 'node:test';
|
|
10
14
|
import { createRequire } from 'node:module';
|
|
@@ -93,6 +97,85 @@ test('every vendored SKILL.md opens with the PLATFORM PREAMBLE, byte-identical t
|
|
|
93
97
|
}
|
|
94
98
|
});
|
|
95
99
|
|
|
100
|
+
// ── every in-house rebuild: the preamble, the Rebuilt-from section, no sidecar (ADR 0220) ──
|
|
101
|
+
const TASTE_CORE = ['design-taste-frontend', 'design-taste-frontend-v1', 'high-end-visual-design', 'redesign-existing-projects'];
|
|
102
|
+
|
|
103
|
+
test('the taste core of task 1003325 exists as four in-house skills and every one is declared', () => {
|
|
104
|
+
for (const name of TASTE_CORE) {
|
|
105
|
+
assert.ok(ownDirs.includes(name), `${name}: skills/${name}/SKILL.md exists`);
|
|
106
|
+
assert.ok(manifest.contributes.skills.includes(name), `${name}: declared in contributes.skills`);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('every in-house SKILL.md opens with the PLATFORM PREAMBLE, byte-identical to PREAMBLE.md, exactly once, right after its frontmatter', () => {
|
|
111
|
+
const pre = read(path.join(SKILLS, 'PREAMBLE.md')).trimEnd();
|
|
112
|
+
for (const name of ownDirs) {
|
|
113
|
+
const text = read(path.join(SKILLS, name, 'SKILL.md'));
|
|
114
|
+
const fm = text.match(/^---\n[\s\S]*?\n---\n/);
|
|
115
|
+
assert.ok(fm, `${name}: SKILL.md keeps its frontmatter first`);
|
|
116
|
+
const body = text.slice(fm[0].length).replace(/^\n+/, '');
|
|
117
|
+
assert.ok(body.startsWith(pre), `${name}: the preamble block is the first thing after the frontmatter and matches PREAMBLE.md byte for byte`);
|
|
118
|
+
assert.equal(text.split(policy.preamble.begin).length, 2, `${name}: the preamble appears exactly once`);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('every in-house skill names its dir, carries a description, and says what it was rebuilt from instead of carrying a sidecar', () => {
|
|
123
|
+
const pre = read(path.join(SKILLS, 'PREAMBLE.md')).trimEnd();
|
|
124
|
+
for (const name of ownDirs) {
|
|
125
|
+
const dir = path.join(SKILLS, name);
|
|
126
|
+
const text = read(path.join(dir, 'SKILL.md'));
|
|
127
|
+
const fm = frontmatter(text);
|
|
128
|
+
assert.equal(fm.name, name, `${name}: frontmatter names its own dir`);
|
|
129
|
+
assert.ok(fm.description && fm.description.length > 80, `${name}: a description the harness can route on`);
|
|
130
|
+
assert.ok(!fs.existsSync(path.join(dir, policy.sidecar)), `${name}: an in-house skill claims no upstream, so no ${policy.sidecar}`);
|
|
131
|
+
assert.ok(!fs.existsSync(path.join(dir, 'LICENSE')), `${name}: no upstream licence text travels with a rebuild`);
|
|
132
|
+
const afterPreamble = text.slice(text.indexOf(pre) + pre.length);
|
|
133
|
+
const section = afterPreamble.match(/^## Rebuilt from\n([\s\S]*?)(?=\n## )/m);
|
|
134
|
+
assert.ok(section, `${name}: a "## Rebuilt from" section is the first thing after the preamble`);
|
|
135
|
+
assert.match(afterPreamble.trimStart(), /^## Rebuilt from\n/, `${name}: nothing sits between the preamble and the Rebuilt-from section`);
|
|
136
|
+
assert.match(section[1], /builders#\/task\/\d+/, `${name}: the section links the spec task`);
|
|
137
|
+
assert.match(section[1], /ADR 0198/, `${name}: the section names the vendoring policy that made it a rebuild`);
|
|
138
|
+
assert.match(section[1], /REBUILD-SPEC/, `${name}: the section names the spec`);
|
|
139
|
+
assert.match(section[1], /no upstream bytes/i, `${name}: the section states no upstream bytes were consulted or copied`);
|
|
140
|
+
assert.match(section[1], /what changed in translation/i, `${name}: the section says where the rebuild departs from the documented behaviour`);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('every in-house skill works inside the world: the style library is the palette source, images are hero plates only, and no step mandates generating one', () => {
|
|
145
|
+
const pre = read(path.join(SKILLS, 'PREAMBLE.md')).trimEnd();
|
|
146
|
+
for (const name of ownDirs) {
|
|
147
|
+
const text = read(path.join(SKILLS, name, 'SKILL.md'));
|
|
148
|
+
const own = text.slice(text.indexOf(pre) + pre.length); // the skill's own rules, not the shared block
|
|
149
|
+
assert.ok(own.includes('modules/ui-design/styles/'), `${name}: names the style library as where a variant comes from`);
|
|
150
|
+
assert.match(own, /hero plates/i, `${name}: restates the owner's imagery rule in its own rules`);
|
|
151
|
+
assert.doesNotMatch(own, /\b(must|always|first|then)\s+generate\s+(an?\s+|the\s+)?(image|asset|photo|render)/i, `${name}: no step mandates image generation (ADR 0198's panel finding; the owner's rule)`);
|
|
152
|
+
assert.doesNotMatch(own, /\b(use|add|insert|prefer|fall back to|source)\s+(seeded\s+)?placeholder (photo|imag)/i, `${name}: no step reaches for seeded placeholder photography (naming the ban is fine; directing it is not)`);
|
|
153
|
+
assert.match(own, /Kill Switch|prefers-reduced-motion/, `${name}: its motion rules name the switch they live inside`);
|
|
154
|
+
assert.match(own, /kit\/render\.js/, `${name}: the kit's render is its pre-flight`);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('the preamble copies are policy, not doc-entropy debt: the scanner skips the block and still flags a plain duplicate', () => {
|
|
159
|
+
const de = require(path.join(ROOT, 'scripts', 'gds', 'docs-entropy.js'));
|
|
160
|
+
const carriers = ['modules/ui-design/skills/PREAMBLE.md', ...ownDirs.map((n) => `modules/ui-design/skills/${n}/SKILL.md`)];
|
|
161
|
+
assert.ok(carriers.length >= 2, 'at least one own skill carries the block beside PREAMBLE.md');
|
|
162
|
+
for (const rel of carriers) assert.ok(read(path.join(ROOT, rel)).includes(policy.preamble.begin), `${rel} carries the block`);
|
|
163
|
+
const dupes = de.scanDuplicateProse(carriers);
|
|
164
|
+
assert.ok(!dupes.some((d) => d.snippet.startsWith(policy.preamble.begin)), 'the byte-identical preamble is not reported as duplicate prose');
|
|
165
|
+
// calibration: the same scanner over the same files still sees a plain paragraph twice
|
|
166
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ui-skills-dupe-'));
|
|
167
|
+
const para = 'A plain paragraph long enough to clear the scanner\'s 120-character floor, written twice so the duplicate-prose scan has something real to report.';
|
|
168
|
+
const a = path.join(tmp, 'a.md');
|
|
169
|
+
const b = path.join(tmp, 'b.md');
|
|
170
|
+
fs.writeFileSync(a, `# a\n\n${para}\n\n${read(path.join(SKILLS, 'PREAMBLE.md'))}`);
|
|
171
|
+
fs.writeFileSync(b, `# b\n\n${para}\n\n${read(path.join(SKILLS, 'PREAMBLE.md'))}`);
|
|
172
|
+
const rel = (p) => path.relative(ROOT, p).split(path.sep).join('/');
|
|
173
|
+
const control = de.scanDuplicateProse([rel(a), rel(b)]);
|
|
174
|
+
assert.equal(control.length, 1, 'the plain paragraph is flagged once');
|
|
175
|
+
assert.ok(control[0].snippet.startsWith('A plain paragraph'), 'and it is the plain paragraph, not the preamble');
|
|
176
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
177
|
+
});
|
|
178
|
+
|
|
96
179
|
// ── the manifest declares what is on disk, and nothing else ─────────────────
|
|
97
180
|
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
181
|
const declared = manifest.contributes.skills;
|
|
@@ -145,24 +228,31 @@ test('a module-owned skill lands in a materialised .claude/skills with its sidec
|
|
|
145
228
|
});
|
|
146
229
|
|
|
147
230
|
// ── the doors: /design lists them, the README rosters them, the ADR is indexed ──
|
|
148
|
-
test('/design lists every
|
|
231
|
+
test('/design lists every module skill (vendored or in-house) by name, one line each, and names the playwright plugin as a prerequisite', () => {
|
|
149
232
|
const design = read(path.join(ROOT, '.claude', 'skills', 'design', 'SKILL.md'));
|
|
150
|
-
for (const name of vendoredDirs) {
|
|
233
|
+
for (const name of [...vendoredDirs, ...ownDirs]) {
|
|
151
234
|
const line = design.split('\n').find((l) => l.includes(`\`/${name}\``) || l.includes(`**${name}**`));
|
|
152
235
|
assert.ok(line, `/design names ${name} on a line of its own`);
|
|
153
236
|
}
|
|
154
237
|
assert.match(design, /playwright/i, '/design names the playwright plugin');
|
|
155
238
|
});
|
|
156
239
|
|
|
157
|
-
test('the skills README
|
|
240
|
+
test('the skills README and the module CLAUDE.md roster every module skill by name', () => {
|
|
158
241
|
const readme = read(path.join(SKILLS, 'README.md'));
|
|
159
|
-
|
|
242
|
+
const claude = read(path.join(MODULE, 'CLAUDE.md'));
|
|
243
|
+
for (const name of [...vendoredDirs, ...ownDirs]) {
|
|
244
|
+
assert.ok(readme.includes(`\`${name}\``), `README rosters ${name}`);
|
|
245
|
+
assert.ok(claude.includes(`\`${name}\``), `modules/ui-design/CLAUDE.md names ${name}`);
|
|
246
|
+
}
|
|
160
247
|
assert.ok(readme.includes('policy.json') && readme.includes('PREAMBLE.md') && readme.includes('PROVENANCE.md'));
|
|
248
|
+
assert.ok(readme.includes('Rebuilt from'), 'the README says what a rebuild carries instead of a sidecar');
|
|
161
249
|
});
|
|
162
250
|
|
|
163
|
-
test('the vendoring
|
|
164
|
-
const adr = fs.readdirSync(path.join(ROOT, 'docs', 'adr')).find((f) => /^0198-/.test(f));
|
|
165
|
-
assert.ok(adr, 'docs/adr/0198-*.md exists');
|
|
251
|
+
test('the vendoring-policy ADR and the in-house-rebuild ADR exist and are indexed', () => {
|
|
166
252
|
const index = read(path.join(ROOT, 'docs', 'adr', 'README.md'));
|
|
167
|
-
|
|
253
|
+
for (const n of ['0198', '0220']) {
|
|
254
|
+
const adr = fs.readdirSync(path.join(ROOT, 'docs', 'adr')).find((f) => f.startsWith(`${n}-`));
|
|
255
|
+
assert.ok(adr, `docs/adr/${n}-*.md exists`);
|
|
256
|
+
assert.ok(index.includes(`](${adr})`), `docs/adr/README.md links ADR ${n}`);
|
|
257
|
+
}
|
|
168
258
|
});
|