super-ux 0.29.0 → 0.30.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,102 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.30.1 — 2026-08-05
4
+
5
+ ### Fixed
6
+ - **`templates/brand/voice.md` told projects to write `PER-NN` where the UX
7
+ contract numbers personas `P-NN`.** `B004` traces `Derived-from` against
8
+ `foundation.md`, so a project following our own template earned a false
9
+ blocking error — the failure mode that teaches people to ignore a linter.
10
+ Shipped in 0.30.0; found by the code graph built afterwards.
11
+ - Four check codes (`B005`, `B054`, `B060`, `B072`) shipped with no fixture
12
+ behind them while the suite was green and the count looked right.
13
+
14
+ ### Added
15
+ - **`brand-contract.md` now owns all 33 check codes** with their severities.
16
+ Eighteen were documented only in the linter's source, in a repo whose canon
17
+ is one owner per fact.
18
+ - **`validate_brand_lint_coverage`** — every code the linter can emit must have
19
+ a fixture and a contract row. The audit finding became a gate rather than a
20
+ ledger entry.
21
+ - `system-map.md` names the brand reference shelf. It names rather than links:
22
+ a link would make every skill ship all ten files, and the map's job is
23
+ telling you what exists.
24
+ - The code graph itself (`graphify-out/`), with `.graphifyignore` excluding the
25
+ 115 sync_references copies whose hubs would describe the distribution
26
+ mechanism rather than the design.
27
+
28
+ — 2026-08-05
29
+
30
+ The verbal identity layer. `docs/ux/` decides what the product does; the new
31
+ `docs/brand/` decides how it speaks — one voice, many registers, and a linter
32
+ that makes copy drift as findable as chain drift.
33
+
34
+ ### The contract — `brand-contract v1`
35
+
36
+ `docs/brand/` is a second artifact root beside `docs/ux/`, seeded by the
37
+ installer: `voice.md` (a voice pack, five fixed axes as IS / IS NOT, the
38
+ narrative, the invariants that survive translation), `terminology.md` (our
39
+ words, banned words, exact entity and tier spellings), `facts.md` (the only
40
+ source of any figure in public copy), `channels.md` (one record per surface),
41
+ `strings.md` (the interface string registry, key → `file:line` → scenario),
42
+ and `locales/<code>.md`.
43
+
44
+ Separate root on purpose: the brand also governs surfaces that are not UX at
45
+ all — a store listing, an ad, a post. The pack derives from `foundation.md`
46
+ and never the reverse.
47
+
48
+ ### Two skills
49
+
50
+ - **`brand-voice`** — defines and holds the identity. Six shipped voice packs
51
+ (`operator-brief`, `calm-expert`, `peer-builder`, `editorial-premium`,
52
+ `plain-service`, `playful-consumer`), each declaring the degeneration it
53
+ collapses into when overdone, so an overshoot is a finding rather than a
54
+ matter of taste. Invoked with no task it reports state and proposes exactly
55
+ one next action, and never invents a missing fact to close a gap.
56
+ - **`copywriting`** — writes in the voice and never writes *to* it. A missing
57
+ term or an unsourced number is reported, because adding it is the other
58
+ skill's decision.
59
+
60
+ Commands: `/brand`, `/brand-init`, `/brand-update`, `/brand-lint`, `/copy`.
61
+
62
+ ### `brand_lint.py` — 31 deterministic checks
63
+
64
+ Seeded as `docs/brand/lint.py`. Contract and sources (B001–B006), terminology
65
+ (B010–B012), string consistency (B020–B025), facts (B030–B032), channel
66
+ physics (B040–B043), bot safety (B050–B054), machine-drafting markers
67
+ (B060–B061), locales (B070–B073). Exit 0 clean, 1 warnings, 2 errors;
68
+ `--fix` touches only casing, the iOS keyword field, and re-pointing a registry
69
+ row whose string is unchanged. 32 fixtures, one per code, each watched failing
70
+ against a planted defect before it was trusted — and they run in CI.
71
+
72
+ Three rules it exists to enforce: one action keeps one name everywhere; a
73
+ number in public copy has a sourced row or is not written; no humor on error,
74
+ destructive-confirm, billing or paywall surfaces, in any voice.
75
+
76
+ ### Catalog
77
+
78
+ `BP-182..205` in six clusters of four — voice and consistency, product
79
+ microcopy, conversion copy, bot safety, channel physics, localization — and
80
+ `PRN-22..24`. New entries carry a sixth field, `Checked:`, dating the last
81
+ verification against the source. It starts at BP-182 deliberately:
82
+ backfilling it onto BP-001..181 would record a verification nobody performed.
83
+ New tags `brand-voice` `copy` `narrative` `terminology` `channel-physics`
84
+ `seo` `aeo` `aso` — `voice` was already taken, and it means a voice interface.
85
+
86
+ ### Elsewhere
87
+
88
+ `ux-audit` gains scope `copy`, the judgement twin of the linter. `ux_doctor`
89
+ reads the brand marker, because a pack on an old contract is internally
90
+ consistent and the linter stays quiet about it. Scenarios gain an optional
91
+ `Strings:` field. The brand hard rule ships in the Claude rule template and a
92
+ new Cursor rule.
93
+
94
+ ### Fixed
95
+
96
+ - `CONTRIBUTING.md` told maintainers to run `npm publish --access public`
97
+ by hand in `sshlg-skills` after a release. Every repo in the family has
98
+ published from CI on a `v*` tag since that text was written.
99
+
3
100
  ## 0.29.0 — 2026-08-05
