super-ux 0.28.0 → 0.30.0

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,116 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.30.0 — 2026-08-05
4
+
5
+ The verbal identity layer. `docs/ux/` decides what the product does; the new
6
+ `docs/brand/` decides how it speaks — one voice, many registers, and a linter
7
+ that makes copy drift as findable as chain drift.
8
+
9
+ ### The contract — `brand-contract v1`
10
+
11
+ `docs/brand/` is a second artifact root beside `docs/ux/`, seeded by the
12
+ installer: `voice.md` (a voice pack, five fixed axes as IS / IS NOT, the
13
+ narrative, the invariants that survive translation), `terminology.md` (our
14
+ words, banned words, exact entity and tier spellings), `facts.md` (the only
15
+ source of any figure in public copy), `channels.md` (one record per surface),
16
+ `strings.md` (the interface string registry, key → `file:line` → scenario),
17
+ and `locales/<code>.md`.
18
+
19
+ Separate root on purpose: the brand also governs surfaces that are not UX at
20
+ all — a store listing, an ad, a post. The pack derives from `foundation.md`
21
+ and never the reverse.
22
+
23
+ ### Two skills
24
+
25
+ - **`brand-voice`** — defines and holds the identity. Six shipped voice packs
26
+ (`operator-brief`, `calm-expert`, `peer-builder`, `editorial-premium`,
27
+ `plain-service`, `playful-consumer`), each declaring the degeneration it
28
+ collapses into when overdone, so an overshoot is a finding rather than a
29
+ matter of taste. Invoked with no task it reports state and proposes exactly
30
+ one next action, and never invents a missing fact to close a gap.
31
+ - **`copywriting`** — writes in the voice and never writes *to* it. A missing
32
+ term or an unsourced number is reported, because adding it is the other
33
+ skill's decision.
34
+
35
+ Commands: `/brand`, `/brand-init`, `/brand-update`, `/brand-lint`, `/copy`.
36
+
37
+ ### `brand_lint.py` — 31 deterministic checks
38
+
39
+ Seeded as `docs/brand/lint.py`. Contract and sources (B001–B006), terminology
40
+ (B010–B012), string consistency (B020–B025), facts (B030–B032), channel
41
+ physics (B040–B043), bot safety (B050–B054), machine-drafting markers
42
+ (B060–B061), locales (B070–B073). Exit 0 clean, 1 warnings, 2 errors;
43
+ `--fix` touches only casing, the iOS keyword field, and re-pointing a registry
44
+ row whose string is unchanged. 32 fixtures, one per code, each watched failing
45
+ against a planted defect before it was trusted — and they run in CI.
46
+
47
+ Three rules it exists to enforce: one action keeps one name everywhere; a
48
+ number in public copy has a sourced row or is not written; no humor on error,
49
+ destructive-confirm, billing or paywall surfaces, in any voice.
50
+
51
+ ### Catalog
52
+
53
+ `BP-182..205` in six clusters of four — voice and consistency, product
54
+ microcopy, conversion copy, bot safety, channel physics, localization — and
55
+ `PRN-22..24`. New entries carry a sixth field, `Checked:`, dating the last
56
+ verification against the source. It starts at BP-182 deliberately:
57
+ backfilling it onto BP-001..181 would record a verification nobody performed.
58
+ New tags `brand-voice` `copy` `narrative` `terminology` `channel-physics`
59
+ `seo` `aeo` `aso` — `voice` was already taken, and it means a voice interface.
60
+
61
+ ### Elsewhere
62
+
63
+ `ux-audit` gains scope `copy`, the judgement twin of the linter. `ux_doctor`
64
+ reads the brand marker, because a pack on an old contract is internally
65
+ consistent and the linter stays quiet about it. Scenarios gain an optional
66
+ `Strings:` field. The brand hard rule ships in the Claude rule template and a
67
+ new Cursor rule.
68
+
69
+ ### Fixed
70
+
71
+ - `CONTRIBUTING.md` told maintainers to run `npm publish --access public`
72
+ by hand in `sshlg-skills` after a release. Every repo in the family has
73
+ published from CI on a `v*` tag since that text was written.
74
+
75
+ ## 0.29.0 — 2026-08-05
76
+
77
+ A pass aimed at the installed base rather than the catalog. Run across the
78
+ twelve projects using super-ux, the tooling turned out to be blind to the
79
+ most common failure: only two are cleanly on contract v4, five carry no
80
+ marker at all — including the two largest bases, at 119 and 120 scenarios —
81
+ one holds v2, v3 and v4 across four artifacts simultaneously, and one keeps
82
+ its base under a name the contract does not own, so four audit reports were
83
+ produced against scenarios the tooling could not find.
84
+
85
+ ### Added
86
+ - **`/ux-doctor` and `docs/ux/doctor.py`.** `ux_lint` checks a chain against
87
+ itself, so a base written entirely to an old contract passes it — from the
88
+ inside such a chain is consistent. The doctor reports the effective
89
+ contract version, what each version since it introduced, mixed versions
90
+ across artifacts, files the tooling cannot find under their contract
91
+ names, audits produced against a base that is not there, and which
92
+ additive sections a project has not adopted. Read-only unless `--fix`,
93
+ which does only what cannot be wrong: renames, and moving loose audit
94
+ reports into `audits/`. Contract upgrades stay content decisions for
95
+ `/ux-update`. Wired into `/ux` status and the Cursor rule.
96
+ - **Information architecture — BP-180, BP-181.** BP-052 requires navigation
97
+ to be visible and BP-049 puts it in reach; neither judges whether the
98
+ *groups* make sense. Card sorting for grouping, tree testing for labels —
99
+ the one navigation decision that cannot be judged from the inside, because
100
+ the team already knows where everything is.
101
+ - **Moderated test tasks generated from `scenarios.md`.** A scenario is
102
+ already the shape a test task wants — situation, goal, observable success
103
+ — so the tasks are a rewrite of the base rather than a new artifact, and
104
+ what comes back grades against the same base.
105
+ - **`benchmark:<competitor>` audit scope.** Every other scope measures the
106
+ product against its own chain and cannot report that a flow is two steps
107
+ longer than everyone else's. Same axes both sides, observable from outside
108
+ only, and gaps recorded as opportunities for the foundation rather than as
109
+ defects in the report.
110
+ - **Reviews and support tickets as WHY-layer evidence** in `ux-foundation` —
111
+ the cheapest input the layer has, and the one most often skipped because
112
+ it does not feel like research.
113
+
3
114
  ## 0.28.0 — 2026-08-05
