@bongos/core 1.19.479 → 1.19.481
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 +49 -19
- package/.claude/skills/design/SKILL.md +3 -1
- package/docs/adr/0231-the-style-session-an-owner-authors-a-look-inside-the-contract.md +32 -0
- package/docs/adr/README.md +1 -0
- package/docs/file-map.md +1 -1
- package/docs/module-api-changelog.md +4 -0
- package/modules/ui-design/module.json +1 -1
- package/modules/ui-design/skills/README.md +8 -0
- package/modules/ui-design/skills/style/SKILL.md +112 -0
- package/modules/ui-design/styles/README.md +5 -0
- package/modules/ui-design/styles/blueprint/DESIGN.md +152 -0
- package/modules/ui-design/styles/blueprint/mock.html +308 -0
- package/modules/ui-design/styles/blueprint/mock.states.json +11 -0
- package/modules/ui-design/styles/blueprint/pack.json +33 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +1 -1
- package/tests/ui_design_skills.mjs +47 -11
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.481",
|
|
6
|
+
"core_contract": "1.19.481",
|
|
7
|
+
"source_commit": "1e15c64fea66158d77a525295d8f0de6762a2d09",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-02T18:
|
|
9
|
+
"built_at": "2026-09-02T18:54:38.572Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 421,
|
|
13
13
|
"agent_docs_stubbed": 24,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 1843,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2288,
|
|
20
|
+
"tree_sha256": "ee9b67479c48cf815519f65d3dfe69cf1f1b4054aa15100612e6ed03897c27f4",
|
|
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": "c9beec980b3abf853b1b6f5d2eaeda42366aeaa77214e90c4a77b26a032e7b35"
|
|
131
131
|
},
|
|
132
132
|
{
|
|
133
133
|
"path": ".claude/skills/dev/SKILL.md",
|
|
@@ -1659,10 +1659,15 @@
|
|
|
1659
1659
|
"mode": "0000644",
|
|
1660
1660
|
"sha256": "a9163be00fa701f69f047a1129db375b433bdcae3430f0776a184d78ead24af9"
|
|
1661
1661
|
},
|
|
1662
|
+
{
|
|
1663
|
+
"path": "docs/adr/0231-the-style-session-an-owner-authors-a-look-inside-the-contract.md",
|
|
1664
|
+
"mode": "0000644",
|
|
1665
|
+
"sha256": "6d47d3d5ee4e6683e1437142e3e2440d6009870bdb06bddc3b52ee210fdbade8"
|
|
1666
|
+
},
|
|
1662
1667
|
{
|
|
1663
1668
|
"path": "docs/adr/README.md",
|
|
1664
1669
|
"mode": "0000644",
|
|
1665
|
-
"sha256": "
|
|
1670
|
+
"sha256": "5a35efc881bf1427c038e445f4003429861ccfda78653e94b0149db4d6eaaebe"
|
|
1666
1671
|
},
|
|
1667
1672
|
{
|
|
1668
1673
|
"path": "docs/api-reference.md",
|
|
@@ -2142,7 +2147,7 @@
|
|
|
2142
2147
|
{
|
|
2143
2148
|
"path": "docs/file-map.md",
|
|
2144
2149
|
"mode": "0000644",
|
|
2145
|
-
"sha256": "
|
|
2150
|
+
"sha256": "67d68e3921adbc1a7d82f15c946bfeaa4f4a22b0f2885b32aa627e8a876b5516"
|
|
2146
2151
|
},
|
|
2147
2152
|
{
|
|
2148
2153
|
"path": "docs/handoff-template.md",
|
|
@@ -2152,7 +2157,7 @@
|
|
|
2152
2157
|
{
|
|
2153
2158
|
"path": "docs/module-api-changelog.md",
|
|
2154
2159
|
"mode": "0000644",
|
|
2155
|
-
"sha256": "
|
|
2160
|
+
"sha256": "e6020c99be6acc88d36c8cc3c4cff695a50e2c94d80141dc9b4f7e61468d6739"
|
|
2156
2161
|
},
|
|
2157
2162
|
{
|
|
2158
2163
|
"path": "docs/modules-contract.md",
|
|
@@ -6447,7 +6452,7 @@
|
|
|
6447
6452
|
{
|
|
6448
6453
|
"path": "modules/ui-design/module.json",
|
|
6449
6454
|
"mode": "0000644",
|
|
6450
|
-
"sha256": "
|
|
6455
|
+
"sha256": "a59a00a1880aa44f7854fb916d0c3039edd559f48ded1e4735430ad960c6cad1"
|
|
6451
6456
|
},
|
|
6452
6457
|
{
|
|
6453
6458
|
"path": "modules/ui-design/scripts/design-sync.js",
|
|
@@ -6472,7 +6477,7 @@
|
|
|
6472
6477
|
{
|
|
6473
6478
|
"path": "modules/ui-design/skills/README.md",
|
|
6474
6479
|
"mode": "0000644",
|
|
6475
|
-
"sha256": "
|
|
6480
|
+
"sha256": "c97a95a53230fc0dc47c40c326e532ef70b1210a4d9e802ee85c7b02ef4cf12e"
|
|
6476
6481
|
},
|
|
6477
6482
|
{
|
|
6478
6483
|
"path": "modules/ui-design/skills/brandkit/SKILL.md",
|
|
@@ -6634,10 +6639,35 @@
|
|
|
6634
6639
|
"mode": "0000644",
|
|
6635
6640
|
"sha256": "f8c6c38ccb5fea529e86731005e8bbf231a9131c890010a29129c06195f48945"
|
|
6636
6641
|
},
|
|
6642
|
+
{
|
|
6643
|
+
"path": "modules/ui-design/skills/style/SKILL.md",
|
|
6644
|
+
"mode": "0000644",
|
|
6645
|
+
"sha256": "7853e7f17cd39caeae5107959905397dcc634aac0af2a7e017f357c8d36e45e5"
|
|
6646
|
+
},
|
|
6637
6647
|
{
|
|
6638
6648
|
"path": "modules/ui-design/styles/README.md",
|
|
6639
6649
|
"mode": "0000644",
|
|
6640
|
-
"sha256": "
|
|
6650
|
+
"sha256": "fdc2cde0abfa6eb513785888daa0f3e1defb33bd02975775090ecc18c588c57a"
|
|
6651
|
+
},
|
|
6652
|
+
{
|
|
6653
|
+
"path": "modules/ui-design/styles/blueprint/DESIGN.md",
|
|
6654
|
+
"mode": "0000644",
|
|
6655
|
+
"sha256": "481abc45442867f95ee89c1e70e36a39d5a784bc5feda9f03870d7de0bb337ac"
|
|
6656
|
+
},
|
|
6657
|
+
{
|
|
6658
|
+
"path": "modules/ui-design/styles/blueprint/mock.html",
|
|
6659
|
+
"mode": "0000644",
|
|
6660
|
+
"sha256": "46a471e74a8a8f6801eb52c769903b642149caac6c83c6890fba517c385e60c2"
|
|
6661
|
+
},
|
|
6662
|
+
{
|
|
6663
|
+
"path": "modules/ui-design/styles/blueprint/mock.states.json",
|
|
6664
|
+
"mode": "0000644",
|
|
6665
|
+
"sha256": "fb96947ff56fdcd020326dee5eff87cebac30562dee6a614f27c7ea6d4ae55bf"
|
|
6666
|
+
},
|
|
6667
|
+
{
|
|
6668
|
+
"path": "modules/ui-design/styles/blueprint/pack.json",
|
|
6669
|
+
"mode": "0000644",
|
|
6670
|
+
"sha256": "23b50cd93d5c9870c627e7fe49739329798a6fa7a396baf7e92482c03714b0ba"
|
|
6641
6671
|
},
|
|
6642
6672
|
{
|
|
6643
6673
|
"path": "modules/ui-design/styles/chrome-world/DESIGN.md",
|
|
@@ -6722,12 +6752,12 @@
|
|
|
6722
6752
|
{
|
|
6723
6753
|
"path": "package-lock.json",
|
|
6724
6754
|
"mode": "0000644",
|
|
6725
|
-
"sha256": "
|
|
6755
|
+
"sha256": "caf8322fc29303944e76c6d9d5ee51d9a953efdd9880160f3ea60f8a44746a9d"
|
|
6726
6756
|
},
|
|
6727
6757
|
{
|
|
6728
6758
|
"path": "package.json",
|
|
6729
6759
|
"mode": "0000644",
|
|
6730
|
-
"sha256": "
|
|
6760
|
+
"sha256": "46e539eb3e734d2748b85c00b7f81e845d9fe184ef608130e2ff5beba8d14026"
|
|
6731
6761
|
},
|
|
6732
6762
|
{
|
|
6733
6763
|
"path": "public-docs/index.html",
|
|
@@ -8382,7 +8412,7 @@
|
|
|
8382
8412
|
{
|
|
8383
8413
|
"path": "src/module-api.js",
|
|
8384
8414
|
"mode": "0000644",
|
|
8385
|
-
"sha256": "
|
|
8415
|
+
"sha256": "0e13f37b5e4b46d75c5ee21c9f445aaa35dc86d699b7fae8aec1ba83b8199a7d"
|
|
8386
8416
|
},
|
|
8387
8417
|
{
|
|
8388
8418
|
"path": "src/module-loader/catalog.js",
|
|
@@ -11342,7 +11372,7 @@
|
|
|
11342
11372
|
{
|
|
11343
11373
|
"path": "tests/ui_design_skills.mjs",
|
|
11344
11374
|
"mode": "0000644",
|
|
11345
|
-
"sha256": "
|
|
11375
|
+
"sha256": "f12817db3dd3486446cfcbe1701a06e19b7d1c18e9a6046f7861a667268fd3d4"
|
|
11346
11376
|
},
|
|
11347
11377
|
{
|
|
11348
11378
|
"path": "tests/ui_design_styles.mjs",
|
|
@@ -71,6 +71,7 @@ Third-party design skills reach a builder only through the module's vendoring po
|
|
|
71
71
|
- **industrial-brutalist-ui** — the technical-document look: one archetype per project (Swiss industrial print or dark tactical terminal, never mixed) with the other mode still complete and measured, massive clamped uppercase display against fixed tracked-out mono metadata, the pack's terracotta as the one hazard accent and its sea as one terminal green, the hairline as a one-pixel grid gap, zero corner rounding, static halftone and scanline degradation.
|
|
72
72
|
- **gpt-taste** — the anti-default pass for a surface that came out looking like every other one: a written plan block before any markup whose selection is derived from the route and bumped against sibling surfaces, the four-stage structure, an ultra-wide hero held to two or three lines, a dense tile grid with zero empty cells, real scroll-driven motion in CSS. Pack-agnostic; it runs over whatever look is on.
|
|
73
73
|
- **stitch-design-taste** — the design contract document for an external screen-generating tool: four dials, eight fixed sections, descriptive language paired with exact values, the thirteen transcribed from the instance's pack and diffed rather than invented, and motion written as an intent brief for whoever codes the screens afterwards.
|
|
74
|
+
- **style** — the `/style` session, the module's first FIRST-PARTY skill (no upstream): four bounded steps in which an owner authors their own look as a branding pack. SEARCH renders the nearest two library entries and asks two or three questions about feel; CREATE drafts the three files inside the contract, never a fork of a sheet and never a sixteenth token; PROVE retunes the thirteen against `contrast.js`, the styles suite, `check-mock.js` and the renders until every named pair is green; ADOPT hands the owner the one-step switch and files the catalogue rows. It never writes `config/branding.json`.
|
|
74
75
|
- **impeccable** — the design-craft method as one skill with sub-commands: `impeccable <sub-command> <target>` loads exactly one of eighteen playbooks (shape, critique, audit, polish, harden, onboard, animate, colorize, typeset, layout, adapt, distill, clarify, optimize, document, extract, bolder, quieter); no argument prints a menu from live project signals and never auto-runs; a bare description is routed to the closest playbook; carries the craft floor (loaded right before UI is edited, never recited) and the bounded finish (build, one kit round, one batch of fixes, at most one confirming round, stop; a read-only reviewer that answers with one of four words). No hooks, no bypass agent, no telemetry, no live server: the live loop is the playwright plugin below and the headless loop is the kit; the detector is task 1003329; native mobile declines to this loop.
|
|
75
76
|
- **imagegen-frontend-web** — the plate-making skill under the hero-only rule: text-free hero plates, hero objects, backdrops and orbs for the slots a composition already has (the count is the slot count, committed out loud), a composition anchor per plate that is not text-left image-right, one material / light / lens across the family, the pack as the palette lock, the instance's art pipeline key, and the pair-and-sidecar output contract (a black-ground JPEG for dark, an alpha WebP cutout for light, a `<file>.json` beside each). Never a section image, a comp or a screen; writes no page code.
|
|
76
77
|
- **imagegen-frontend-mobile** — the same, for the 390 and 320 compositions: portrait mastheads and fields clear of the regions a phone reserves, the first view held to one focal point, a scrim under any text over a plate. A screen is the surface's states file rendered by the kit; no phone mockups, no flows of screens; a native app target declines to this loop.
|
|
@@ -86,8 +87,9 @@ A **look** is a branding pack, never a sheet ([`modules/ui-design/styles/README.
|
|
|
86
87
|
- `chrome-world` — the canonical entry: one iridescent chrome sculpture in a black void, pale grey paper in light, one warm salmon. Pinned to the neutral pack; its recipes are the root `DESIGN.md`.
|
|
87
88
|
- `expedition` — a printed field journal: warm paper under a halftone screen, hairline frames, big heavy uppercase Archivo headlines, one lamp yellow as a fill only.
|
|
88
89
|
- `grove` — a sculpted paper garden: pale sage, soft corners, tinted shadows, the sea green as a slab, the tan as the one lit thing, Nunito.
|
|
90
|
+
- `blueprint` — a drafting table under north light: cool grey-blue paper on a graph lattice, compartments registered with a crosshair tick and a 2px cut edge, one cyanotype blue on what is pressable, a slate green for what was measured, IBM Plex Sans in sentence case. The library's first cool look, authored through `/style`.
|
|
89
91
|
|
|
90
|
-
A look that is not listed here is not in the library. Adding one is the README's five steps under a claimed task; a look that needs a sixteenth token is a contract change and an ADR, not a style.
|
|
92
|
+
A look that is not listed here is not in the library. Adding one is the README's five steps under a claimed task, or the `/style` session above, which walks them with an owner; a look that needs a sixteenth token is a contract change and an ADR, not a style.
|
|
91
93
|
|
|
92
94
|
## Stay clear of
|
|
93
95
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# ADR 0231 — The `/style` session: an owner authors a look inside the contract, and a module skill declares one of two origins
|
|
2
|
+
|
|
3
|
+
- **Status:** accepted
|
|
4
|
+
- **Date:** 2026-09-02
|
|
5
|
+
- **Task:** [task 1003475](https://cloudbongos.com/builders#/task/1003475) (goal 1000083 — the ui-design module as a platform capability; criterion `uidesign-skills-in-house`)
|
|
6
|
+
- **Relates to:** [ADR 0219](<redacted>.md) (a look is a branding pack), [ADR 0220](<redacted>.md) (the landing shape of a rebuild), [ADR 0230](<redacted>.md) (a look skill carries no colours), [ADR 0198](<redacted>.md) (the vendoring policy), [ADR 0062 §7](<redacted>.md) (the core ↔ host boundary), `modules/ui-design/styles/README.md`
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
The style library ships three looks an instance can adopt by copying a `theme` block into `config/branding.json`. The owner's next ask, filed on the library's own sheet, was the obvious one: let an owner **make** a look, with Claude beside them, rather than choose from three.
|
|
11
|
+
|
|
12
|
+
The object is unusually well suited to that. A look is thirteen colours, two font stacks, a `DESIGN.md` of derived recipes and one specimen — small enough for a non-engineer to have real opinions about, and strict enough that a wrong guess fails a measurement instead of shipping. What was missing was the loop between the two ends: the search that shows what already exists, the draft that stays inside the contract, the retune that is arithmetic rather than argument, and the one-line adoption at the end.
|
|
13
|
+
|
|
14
|
+
Writing that loop as a skill exposed a second problem the module had not hit before. Every skill under `modules/ui-design/skills/` so far was a **rebuild** from a REBUILD-SPEC, and `tests/ui_design_skills.mjs` pinned that shape for all of them: a `## Rebuilt from` section naming the origin, the scan verdict, the spec task, "no upstream bytes" and what changed in translation. `/style` has no upstream at all. It could either be given a contorted section claiming a spec it does not have, or the pin could learn that a first-party original is a legitimate second kind.
|
|
15
|
+
|
|
16
|
+
## Decision
|
|
17
|
+
|
|
18
|
+
1. **`/style` is a bounded four-step session, and nothing else.** SEARCH, CREATE, PROVE, ADOPT. It authors one look end to end, lands it in `modules/ui-design/styles/<name>/` under a claimed task, and stops. It is not a surface redesign (`impeccable shape`, `design-taste-frontend`), not a change to the instance's live world, and not a contract change.
|
|
19
|
+
2. **SEARCH renders before it asks.** The nearest two library entries are rendered through the kit — `/` and the hall's `/builders/ranks`, because that page carries the light tier and the fixed status vocabulary a look must not repaint — and the owner *looks*. Then two or three questions, about feel and never about tokens: <redacted>, a colour they already own, paper or void, quiet or loud. **The light-only fact is stated up front** (a pack carries light values; the dark ground is the platform's black, reached through the accent and the faces), because it is the thing owners are most surprised by later. The session then states **adapt or draft** in one sentence before writing a file, so a fourth almost-identical look is a decision rather than an accident.
|
|
20
|
+
3. **CREATE stays inside the contract or refuses.** The three files in the library's shape plus `mock.states.json`; exactly the thirteen and the two stacks with a `links` line; `origin` records the brief in the owner's own words and every retune. **Never a fork of a sheet, and never a sixteenth token** — a radius, a shadow, a glass and a texture are a *sheet's* derived tier; a look that needs one is an ADR, and the session says so and stops.
|
|
21
|
+
4. **PROVE is arithmetic, not taste.** `contrast.js --pack` for the pack floor, `tests/ui_design_styles.mjs` for the world / hub / hall suites under the pack, `check-mock.js` for the specimen, `render.js` for the shots — each round naming the failing pair, one change, then the shots shown to the owner. The skill carries a failure-to-fix table (the thinnest ink pair, an accent-as-text pair, the hall's info state drifting out of `sea`'s band, a warn chip, two grounds too close together) so the retune is a lookup rather than an argument. The four green lines are pasted into the task's notes as the evidence the look is landable.
|
|
22
|
+
5. **ADOPT hands the owner one step and does not take it.** Copy the pack's `theme` into `config/branding.json`. **The session never writes that file**: the instance's world is the host boundary (ADR 0062 §7) and adopting a look is the owner's decision. It does file the look into the catalogue under the claimed task — the styles README row, the module `CLAUDE.md` line, the `/design` line — because a dir named in none of the three is not in the library.
|
|
23
|
+
6. **A module skill declares exactly one of two origins.** `## Rebuilt from` (a rebuild from a REBUILD-SPEC: the origin, the verdict, the spec task, "no upstream bytes", what changed in translation — ADR 0198 / 0220) **or** `## First party` (no upstream at all: the task that authored it, the statement that it claims none, this ADR, and *why it exists*, since it has no spec to point at). `tests/ui_design_skills.mjs` now asserts exactly one section is present and branches its pins on which. Everything else — the preamble first and once, the frontmatter, no sidecar, no licence text, the style library named, no image mandate, the switch, the kit as the pre-flight — applies to both kinds unchanged.
|
|
24
|
+
7. **The session's first output is its own proof.** `blueprint` was authored through these four steps from a two-line brief and lands with this task: the library's first **cool** look (grey-blue paper, graphite ink, a cyanotype blue on what is pressable, a slate green for what has been measured, a rust pencil for what went wrong), a graph lattice and a corner tick as its materials, and a 2px cut edge instead of a rounded card. The retune took one pass — the pack floor and all three token suites cleared on the first measurement.
|
|
25
|
+
|
|
26
|
+
## Consequences
|
|
27
|
+
|
|
28
|
+
- An owner who cannot write CSS can now produce a look that is provably landable, because every claim the look makes is measured against three sheets that already exist before anyone adopts it. The session's cost is bounded by four steps and a fixed set of commands, not by how long the conversation runs.
|
|
29
|
+
- The library gains a cool entry, which the four look skills of [ADR 0230](<redacted>.md) immediately need: `industrial-brutalist-ui`'s Swiss-print archetype and `minimalist-ui`'s quiet grammar both read differently on cool paper, and until now every entry was warm.
|
|
30
|
+
- The module's skills can grow in two directions without either kind lying about itself. The next original — a session, a playbook, a generator that has no third-party ancestor — declares `## First party` and is counted the same way.
|
|
31
|
+
- Rejected: **giving `/style` a `## Rebuilt from` section** naming a spec it does not have (the section exists so a reader can trace provenance; a fabricated one destroys exactly that); **letting the session edit `config/branding.json`** so the owner sees the look live immediately (it is the host boundary, and a session that repaints the instance mid-conversation is a change nobody claimed); **shipping the look without the catalogue rows** and filing them later (a look nobody can find is not in the library, and "later" is how the markdown graveyard starts); and **a wizard that generates a palette from a reference image** (an interesting idea, but it would want the art pipeline, and the retune — the part that actually decides whether a look can ship — is unchanged by where the first guess came from).
|
|
32
|
+
- Open: the retune table encodes five failures seen across four looks. If a sixth recurs, it belongs in the table; if the table reaches ten, the loop wants a `--suggest` mode on the suites rather than a longer list a human reads.
|
package/docs/adr/README.md
CHANGED
|
@@ -316,6 +316,7 @@ This keeps the decision history honest and traceable.
|
|
|
316
316
|
| 0228 | [**The reading room owes a reference its content rules, not a second archetype** ([task 1003473](https://cloudbongos.com/builders#/task/1003473) · goal 1000081). The archetype task 1003306 built for the primer — a linear read with a gate — now serves drachmae, a REFERENCE people open to answer one question about how they get paid. Decision: the same room with ONE body modifier, `room--reference`, switching on content rules in `room.css` §8 rather than a second sheet or a `.page-head--reference` voice: the linear-read instruments are off (no progress hairline, no section counters, no read time; `primer.css`, which holds only the acknowledge gate, is no longer loaded), the outline goes to h3 depth read from the rendered DOM and lights the entry and its part, every h2/h3/h4 is an always-visible address (`.room-anchor`) that copies its link, a deep link is landed by the page after the document, the live section and the fonts (a swap moved the browser's own landing 300px, and `:target` had resolved against nothing), the collapsed phone outline rides under the top bar naming the part, and the live bounty table is a section ahead of the closing links rather than an appendix after them. Found by rendering before touching anything and fixed in the shared reader: `md-reader.js` stopped at `###`, so the doc's six earning sources rendered as paragraphs with a literal `####`; the bold rule refused an inner `*`; a linked heading slugged its target's path into the address. Rejected: a second archetype sheet, hover-only anchors (a phone has no hover), an accordion outline (the sub-questions are what a reader scans for), placing the live table inside the prose (it would lose its outline entry), and teaching the kit stub to serve markdown (the ui-design module's). Pinned by `tests/hall_reference_world.mjs`, four of whose tests run the reader in a vm.](<redacted>.md) | hall / design |
|
|
317
317
|
| 0229 | [**The title is the door into a task record, and the record exits to where it was opened from** ([task 1003468](https://cloudbongos.com/builders#/task/1003468) · goal 1000081). The owner, working the live hall: a to-do card "just gets selected", and a record opened from a goal "only offers the tasks page". Both were shipped behaviour: tasks 1759/1869 made multi-select the primary card interaction and left a 26px `--ink-faint` ⤢ glyph as the only way from a claimable card into the record, and `task.js` hardcoded `Open the work board` in three renders with `task.html`'s crumb a fourth. Decision: `.task__title` carries a real link (`.task__link`) on both surfaces that render the card — the convention `.gr-title` and the ledger rows already use — bought up to the 24px floor with the inline-block + padding + equal-negative-margin recipe so the card does not re-space; the card body still toggles selection and bulk claim is untouched; the glyph goes (a second door to the same room, one the owner never found). The link carries WHERE it was opened from as `?from=<path+query+hash>` (`OTBTaskDetail.href`), a query param because the origin must survive a reload and a copied link and because a referrer drops the `#/goal/N` fragment; `task.js` resolves the exit once — `?from=` → same-origin referrer → the work board — points the crumb before the fetch so the way back exists in every state, and labels it the way the sidebar does. A value is honoured only as a path on this host: one leading slash, no backslash, no whitespace, never `/api/`, never this same record. Rejected: opening on the card body (deletes bulk claim), keeping the glyph beside the link, in-memory origin, referrer-first, `history.back()`. Pinned by seven vm tests in `tests/hall_record_world.mjs` §10 and four in `tests/hall_board_world.mjs` §7.](<redacted>.md) | hall / design |
|
|
318
318
|
| 0230 | [**A look skill is a composition grammar; the palette is the pack and the texture is a library entry** ([task 1003327](https://cloudbongos.com/builders#/task/1003327) · goal 1000083, criterion `uidesign-skills-in-house`). Four groups of the taste-skill rebuild spec ship a *look*, not a method, and upstream each one shipped a palette and a set of faces — which is the one thing the fifteen-token contract forbids (ADR 0196, 0219). Decisions: (1) a look skill ships **structure, rhythm, type roles, component anatomy, motion and bans, and no colours** — every colour a `var()` or `color-mix()` of the fifteen, the palette the instance's pack, the texture a named entry in `modules/ui-design/styles/`, and no skill adds a face. (2) The brutalist "one substrate per project" becomes an **archetype** deciding which mode the surface is composed *for*, while the other mode ships complete and measured; "never mixed in one interface" is the part that was about craft. (3) `gpt-taste`'s prompt-seeded **random** selection becomes a deterministic one derived from the route and bumped against sibling surfaces, written into a plan block before any markup with the hero arithmetic, the grid-density proof and the measured contrast pairs — a roll nobody can reproduce is not a decision. (4) `stitch-design-taste` **transcribes** the world instead of authoring one: the platform's own `DESIGN.md` frontmatter shape, the thirteen diffed from the pack rather than retyped, and its universal bans stated as defaults the instance overrules. (5) The upstream's seeded placeholder photography is **dropped, not softened** (ADR 0222); (6) motion is CSS inside the Kill Switch and what cannot live there is named *not available*; (7) the pre-flight is the kit and `tells.js` is its mechanical half, so a look skill adds no reviewer of its own. Rejected: shipping each look as a pack, a sixteenth token for a substrate, a fixed seed behind the same roll.](<redacted>.md) | ui-design / skills |
|
|
319
|
+
| 0231 | [**The `/style` session: an owner authors a look inside the contract, and a module skill declares one of two origins** ([task 1003475](https://cloudbongos.com/builders#/task/1003475) · goal 1000083, criterion `uidesign-skills-in-house`). The library ships three looks to choose from; the owner's ask was to *make* one. A look is a small, strict object (thirteen colours, two font stacks, a `DESIGN.md`, one specimen), so what was missing was the loop between the ends. Decisions: (1) `/style` is a **bounded four-step session** - SEARCH, CREATE, PROVE, ADOPT - and nothing else. (2) SEARCH **renders before it asks**: the nearest two entries shown through the kit (`/` and the hall's light tier), then two or three questions about *feel*, the light-only fact of a pack stated up front, and adapt-or-draft declared in one sentence before a file is written. (3) CREATE stays inside the contract or refuses - never a fork of a sheet, **never a sixteenth token** (a radius, a shadow, a glass are a sheet's derived tier; a look that needs one is an ADR). (4) PROVE is **arithmetic, not taste**: the pack floor, the three token suites under the pack, `check-mock`, the renders, with a failure-to-fix table so the retune is a lookup, and the four green lines pasted as evidence. (5) ADOPT hands the owner one step and **never writes `config/branding.json`** - the host boundary is theirs (ADR 0062 §7) - but does file the catalogue rows under the claimed task. (6) A module skill declares **exactly one of two origins**, `## Rebuilt from` (ADR 0198 / 0220) or `## First party` (no upstream; the authoring task, this ADR, and why it exists), and the pins branch on which - a fabricated spec citation would destroy the provenance the section exists for. (7) The session's first output is its own proof: `blueprint`, the library's first cool look, cleared the pack floor and all three suites on the first measurement. Rejected: a fabricated Rebuilt-from, a session that repaints the live instance, filing the catalogue rows "later", a palette generated from a reference image.](<redacted>.md) | ui-design / skills |
|
|
319
320
|
| 0226 | [**One stale-claim timer; reporting degrades where enforcement refuses** ([task 1003476](https://cloudbongos.com/builders#/task/1003476), goal 1000072, criterion `legibility-sort-and-staleness`). "How long may a claim go silent" had FOUR answers and none of them was the project's: the sweeper that actually releases a claim read `GDS_STALE_CLAIM_HOURS` (else 6h); `GET /builders/roster` and `GET /builders/:id/active-claims` each hardcoded 24h in byte-identical blocks; and the two `session_pulses` helpers defaulted to 30 minutes. The 6-vs-24 pair is the harm: the roster is the screen an Archon opens to decide whether to intervene, and for **eighteen hours** after the sweeper had released a claim, stamped its session log `[auto-released:stale]` and returned the task to `ready`, that screen still showed it healthy — while the roster's own comment claimed to "mirror" the sweeper, true of the definition and false of the number. Decision: ONE resolver, `src/stale-timer.js` (a KERNEL file, the `instance-config.js`/`branding.js` category — host config a module reads, never hardcodes), reached by modules through the doorway and by core routes and the CLI directly. Precedence: explicit override (`--threshold-hours`, `?stale_hours=` — a LENS, labelled `query`, never a reconfiguration) → `<PREFIX>_STALE_CLAIM_HOURS` via `instance-config.resolveEnv`, whose legacy prefix chain still honours the live `GDS_STALE_CLAIM_HOURS` so **nothing in a running deployment has to change** (and which is the channel ADR 0190's runner already patches into `web.env`) → **6**, the SWEEP's historical number and deliberately not the roster's 24, because the sweep is the surface that acts and unifying on 24 would have made every project three times more patient with a dead claim under cover of a refactor. Bound 1..720h — the range `/builders/roster` has published since V3.R14; one timer, one bound. Every surface that prints the timer prints its PROVENANCE (`stale_hours_source`, the sweep header, the auto-release note): a number without it is exactly what let 6 and 24 coexist unnoticed. **The fail posture is asymmetric and that is the decision**: the resolver reports an unusable value rather than throwing, then a surface that only READS degrades to the default and names the bad var (500-ing the roster an Archon opened to find the problem would hide the problem), while the SWEEP refuses to run at all (it releases other people's claims and stamps their logs; no reload undoes that) — a surface that only reads may guess and say so, a surface that acts may not guess. Both routes now share one `staleHoursFor(req, res)`; two copies of a literal is how the drift happened twice inside one file. Rejected: a new `config/stale.json` tier (the env route already IS the per-project channel); making 24 the shared default; throwing from the resolver (forces a second reader — the original bug); folding in the boards' 14/30/60-DAY task buckets (a different question — days, about a task, a browsing lens with no enforcement behind it); a DB row + settings UI (a reasonable next step, but the defect was four numbers, not a missing knob — a row added before the readers agreed would have been a fifth).](<redacted>.md) | GDS / claims + ops |
|
|
320
321
|
|
|
321
322
|
> ⚠️ **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
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
│ ├── 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, `people.css` the directory's identity cell (task 1003470 made the legacy door `/scouting` ship the same body and script as `/people`; task 1003440 then deleted the door — `scouting.html` and `scouting.states.json` are gone and the URL 301s to `/people`).
|
|
134
134
|
│ ├── 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).
|
|
135
135
|
│ ├── 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.
|
|
136
|
-
│ ├── 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, then the detector), tells.js (task 1003329 / ADR 0223: the anti-pattern detector as two tiers — floors FAIL, craft tells WARN, kit-ignore waivers per file; the rendered twins live in lib.js factsInPage), 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. impeccable/ (task 1003326 / ADR 0221) is the design-craft method as ONE skill with sub-commands: SKILL.md (the router, the menu, the craft floor, the bounded finish) + reference/<sub-command>.md (eighteen playbooks, one loaded per request); no hooks, no bypass agent, no telemetry, no live server. minimalist-ui/, industrial-brutalist-ui/, gpt-taste/, stitch-design-taste/ (task 1003327 / ADR 0230) are the four looks, composition grammars with no colours of their own: the palette is the instance's pack and the texture is a named entry in styles/; a substrate is an archetype that still ships both modes, gpt-taste's selection is derived from the route rather than rolled, and stitch-design-taste writes a DESIGN.md-shaped contract document whose thirteen are transcribed from the pack. image-to-code/, imagegen-frontend-web/, imagegen-frontend-mobile/, brandkit/ (task 1003328 / ADR 0222) are the image family under the hero-only rule: plates and hero objects only, the pair (JPEG dark plate + WebP alpha cutout) with a <file>.json sidecar beside each, through the instance's art pipeline key; the grove specimen's styles/grove/assets/ is the worked example. 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).
|
|
136
|
+
│ ├── 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, then the detector), tells.js (task 1003329 / ADR 0223: the anti-pattern detector as two tiers — floors FAIL, craft tells WARN, kit-ignore waivers per file; the rendered twins live in lib.js factsInPage), 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. impeccable/ (task 1003326 / ADR 0221) is the design-craft method as ONE skill with sub-commands: SKILL.md (the router, the menu, the craft floor, the bounded finish) + reference/<sub-command>.md (eighteen playbooks, one loaded per request); no hooks, no bypass agent, no telemetry, no live server. minimalist-ui/, industrial-brutalist-ui/, gpt-taste/, stitch-design-taste/ (task 1003327 / ADR 0230) are the four looks, composition grammars with no colours of their own: the palette is the instance's pack and the texture is a named entry in styles/; a substrate is an archetype that still ships both modes, gpt-taste's selection is derived from the route rather than rolled, and stitch-design-taste writes a DESIGN.md-shaped contract document whose thirteen are transcribed from the pack. image-to-code/, imagegen-frontend-web/, imagegen-frontend-mobile/, brandkit/ (task 1003328 / ADR 0222) are the image family under the hero-only rule: plates and hero objects only, the pair (JPEG dark plate + WebP alpha cutout) with a <file>.json sidecar beside each, through the instance's art pipeline key; the grove specimen's styles/grove/assets/ is the worked example. style/ (task 1003475 / ADR 0231) is the module's first FIRST-PARTY skill (no upstream, so a `## First party` section instead of `## Rebuilt from`): the /style session in which an owner authors their own look in four bounded steps, rendering the nearest library entries before asking, drafting inside the contract, retuning by measurement, and handing over the one-step adoption without ever writing config/branding.json. 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, blueprint (the first cool look, authored through /style at task 1003475); 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).
|
|
137
137
|
│ ├── character-anim/ ← consistent-character generative animation (ADR 0088) — graded painterly frame sets + manifest, the otb-character-review skill. CLAUDE.md, module.json.
|
|
138
138
|
│ ├── 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.
|
|
139
139
|
│ └── 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.
|
|
@@ -1395,5 +1395,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1395
1395
|
landed since 1.19.477 with no explicit bump. run 33664027105. (task 1002620)
|
|
1396
1396
|
1.19.479 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1397
1397
|
landed since 1.19.478 with no explicit bump. run 33665076054. (task 1002620)
|
|
1398
|
+
1.19.480 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1399
|
+
landed since 1.19.479 with no explicit bump. run 33668172411. (task 1002620)
|
|
1400
|
+
1.19.481 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1401
|
+
landed since 1.19.480 with no explicit bump. run 33670051626. (task 1002620)
|
|
1398
1402
|
---------------------------------------------------------------------------
|
|
1399
1403
|
```
|
|
@@ -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", "design-taste-frontend", "design-taste-frontend-v1", "high-end-visual-design", "redesign-existing-projects", "impeccable", "minimalist-ui", "industrial-brutalist-ui", "gpt-taste", "stitch-design-taste", "image-to-code", "imagegen-frontend-web", "imagegen-frontend-mobile", "brandkit"] },
|
|
8
|
+
"contributes": { "skills": ["design", "design-sync", "figma-design-sync", "design-taste-frontend", "design-taste-frontend-v1", "high-end-visual-design", "redesign-existing-projects", "impeccable", "style", "minimalist-ui", "industrial-brutalist-ui", "gpt-taste", "stitch-design-taste", "image-to-code", "imagegen-frontend-web", "imagegen-frontend-mobile", "brandkit"] },
|
|
9
9
|
"provides": [],
|
|
10
10
|
"consumes": []
|
|
11
11
|
}
|
|
@@ -47,6 +47,14 @@ A rebuild spec becomes a **first-party skill** at `<name>/` — no `vendor/` seg
|
|
|
47
47
|
|
|
48
48
|
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. The image family (task 1003328, [ADR 0222](../../../docs/adr/<redacted>.md)) carries the one rewrite the upstream never had: images are for text-free hero plates and hero objects only, so a screen is the surface itself, the mark is drawn, the board is a contact sheet, and what ships is the pair (a black-ground JPEG for dark, an alpha WebP cutout for light) with a provenance sidecar beside every file, through the instance's art pipeline key. The four looks (task 1003327, [ADR 0230](../../../docs/adr/<redacted>.md)) carry the other rewrite: a look skill ships structure, rhythm, type roles, component anatomy, motion and bans and **no colours**, so a substrate becomes an archetype that still ships both modes, a prompt-seeded roll becomes a deterministic selection derived from the route, a design document transcribes the world instead of authoring one, and the upstream's seeded placeholder photography is dropped rather than softened. The whole scoped set of thirteen is now rebuilt in-house; nothing from either origin is still waiting on a spec.
|
|
49
49
|
|
|
50
|
+
## First-party originals (2026-09-02)
|
|
51
|
+
|
|
52
|
+
A skill the platform wrote from nothing declares the **other** origin. It lives at `<name>/` like a rebuild, opens with the same PLATFORM PREAMBLE, and carries a **`## First party`** section instead: the task that authored it, the plain statement that it has no upstream, [ADR 0231](../../../docs/adr/<redacted>.md), and **why it exists** — since it has no spec to point at, that sentence is the only provenance a reader gets. `tests/ui_design_skills.mjs` asserts a skill carries **exactly one** of the two sections and branches its pins on which; everything else (the preamble, the frontmatter, no sidecar, no licence text, the style library named, no image mandate, the switch, the kit as the pre-flight) applies to both kinds unchanged. Fabricating a `## Rebuilt from` for an original was rejected: the section exists so a reader can trace provenance, and a fabricated one destroys exactly that.
|
|
53
|
+
|
|
54
|
+
| Skill | Origin | Task | What it is |
|
|
55
|
+
|---|---|---|---|
|
|
56
|
+
| `style` | first party (no upstream) | [task 1003475](https://cloudbongos.com/builders#/task/1003475) | the `/style` session: four bounded steps in which an owner authors their own look as a branding pack. SEARCH renders the nearest two library entries before asking two or three questions about feel; CREATE drafts the three files inside the contract, never a fork of a sheet and never a sixteenth token; PROVE retunes the thirteen against `contrast.js`, the styles suite, `check-mock.js` and the renders with a failure-to-fix table so the retune is arithmetic; ADOPT hands the owner the one-step switch, never writes `config/branding.json`, and files the catalogue rows. Its first output is [`../styles/blueprint`](../styles/README.md), the library's first cool look |
|
|
57
|
+
|
|
50
58
|
**In the core checkout itself**, where these skills are written, Claude Code reads only `.claude/skills/` at the repo root and the materialiser's self-copy guard leaves it empty of module skills. `node scripts/gds/claude-materialize.js --module-skills-only` lands every enabled module's skills there as untracked, self-ignored copies (a generated `.claude/skills/.module-skills.json` manifest and a `.gitignore` beside them; `--clean` removes them; the file map skips them by the manifest) so a builder can invoke them here too ([task 1003330](https://cloudbongos.com/builders#/task/1003330), [ADR 0224](../../../docs/adr/<redacted>.md)). Edit the module's `skills/<name>/`, never the copy, and re-run to refresh.
|
|
51
59
|
|
|
52
60
|
**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.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: style
|
|
3
|
+
description: Runs a bounded session in which an owner authors their OWN look for the instance, as a branding pack inside the fifteen-token contract, and proves it clean before it lands. Four steps and no more. SEARCH reads the instance's world and the style library, renders the nearest two entries so the owner looks rather than imagines, and asks two or three questions about feel. CREATE drafts modules/ui-design/styles/<name>/ in the library's contract shape (pack.json, DESIGN.md, mock.html), never a fork of a sheet and never a sixteenth token. PROVE retunes the thirteen against contrast.js, the styles suite, check-mock and the kit's renders until every named pair is green, showing the shots each round. ADOPT hands the owner the one-step switch and files the look into the catalogue. Triggers on "/style", "make a look", "author a style", "I want our own theme", "can we have a different look", "add a style to the library".
|
|
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
|
+
## First party
|
|
21
|
+
|
|
22
|
+
This skill has **no upstream**. It was authored for the platform by [task 1003475](https://cloudbongos.com/builders#/task/1003475) from the owner's idea on the style-library sheet: the library ships looks an instance can adopt, and the next step is letting an owner *make* one with Claude beside them. It is not a rebuild, carries no vendored bytes and claims no origin; the two-origin rule for the module's skills is [ADR 0231](../../../../docs/adr/<redacted>.md).
|
|
23
|
+
|
|
24
|
+
Why it exists: a look in this library is a **branding pack** ([ADR 0219](../../../../docs/adr/<redacted>.md)) — thirteen colours, two font stacks, a `DESIGN.md` of derived recipes and one specimen. That is a small enough object for an owner to have opinions about and a strict enough one that a wrong guess fails a measurement rather than shipping. What was missing was the loop between the two: the search that shows what already exists, the draft that stays inside the contract, the retune that is arithmetic rather than taste, and the one-line adoption at the end. This skill is that loop, bounded.
|
|
25
|
+
|
|
26
|
+
## What this session is, and is not
|
|
27
|
+
|
|
28
|
+
**Is:** one look, authored end to end, landed in `modules/ui-design/styles/<name>/` under a claimed task, measured clean before the owner ever adopts it.
|
|
29
|
+
|
|
30
|
+
**Is not:** a redesign of a surface (that is `impeccable shape` or `design-taste-frontend`), a change to the instance's live world (that is the owner's `config/branding.json` and their decision, taken at step 4 and never by this session), or a contract change. **A look that needs a sixteenth token is an ADR, not a style** — say so and stop.
|
|
31
|
+
|
|
32
|
+
## Step 1 — Search, before a single colour is chosen
|
|
33
|
+
|
|
34
|
+
Read the world first, then look at what the library already has, then ask.
|
|
35
|
+
|
|
36
|
+
1. **Read.** `config/branding.json` over `config/branding.neutral.json` (`theme.ui`: the thirteen colours and the two font stacks), the repo-root `DESIGN.md`, and every entry in `modules/ui-design/styles/` — each one's `pack.json` summary and its `DESIGN.md` North Star. State in one line what the instance's world is today.
|
|
37
|
+
2. **Show, do not describe.** Render the nearest two entries and let the owner look:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
node modules/ui-design/kit/render.js --page / --style <name>
|
|
41
|
+
node modules/ui-design/kit/render.js --page /builders/ranks --surface hall-ui --style <name>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The hall's page is worth the second render because it carries the light tier and the fixed status vocabulary a look must not repaint.
|
|
45
|
+
3. **Ask two or three questions, no more.** Ask about the feel, never about tokens:
|
|
46
|
+
- **References.** A site, a book cover, a room, a piece of packaging. One or two, not a mood board.
|
|
47
|
+
- **A colour they already own.** A brand colour, a product colour, a colour from the references. If they have none, say so and derive one at step 2.
|
|
48
|
+
- **Paper or void.** Does the surface read as a lit sheet or as a dark room? (A pack carries **light** values only; the dark ground is the platform's black, which every look reaches through its accent and its faces. Say this out loud now, because it is the single thing owners are most surprised by later.)
|
|
49
|
+
- **Quiet or loud.** How much of the page is allowed to be the accent.
|
|
50
|
+
4. **Decide adapt or draft, and say which.** If a library entry is within a retune of the answers, adapt it and name it. If nothing is near, draft. Either way, state the decision in one sentence before writing a file; a session that quietly drafts when an adapt would have done has spent the owner's time on a fourth almost-identical look.
|
|
51
|
+
|
|
52
|
+
## Step 2 — Create, inside the contract
|
|
53
|
+
|
|
54
|
+
Make `modules/ui-design/styles/<name>/` with the three files the library's README specifies. Start from the nearest entry's files, never from a served sheet.
|
|
55
|
+
|
|
56
|
+
- **`pack.json`** — `{ name, title, summary, origin, theme: { ui: { colors: <exactly the thirteen>, fonts: { display, body, links } } } }` and nothing else. `name` is the dir, kebab-case. Every colour is a plain six-digit hex (the sheets do hex arithmetic on this tier). `origin` records the brief in the owner's own words and every retune you make, so a later reader knows what moved and why.
|
|
57
|
+
- **`DESIGN.md`** — the frontmatter shape the root `DESIGN.md` uses: the thirteen value for value with `pack.json` (a test holds them in lockstep), the dark facts as a comment (dark is not this look's to set), the derived recipes, then `typography`, `rounded`, `spacing`, `components`; then the look's prose in the root document's sections, ending in a `### Don't` block, which is where the copy bans the kit reads live.
|
|
58
|
+
- **`mock.html`** — the specimen: no page `:root`, tokens on `body`, every dark rule written twice (`:root[data-mode="dark"] body` and the `prefers-color-scheme` twin), every colour a `var()` or a `color-mix()` of the pack, one `<header>` / `<main>` / `<h1>`, a skip link, `?state=` and `?mode=`, and a head comment saying what the sheet claims. Add `mock.states.json` beside it so the kit can render it.
|
|
59
|
+
|
|
60
|
+
The rules that decide what a look may be:
|
|
61
|
+
|
|
62
|
+
- **Never a fork of a sheet.** The library ships a pack, not CSS. If you find yourself copying `world.css`, stop.
|
|
63
|
+
- **Never a sixteenth token.** A radius, a shadow, a glass and a texture are a *sheet's* derived tier, not a pack's. A look expresses them in its `DESIGN.md` recipes and its specimen, and the instance's surfaces keep their own.
|
|
64
|
+
- **The platform's stays the platform's** whichever look is on: the dark ground, the hall's green / amber / red verification vocabulary, the status ledger's pillars, and the floors.
|
|
65
|
+
- **Faces come with a `links` line.** Two stacks with real fallbacks and the stylesheet URL that loads them; a face with no fallback is a layout shift.
|
|
66
|
+
- **Reference plates, if any, are hero plates and hero objects only** — text-free, with a provenance sidecar beside each file, through the instance's art pipeline. A look never needs one to ship, and this session never asks for one on its own.
|
|
67
|
+
- **Motion belongs to the specimen, not the pack**, and every rule of it sits inside the Kill Switch (`@media (prefers-reduced-motion: reduce)` at the foot of the token layer), with the rest frame a complete composition.
|
|
68
|
+
|
|
69
|
+
## Step 3 — Prove, and retune with the owner watching
|
|
70
|
+
|
|
71
|
+
The retune is **arithmetic, not taste**: a pack is laid through sheets that already exist and every pair they paint must clear AA. Tune the thirteen, never the sheets.
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
node modules/ui-design/kit/contrast.js --pack <name> # the pack floor: every ink on every ground
|
|
75
|
+
node --test --test-reporter=tap tests/ui_design_styles.mjs # the world / hub / hall suites under the pack
|
|
76
|
+
node modules/ui-design/kit/check-mock.js --file modules/ui-design/styles/<name>/mock.html --pack <name>
|
|
77
|
+
node modules/ui-design/kit/render.js --page / --style <name> # every state x width x mode, audited
|
|
78
|
+
node modules/ui-design/kit/render.js --page /builders/ranks --surface hall-ui --style <name>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Each round: run, read the **named failing pair** the suite prints, make one change, show the owner the shots. Common failures and their one right fix:
|
|
82
|
+
|
|
83
|
+
| What fails | What it means | The fix |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| `ink-faint on the page ground` | the thinnest pair on the paper | deepen `inkFaint`; never lighten the ground to save it |
|
|
86
|
+
| an accent-as-text pair | the accent is a fill, not a word | send the text job to `accentDeep` and deepen that |
|
|
87
|
+
| the hall's info state in dark | `sea` sits outside the band the hall derives from it | move `sea` toward the middle of its lightness range |
|
|
88
|
+
| a warn or error chip | `terracotta` drifted | return it to a red that reads on both grounds |
|
|
89
|
+
| the pack floor on `bgCard` | the card is too close to the paper | separate the two grounds by one real step |
|
|
90
|
+
|
|
91
|
+
Stop when `PACK CLEAN`, the styles suite is green, `ALL RULES PASS` on the specimen, and the renders say `ALL CLEAN`. Paste those four lines into the task's notes; they are the evidence the look is landable.
|
|
92
|
+
|
|
93
|
+
## Step 4 — Adopt, and file it
|
|
94
|
+
|
|
95
|
+
- **Hand the owner one step**, and let them take it: copy `pack.json`'s `theme` into `config/branding.json` (the file `cloudbongos init` writes; an existing instance merges the block over its own). Removing the block goes back to the neutral pack. **This session does not edit `config/branding.json`** — the instance's world is the host boundary and adopting a look is the owner's decision.
|
|
96
|
+
- **File the look into the catalogue** under the claimed task: the row in `modules/ui-design/styles/README.md`, the line in the module's `CLAUDE.md`, and the line in `/design`. A dir that is not named in all three is not in the library.
|
|
97
|
+
- **Say what stays the platform's** in the handover, so the owner is not surprised later: the black dark ground, the verification vocabulary, the ledger's pillars, the floors.
|
|
98
|
+
|
|
99
|
+
## What this skill never does
|
|
100
|
+
|
|
101
|
+
No hooks are written into any harness config. No child agent is spawned with permissions bypassed. No telemetry is collected or sent. No remote bundle is downloaded. No image generation is required to finish a look, and none is invoked without the owner asking. The instance's `config/branding.json` and its served sheets are read, never written, by this session.
|
|
102
|
+
|
|
103
|
+
## Pre-flight
|
|
104
|
+
|
|
105
|
+
- [ ] The world was read and stated in one line before any colour was chosen.
|
|
106
|
+
- [ ] The nearest two library entries were **rendered and looked at**, not just named.
|
|
107
|
+
- [ ] Two or three questions asked, about feel; the light-only fact about packs was said out loud.
|
|
108
|
+
- [ ] Adapt-or-draft decided and stated before a file was written.
|
|
109
|
+
- [ ] Three files (plus `mock.states.json`), exactly the thirteen and the two stacks, no sixteenth token, no forked sheet.
|
|
110
|
+
- [ ] `PACK CLEAN`, the styles suite green, `ALL RULES PASS`, `ALL CLEAN` on `/` and on the hall's light tier, all four pasted into the notes.
|
|
111
|
+
- [ ] The catalogue row, the module `CLAUDE.md` line and the `/design` line landed under the claimed task.
|
|
112
|
+
- [ ] The owner has the one-step adoption and knows what stays the platform's.
|
|
@@ -8,6 +8,7 @@ A **look** is something a builder or an instance owner picks for a new surface o
|
|
|
8
8
|
|---|---|---|---|
|
|
9
9
|
| `chrome-world` | the neutral pack, byte for byte | The canonical entry: one iridescent chrome sculpture in a black void, pale grey paper in light, one warm salmon that lights only the pressable and the live. Its recipes are the repo-root [`DESIGN.md`](../../../DESIGN.md); [`chrome-world/DESIGN.md`](chrome-world/DESIGN.md) points there. | the world round's pick (task 1003296 → 1003298) |
|
|
10
10
|
| `expedition` | warm paper, lamp yellow, Archivo | A printed field journal: a halftone screen over the paper, hairline frames instead of a grid, big heavy uppercase headlines, the yellow as a fill only. [`expedition/DESIGN.md`](expedition/DESIGN.md) | `docs/design/mocks/world/natgeo-editorial` (kept in round 1) |
|
|
11
|
+
| `blueprint` | cool grey-blue paper, cyanotype blue, slate green, IBM Plex Sans | A drafting table under north light: a faint graph lattice on the ground, compartments registered to the board with a crosshair tick at a 2px cut edge, the display voice in sentence case, every figure mono and tabular, one cyanotype blue on what is pressable, a slate green for what was measured and a rust pencil for what went wrong. The library's first cool look. [`blueprint/DESIGN.md`](blueprint/DESIGN.md) | authored through the `/style` session ([task 1003475](https://cloudbongos.com/builders#/task/1003475), [ADR 0231](../../../docs/adr/<redacted>.md)) |
|
|
11
12
|
| `grove` | pale sage, tan, sea green, Nunito | A sculpted paper garden: soft corners on everything that holds content, tinted shadows, the sea as a slab a whole band sits on, the tan as the one lit thing. [`grove/DESIGN.md`](grove/DESIGN.md) | `docs/design/mocks/world/organic-cards` (kept in round 1) |
|
|
12
13
|
|
|
13
14
|
The owner looked at both alternates on the sheet on 2026-09-01 and kept them ("styles look good for a start"). Both alternates were **retuned against the contract**, which is a measurement and not a taste: a pack's thirteen are laid through three sheets that already exist (the landing's `world.css`, the hub's `cosmos.css`, the hall's `style.css`) and every pair those sheets paint must clear AA under it. The grove's sage ground lifted and its inks deepened until the hub's fixed status vocabulary and the hall's chips cleared; the expedition's `ink-faint` deepened one step. What a pack cannot carry is **dark**: the contract is light values only, and every sheet's dark tier is its own black, which a look reaches through its accent and its faces. So the grove's green dark ground stays in the world round's mock and does not travel.
|
|
@@ -42,12 +43,16 @@ node modules/ui-design/kit/check-mock.js --file modules/ui-design/styles/grove/m
|
|
|
42
43
|
SURFACE=modules/ui-design/styles/expedition STYLE=expedition node modules/ui-design/kit/serve.js # the specimen at /mock
|
|
43
44
|
```
|
|
44
45
|
|
|
46
|
+
**`--pack <name>` is not optional when you check a look's specimen** (verified on a scaffolded instance, [task 1003331](https://cloudbongos.com/builders#/task/1003331)). Without it `check-mock.js` reads the taste bans from the **checkout's own** root `DESIGN.md`, not the look's, so a specimen that is clean under its own rules fails under the chrome world's (its `eyebrow` ban promotes the kicker-above-the-h1 tell to a FAIL). `--pack` points both the colours and the bans at the look. The same run confirmed the reverse direction works as designed: pointing `GDS_BRANDING_FILE` at an instance's `config/branding.json` makes every kit command measure **that instance's** world, so a look adopted there is what `contrast.js`, `render.js` and `check-mock.js` see with no flag at all.
|
|
47
|
+
|
|
45
48
|
**On an instance** (the switch is the contract, not a page): copy the look's `pack.json` `theme` into `config/branding.json`, the file `cloudbongos init` writes ([`docs/branding-contract.md`](../../../docs/branding-contract.md) §Branding a new instance). Every served page re-themes at once, because the server splices `theme.ui` into `:root` at the end of `<head>` and every sheet reads only that. To leave a look, remove the block: the neutral pack (the chrome world) resolves.
|
|
46
49
|
|
|
47
50
|
**What stays the platform's, whichever look is on:** the dark ground (black), the hall's fixed green / amber / red **verification** vocabulary and the status ledger's pillars (a look must not repaint a status chip, ADR 0196 Context §2), the derived recipes each sheet carries (a corner radius, a shadow, a glass are a sheet's tier, not a pack's), and the platform floors.
|
|
48
51
|
|
|
49
52
|
## Adding a look
|
|
50
53
|
|
|
54
|
+
The steps below are the manual path. The **`/style` session** ([task 1003475](https://cloudbongos.com/builders#/task/1003475), [ADR 0231](../../../docs/adr/<redacted>.md)) walks them with an owner instead: it renders the nearest entries before it asks anything, drafts inside the contract, retunes against the same measurements below with a failure-to-fix table, and hands over the one-step adoption without taking it. `blueprint` was authored that way.
|
|
55
|
+
|
|
51
56
|
1. Make the dir with the three files; start from the alternate nearest to it.
|
|
52
57
|
2. `node modules/ui-design/kit/contrast.js --pack <name>` until the floor is clean; then `node --test tests/ui_design_styles.mjs`, which runs the world / cosmos / hall suites under the pack and tells you the exact pair that fails. Tune the thirteen, never the sheets.
|
|
53
58
|
3. `node modules/ui-design/kit/check-mock.js --file modules/ui-design/styles/<name>/mock.html --pack <name>` until ALL RULES PASS.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: The Blueprint
|
|
3
|
+
description: A drafting table under north light. Cool grey-blue paper on a faint graph lattice, graphite rules with crosshair ticks at the corners, one cyanotype blue on everything pressable or live, a slate green for what has been measured, and a rust pencil held for what went wrong.
|
|
4
|
+
colors:
|
|
5
|
+
# ---- contract tier, LIGHT: the thirteen this look's pack.json carries, value for value
|
|
6
|
+
# (tests/ui_design_styles.mjs holds the two in lockstep). Spliced into :root at the end of
|
|
7
|
+
# <head> at (0,1,0) on any instance that adopts the look.
|
|
8
|
+
bg: "#e6ebf0"
|
|
9
|
+
bg-card: "#f7fafc"
|
|
10
|
+
bg-deep: "#d3dae2"
|
|
11
|
+
bg-frame: "#edf1f5"
|
|
12
|
+
ink: "#10161d"
|
|
13
|
+
ink-soft: "#464f5a"
|
|
14
|
+
ink-faint: "#566069"
|
|
15
|
+
rule: "#aeb9c4"
|
|
16
|
+
rule-soft: "#cfd7de"
|
|
17
|
+
accent: "#4f9fd6"
|
|
18
|
+
accent-deep: "#17527e"
|
|
19
|
+
sea: "#3f7f6a"
|
|
20
|
+
terracotta: "#b04a2c"
|
|
21
|
+
# ---- contract tier, DARK: NOT this look's to set. A pack carries light values only; dark is
|
|
22
|
+
# each sheet's own black tier (bg #000000, ink #ffffff, rules as white at 18% / 9%), which the
|
|
23
|
+
# look reaches through its accent and its faces. Written here so a reader knows what dark IS.
|
|
24
|
+
bg-dark: "#000000"
|
|
25
|
+
ink-dark: "#ffffff"
|
|
26
|
+
accent-deep-dark: "color-mix(in srgb, var(--accent) 74%, #ffffff)"
|
|
27
|
+
# ---- derived: the accent's jobs. Unlike the library's three warm looks, this accent is COOL
|
|
28
|
+
# and light enough to carry near-black ink, so it fills a door and a lit cell; text still goes
|
|
29
|
+
# to accent-deep, because a 2.4:1 blue on paper is not a word.
|
|
30
|
+
accent-ink: "var(--accent-deep)"
|
|
31
|
+
accent-ink-dark: "var(--accent)"
|
|
32
|
+
accent-on: "color-mix(in srgb, var(--accent) 10%, #000000)"
|
|
33
|
+
accent-lift: "color-mix(in srgb, var(--accent) 74%, #ffffff)"
|
|
34
|
+
accent-tint: "color-mix(in srgb, var(--accent) 16%, var(--bg-card))"
|
|
35
|
+
accent-tint-dark: "color-mix(in srgb, var(--accent) 14%, #000000)"
|
|
36
|
+
focus: "var(--accent-deep)"
|
|
37
|
+
focus-dark: "var(--accent)"
|
|
38
|
+
# ---- derived: the second and third voices. Sea is what has been MEASURED (a verified count,
|
|
39
|
+
# a cleared check); terracotta is the annotation pencil, and it is the only red on the sheet.
|
|
40
|
+
measure: "var(--sea)"
|
|
41
|
+
note: "var(--terracotta)"
|
|
42
|
+
# ---- derived: the drafting materials
|
|
43
|
+
grid: "rgba(0, 0, 0, .05)"
|
|
44
|
+
grid-dark: "rgba(255, 255, 255, .055)"
|
|
45
|
+
grid-major: "rgba(0, 0, 0, .105)"
|
|
46
|
+
grid-major-dark: "rgba(255, 255, 255, .115)"
|
|
47
|
+
tick: "var(--rule)"
|
|
48
|
+
tick-dark: "rgba(255, 255, 255, .28)"
|
|
49
|
+
glass: "var(--bg-card)"
|
|
50
|
+
glass-dark: "rgba(255, 255, 255, .06)"
|
|
51
|
+
typography:
|
|
52
|
+
display: { fontFamily: "IBM Plex Sans, Segoe UI, system-ui, -apple-system, sans-serif", fontSize: "clamp(34px, 5.4vw, 68px)", fontWeight: 600, lineHeight: 1.02, letterSpacing: "-0.03em" }
|
|
53
|
+
headline: { fontFamily: "{typography.display.fontFamily}", fontSize: "clamp(22px, 2.6vw, 34px)", fontWeight: 600, lineHeight: 1.1, letterSpacing: "-0.02em" }
|
|
54
|
+
body: { fontFamily: "IBM Plex Sans, Segoe UI, system-ui, -apple-system, sans-serif", fontSize: "16px", fontWeight: 400, lineHeight: 1.55 }
|
|
55
|
+
label: { fontFamily: "JetBrains Mono, ui-monospace, Cascadia Mono, Consolas, monospace", fontSize: "11px", fontWeight: 400, letterSpacing: "0.14em", textTransform: "uppercase" }
|
|
56
|
+
mono: { fontFamily: "{typography.label.fontFamily}", fontSize: "13px", fontWeight: 400, fontFeature: "tabular-nums" }
|
|
57
|
+
rounded:
|
|
58
|
+
pill: "999px"
|
|
59
|
+
panel: "2px"
|
|
60
|
+
tile: "2px"
|
|
61
|
+
spacing:
|
|
62
|
+
grid-pitch: "22px"
|
|
63
|
+
grid-major-pitch: "110px"
|
|
64
|
+
band: "clamp(44px, 7.5vh, 78px)"
|
|
65
|
+
gutter: "clamp(16px, 4vw, 26px)"
|
|
66
|
+
container: "1180px"
|
|
67
|
+
components:
|
|
68
|
+
mark-tick: { textColor: "{colors.ink}", height: "36px", padding: "0 10px 0 0", rounded: "0" }
|
|
69
|
+
door-primary: { backgroundColor: "{colors.accent}", textColor: "{colors.accent-on}", rounded: "{rounded.pill}", height: "48px", padding: "0 28px" }
|
|
70
|
+
door-secondary: { backgroundColor: "transparent", textColor: "{colors.ink}", border: "1px solid {colors.rule}", rounded: "{rounded.pill}", height: "48px" }
|
|
71
|
+
plate: { backgroundColor: "{colors.bg-card}", border: "1px solid {colors.rule}", rounded: "{rounded.panel}" }
|
|
72
|
+
cell-lit: { backgroundColor: "{colors.accent-tint}", textColor: "{colors.ink}" }
|
|
73
|
+
ledger-row: { textColor: "{colors.ink}", padding: "14px 0", border: "1px solid {colors.rule}" }
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
# Style: The Blueprint
|
|
77
|
+
|
|
78
|
+
**Origin.** Authored through the `/style` session ([task 1003475](https://cloudbongos.com/builders#/task/1003475), [ADR 0231](../../../../docs/adr/<redacted>.md)) from a two-line brief: *"a drafting table under north light, cool paper and graphite; one cyanotype blue on what is pressable, a rust pencil for what went wrong."* The library's three existing looks are all warm and all carry a warm accent (salmon, lamp yellow, tan), so the search step found no near neighbour and the session drafted rather than adapted. The retune against the contract took one pass: the pack floor and all three token suites cleared on the first measurement, which is what a cool ground and a deep graphite ink buy you. The candidate's own dark ground does not travel, as with every look: a pack carries light values only.
|
|
79
|
+
|
|
80
|
+
## Overview
|
|
81
|
+
|
|
82
|
+
**Creative North Star: "The drafting table, under north light"**
|
|
83
|
+
|
|
84
|
+
A page in this look is a sheet on a drafting table. The ground is a cool grey-blue paper carrying a faint graph lattice, so nothing is perfectly empty; structure is a compartment on a hairline rule with a small crosshair tick at its corner, the way a drawing registers to its board; corners are 2px, which reads as a cut edge rather than a rounded card; and the type is a technical grotesque set at a workable weight, sentence case, with mono for every number. One cyanotype blue lights what is pressable or live. A slate green marks what has been measured. One rust pencil annotates what went wrong, and it is the only red on the sheet.
|
|
85
|
+
|
|
86
|
+
**Key Characteristics:**
|
|
87
|
+
|
|
88
|
+
- Cool grey-blue paper (`#e6ebf0`) under a two-tier graph lattice on its own fixed layer, fine at 22px and major at 110px; pure black in dark, where both tiers turn to white.
|
|
89
|
+
- Compartments, not cards: a 1px `rule` outline at a 2px radius, with a crosshair tick at the top-left corner.
|
|
90
|
+
- IBM Plex Sans at 600 for the display voice, sentence case, tracking at `-0.03em`; the same family at 400 for body.
|
|
91
|
+
- One cyanotype blue (`accent`) as a fill; a deep prussian (`accent-deep`) wherever the accent must be read.
|
|
92
|
+
- Every number is mono and tabular. A readout that reflows when it changes is a defect.
|
|
93
|
+
|
|
94
|
+
## Colors
|
|
95
|
+
|
|
96
|
+
- **Cyanotype blue** (`accent`, `#4f9fd6`): the one lit colour. It fills the primary door, the lit rail cell and the focus ring in dark. It is light enough to carry near-black ink (`accent-on`), which is why the door works, and too light to be a word on paper (2.4:1), which is why text goes to `accent-deep`.
|
|
97
|
+
- **Deep prussian** (`accent-deep`, `#17527e`): the blue's text-safe twin, 6.88:1 on the paper and 7.87:1 on the card. In dark it is derived (`color-mix(in srgb, var(--accent) 74%, #ffffff)`), never restated.
|
|
98
|
+
- **Slate green** (`sea`, `#3f7f6a`): the second voice, spent on what has been **measured** — a verified count, a cleared check, a passing readout. It sits in the lightness band the contract needs, because the hall derives its info state from sea at 80% into white in dark and at 86% into black in light.
|
|
99
|
+
- **Rust** (`terracotta`, `#b04a2c`): the annotation pencil, and the only red on the sheet. It is the error and warn source, and it never decorates.
|
|
100
|
+
- **Paper, card, deep, frame** (`bg`, `bg-card`, `bg-deep`, `bg-frame`): the cool ground and its tonal layers. `bg-card` is not white; a compartment on this table is another sheet of the same paper, one step lighter.
|
|
101
|
+
- **Graphite** (`ink`, `ink-soft`, `ink-faint`): a blue-black display and body ink, then two cool greys. `ink-faint` on paper is the thinnest pair at 5.35:1; do not lighten it.
|
|
102
|
+
- **Rule, rule-soft**: the hairlines. `rule` outlines a compartment and draws a tick; `rule-soft` divides inside one.
|
|
103
|
+
|
|
104
|
+
## Typography
|
|
105
|
+
|
|
106
|
+
**Display Font:** IBM Plex Sans 600 (with Segoe UI, system-ui, -apple-system, sans-serif)
|
|
107
|
+
**Body Font:** IBM Plex Sans 400/500/600 (same stack)
|
|
108
|
+
**Label / Mono Font:** JetBrains Mono 400 (with ui-monospace, Cascadia Mono, Consolas)
|
|
109
|
+
|
|
110
|
+
**Character:** A technical grotesque doing two jobs, and a mono doing the third. The display voice is **sentence case**, not uppercase: this look's precision comes from the lattice and the ticks, so shouting the headline as well would be one signal too many. Labels are mono, uppercase and tracked to `0.14em`. Every figure on the page is mono with tabular numerals, in both modes, because a drafting sheet's numbers are read in columns.
|
|
111
|
+
|
|
112
|
+
## Shapes
|
|
113
|
+
|
|
114
|
+
**Two-pixel or pill.** Every container is a rectangle with a 2px radius, a cut edge rather than a rounded card. Every control is a full pill. There is no third shape and no shadow: elevation on this table is the lattice showing through, the outline, and the tick.
|
|
115
|
+
|
|
116
|
+
## Materials
|
|
117
|
+
|
|
118
|
+
- **The lattice, in two tiers, on its own layer.** Four `repeating-linear-gradient`s: a fine rule every 22px in `grid` (black at 5%, white at 5.5%) and a major one every 110px in `grid-major` (black at 10.5%, white at 11.5%), the way a drawing sheet is ruled fine and counted coarse. The major tier is what makes the table readable in a screenshot; the fine tier is what makes it feel measured. It lives on a **fixed, `pointer-events: none` `body::before` at `z-index: -1`**, never as `body`'s `background-image`: a branding pack's `backdrop` emits `body{background:...}` *after* the page's own stylesheet (`themeMarkup()` in `serve-internal.js`), and that shorthand resets `background-image` to `none` on every served page. A texture on `body` silently does not exist; a texture on a layer does. Neither tier sits over the drawing, and neither changes the contrast of anything measured on top.
|
|
119
|
+
- **The tick.** An 8px crosshair in `tick`, drawn with a pseudo-element on the top-left corner of a compartment. One per compartment. It registers the drawing to the board and it is the look's signature.
|
|
120
|
+
- **Compartments.** A 1px `rule` outline at `rounded.panel`; `rule-soft` divides inside one. No shadow anywhere, in either mode.
|
|
121
|
+
- **Plates** (when a surface adds them): a surface's plates are its own; this look brings none. Where one is used it is a text-free hero plate with its provenance sidecar, as the platform's imagery rule says.
|
|
122
|
+
|
|
123
|
+
## Motion
|
|
124
|
+
|
|
125
|
+
Measured, not felt. State transitions at 180ms on `cubic-bezier(.2,.7,.3,1)`; no ambient loops in this look, and nothing moves in script. The kill switch stands as the platform's, and the rest frame is the whole drawing.
|
|
126
|
+
|
|
127
|
+
## Do's and Don'ts
|
|
128
|
+
|
|
129
|
+
### Do
|
|
130
|
+
|
|
131
|
+
- **Do** fill with `accent` and write with `accent-deep`; put `accent-on` on any blue.
|
|
132
|
+
- **Do** outline a compartment with `rule` and divide inside it with `rule-soft`; give it one tick.
|
|
133
|
+
- **Do** keep every container at a 2px radius and every control a pill.
|
|
134
|
+
- **Do** set every figure in the mono with tabular numerals, in both modes.
|
|
135
|
+
- **Do** spend `sea` on what was measured and `terracotta` on what went wrong, and nowhere else.
|
|
136
|
+
|
|
137
|
+
### Don't
|
|
138
|
+
|
|
139
|
+
- **Don't** write words in the cyanotype blue, or put white on it.
|
|
140
|
+
- **Don't** drop a shadow under a container, or round one past 2px; the outline and the tick are the whole elevation system.
|
|
141
|
+
- **Don't** use an em dash or en dash in visible copy, an emoji, gradient text, or a coloured text glow.
|
|
142
|
+
- **Don't** uppercase the display voice; the lattice and the ticks already carry the precision.
|
|
143
|
+
- **Don't** pin the accent, sea or terracotta in a dark block; dark derives.
|
|
144
|
+
- **Don't** declare a `:root` in a page; page tokens live on `body`.
|
|
145
|
+
|
|
146
|
+
## Accessibility Floors
|
|
147
|
+
|
|
148
|
+
The platform's, unchanged: 4.5:1 body text and 3:1 large text and non-text in both modes, measured from the real stylesheet; every control 24px on both sides (the pills are 46 and 48); the kill switch at the foot of the token layer. This pack measures clean against every pair the landing's `world.css`, the hub's `cosmos.css` and the hall's `style.css` paint (`tests/ui_design_styles.mjs` runs their suites under it) and on its own grounds (`node modules/ui-design/kit/contrast.js --pack blueprint`).
|
|
149
|
+
|
|
150
|
+
## Adopting this look
|
|
151
|
+
|
|
152
|
+
Copy `pack.json`'s `theme` into the instance's `config/branding.json` (a fresh instance's `cloudbongos init` writes that file; an existing one merges the block over its own). Nothing else changes: every sheet reads the contract and only the contract, so the landing, the hub, the hall and the status pages re-theme at once. What stays the platform's: the dark ground (black), the hall's fixed green / amber / red verification vocabulary, the status ledger's pillars, and every derived recipe a sheet already carries. Look before you ship: `node modules/ui-design/kit/render.js --page / --style blueprint`.
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>{{productName}} · the blueprint, on a sheet</title>
|
|
7
|
+
<script>
|
|
8
|
+
(function(){try{var q=new URLSearchParams(location.search);var m=q.get('mode');if(m==='light'||m==='dark')document.documentElement.setAttribute('data-mode',m);}catch(e){}})();
|
|
9
|
+
</script>
|
|
10
|
+
<style>
|
|
11
|
+
html { color-scheme: light; }
|
|
12
|
+
body {
|
|
13
|
+
/* the blueprint's derived tier, by the recipes in this style's DESIGN.md. The fifteen arrive
|
|
14
|
+
from the pack the stub splices after this sheet; nothing below is a colour of its own: a
|
|
15
|
+
var(), a color-mix(), or black or white with alpha. The paper sits on a graph lattice, a
|
|
16
|
+
compartment is an outline with one crosshair tick at its corner, corners are 2px and every
|
|
17
|
+
control is a pill. There is no shadow in either mode. */
|
|
18
|
+
--font-mono: "JetBrains Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;
|
|
19
|
+
--glass: var(--bg-card);
|
|
20
|
+
--glass-strong: var(--bg-deep);
|
|
21
|
+
--glass-edge: var(--rule);
|
|
22
|
+
--accent-ink: var(--accent-deep);
|
|
23
|
+
--accent-lift: color-mix(in srgb, var(--accent) 74%, #ffffff);
|
|
24
|
+
--accent-tint: color-mix(in srgb, var(--accent) 16%, var(--bg-card));
|
|
25
|
+
--accent-on: color-mix(in srgb, var(--accent) 10%, #000000);
|
|
26
|
+
--focus: var(--accent-deep);
|
|
27
|
+
--measure: var(--sea);
|
|
28
|
+
--note: var(--terracotta);
|
|
29
|
+
--grid: rgba(0, 0, 0, .05);
|
|
30
|
+
--grid-major: rgba(0, 0, 0, .105);
|
|
31
|
+
--grid-pitch: 22px;
|
|
32
|
+
--grid-major-pitch: 110px;
|
|
33
|
+
--tick: var(--rule);
|
|
34
|
+
--label-face: var(--font-mono);
|
|
35
|
+
--label-size: 11px;
|
|
36
|
+
--label-space: .14em;
|
|
37
|
+
--radius: 2px;
|
|
38
|
+
--radius-sm: 2px;
|
|
39
|
+
--radius-pill: 999px;
|
|
40
|
+
--top-h: 64px;
|
|
41
|
+
--gutter: clamp(16px, 4vw, 26px);
|
|
42
|
+
--band: clamp(44px, 7.5vh, 78px);
|
|
43
|
+
margin: 0;
|
|
44
|
+
background-color: var(--bg);
|
|
45
|
+
color: var(--ink);
|
|
46
|
+
font-family: var(--font-body);
|
|
47
|
+
font-size: 16px;
|
|
48
|
+
line-height: 1.55;
|
|
49
|
+
-webkit-font-smoothing: antialiased;
|
|
50
|
+
}
|
|
51
|
+
:root[data-mode="dark"] body {
|
|
52
|
+
color-scheme: dark;
|
|
53
|
+
--bg: #000000;
|
|
54
|
+
--bg-card: color-mix(in srgb, #ffffff 7%, #000000);
|
|
55
|
+
--bg-deep: #000000;
|
|
56
|
+
--bg-frame: color-mix(in srgb, #ffffff 4%, #000000);
|
|
57
|
+
--ink: #ffffff;
|
|
58
|
+
--ink-soft: rgba(255, 255, 255, .72);
|
|
59
|
+
--ink-faint: rgba(255, 255, 255, .62);
|
|
60
|
+
--rule: rgba(255, 255, 255, .18);
|
|
61
|
+
--rule-soft: rgba(255, 255, 255, .09);
|
|
62
|
+
--accent-deep: color-mix(in srgb, var(--accent) 74%, #ffffff);
|
|
63
|
+
--glass: rgba(255, 255, 255, .06);
|
|
64
|
+
--glass-strong: rgba(255, 255, 255, .12);
|
|
65
|
+
--glass-edge: rgba(255, 255, 255, .18);
|
|
66
|
+
--accent-ink: var(--accent);
|
|
67
|
+
--accent-tint: color-mix(in srgb, var(--accent) 14%, #000000);
|
|
68
|
+
--focus: var(--accent);
|
|
69
|
+
--grid: rgba(255, 255, 255, .055);
|
|
70
|
+
--grid-major: rgba(255, 255, 255, .115);
|
|
71
|
+
--tick: rgba(255, 255, 255, .28);
|
|
72
|
+
}
|
|
73
|
+
@media (prefers-color-scheme: dark) {
|
|
74
|
+
:root:not([data-mode="light"]) body {
|
|
75
|
+
color-scheme: dark;
|
|
76
|
+
--bg: #000000;
|
|
77
|
+
--bg-card: color-mix(in srgb, #ffffff 7%, #000000);
|
|
78
|
+
--bg-deep: #000000;
|
|
79
|
+
--bg-frame: color-mix(in srgb, #ffffff 4%, #000000);
|
|
80
|
+
--ink: #ffffff;
|
|
81
|
+
--ink-soft: rgba(255, 255, 255, .72);
|
|
82
|
+
--ink-faint: rgba(255, 255, 255, .62);
|
|
83
|
+
--rule: rgba(255, 255, 255, .18);
|
|
84
|
+
--rule-soft: rgba(255, 255, 255, .09);
|
|
85
|
+
--accent-deep: color-mix(in srgb, var(--accent) 74%, #ffffff);
|
|
86
|
+
--glass: rgba(255, 255, 255, .06);
|
|
87
|
+
--glass-strong: rgba(255, 255, 255, .12);
|
|
88
|
+
--glass-edge: rgba(255, 255, 255, .18);
|
|
89
|
+
--accent-ink: var(--accent);
|
|
90
|
+
--accent-tint: color-mix(in srgb, var(--accent) 14%, #000000);
|
|
91
|
+
--focus: var(--accent);
|
|
92
|
+
--grid: rgba(255, 255, 255, .055);
|
|
93
|
+
--grid-major: rgba(255, 255, 255, .115);
|
|
94
|
+
--tick: rgba(255, 255, 255, .28);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* the drafting table itself: a fixed, pointer-inert lattice UNDER the content. It is a layer
|
|
99
|
+
and not a body background-image on purpose - the branding pack's `backdrop` emits a
|
|
100
|
+
`body{background:...}` rule after the page's own sheet (serve-internal.js themeMarkup),
|
|
101
|
+
and that shorthand resets background-image to none on any served page. */
|
|
102
|
+
body::before {
|
|
103
|
+
content: "";
|
|
104
|
+
position: fixed;
|
|
105
|
+
inset: 0;
|
|
106
|
+
z-index: -1;
|
|
107
|
+
pointer-events: none;
|
|
108
|
+
background-image:
|
|
109
|
+
repeating-linear-gradient(to right, var(--grid-major) 0 1px, transparent 1px var(--grid-major-pitch)),
|
|
110
|
+
repeating-linear-gradient(to bottom, var(--grid-major) 0 1px, transparent 1px var(--grid-major-pitch)),
|
|
111
|
+
repeating-linear-gradient(to right, var(--grid) 0 1px, transparent 1px var(--grid-pitch)),
|
|
112
|
+
repeating-linear-gradient(to bottom, var(--grid) 0 1px, transparent 1px var(--grid-pitch));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
116
|
+
a { color: inherit; }
|
|
117
|
+
:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
|
|
118
|
+
.skip { position: absolute; left: 12px; top: -60px; padding: 11px 18px; border-radius: var(--radius-pill); background: var(--bg-card); color: var(--ink); text-decoration: none; }
|
|
119
|
+
.skip:focus { top: 12px; }
|
|
120
|
+
.wrap { max-width: 1180px; margin: 0 auto; padding: 0 var(--gutter); }
|
|
121
|
+
|
|
122
|
+
/* the registered bar: a hairline rule under it, the mark behind its own tick */
|
|
123
|
+
.bar { height: var(--top-h); border-bottom: 1px solid var(--rule); background: var(--bg-frame); }
|
|
124
|
+
.bar .wrap { height: 100%; display: flex; align-items: center; gap: 18px; }
|
|
125
|
+
.mark { position: relative; display: inline-flex; align-items: center; height: 36px; padding-left: 16px; font-weight: 600; letter-spacing: -.01em; font-size: 15px; text-decoration: none; }
|
|
126
|
+
.mark::before { content: ""; position: absolute; left: 0; top: 12px; width: 9px; height: 9px; background:
|
|
127
|
+
linear-gradient(to right, var(--accent-deep) 0 9px) center / 9px 1px no-repeat,
|
|
128
|
+
linear-gradient(to bottom, var(--accent-deep) 0 9px) center / 1px 9px no-repeat; }
|
|
129
|
+
.nav { margin-left: auto; display: flex; align-items: center; gap: 4px; }
|
|
130
|
+
.nav a { text-decoration: none; font-family: var(--label-face); font-size: 11px; letter-spacing: var(--label-space); text-transform: uppercase; color: var(--ink-soft); padding: 14px 10px; display: inline-block; line-height: 16px; }
|
|
131
|
+
.nav a:hover { color: var(--ink); }
|
|
132
|
+
.pill { display: inline-flex; align-items: center; height: 46px; padding: 0 22px; border-radius: var(--radius-pill); text-decoration: none; font-weight: 600; font-family: var(--font-body); font-size: 15px; transition: background-color 180ms cubic-bezier(.2, .7, .3, 1); }
|
|
133
|
+
.pill.in { background: var(--ink); color: var(--bg); }
|
|
134
|
+
.who { display: none; align-items: center; gap: 8px; height: 46px; padding: 0 6px; border-radius: var(--radius-pill); border: 1px solid var(--rule); }
|
|
135
|
+
.who .dot { width: 30px; height: 30px; border-radius: 50%; background: var(--bg-deep); border: 1px solid var(--rule); }
|
|
136
|
+
body[data-state="signed-in"] .pill.in { display: none; }
|
|
137
|
+
body[data-state="signed-in"] .who { display: inline-flex; }
|
|
138
|
+
|
|
139
|
+
/* the bands: the lattice shows through; a rule between chapters */
|
|
140
|
+
.band { padding: var(--band) 0; border-top: 1px solid var(--rule-soft); }
|
|
141
|
+
.band:first-of-type { border-top: 0; }
|
|
142
|
+
h1 { font-family: var(--font-display); font-weight: 600; font-size: clamp(34px, 5.4vw, 68px); line-height: 1.02; letter-spacing: -.03em; margin: 0 0 18px; max-width: 16ch; }
|
|
143
|
+
h2 { font-family: var(--font-display); font-weight: 600; font-size: clamp(22px, 2.6vw, 34px); line-height: 1.1; letter-spacing: -.02em; margin: 0 0 14px; }
|
|
144
|
+
.lede { color: var(--ink-soft); max-width: 56ch; margin: 0; font-size: 15px; }
|
|
145
|
+
.label { font-family: var(--label-face); font-size: var(--label-size); letter-spacing: var(--label-space); text-transform: uppercase; color: var(--ink-faint); }
|
|
146
|
+
.kicker { display: inline-block; margin-bottom: 14px; color: var(--accent-ink); }
|
|
147
|
+
|
|
148
|
+
/* a compartment: an outline at 2px with one crosshair tick at its corner */
|
|
149
|
+
.plate { position: relative; background: var(--bg-card); border: 1px solid var(--rule); border-radius: var(--radius); padding: 20px; }
|
|
150
|
+
.plate::before { content: ""; position: absolute; left: -4px; top: -4px; width: 9px; height: 9px; background:
|
|
151
|
+
linear-gradient(to right, var(--tick) 0 9px) center / 9px 1px no-repeat,
|
|
152
|
+
linear-gradient(to bottom, var(--tick) 0 9px) center / 1px 9px no-repeat; }
|
|
153
|
+
|
|
154
|
+
/* the thirteen, as a specimen strip inside one compartment */
|
|
155
|
+
.sw { display: grid; grid-template-columns: repeat(auto-fill, minmax(72px, 1fr)); gap: 12px; margin: 0; padding: 0; list-style: none; }
|
|
156
|
+
.sw i { display: block; height: 52px; border: 1px solid var(--rule); border-radius: var(--radius-sm); background: var(--c); }
|
|
157
|
+
.sw b { display: block; margin-top: 6px; font-family: var(--font-mono); font-weight: 400; font-size: 11px; color: var(--ink-faint); overflow-wrap: anywhere; }
|
|
158
|
+
|
|
159
|
+
/* the door pair: pills, the primary in cyanotype blue */
|
|
160
|
+
.doors { display: inline-flex; flex-wrap: wrap; gap: 8px; }
|
|
161
|
+
.door { display: inline-flex; align-items: center; height: 48px; padding: 0 28px; border-radius: var(--radius-pill); text-decoration: none; font-weight: 600; transition: background-color 180ms cubic-bezier(.2, .7, .3, 1); }
|
|
162
|
+
.door.go { background: var(--accent); color: var(--accent-on); }
|
|
163
|
+
.door.go:hover { background: var(--accent-lift); }
|
|
164
|
+
.door.more { color: var(--ink); border: 1px solid var(--rule); }
|
|
165
|
+
.door.more:hover { background: var(--glass-strong); }
|
|
166
|
+
|
|
167
|
+
/* the lifecycle rail: five compartments on one lattice row, one lit */
|
|
168
|
+
.rail { display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; margin: 0; padding: 0; list-style: none; }
|
|
169
|
+
.rail li { position: relative; padding: 14px; border: 1px solid var(--rule); border-radius: var(--radius); }
|
|
170
|
+
.rail li::before { content: ""; position: absolute; left: -4px; top: -4px; width: 9px; height: 9px; background:
|
|
171
|
+
linear-gradient(to right, var(--tick) 0 9px) center / 9px 1px no-repeat,
|
|
172
|
+
linear-gradient(to bottom, var(--tick) 0 9px) center / 1px 9px no-repeat; }
|
|
173
|
+
.rail .dot { display: block; width: 8px; height: 8px; border-radius: 50%; border: 1px solid var(--ink-faint); margin-bottom: 10px; }
|
|
174
|
+
.rail .lit { background: var(--accent-tint); border-color: var(--accent-deep); }
|
|
175
|
+
.rail .lit .dot { background: var(--accent); border-color: var(--accent-deep); }
|
|
176
|
+
.rail .lit .label { color: var(--ink); }
|
|
177
|
+
.rail p { margin: 6px 0 0; color: var(--ink-soft); font-size: 14px; }
|
|
178
|
+
|
|
179
|
+
/* ledger rows: the drawing register, every figure in the mono */
|
|
180
|
+
.ledger { margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--rule); }
|
|
181
|
+
.ledger li { display: grid; grid-template-columns: 88px 1fr 152px 96px; gap: 20px; align-items: baseline; padding: 14px 0; border-bottom: 1px solid var(--rule-soft); }
|
|
182
|
+
.ledger .id { font-family: var(--font-mono); font-size: 13px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
|
|
183
|
+
.ledger .by, .ledger .when { color: var(--ink-soft); font-size: 15px; }
|
|
184
|
+
.ledger .when { text-align: right; font-family: var(--font-mono); font-size: 13px; font-variant-numeric: tabular-nums; }
|
|
185
|
+
.ledger a { color: var(--accent-ink); text-underline-offset: 3px; display: inline-block; padding: 2px 0; }
|
|
186
|
+
.ledger .ok { color: var(--measure); font-family: var(--font-mono); font-size: 12px; letter-spacing: .06em; text-transform: uppercase; }
|
|
187
|
+
.ledger .bad { color: var(--note); font-family: var(--font-mono); font-size: 12px; letter-spacing: .06em; text-transform: uppercase; }
|
|
188
|
+
|
|
189
|
+
footer { border-top: 1px solid var(--rule); padding: 26px 0 40px; }
|
|
190
|
+
footer .wrap { display: flex; flex-wrap: wrap; gap: 8px 22px; align-items: baseline; color: var(--ink-soft); font-size: 14px; }
|
|
191
|
+
footer p { margin: 0; flex: 1 1 320px; }
|
|
192
|
+
footer a { color: var(--accent-ink); display: inline-block; padding: 4px 0; font-family: var(--label-face); font-size: 11px; letter-spacing: .1em; text-transform: uppercase; }
|
|
193
|
+
|
|
194
|
+
@media (max-width: 900px) {
|
|
195
|
+
body { --top-h: 60px; }
|
|
196
|
+
.rail { grid-template-columns: 1fr; }
|
|
197
|
+
}
|
|
198
|
+
@media (max-width: 600px) {
|
|
199
|
+
.ledger li { grid-template-columns: 1fr; gap: 4px; }
|
|
200
|
+
.ledger .when { text-align: left; }
|
|
201
|
+
.nav a { display: none; }
|
|
202
|
+
}
|
|
203
|
+
@media (prefers-reduced-motion: reduce) {
|
|
204
|
+
*, *::before, *::after { animation: none !important; transition: none !important; }
|
|
205
|
+
}
|
|
206
|
+
</style>
|
|
207
|
+
</head>
|
|
208
|
+
<body>
|
|
209
|
+
<!-- THESIS: the blueprint as a specimen sheet. The thirteen colours and the two faces the pack carries, laid through this look's own derived recipes (its DESIGN.md frontmatter): a graph lattice on the page ground, compartments that are an outline at a 2px radius with one crosshair tick at the corner, a sentence-case technical grotesque for the display voice, mono tabular figures everywhere a number appears, door pills with the primary in cyanotype blue, a rail with one lit cell, and a register whose verdict column spends sea on what was measured and terracotta on what went wrong. This page declares no :root and no colour of its own: the pack is spliced by the stub (STYLE=blueprint) and every value is a var() or a color-mix() of it. Dark restates the platform's black tier on body, since a mock links no sheet; this look reaches dark through its blue and its faces, which is all a pack carries. STATES: rest (signed out), signed-in. MODES: ?mode=dark|light. -->
|
|
210
|
+
<a class="skip" href="#main">Skip to content</a>
|
|
211
|
+
<header class="bar">
|
|
212
|
+
<div class="wrap">
|
|
213
|
+
<a class="mark" href="/">{{productName}}</a>
|
|
214
|
+
<nav class="nav" aria-label="Primary">
|
|
215
|
+
<a href="#swatches">Projects</a>
|
|
216
|
+
<a href="#type">Docs</a>
|
|
217
|
+
<a class="pill in" href="#main">Sign in</a>
|
|
218
|
+
<span class="who"><span class="dot" aria-hidden="true"></span><span class="label">builder</span></span>
|
|
219
|
+
</nav>
|
|
220
|
+
</div>
|
|
221
|
+
</header>
|
|
222
|
+
<main id="main" tabindex="-1">
|
|
223
|
+
<section class="band">
|
|
224
|
+
<div class="wrap">
|
|
225
|
+
<p class="label kicker">A look from the style library</p>
|
|
226
|
+
<h1>The blueprint</h1>
|
|
227
|
+
<p class="lede">A drafting table under north light. Cool grey-blue paper on a faint graph lattice, compartments registered to the board with a crosshair tick, one cyanotype blue on everything pressable or live, a slate green for what has been measured, and a rust pencil held for what went wrong.</p>
|
|
228
|
+
</div>
|
|
229
|
+
</section>
|
|
230
|
+
<section class="band" id="swatches">
|
|
231
|
+
<div class="wrap">
|
|
232
|
+
<h2>The thirteen</h2>
|
|
233
|
+
<div class="plate">
|
|
234
|
+
<ul class="sw">
|
|
235
|
+
<li><i style="--c: var(--bg)"></i><b>bg</b></li>
|
|
236
|
+
<li><i style="--c: var(--bg-card)"></i><b>bg-card</b></li>
|
|
237
|
+
<li><i style="--c: var(--bg-deep)"></i><b>bg-deep</b></li>
|
|
238
|
+
<li><i style="--c: var(--bg-frame)"></i><b>bg-frame</b></li>
|
|
239
|
+
<li><i style="--c: var(--ink)"></i><b>ink</b></li>
|
|
240
|
+
<li><i style="--c: var(--ink-soft)"></i><b>ink-soft</b></li>
|
|
241
|
+
<li><i style="--c: var(--ink-faint)"></i><b>ink-faint</b></li>
|
|
242
|
+
<li><i style="--c: var(--rule)"></i><b>rule</b></li>
|
|
243
|
+
<li><i style="--c: var(--rule-soft)"></i><b>rule-soft</b></li>
|
|
244
|
+
<li><i style="--c: var(--accent)"></i><b>accent</b></li>
|
|
245
|
+
<li><i style="--c: var(--accent-deep)"></i><b>accent-deep</b></li>
|
|
246
|
+
<li><i style="--c: var(--sea)"></i><b>sea</b></li>
|
|
247
|
+
<li><i style="--c: var(--terracotta)"></i><b>terracotta</b></li>
|
|
248
|
+
</ul>
|
|
249
|
+
</div>
|
|
250
|
+
</div>
|
|
251
|
+
</section>
|
|
252
|
+
<section class="band" id="type">
|
|
253
|
+
<div class="wrap">
|
|
254
|
+
<h2>Sentence case, and a mono that counts</h2>
|
|
255
|
+
<p class="lede">The display face is a technical grotesque at 600, set in sentence case with the tracking pulled in. This look does not shout its headline: the lattice and the ticks already carry the precision, so a second precision signal in the type would be one too many. Every figure is mono with tabular numerals, in both modes.</p>
|
|
256
|
+
<p class="label" style="margin-top: 22px">Label face, tracked and uppercase</p>
|
|
257
|
+
<p style="margin: 6px 0 0; font-family: var(--font-mono); font-size: 13px; font-variant-numeric: tabular-nums">1003475 · 15,647 credits · 7 ships in 24h</p>
|
|
258
|
+
</div>
|
|
259
|
+
</section>
|
|
260
|
+
<section class="band">
|
|
261
|
+
<div class="wrap">
|
|
262
|
+
<h2>The doors</h2>
|
|
263
|
+
<p class="lede" style="margin-bottom: 22px">Two pills side by side. The primary is the cyanotype blue with near-black ink on it; the secondary is a hairline outline, filled only on hover.</p>
|
|
264
|
+
<div class="doors">
|
|
265
|
+
<a class="door more" href="#swatches">Open the register</a>
|
|
266
|
+
<a class="door go" href="#main">Start a drawing</a>
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
</section>
|
|
270
|
+
<section class="band">
|
|
271
|
+
<div class="wrap">
|
|
272
|
+
<h2>The lifecycle rail</h2>
|
|
273
|
+
<ol class="rail">
|
|
274
|
+
<li><span class="dot"></span><span class="label">Claim</span><p>A builder locks a task.</p></li>
|
|
275
|
+
<li><span class="dot"></span><span class="label">Build</span><p>The code is written in a worktree.</p></li>
|
|
276
|
+
<li class="lit" aria-current="step"><span class="dot"></span><span class="label">Grade</span><p>An adversarial panel reads the diff.</p></li>
|
|
277
|
+
<li><span class="dot"></span><span class="label">Ship</span><p>Green checks land it on main.</p></li>
|
|
278
|
+
<li><span class="dot"></span><span class="label">Credit</span><p>The ledger pays on landing.</p></li>
|
|
279
|
+
</ol>
|
|
280
|
+
</div>
|
|
281
|
+
</section>
|
|
282
|
+
<section class="band">
|
|
283
|
+
<div class="wrap">
|
|
284
|
+
<h2>The register</h2>
|
|
285
|
+
<ul class="ledger">
|
|
286
|
+
<li><span class="id">1003323</span><span>The style library: looks as branding packs an instance can adopt</span><span class="by ok">measured</span><span class="when">3d ago</span></li>
|
|
287
|
+
<li><span class="id">1003327</span><span>The four looks: a composition grammar, never a palette</span><span class="by ok">measured</span><span class="when">today</span></li>
|
|
288
|
+
<li><span class="id">1003475</span><span><a href="#main">The style session: an owner authors a look of their own</a></span><span class="by bad">open</span><span class="when">today</span></li>
|
|
289
|
+
</ul>
|
|
290
|
+
</div>
|
|
291
|
+
</section>
|
|
292
|
+
</main>
|
|
293
|
+
<footer>
|
|
294
|
+
<div class="wrap">
|
|
295
|
+
<p>{{productName}} is built by AI agents claiming tasks from a ledger. This sheet is served through the kit with the look's pack spliced in.</p>
|
|
296
|
+
<a href="#main">Privacy</a>
|
|
297
|
+
<a href="#main">Terms</a>
|
|
298
|
+
<a href="#main">Contact</a>
|
|
299
|
+
</div>
|
|
300
|
+
</footer>
|
|
301
|
+
<script>
|
|
302
|
+
(function () {
|
|
303
|
+
var state = new URLSearchParams(location.search).get('state');
|
|
304
|
+
if (state === 'signed-in') document.body.setAttribute('data-state', 'signed-in');
|
|
305
|
+
})();
|
|
306
|
+
</script>
|
|
307
|
+
</body>
|
|
308
|
+
</html>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_doc": "The specimen's states for the kit (task 1003323): node modules/ui-design/kit/render.js --states modules/ui-design/styles/blueprint/mock.states.json --style blueprint. Both modes; the mock reads ?mode= itself.",
|
|
3
|
+
"page": "/mock",
|
|
4
|
+
"surface": "modules/ui-design/styles/blueprint",
|
|
5
|
+
"modes": ["dark", "light"],
|
|
6
|
+
"states": {
|
|
7
|
+
"rest": { "auth": false },
|
|
8
|
+
"signed-in": { "auth": true, "url": "/mock?state=signed-in", "expect": { "visible": [".who"], "hidden": [".pill.in"] } }
|
|
9
|
+
},
|
|
10
|
+
"reducedMotion": { "auth": false }
|
|
11
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_doc": "A look from the ui-design module's style library (task 1003323): the theme.ui overlay a branding pack carries, and nothing else. The stub lays it over the instance's pack (STYLE=blueprint); an instance adopts it by copying `theme` into config/branding.json. Light values only, as the contract says: dark is each sheet's own black tier, which this look reaches through the accent and the faces. Authored through the /style session of task 1003475.",
|
|
3
|
+
"name": "blueprint",
|
|
4
|
+
"title": "The Blueprint",
|
|
5
|
+
"summary": "A drafting table under north light: cool grey-blue paper, graphite rules, one cyanotype blue on everything pressable or live, and a rust annotation held for what went wrong.",
|
|
6
|
+
"origin": "authored through the /style session (task 1003475, ADR 0231) from a two-line brief: \"a drafting table under north light, cool paper and graphite; one cyanotype blue on what is pressable, a rust pencil for what went wrong\". The library had no cool look and no cool accent; the retune against the contract is recorded in DESIGN.md.",
|
|
7
|
+
"theme": {
|
|
8
|
+
"ui": {
|
|
9
|
+
"colors": {
|
|
10
|
+
"bg": "#e6ebf0",
|
|
11
|
+
"bgCard": "#f7fafc",
|
|
12
|
+
"bgDeep": "#d3dae2",
|
|
13
|
+
"bgFrame": "#edf1f5",
|
|
14
|
+
"ink": "#10161d",
|
|
15
|
+
"inkSoft": "#464f5a",
|
|
16
|
+
"inkFaint": "#566069",
|
|
17
|
+
"rule": "#aeb9c4",
|
|
18
|
+
"ruleSoft": "#cfd7de",
|
|
19
|
+
"accent": "#4f9fd6",
|
|
20
|
+
"accentDeep": "#17527e",
|
|
21
|
+
"sea": "#3f7f6a",
|
|
22
|
+
"terracotta": "#b04a2c"
|
|
23
|
+
},
|
|
24
|
+
"fonts": {
|
|
25
|
+
"display": "\"IBM Plex Sans\", \"Segoe UI\", system-ui, -apple-system, sans-serif",
|
|
26
|
+
"body": "\"IBM Plex Sans\", \"Segoe UI\", system-ui, -apple-system, sans-serif",
|
|
27
|
+
"links": [
|
|
28
|
+
"https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600&family=JetBrains+Mono:wght@400&display=swap"
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.481",
|
|
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.481",
|
|
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.481",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/src/module-api.js
CHANGED
|
@@ -34,7 +34,7 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
34
34
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
35
35
|
// the entry to that file. Look for a version's history there, not here.
|
|
36
36
|
// ---------------------------------------------------------------------------
|
|
37
|
-
const CORE_VERSION = '1.19.
|
|
37
|
+
const CORE_VERSION = '1.19.481'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
38
38
|
|
|
39
39
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
40
40
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
// (ADR 0230) it also pins the four LOOKS: each names the style-library entry it was tuned
|
|
13
13
|
// against, declares no colour of its own outside the legal black/white carve-out, adds no
|
|
14
14
|
// face, and keeps the translations that ADR fixed (the archetype that still ships both modes,
|
|
15
|
-
// the deterministic selection, the transcribed colour contract).
|
|
15
|
+
// the deterministic selection, the transcribed colour contract). Since task 1003475 (ADR
|
|
16
|
+
// 0231) a module skill declares one of TWO origins - a rebuild from a spec, or first party -
|
|
17
|
+
// and the origin pins branch on which; the /style session is the first original. DB-free.
|
|
16
18
|
import assert from 'node:assert/strict';
|
|
17
19
|
import { test } from 'node:test';
|
|
18
20
|
import { createRequire } from 'node:module';
|
|
@@ -267,7 +269,7 @@ test('every in-house SKILL.md opens with the PLATFORM PREAMBLE, byte-identical t
|
|
|
267
269
|
}
|
|
268
270
|
});
|
|
269
271
|
|
|
270
|
-
test('every in-house skill names its dir, carries a description, and
|
|
272
|
+
test('every in-house skill names its dir, carries a description, and declares exactly one origin: a rebuild from a spec, or first party (ADR 0231)', () => {
|
|
271
273
|
const pre = read(path.join(SKILLS, 'PREAMBLE.md')).trimEnd();
|
|
272
274
|
for (const name of ownDirs) {
|
|
273
275
|
const dir = path.join(SKILLS, name);
|
|
@@ -276,19 +278,53 @@ test('every in-house skill names its dir, carries a description, and says what i
|
|
|
276
278
|
assert.equal(fm.name, name, `${name}: frontmatter names its own dir`);
|
|
277
279
|
assert.ok(fm.description && fm.description.length > 80, `${name}: a description the harness can route on`);
|
|
278
280
|
assert.ok(!fs.existsSync(path.join(dir, policy.sidecar)), `${name}: an in-house skill claims no upstream, so no ${policy.sidecar}`);
|
|
279
|
-
assert.ok(!fs.existsSync(path.join(dir, 'LICENSE')), `${name}: no upstream licence text travels with
|
|
281
|
+
assert.ok(!fs.existsSync(path.join(dir, 'LICENSE')), `${name}: no upstream licence text travels with an in-house skill`);
|
|
280
282
|
const afterPreamble = text.slice(text.indexOf(pre) + pre.length);
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
assert.
|
|
284
|
-
assert.match(
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
283
|
+
const rebuilt = afterPreamble.match(/^## Rebuilt from\n([\s\S]*?)(?=\n## )/m);
|
|
284
|
+
const original = afterPreamble.match(/^## First party\n([\s\S]*?)(?=\n## )/m);
|
|
285
|
+
assert.ok(!!rebuilt !== !!original, `${name}: exactly one origin section - a rebuild carries "## Rebuilt from", an original carries "## First party"`);
|
|
286
|
+
assert.match(afterPreamble.trimStart(), /^## (Rebuilt from|First party)\n/, `${name}: the origin section is the first thing after the preamble, with nothing between`);
|
|
287
|
+
if (rebuilt) {
|
|
288
|
+
assert.match(rebuilt[1], /builders#\/task\/\d+/, `${name}: the section links the spec task`);
|
|
289
|
+
assert.match(rebuilt[1], /ADR 0198/, `${name}: the section names the vendoring policy that made it a rebuild`);
|
|
290
|
+
assert.match(rebuilt[1], /REBUILD-SPEC/, `${name}: the section names the spec`);
|
|
291
|
+
assert.match(rebuilt[1], /no upstream bytes/i, `${name}: the section states no upstream bytes were consulted or copied`);
|
|
292
|
+
assert.match(rebuilt[1], /what changed in translation/i, `${name}: the section says where the rebuild departs from the documented behaviour`);
|
|
293
|
+
} else {
|
|
294
|
+
assert.match(original[1], /builders#\/task\/\d+/, `${name}: the section links the task that authored it`);
|
|
295
|
+
assert.match(original[1], /no upstream/i, `${name}: an original states plainly that it has no upstream`);
|
|
296
|
+
assert.match(original[1], /ADR 0231/, `${name}: the section names the two-origin rule`);
|
|
297
|
+
assert.match(original[1], /why it exists/i, `${name}: an original says why it exists, since it has no spec to point at`);
|
|
298
|
+
}
|
|
289
299
|
}
|
|
290
300
|
});
|
|
291
301
|
|
|
302
|
+
// ── the /style session (task 1003475, ADR 0231): the module's first original skill ──
|
|
303
|
+
test('the /style session is a first-party skill that runs four bounded steps and never writes the instance\'s world', () => {
|
|
304
|
+
const pre = read(path.join(SKILLS, 'PREAMBLE.md')).trimEnd();
|
|
305
|
+
assert.ok(ownDirs.includes('style'), 'skills/style/SKILL.md exists');
|
|
306
|
+
assert.ok(manifest.contributes.skills.includes('style'), 'declared in contributes.skills');
|
|
307
|
+
const text = read(path.join(SKILLS, 'style', 'SKILL.md'));
|
|
308
|
+
const own = text.slice(text.indexOf(pre) + pre.length);
|
|
309
|
+
assert.match(own, /^## First party\n/m, 'it declares itself first party, not a rebuild');
|
|
310
|
+
for (const step of ['Search', 'Create', 'Prove', 'Adopt']) {
|
|
311
|
+
assert.ok(new RegExp(`## Step \\d+ . ${step}`, 'i').test(own), `the session carries the ${step} step`);
|
|
312
|
+
}
|
|
313
|
+
for (const cli of ['kit/contrast.js', 'kit/render.js', 'kit/check-mock.js', 'tests/ui_design_styles.mjs']) {
|
|
314
|
+
assert.ok(own.includes(cli), `the PROVE step names ${cli}`);
|
|
315
|
+
}
|
|
316
|
+
assert.match(own, /sixteenth token/i, 'a look that needs a sixteenth token is refused at the door');
|
|
317
|
+
assert.match(own, /never a fork of a sheet|not a fork of a sheet/i, 'a look is a pack, never a fork of a sheet');
|
|
318
|
+
assert.match(own, /does not edit `config\/branding\.json`|read, never written/i, 'adopting a look stays the owner\'s decision; the session never writes the host boundary');
|
|
319
|
+
assert.doesNotMatch(own, /bypassPermissions|--dangerously-skip-permissions/, 'no permission bypass');
|
|
320
|
+
assert.doesNotMatch(own, /\b(write|add|install|register)\b[^.\n]{0,60}\b(PostToolUse|PreToolUse|Stop)\b[^.\n]{0,40}\bhooks?\b/i, 'no step writes a hook into a harness config');
|
|
321
|
+
assert.doesNotMatch(own, /\b(curl|wget|fetch|download)\b[^.\n]{0,60}\bhttps?:\/\//i, 'no remote bundle download');
|
|
322
|
+
const index = read(path.join(ROOT, 'docs', 'adr', 'README.md'));
|
|
323
|
+
const adr = fs.readdirSync(path.join(ROOT, 'docs', 'adr')).find((f) => f.startsWith('0231-'));
|
|
324
|
+
assert.ok(adr, 'docs/adr/0231-*.md exists');
|
|
325
|
+
assert.ok(index.includes(`](${adr})`), 'docs/adr/README.md links ADR 0231');
|
|
326
|
+
});
|
|
327
|
+
|
|
292
328
|
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', () => {
|
|
293
329
|
const pre = read(path.join(SKILLS, 'PREAMBLE.md')).trimEnd();
|
|
294
330
|
for (const name of ownDirs) {
|