4
101
 
5
102
  A pass aimed at the installed base rather than the catalog. Run across the
package/README.md CHANGED
@@ -100,6 +100,41 @@ plugin user-globally — any combination in one run. Also works straight from
100
100
  GitHub: `npx github:ssheleg/super-ux --cursor <dir>`, or clone and run
101
101
  `./install.sh --cursor <dir>`.
102
102
 
103
+ ## The brand layer — how the product speaks
104
+
105
+ `docs/ux/` decides what the product does. **`docs/brand/`** decides how it
106
+ speaks, under `brand-contract v1`: one voice, many registers, and a linter
107
+ that makes copy drift as findable as chain drift.
108
+
109
+ | File | Holds |
110
+ |---|---|
111
+ | `voice.md` | the pack, five fixed axes, narrative, invariants, locales |
112
+ | `terminology.md` | our words, banned words, entity and tier names |
113
+ | `facts.md` | canonical figures — the only source of a number in public copy |
114
+ | `channels.md` | one record per surface: register deltas, limits, bans |
115
+ | `strings.md` | the interface string registry → `file:line` → scenario |
116
+ | `locales/<code>.md` | address form, length coefficient, dead idioms, keywords |
117
+
118
+ Two skills: **`brand-voice`** defines and holds the identity (six shipped
119
+ voice packs, each declaring the degeneration it collapses into when overdone);
120
+ **`copywriting`** writes in it and never writes *to* it — a missing term or an
121
+ unsourced number is reported, never invented.
122
+
123
+ Commands: `/brand` (status → one recommended action), `/brand-init`,
124
+ `/brand-update`, `/brand-lint`, `/copy`.
125
+
126
+ ```bash
127
+ python3 docs/brand/lint.py
128
+ ```
129
+
130
+ 31 deterministic checks — banned words, one action under two names, a figure
131
+ with no sourced fact, a field over its limit with the locale coefficient
132
+ applied, blocked AI crawlers, keyword stuffing, humor on a billing screen,
133
+ a locale that lags without saying so. Exit 0 clean, 1 warnings, 2 errors.
134
+
135
+ Clean means *checkable*, not *good*: tone drift, unproven claims and a voice
136
+ that has overshot its own failure mode are judged by `/ux-audit copy`.
137
+
103
138
  ## The hard rule
104
139
 
105
140
  Installed into your project's `CLAUDE.md` (and as the always-on Cursor rule):
package/bin/super-ux.js CHANGED
@@ -86,23 +86,46 @@ function installCursor(target, force) {
86
86
  console.log(`seed: ${dst}`);
87
87
  }
88
88
  }
89
+ // The brand pack lives beside the UX chain, not inside it: it also governs
90
+ // surfaces that are not UX at all — a store listing, an ad, a post.
91
+ fs.mkdirSync(path.join(target, 'docs', 'brand', 'locales'), { recursive: true });
92
+ for (const tpl of [
93
+ 'README', 'voice', 'terminology', 'facts', 'channels', 'strings',
94
+ ]) {
95
+ const dst = path.join(target, 'docs', 'brand', `${tpl}.md`);
96
+ if (fs.existsSync(dst)) {
97
+ console.log(`keep: ${dst} exists (never overwritten)`);
98
+ } else {
99
+ fs.copyFileSync(path.join(ROOT, 'templates', 'brand', `${tpl}.md`), dst);
100
+ console.log(`seed: ${dst}`);
101
+ }
102
+ }
103
+ {
104
+ const dst = path.join(target, 'docs', 'brand', 'locales', 'en.md');
105
+ if (!fs.existsSync(dst)) {
106
+ fs.copyFileSync(path.join(ROOT, 'templates', 'brand', 'locale.md'), dst);
107
+ console.log(`seed: ${dst}`);
108
+ }
109
+ }
110
+
89
111
  // The linter is code, not a template — refresh it to the shipped version.