4
115
 
5
116
  Closes the carry-over ledger the 0.27 audit opened. Twenty-three practices,
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):
@@ -185,9 +220,10 @@ The contracts every skill reads:
185
220
  |---|---|
186
221
  | [scenario-format.md](plugins/super-ux/skills/references/scenario-format.md) | **The contract (ux-contract v4).** File layout, every field name, stable IDs (`P` `JTBD` `JRN` `ST` `FLW` `SCR` `SCN`), completeness checklists, the `draft → validated → implemented` lifecycle, audit verdicts and severities, the UX-plan format |
187
222
  | [system-map.md](plugins/super-ux/skills/references/system-map.md) | The whole system on one page — pipeline, files, skills, companions, and the four sync rules; every skill points here |
223
+ | [ux_doctor.py](plugins/super-ux/scripts/ux_doctor.py) | Contract doctor — reports mixed or stale contract versions across a project's artifacts, files the tooling cannot find under their contract names, and audits produced against a base that is not there. `/ux-lint` checks a chain against itself; this checks it against the contract. Installed as `docs/ux/doctor.py`, read-only unless `--fix` |
188
224
  | [best-practices-index.md](plugins/super-ux/skills/references/best-practices-index.md) | Generated tag index over the catalog — tag → ids, id → title. Read it to decide which entries to open; regenerated by `plugins/super-ux/scripts/bp_index.py` and checked for drift by the validator |
189
225
  | [ux-design-principles.md](plugins/super-ux/skills/references/ux-design-principles.md) | How the agent thinks: the design pipeline (forward and backwards), task analysis, flow rules, heuristics PRN-01..21, the improvement procedure, anti-patterns |
190
- | [best-practices.md](plugins/super-ux/skills/references/best-practices.md) | Living, tag-indexed catalog of 179 proven practices — subscription-app laws, mobile/web/voice guidance (Apple HIG 2025, M3 Expressive, NN/g, Baymard, WCAG 2.2), monetization economics (RevenueCat/PLG 2025 benchmarks, ASO, freemium boundaries), web funnels end to end (landing, pricing, checkout, dunning, cancel) and web2app (paid handoff, deferred deep links, storefront rules), motion and page weight (HTTP Archive field data, W3C sustainability), accessibility as it actually fails (WebAIM Million, EAA/ADA exposure), frustration telemetry, gamification and trend governance, growth loops and referral mechanics, empty states, authentication (NIST SP 800-63B rev 4) and form recovery, motion craft and perceived quality, the defaults that make an interface read as generated, interface state, locale and platform surfaces (Web Interface Guidelines), visual craft, Figma structure |
226
+ | [best-practices.md](plugins/super-ux/skills/references/best-practices.md) | Living, tag-indexed catalog of 181 proven practices — subscription-app laws, mobile/web/voice guidance (Apple HIG 2025, M3 Expressive, NN/g, Baymard, WCAG 2.2), monetization economics (RevenueCat/PLG 2025 benchmarks, ASO, freemium boundaries), web funnels end to end (landing, pricing, checkout, dunning, cancel) and web2app (paid handoff, deferred deep links, storefront rules), motion and page weight (HTTP Archive field data, W3C sustainability), accessibility as it actually fails (WebAIM Million, EAA/ADA exposure), frustration telemetry, gamification and trend governance, growth loops and referral mechanics, empty states, authentication (NIST SP 800-63B rev 4) and form recovery, motion craft and perceived quality, the defaults that make an interface read as generated, interface state, locale and platform surfaces (Web Interface Guidelines), visual craft, Figma structure |
191
227
  | [practice-selection.md](plugins/super-ux/skills/references/practice-selection.md) | The deterministic bridge: product profile → mandatory consideration sets → per-artifact checklists → a compliance table where every pulled practice gets a verdict. No silent skips, no cargo cult |
192
228
  | [component-guidelines.md](plugins/super-ux/skills/references/component-guidelines.md) | Which control for which job (radios/select/switch, sheet/alert, modal/disclosure, combobox, nav bar/rail, FAB, dates, toasts) and the platform rules — Apple HIG, Material 3, W3C ARIA APG, GOV.UK |
193
229
  | [visual-identity.md](plugins/super-ux/skills/references/visual-identity.md) | The visual layer and its owner: one style pack for the whole product, where it's recorded, how it meets Figma and code, and the division of labor with the craft floors |
package/bin/super-ux.js CHANGED
@@ -86,19 +86,49 @@ 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
- const lintSrc = path.join(ROOT, 'plugins', 'super-ux', 'scripts', 'ux_lint.py');
93
- const lintDst = path.join(target, 'docs', 'ux', 'lint.py');
94
- if (fs.existsSync(lintSrc)) {
95
- fs.copyFileSync(lintSrc, lintDst);
96
- console.log(`sync: ${lintDst}`);
97
- } else {
98
- console.error(
99
- `warning: linter not found in this package (${lintSrc}); docs/ux/lint.py was not installed.\n` +
100
- ` Get it from https://github.com/${REPO}/blob/main/plugins/super-ux/scripts/ux_lint.py`
101
- );
114
+ // Paths stay literal so test/validate.py can read them out of this source and
115
+ // check them against package.json files[] a variable segment here silently
116
+ // turns that check into a directory prefix nobody ships.
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'],
121
+ ]) {
122
+ const to = path.join(target, 'docs', area, dst);
123
+ if (fs.existsSync(from)) {
124
+ fs.copyFileSync(from, to);
125
+ console.log(`sync: ${to}`);
126
+ } else {
127
+ console.error(
128
+ `warning: ${src} not found in this package (${from}); docs/${area}/${dst} was not installed.\n` +
129
+ ` Get it from https://github.com/${REPO}/blob/main/plugins/super-ux/scripts/${src}`
130
+ );
131
+ }
102
132
  }
103
133
 
104
134
  console.log(`done: ${installed} installed, ${skipped} skipped`);
@@ -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`.
@@ -14,6 +14,10 @@ alwaysApply: true
14
14
  - Run the linter after any UX change and before calling work done:
15
15
  `python3 docs/ux/lint.py`. It must pass — drift must not merge; wire it
16
16
  into CI/pre-commit.
17
+ - Run `python3 docs/ux/doctor.py` when picking this base up after a while.
18
+ The linter checks the chain against itself and stays silent when the whole
19
+ base is written to an old contract; the doctor is what notices. Fix a
20
+ mixed or stale contract before designing on top of it.
17
21
  - Any change that touches user-facing behavior MUST update
18
22
  `docs/ux/scenarios.md` in the same change (add/adjust scenarios, statuses,
19
23
  coverage). New user-facing behavior with no scenario is a blocker, not a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "super-ux",
3
- "version": "0.28.0",
3
+ "version": "0.30.0",
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"
@@ -10,6 +10,8 @@
10
10
  "cursor",
11
11
  "templates",
12
12
  "plugins/super-ux/scripts/ux_lint.py",
13
+ "plugins/super-ux/scripts/ux_doctor.py",
14
+ "plugins/super-ux/scripts/brand_lint.py",
13
15
  "README.md",
14
16
  "LICENSE",
15
17
  "CHANGELOG.md"