90
112
  // Shipped via package.json files[]; if that ever regresses, warn instead of
91
113
  // dying on an ENOENT stack trace after the rules are already installed.
92
114
  // Paths stay literal so test/validate.py can read them out of this source and
93
115
  // check them against package.json files[] — a variable segment here silently
94
116
  // turns that check into a directory prefix nobody ships.
95
- for (const [src, from, dst] of [
96
- ['ux_lint.py', path.join(ROOT, 'plugins', 'super-ux', 'scripts', 'ux_lint.py'), 'lint.py'],
97
- ['ux_doctor.py', path.join(ROOT, 'plugins', 'super-ux', 'scripts', 'ux_doctor.py'), 'doctor.py'],
117
+ for (const [src, from, area, dst] of [
118
+ ['ux_lint.py', path.join(ROOT, 'plugins', 'super-ux', 'scripts', 'ux_lint.py'), 'ux', 'lint.py'],
119
+ ['ux_doctor.py', path.join(ROOT, 'plugins', 'super-ux', 'scripts', 'ux_doctor.py'), 'ux', 'doctor.py'],
120
+ ['brand_lint.py', path.join(ROOT, 'plugins', 'super-ux', 'scripts', 'brand_lint.py'), 'brand', 'lint.py'],
98
121
  ]) {
99
- const to = path.join(target, 'docs', 'ux', dst);
122
+ const to = path.join(target, 'docs', area, dst);
100
123
  if (fs.existsSync(from)) {
101
124
  fs.copyFileSync(from, to);
102
125
  console.log(`sync: ${to}`);
103
126
  } else {
104
127
  console.error(
105
- `warning: ${src} not found in this package (${from}); docs/ux/${dst} was not installed.\n` +
128
+ `warning: ${src} not found in this package (${from}); docs/${area}/${dst} was not installed.\n` +
106
129
  ` Get it from https://github.com/${REPO}/blob/main/plugins/super-ux/scripts/${src}`
107
130
  );
108
131
  }
@@ -0,0 +1,37 @@
1
+ ---
2
+ description: Brand voice and copy — docs/brand/ is the source of truth for how the product speaks; any public-facing text change updates it in the same change and passes docs/brand/lint.py
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # Brand voice (super-ux)
7
+
8
+ `docs/ux/` decides what the product does. `docs/brand/` decides how it
9
+ speaks, under `brand-contract v1`.
10
+
11
+ ## Before writing any user-visible text
12
+
13
+ Read `docs/brand/voice.md`, `terminology.md`, and the `channels.md` record
14
+ for the surface. No pack yet → run `/brand-init` rather than improvising a
15
+ voice; a product whose copy was invented surface by surface is the drift
16
+ this layer removes.
17
+
18
+ ## Rules
19
+
20
+ - Any change to public text updates `docs/brand/` in the same change.
21
+ - Never quote a figure with no row in `facts.md`. Never invent a fact,
22
+ statistic, quote or expert — report the gap.
23
+ - One action, one name, everywhere it appears. Check `strings.md` first.
24
+ - A register moves the voice axes; it never crosses the invariants listed in
25
+ `voice.md`.
26
+ - No humor, exclamation marks or emoji on error, destructive confirm,
27
+ billing or paywall surfaces, in any voice.
28
+ - New interface strings get a `strings.md` row: key, `file:line`, scenario.
29
+
30
+ ## Check
31
+
32
+ ```bash
33
+ python3 docs/brand/lint.py
34
+ ```
35
+
36
+ Exit 0 clean, 1 warnings, 2 errors. Clean means checkable, not good — the
37
+ judgement half is `/ux-audit copy`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "super-ux",
3
- "version": "0.29.0",
3
+ "version": "0.30.1",
4
4
  "description": "Scenario-driven UI development for AI agents (Claude Code, Cursor, 70+ agents): a versioned design chain in docs/ux/, a scenario-first hard rule, a deterministic drift linter, and evidence-backed UX audits. This package is the installer CLI.",
5
5
  "bin": {
6
6
  "super-ux": "bin/super-ux.js"
@@ -11,6 +11,7 @@
11
11
  "templates",
12
12
  "plugins/super-ux/scripts/ux_lint.py",
13
13
  "plugins/super-ux/scripts/ux_doctor.py",
14
+ "plugins/super-ux/scripts/brand_lint.py",
14
15
  "README.md",
15
16
  "LICENSE",
16
17
  "CHANGELOG.md"