super-ux 0.30.2 → 0.32.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,120 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.32.0 — 2026-08-10
4
+
5
+ A structural audit of 0.31.0 found twenty-two defects that 3427 green checks
6
+ could not see, because the suite verified shape and never once verified
7
+ composition. This release closes them and turns each finding class into a
8
+ check that fails.
9
+
10
+ ### Fixed
11
+
12
+ - **The system map linked `brand-contract.md`, and every skill links the
13
+ map.** `test/sync_references.py` copies the transitive closure of a skill's
14
+ links, so one pointer put all nine brand contracts inside every UX skill —
15
+ App Store guidance riding along in `ux-foundation`. The map now names
16
+ contracts and links none. `ux-flows`, `ux-foundation` and `ux-scenarios`
17
+ drop from 19 shipped contracts to 10, `brand-voice` from 19 to 10,
18
+ `copywriting` from 20 to 11; `ux-audit` keeps both shelves and now links
19
+ them deliberately, because its `copy` scope reads them.
20
+ - **`vision` shipped in 0.31.0 connected to nothing.** It is now in the
21
+ system map's pipeline, file table, skill list and command list; it points
22
+ back at the map; `ux_lint.py` checks its nine sections, its anti-vision and
23
+ whether its alignment rule was ever installed; `ux_doctor.py` reads its
24
+ contract marker; `templates/vision.md` seeds it; `cursor/rules/vision.mdc`
25
+ carries it to Cursor; `/ux-init` offers it as step 0.
26
+ - **`/ux` called itself the only command a user needs and could not reach
27
+ three of seven skills.** `vision`, `brand-voice` and `copywriting` now have
28
+ routing rows, status rows and menu entries. The action menu is renumbered
29
+ 1..15.
30
+ - **The vision hard rule had no single source.** It now lives in
31
+ `templates/vision-rule.md`, and `validate_hard_rule_copies` is driven by a
32
+ pair list covering both rules instead of one.
33
+ - **Three commands told the reader to run a script nothing installed.**
34
+ `/ux-rule` seeds `doctor.py` beside `lint.py`, `/brand-init` seeds
35
+ `docs/brand/lint.py`, and `/ux-doctor` no longer claims `/ux-rule` "seeds
36
+ both scripts" when it seeded one.
37
+ - **Numbers that had agreed with themselves for months.** 181 practices
38
+ against a catalog of 206; "31 deterministic checks" against a linter
39
+ emitting 33; the heuristic range written as `PRN-01..10`, `..16`, `..21`
40
+ and `..24` in six files; "four skills" and "four agent-requested rules"
41
+ against seven of each; `plugin.json` and `marketplace.json` naming five of
42
+ seven skills; a documented update command that omitted two skills and
43
+ recommended the bare `skills update` that shadows the plugin.
44
+ - **`bin/super-ux.js`** — a Russian-only fallback message in an otherwise
45
+ English CLI; the routing-block offer firing on the `--cursor` path and not
46
+ from the menu; `--help` describing a skeleton three categories smaller than
47
+ the one it writes; and a `done:` line that counted rules and called it the
48
+ whole install.
49
+ - **`brand_lint.py`, four defects found by pointing it at real prose.**
50
+ `B030` read `BP-079..090`, `NIST SP 800-63B` and `Apple HIG 2025` as
51
+ unsourced claims. The literal extractor ignored which quote opened a string
52
+ and read the gaps between template interpolations as copy — 598 fragments
53
+ from one file. `B022` rescanned a file once per registry row. `B024` had no
54
+ allowance for declared proper nouns, acronyms, sentence boundaries or
55
+ escape sequences. The registry parser could not hold a string containing a
56
+ pipe, or one with leading or trailing space.
57
+
58
+ ### Added
59
+
60
+ - **Three gates that ask for composition, not shape.**
61
+ `validate_stated_numbers` recomputes every count written in prose against
62
+ the artifact it counts. `validate_skill_parity` asks for each skill by name
63
+ in five places — its directory, a Cursor rule, the system map, both
64
+ manifest descriptions, and `commands/ux.md`. `validate_seeded_scripts`
65
+ proves every script an instruction names is copied there by some command.
66
+ Each was verified against a planted defect before being trusted.
67
+ - `validate_commands` now requires all fifteen commands, not eight;
68
+ `validate_templates` requires the vision skeleton and both rule snippets.
69
+ - `cursor/rules/copywriting.mdc` — the Cursor channel shipped four of seven
70
+ domains.
71
+ - **super-ux now runs its own chain.** `CLAUDE.md` with all three hard rules,
72
+ `docs/ux/` (vision, foundation, flows, screens, 15 scenarios covering the
73
+ installer TUI) and `docs/brand/` (peer-builder pack, terminology, a facts
74
+ table where every row names the command that recomputes it, channels, and a
75
+ string registry generated from the source). Both linters exit clean. Six of
76
+ the defects above were found by writing them.
77
+ - `docs/superpowers/backlog.md` and `verification.md` — the board and the
78
+ ledger the pipeline reads at stage 0 and writes at stage 8.
79
+ - Four `B030` regression fixtures, so the identifier, standard, year and
80
+ real-figure cases each have a test that has been watched fail.
81
+
82
+ ### Changed
83
+
84
+ - `practice-selection.md`'s `ALWAYS` set is `PRN-01..24`. PRN-22..24 are the
85
+ verbal heuristics; they read as brand rules and were therefore filed as
86
+ optional, but any product with text has them.
87
+ - `templates/README.md` — the map seeded into every project now carries the
88
+ vision row, the `docs/brand/` sibling and `doctor.py`.
89
+ - The `ux-contract` gains an optional `vision.md` section. Still v4: the file
90
+ is additive and optional.
91
+
92
+ ## 0.31.0 — 2026-08-06
93
+
94
+ ### Added
95
+ - **`vision` skill + `/vision`** — the layer above `ux-foundation`. Foundation
96
+ answers who uses the product and why; vision answers what the product **is**
97
+ and what it refuses to become. Writes `docs/ux/vision.md` in nine layers —
98
+ essence, core idea, system behaviour, the user's role, principles that each
99
+ name a rejected alternative, the **anti-vision**, horizon, the one sentence,
100
+ and an alignment test — then installs that test as a rule so later features
101
+ are checked against it.
102
+
103
+ The chain now reads **vision -> foundation -> flows -> scenarios -> audit**.
104
+
105
+ ### Notes
106
+ - Ported from a Cursor-only `vision-generator`. Two things changed on the way in.
107
+ It wrote its guardian rule to a **hardcoded `.cursor/skills/` path** (three
108
+ occurrences); it now installs into whichever instruction file the project
109
+ actually uses — `CLAUDE.md`, `AGENTS.md` or `GEMINI.md` — because a rule
110
+ installed where the running agent cannot see it is worse than no rule: absence
111
+ looks identical to compliance. And the document moved to `docs/ux/vision.md`,
112
+ into the chain the rest of this plugin maintains, rather than beside it in the
113
+ project root.
114
+ - The skill raises a contradiction with `foundation.md` as a finding rather than
115
+ smoothing it over. Two documents that disagree are worse than one that is
116
+ wrong, because teams follow whichever they read last.
117
+
3
118
  ## 0.30.2 — 2026-08-05
4
119
 
5
120
  ### Added
package/README.md CHANGED
@@ -18,7 +18,8 @@ used as the checklist for evidence-backed audits of the code.
18
18
 
19
19
  ```mermaid
20
20
  flowchart LR
21
- F["Foundation<br/>personas · JTBD<br/>journeys · stories"] --> L["Flows<br/>task analysis<br/>+ branches"]
21
+ V["Vision<br/>essence · principles<br/>anti-vision"] --> F["Foundation<br/>personas · JTBD<br/>journeys · stories"]
22
+ F --> L["Flows<br/>task analysis<br/>+ branches"]
22
23
  L --> S["Screens<br/>states · elements<br/>Figma frames"]
23
24
  S --> C["Scenarios<br/>action → response<br/>alt + error paths"]
24
25
  C --> B["Build UI<br/>only now"]
@@ -26,6 +27,7 @@ flowchart LR
26
27
  A --> P["Fix plan<br/>Freq × Severity<br/>× Solvability"]
27
28
  P --> B
28
29
  B -.->|same change| C
30
+ V -.->|alignment check| B
29
31
  ```
30
32
 
31
33
  Every layer traces to the one above it. New product? Build it forward. Existing
@@ -69,15 +71,15 @@ routing is the agent's job.
69
71
  npx super-ux --cursor /path/to/your/project
70
72
  ```
71
73
 
72
- Copies the rules into `.cursor/rules/` (one always-on hard rule + four
73
- agent-requested rules), seeds `docs/ux/`, and installs the linter. An existing
74
+ Copies the rules into `.cursor/rules/` (one always-on hard rule + seven
75
+ agent-requested rules), seeds `docs/ux/`, and installs the linters. An existing
74
76
  scenario base is never overwritten; re-run with `--force` after a release to
75
77
  refresh rules and linter only.
76
78
 
77
79
  ### Any agent (70+, via the skills CLI)
78
80
 
79
81
  ```sh
80
- npx skills add ssheleg/super-ux # all four skills, current project
82
+ npx skills add ssheleg/super-ux # all seven skills, current project
81
83
  npx skills add ssheleg/super-ux -g # user-global
82
84
  npx skills add ssheleg/super-ux --skill ux-audit # one skill
83
85
  ```
@@ -127,7 +129,7 @@ Commands: `/brand` (status → one recommended action), `/brand-init`,
127
129
  python3 docs/brand/lint.py
128
130
  ```
129
131
 
130
- 31 deterministic checks — banned words, one action under two names, a figure
132
+ 33 deterministic checks (`B001`..`B073`) — banned words, one action under two names, a figure
131
133
  with no sourced fact, a field over its limit with the locale coefficient
132
134
  applied, blocked AI crawlers, keyword stuffing, humor on a billing screen,
133
135
  a locale that lags without saying so. Exit 0 clean, 1 warnings, 2 errors.
@@ -200,19 +202,24 @@ compliance table. Full protocol:
200
202
 
201
203
  ## What's inside
202
204
 
203
- Four skills, one entry point, and a set of contracts they all obey.
205
+ Seven skills, one entry point, and a set of contracts they all obey. Every
206
+ one of them is reachable from `/ux` — a skill the entry point cannot route
207
+ to is a skill nobody runs.
204
208
 
205
209
  | Piece | Purpose |
206
210
  |---|---|
211
+ | skill `vision` | What the product **is** (`docs/ux/vision.md`) — the layer above the chain, never to be confused with `scenarios.md`, which says what it **does**: essence, core idea, system behaviour, the user's role, principles with a rejected side, the **anti-vision**, horizon, one sentence, and an alignment test later features are checked against. Installs that check into the project's own instruction file |
207
212
  | skill `ux-foundation` | The WHY layer (`docs/ux/foundation.md`): personas, jobs to be done with forces, customer journey maps, user stories with Given/When/Then acceptance criteria, the monetization model |
208
213
  | skill `ux-flows` | The HOW layer + the UI map: `docs/ux/flows.md` (task analysis, mermaid flows referencing screens by ID) and `docs/ux/screens.md` — every screen and state with its Figma frame, wireframe, code coverage, scenarios and resources. Also heuristic evaluation and traced redesign proposals |
209
214
  | skill `ux-scenarios` | `docs/ux/scenarios.md`: use-case scenarios (action → observable response, alt and error paths) covering every flow node and edge, `Traces:` to stories and flows, validated for conflicts, coverage and traceability |
210
215
  | skill `ux-audit` | Batched audit with full context: code vs every scenario plus its story's acceptance criteria; verdicts PASS / PARTIAL / FAIL / BLOCKED with `file:line` evidence; depths `quick` / `standard` / `deep`; a `coverage` scope that audits the chain itself |
216
+ | skill `brand-voice` | `docs/brand/`: the pack and its five axes, the words the product owns and bans, canonical facts, the per-surface register, locales — six shipped voice packs, each declaring the degeneration it collapses into when overdone |
217
+ | skill `copywriting` | Writes in that voice and never writes *to* it: interface strings, errors, empty states, landing and pricing pages, posts, changelogs, store listings, ads, lifecycle email. A missing term or an unsourced number is reported, never invented |
211
218
  | `/ux` | **The one command**: sets up whatever is missing, reports status across every layer, then offers only the applicable actions with one marked recommended. Idempotent |
212
- | `/ux-init` `/ux-foundation` `/ux-flows` `/ux-update` `/ux-audit` `/ux-rule` `/ux-lint` | Direct controls for when you know exactly what you want; `/ux-rule` installs the hard rule into `CLAUDE.md` |
219
+ | `/vision` `/ux-init` `/ux-foundation` `/ux-flows` `/ux-update` `/ux-audit` `/ux-rule` `/ux-lint` `/ux-doctor` · `/brand` `/brand-init` `/brand-update` `/brand-lint` `/copy` | Direct controls for when you know exactly what you want; `/ux-rule` installs both hard rules and seeds `lint.py` + `doctor.py`; `/brand-init` seeds `docs/brand/` and its linter |
213
220
  | `docs/ux/lint.py` + `/ux-lint` | The deterministic half: missing Figma frames, unresolved SCR/story traces, orphans, built screens without coverage, index desync, ID gaps, broken links. Stdlib-only, exit 1 on problems — wire it into CI so drift can't merge |
214
- | `cursor/rules/*.mdc` | The same methodology for Cursor: one always-on hard rule + four agent-requested rules |
215
- | `templates/` | Seeds for `docs/ux/`: foundation, flows, screens, scenario base, the folder README, the audit-report skeleton, and the CLAUDE.md rule snippet |
221
+ | `cursor/rules/*.mdc` | The same methodology for Cursor: one always-on hard rule + seven agent-requested rules (vision, foundation, flows, scenarios, audit, brand voice, copywriting) |
222
+ | `templates/` | Seeds for `docs/ux/`: the vision skeleton, foundation, flows, screens, scenario base, the folder README, and the audit-report skeleton. Both hard-rule snippets live here as their single source — `claude-rule.md` (scenario-first) and `vision-rule.md` (vision alignment) — and the validator fails if a command's embedded copy drifts from them. Seeds for `docs/brand/`: voice, terminology, facts, channels, the string registry, a locale delta, and its folder README |
216
223
 
217
224
  The contracts every skill reads:
218
225
 
@@ -222,8 +229,8 @@ The contracts every skill reads:
222
229
  | [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
230
  | [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` |
224
231
  | [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 |
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 |
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 |
232
+ | [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..24, the improvement procedure, anti-patterns |
233
+ | [best-practices.md](plugins/super-ux/skills/references/best-practices.md) | Living, tag-indexed catalog of 206 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 |
227
234
  | [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 |
228
235
  | [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 |
229
236
  | [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 |
@@ -231,15 +238,28 @@ The contracts every skill reads:
231
238
 
232
239
  ## Keeping installs current
233
240
 
234
- Global channels (run after a release, then restart the Claude Code session so
235
- the plugin reloads):
241
+ One command, every channel (run after a release, then restart the Claude Code
242
+ session so the plugin reloads):
236
243
 
237
244
  ```sh
238
- claude plugin marketplace update super-ux && \
239
- claude plugin update super-ux@super-ux && \
240
- npx --yes skills update ux-audit ux-flows ux-foundation ux-scenarios --global --yes
245
+ npx --yes sshlg-skills@latest update
241
246
  ```
242
247
 
248
+ It updates the Claude Code plugin **and** the agent copies, and clears any
249
+ plain copy under `~/.claude/skills/` that would shadow the plugin.
250
+
251
+ Per-plugin, only when the launcher is unavailable:
252
+
253
+ ```sh
254
+ claude plugin marketplace update super-ux && claude plugin update super-ux@super-ux
255
+ ```
256
+
257
+ **Do not run a bare `npx skills update <skill>` for a skill you installed as a
258
+ plugin.** Without an explicit `--agent` list the skills CLI detects Claude Code
259
+ and re-creates `~/.claude/skills/<skill>` as a plain copy, which shadows the
260
+ plugin and serves its frozen version forever. Nothing reports this: the plugin
261
+ updates, the copy does not, and the copy is what loads.
262
+
243
263
  Cursor rules and the seeded `docs/ux/lint.py` are per-project (Cursor has no
244
264
  global rules directory) — refresh each project you use:
245
265
 
package/bin/super-ux.js CHANGED
@@ -20,7 +20,7 @@ const REPO = 'ssheleg/super-ux';
20
20
 
21
21
  const MENU_ITEMS = [
22
22
  { key: 'skills', label: 'Skills for any AI agent (Claude Code, Codex, Cursor, 70+ — opens agent picker)' },
23
- { key: 'cursor', label: 'Cursor rules (always-on hard rule + docs/ux skeleton) into a project' },
23
+ { key: 'cursor', label: 'Cursor rules + docs/ux skeleton + docs/brand pack + linters, into a project' },
24
24
  { key: 'claude', label: 'Claude Code plugin (skills + /ux commands, user-global)' },
25
25
  ];
26
26
 
@@ -35,9 +35,12 @@ Usage:
35
35
  Menu items (select any combination, 'a' = all):
36
36
  1. Skills for any AI agent (Claude Code, Codex, Cursor, 70+) — delegates to
37
37
  'npx skills add ${REPO}' with its agent/global/project picker.
38
- 2. Cursor rules: cursor/rules/*.mdc -> <project>/.cursor/rules/ plus the
39
- docs/ux skeleton. Existing scenario base is NEVER overwritten; existing
40
- rule files are skipped unless --force.
38
+ 2. Cursor rules: cursor/rules/*.mdc -> <project>/.cursor/rules/, plus the
39
+ docs/ux skeleton, the docs/brand pack, and all three linters
40
+ (docs/ux/lint.py, docs/ux/doctor.py, docs/brand/lint.py). Existing
41
+ scenario base and brand pack are NEVER overwritten; existing rule files
42
+ are skipped unless --force. docs/ux/vision.md is not seeded: an empty
43
+ vision reads as a decided one — write it with the vision skill.
41
44
  3. Claude Code plugin (skills + /ux commands, user-global) — runs
42
45
  'claude plugin marketplace add ${REPO}' + 'claude plugin install' when
43
46
  the claude CLI is available, otherwise prints the /plugin commands.`);
@@ -59,6 +62,7 @@ function installCursor(target, force) {
59
62
 
60
63
  let installed = 0;
61
64
  let skipped = 0;
65
+ let seeded = 0;
62
66
  const rules = fs.readdirSync(rulesSrc).filter((f) => f.endsWith('.mdc')).sort();
63
67
  if (rules.length === 0) fail(`no .mdc rules found in ${rulesSrc}`);
64
68
 
@@ -84,6 +88,7 @@ function installCursor(target, force) {
84
88
  } else {
85
89
  fs.copyFileSync(path.join(ROOT, 'templates', `${tpl}.md`), dst);
86
90
  console.log(`seed: ${dst}`);
91
+ seeded += 1;
87
92
  }
88
93
  }
89
94
  // The brand pack lives beside the UX chain, not inside it: it also governs
@@ -98,6 +103,7 @@ function installCursor(target, force) {
98
103
  } else {
99
104
  fs.copyFileSync(path.join(ROOT, 'templates', 'brand', `${tpl}.md`), dst);
100
105
  console.log(`seed: ${dst}`);
106
+ seeded += 1;
101
107
  }
102
108
  }
103
109
  {
@@ -105,6 +111,7 @@ function installCursor(target, force) {
105
111
  if (!fs.existsSync(dst)) {
106
112
  fs.copyFileSync(path.join(ROOT, 'templates', 'brand', 'locale.md'), dst);
107
113
  console.log(`seed: ${dst}`);
114
+ seeded += 1;
108
115
  }
109
116
  }
110
117
 
@@ -131,7 +138,12 @@ function installCursor(target, force) {
131
138
  }
132
139
  }
133
140
 
134
- console.log(`done: ${installed} installed, ${skipped} skipped`);
141
+ // Report what happened, not one third of it: the old line counted rules
142
+ // only, so a run that wrote twenty files announced eight.
143
+ console.log(
144
+ `done: ${installed} rule(s) installed, ${skipped} skipped, ` +
145
+ `${seeded} doc(s) seeded, linters synced`
146
+ );
135
147
  }
136
148
 
137
149
  function run(cmd, args) {
@@ -329,6 +341,10 @@ async function menu() {
329
341
  if (keys.includes('cursor')) installCursor(cursorDir, false);
330
342
  if (keys.includes('claude')) installClaudePlugin();
331
343
  if (keys.includes('skills')) installSkillsCli();
344
+
345
+ // Same offer the --cursor flag path makes. Two doors into one install that
346
+ // behave differently is how a feature comes to exist for half its users.
347
+ offerRouters();
332
348
  }
333
349
 
334
350
  /**
@@ -349,8 +365,8 @@ function offerRouters() {
349
365
  );
350
366
  if (r.status !== 0) {
351
367
  console.log(
352
- '\nЧтобы скилы включались по умолчанию во всех проектах, допиши блок\n' +
353
- 'роутинга в глобальные инструкции агента:\n\n' +
368
+ '\nTo have these skills apply by default in every project, add the\n' +
369
+ "family's routing block to your agent's global instructions:\n\n" +
354
370
  ' npx --yes sshlg-skills routers --member super-ux\n'
355
371
  );
356
372
  }
@@ -0,0 +1,65 @@
1
+ ---
2
+ description: "Write or rewrite any text a user reads (interface strings, errors, empty states, landing and pricing pages, posts, changelogs, store listings, ads, email): apply when asked to write copy, rewrite a headline or CTA, fix microcopy, or when a draft reads machine-made"
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # copywriting — write it in the product's own voice
7
+
8
+ `brand-voice` decides how the product speaks. This is the pass that writes
9
+ in it. The two are not interchangeable: defining a voice mid-sentence to
10
+ finish a button is how a product ends up with six voices.
11
+
12
+ ## First action, every time
13
+
14
+ Read the pack: `docs/brand/voice.md`, `terminology.md`, and the
15
+ `channels.md` record for the surface being written. **No pack? Stop and run
16
+ `/brand-init`.** Guessing the voice and being wrong costs more than the
17
+ pause.
18
+
19
+ **Never write to `docs/brand/`.** A term missing from the dictionary, or a
20
+ number with no row in `facts.md`, is **reported** — never invented to finish
21
+ the sentence. Adding it is `brand-voice`'s decision, not this pass's.
22
+
23
+ ## Rules that hold in every voice
24
+
25
+ - **Every figure comes from `facts.md`.** No sourced fact, no number. Never
26
+ fabricate a statistic, a quote, or an expert.
27
+ - **One action keeps one name** across button, confirmation, toast, history,
28
+ notification and accessible name. Search `strings.md` before naming one.
29
+ - **No humor, exclamation marks or emoji** on error, destructive confirm,
30
+ billing or paywall surfaces — including in a playful voice.
31
+ - **Errors say what happened and how to recover**, in the user's words, and
32
+ never blame the user.
33
+ - **A register is a delta on the voice axes**, never a crossing of the
34
+ invariants recorded in `voice.md`.
35
+ - **Every new interface string gets a `strings.md` row**: key, `file:line`,
36
+ and the scenario it serves. A string with no scenario is a candidate for
37
+ deletion, not translation.
38
+ - **Claims reach a consequence.** "AI-powered" is a mechanism; what the user
39
+ stops doing is the copy.
40
+
41
+ ## The any-other-SaaS test
42
+
43
+ Could this sentence sit unchanged on a competitor's page? Then it says
44
+ nothing. Rewrite it until it could not.
45
+
46
+ Machine-made tells to strip: tricolons that add nothing, "seamless /
47
+ effortless / unlock / elevate / robust", em-dash cadence in every paragraph,
48
+ "it's not just X, it's Y", hedges stacked two deep, and a closing sentence
49
+ that summarizes what was just said.
50
+
51
+ ## Localization
52
+
53
+ A locale is not a translation of the English: the length coefficient, the
54
+ address form, and the dead idioms are recorded per locale. A locale that
55
+ lags the primary says so rather than pretending to be current.
56
+
57
+ ## Check
58
+
59
+ ```bash
60
+ python3 docs/brand/lint.py
61
+ ```
62
+
63
+ Exit 0 clean, 1 warnings, 2 errors. Clean means *checkable*, not *good* —
64
+ tone drift, unproven claims and a voice that overshot its declared failure
65
+ mode are judged by `/ux-audit copy`.
@@ -0,0 +1,67 @@
1
+ ---
2
+ description: "Product vision (docs/ux/vision.md): apply when defining what the product is and what it refuses to become, when a proposed feature feels off-strategy, or when a scope argument has no document that settles it"
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # vision — what the product is, and what it refuses to become
7
+
8
+ The layer above the chain. `foundation.md` answers **who** uses the product
9
+ and **why**; `vision.md` answers **what the product is**. Do not confuse it
10
+ with `scenarios.md`, the other layer whose name starts with "what":
11
+ scenarios say what the product **does**, step by step. A feature can satisfy
12
+ every scenario and still violate the anti-vision — that case is the whole
13
+ reason this layer exists.
14
+
15
+ ## Before writing a word
16
+
17
+ Read the project, not its README: source entry points, the schema, the
18
+ routing table, the live UI, `package.json`, and `docs/ux/foundation.md` if
19
+ it exists. **A vision written from the README alone describes the README.**
20
+ The gap between what a project claims and what its code does is usually
21
+ where the real vision is hiding, and naming that gap is often the most
22
+ valuable thing this pass does.
23
+
24
+ ## The document — nine sections, these headings, in this order
25
+
26
+ ```
27
+ ## 1. Essence one sentence; survives a rewrite on another stack
28
+ ## 2. Core idea an observation about the world, not a problem statement
29
+ ## 3. What the system does continuous behaviour, verbs not nouns
30
+ ## 4. The user's role what the user becomes: directs, judges, decides
31
+ ## 5. Principles "we do X, not Y" — Y is something reasonable people pick
32
+ ## 6. Anti-vision what it refuses to become, named product by product
33
+ ## 7. Horizon a direction for two to three years, never a roadmap
34
+ ## 8. The one sentence if someone remembers exactly one thing
35
+ ## 9. The alignment test three to five questions a feature must pass
36
+ ```
37
+
38
+ `**Status:** draft | approved` and `**Last reviewed:** YYYY-MM-DD` at the
39
+ top. A section with no rejected alternative is a slogan; a vision with no
40
+ anti-vision settles no argument.
41
+
42
+ ## The half that is actually the point
43
+
44
+ Writing the document is half the job. The other half is the alignment rule
45
+ in this project's own instruction file (`.cursor/rules/`, `AGENTS.md` or
46
+ `CLAUDE.md` — whichever this project reads), so that later features get
47
+ checked against the vision instead of the vision being written once and
48
+ never read. **A vision nothing reads is a document, not a constraint.**
49
+
50
+ Before planning any new feature, check it against the anti-vision and the
51
+ alignment test. Aligned → proceed, saying in one line which part it serves.
52
+ Misaligned → stop before writing code, name the conflict quoting the layer
53
+ it contradicts, and offer two paths: reshape the feature, or amend the
54
+ vision and say what that costs. Do not pick one silently.
55
+
56
+ **Do NOT trigger for** bug fixes, refactors, dependency work, tests, docs,
57
+ or anything with no user-facing surface. A vision check on a typo fix is how
58
+ a team learns to skip the check that matters.
59
+
60
+ ## Check
61
+
62
+ ```bash
63
+ python3 docs/ux/lint.py
64
+ ```
65
+
66
+ It reports missing sections, an approved vision with an empty anti-vision,
67
+ and a `vision.md` whose alignment rule was never installed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "super-ux",
3
- "version": "0.30.2",
3
+ "version": "0.32.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"
@@ -89,7 +89,12 @@ def table_rows(text: str) -> list[list[str]]:
89
89
  line = line.strip()
90
90
  if not line.startswith("|") or not line.endswith("|"):
91
91
  continue
92
- cells = [c.strip() for c in line.strip("|").split("|")]
92
+ # A markdown cell escapes a literal pipe as `\|`. Splitting on a raw
93
+ # pipe therefore tore `Select [e.g. 1,3 | all | q]:` into three cells
94
+ # and pointed a registry row at a file called "all". A string registry
95
+ # that cannot hold a string containing a pipe cannot describe a CLI.
96
+ parts = re.split(r"(?<!\\)\|", line.strip("|"))
97
+ cells = [c.strip().replace("\\|", "|") for c in parts]
93
98
  if all(re.fullmatch(r":?-{2,}:?", c) for c in cells if c):
94
99
  continue
95
100
  rows.append(cells)
@@ -297,14 +302,50 @@ WEAK_LABELS = {
297
302
  "learn more", "get started", "continue",
298
303
  }
299
304
 
300
- LITERAL_RE = re.compile(r"""["']([^"'\n]{3,60})["']""")
305
+ LITERAL_RE = re.compile(r"""(["'`])((?:(?!\1)[^\n]){3,200})\1""")
306
+
307
+ # A template literal's interpolations split it into pieces, and the pieces are
308
+ # not strings: `${a} : ${b}` yielded a literal " : ". Running this check over
309
+ # super-ux's own installer produced 598 such fragments and buried the four real
310
+ # findings under them. A check whose output nobody reads is not a check.
311
+ CODE_FRAGMENT_RE = re.compile(
312
+ r"""
313
+ ^\s*(?:\|\||&&|\?|:|\}|\{|\)) # opens with an operator or a brace
314
+ | (?:\|\||&&|===|!==|=>)\s*$ # ends with one
315
+ | \$\{ # carries an interpolation opener
316
+ | ^\s*[:?]\s*$ # is only a ternary arm separator
317
+ """,
318
+ re.VERBOSE,
319
+ )
301
320
 
302
321
 
303
322
  def _looks_like_copy(literal: str) -> bool:
304
323
  """A quoted literal that could plausibly be user-visible text."""
305
324
  if not literal or literal[0].islower() and " " not in literal:
306
325
  return False
307
- return bool(re.match(r"^[A-Z]", literal)) or " " in literal
326
+ if CODE_FRAGMENT_RE.search(literal):
327
+ return False
328
+ # Prose has at least two word characters in a row somewhere, and at least
329
+ # one letter. "} ${selected.has(i) ? " has neither once operators are gone.
330
+ if not re.search(r"[A-Za-z]{2,}", literal):
331
+ return False
332
+ # A lone ALL-CAPS token is an identifier -- ENOENT, README. A lone
333
+ # capitalised one is a button label -- Publish, Archive -- and excluding
334
+ # those would silently switch B022 off for exactly the strings it exists
335
+ # to catch.
336
+ if " " not in literal and literal.isupper():
337
+ return False
338
+ # A lone token opening with `-` or `.` is a flag or a file extension:
339
+ # `--force`, `.mdc`, `.cursor`. Nobody reads them as sentences.
340
+ if " " not in literal and literal[0] in "-.":
341
+ return False
342
+ if literal.strip() in DIRECTIVES:
343
+ return False
344
+ return True
345
+
346
+
347
+ # Language directives that are quoted strings and never reach a user.
348
+ DIRECTIVES = {"use strict", "use client", "use server"}
308
349
 
309
350
 
310
351
  def check_consistency(brand_dir: Path, sources: dict) -> list[Finding]:
@@ -315,6 +356,7 @@ def check_consistency(brand_dir: Path, sources: dict) -> list[Finding]:
315
356
  _, _, entities = dictionary(brand_dir)
316
357
  entity_words = {name for name, _ in entities}
317
358
 
359
+ swept: set[str] = set() # one unregistered-literal sweep per file
318
360
  by_key: dict[str, set] = {}
319
361
  for row in rows:
320
362
  by_key.setdefault(row["key"], set()).add(row["text"])
@@ -340,27 +382,48 @@ def check_consistency(brand_dir: Path, sources: dict) -> list[Finding]:
340
382
 
341
383
  body = read(target) or ""
342
384
  literals = [
343
- lit for lit in LITERAL_RE.findall(body) if _looks_like_copy(lit)
385
+ lit for _q, lit in LITERAL_RE.findall(body) if _looks_like_copy(lit)
344
386
  ]
345
387
  if literals:
346
- if row["text"] not in body:
388
+ if row["text"] not in body and row["text"].strip() not in body:
347
389
  findings.append(Finding(
348
390
  "B021", SEVERITY_ERROR, location, 0,
349
391
  f"`{row['key']}` is \"{row['text']}\" in the registry, "
350
392
  f"but that text is not in {file_part}",
351
393
  ))
352
- known = {r["text"] for r in rows}
353
- for lit in literals:
354
- if lit not in known:
355
- findings.append(Finding(
356
- "B022", SEVERITY_WARN, f"{file_part}", 0,
357
- f"\"{lit}\" is in the code with no registry row -- "
358
- f"agree it or retire it",
359
- ))
360
-
394
+ if file_part not in swept:
395
+ swept.add(file_part)
396
+ known = {r["text"] for r in rows}
397
+ trimmed = {t.strip() for t in known}
398
+ for lit in dict.fromkeys(literals): # one report per literal
399
+ if lit not in known and lit.strip() not in trimmed:
400
+ findings.append(Finding(
401
+ "B022", SEVERITY_WARN, f"{file_part}", 0,
402
+ f"\"{lit}\" is in the code with no registry row -- "
403
+ f"agree it or retire it",
404
+ ))
405
+
406
+ proper: set[str] = set()
407
+ for name in entity_words:
408
+ proper.update(name.split())
361
409
  for row in rows:
362
- words = row["text"].split()
363
- for word in words[1:]:
410
+ # Escape sequences are not words: "\\n--- Skills for ..." begins with a
411
+ # token whose only letter is the n of \\n.
412
+ readable = re.sub(r"\\[nrt]|\\x[0-9a-fA-F]{2}\[[0-9;]*[A-Za-z]", " ",
413
+ row["text"])
414
+ words = readable.split()
415
+ first = next(
416
+ (i for i, w in enumerate(words) if re.search(r"[A-Za-z]", w)), 0
417
+ )
418
+ for index, word in enumerate(words[first + 1:], start=first + 1):
419
+ stripped = word.strip(".,:;!?()[]\"'")
420
+ if stripped in proper or (stripped.isupper() and len(stripped) > 1):
421
+ continue # a declared entity, or an acronym like AI / CLI
422
+ # A capital after a full stop is a sentence, not Title Case. The
423
+ # check read "…development. Select what to install:" as miscased
424
+ # until super-ux ran it over its own menu.
425
+ if words[index - 1].rstrip('"\')').endswith((".", "!", "?", ":")):
426
+ continue
364
427
  bare = word.strip(".,:;!?()")
365
428
  if not bare or bare in entity_words:
366
429
  continue
@@ -384,7 +447,26 @@ def check_consistency(brand_dir: Path, sources: dict) -> list[Finding]:
384
447
 
385
448
 
386
449
  FRONT_MATTER_RE = re.compile(r"^---\n(.*?)\n---\n", re.S)
387
- NUMBER_RE = re.compile(r"\d+\s?%|[$€£]\s?\d[\d,.]*|\b\d{3,}\b")
450
+ # A figure is a claim. An identifier, a standard's designation and a year are
451
+ # not, and B030 read all three as unsourced claims until this project ran the
452
+ # linter over its own README: `BP-079`, `NIST SP 800-63B` and `Apple HIG 2025`
453
+ # each produced an error nobody could act on. The check had the right shape
454
+ # and the wrong meaning, and only a pack pointed at real prose could tell.
455
+ NUMBER_RE = re.compile(
456
+ r"""
457
+ \d+\s?% # 40%, 40 %
458
+ | [$€£]\s?\d[\d,.]* # $3.10, €1,200
459
+ | (?<![A-Za-z]-) # not the tail of BP-079, SCN-001, PRN-24
460
+ (?<!\.\.) # not the far end of a range: BP-079..090
461
+ (?<!\d-) # not the tail of 800-63B
462
+ \b\d{3,}\b
463
+ (?![-\d]) # not the head of 800-63B
464
+ """,
465
+ re.VERBOSE,
466
+ )
467
+
468
+ # A bare four-digit year dates a claim; it is not the claim.
469
+ YEAR_RE = re.compile(r"^(?:19|20)\d{2}$")
388
470
  SUPERLATIVES = (
389
471
  "the best", "best-in-class", "leading", "fastest", "most trusted",
390
472
  "#1", "number one", "world-class", "unmatched",
@@ -473,6 +555,8 @@ def check_facts(brand_dir: Path, sources: dict) -> list[Finding]:
473
555
  for path, _fields, body in documents(brand_dir, sources, "marketing"):
474
556
  for number in NUMBER_RE.findall(body):
475
557
  compact = number.replace(" ", "")
558
+ if YEAR_RE.match(compact):
559
+ continue
476
560
  if compact not in known.replace(" ", ""):
477
561
  findings.append(Finding(
478
562
  "B030", SEVERITY_ERROR, path, 0,
@@ -38,7 +38,7 @@ ADDITIVE = [
38
38
  ("foundation.md", "**Kill criteria:**", "`Kill criteria` on stories — gives `dropped` a definition, 0.28.0"),
39
39
  ]
40
40
 
41
- ARTIFACTS = ["foundation.md", "flows.md", "screens.md", "scenarios.md"]
41
+ ARTIFACTS = ["vision.md", "foundation.md", "flows.md", "screens.md", "scenarios.md"]
42
42
 
43
43
  # Names the contract owns, and the near-misses seen in the wild.
44
44
  RENAMES = {
@@ -47,7 +47,7 @@ def read(path: Path) -> str:
47
47
  def find_ux_dir(arg: str | None) -> Path | None:
48
48
  base = Path(arg) if arg else Path.cwd()
49
49
  for cand in (base, base / "docs" / "ux", base.parent if base.name else base):
50
- if (cand / "scenarios.md").exists() or (cand / "foundation.md").exists():
50
+ if any((cand / n).exists() for n in ("scenarios.md", "foundation.md", "vision.md")):
51
51
  return cand
52
52
  return None
53
53
 
@@ -102,6 +102,60 @@ def screen_blocks(text: str) -> dict[str, str]:
102
102
  return out
103
103
 
104
104
 
105
+ VISION_SECTIONS = [
106
+ "1. Essence",
107
+ "2. Core idea",
108
+ "3. What the system does",
109
+ "4. The user's role",
110
+ "5. Principles",
111
+ "6. Anti-vision",
112
+ "7. Horizon",
113
+ "8. The one sentence",
114
+ "9. The alignment test",
115
+ ]
116
+
117
+ VISION_RULE_HEADING = "## Vision alignment — hard rule (super-ux)"
118
+ INSTRUCTION_FILES = ("CLAUDE.md", "AGENTS.md", "GEMINI.md")
119
+
120
+
121
+ def check_vision(ux: Path, vision: str) -> None:
122
+ """The vision layer: all nine sections, and the rule that makes it read.
123
+
124
+ A vision with no alignment rule in the project's instruction file is a
125
+ document, not a constraint — and its absence looks exactly like
126
+ compliance, which is why it is checked rather than trusted.
127
+ """
128
+ if not vision.strip():
129
+ return
130
+ for section in VISION_SECTIONS:
131
+ if not re.search(rf"^##\s+{re.escape(section)}\s*$", vision, re.MULTILINE):
132
+ err(f"vision.md: missing section '## {section}'")
133
+ # Emptiness is a defect only once the document claims to be finished.
134
+ # A freshly seeded template is all headings and no content by design, and
135
+ # a linter that fails on its own seed teaches people to skip the linter.
136
+ approved = bool(re.search(r"\*\*Status:\*\*\s*approved", vision, re.IGNORECASE))
137
+ if approved:
138
+ for section in ("6. Anti-vision", "9. The alignment test"):
139
+ body = re.split(rf"^##\s+{re.escape(section)}\s*$", vision, maxsplit=1,
140
+ flags=re.MULTILINE)
141
+ if len(body) == 2:
142
+ tail = re.split(r"^##\s", body[1], maxsplit=1, flags=re.MULTILINE)[0]
143
+ if not tail.strip():
144
+ err(f"vision.md: approved but '## {section}' is empty — "
145
+ f"the section that settles arguments cannot be blank")
146
+
147
+ root = ux.parent.parent if ux.name == "ux" else ux.parent
148
+ present = [root / n for n in INSTRUCTION_FILES if (root / n).is_file()]
149
+ if not present:
150
+ warn("vision.md exists but the project has no CLAUDE.md / AGENTS.md / "
151
+ "GEMINI.md — the alignment rule has nowhere to live")
152
+ return
153
+ if not any(VISION_RULE_HEADING in read(p) for p in present):
154
+ warn(f"vision.md exists but no '{VISION_RULE_HEADING}' block in "
155
+ f"{', '.join(p.name for p in present)} — nothing ever reads the vision "
156
+ f"(run the `vision` skill's step 4)")
157
+
158
+
105
159
  def check_links(ux: Path) -> None:
106
160
  link_re = re.compile(r"\[[^\]]*\]\(([^)\s]+)\)")
107
161
  for md in sorted(ux.rglob("*.md")):
@@ -122,6 +176,7 @@ def main() -> int:
122
176
  print("no UX docs found (docs/ux/scenarios.md). Run /ux to set up.")
123
177
  return 2
124
178
 
179
+ vision = read(ux / "vision.md")
125
180
  foundation = read(ux / "foundation.md")
126
181
  flows = read(ux / "flows.md")
127
182
  screens = read(ux / "screens.md")
@@ -210,6 +265,7 @@ def main() -> int:
210
265
  if status == "built" and (not cov or cov.lower().startswith("none")):
211
266
  warn(f"screens.md: {sid} is 'built' but has no Coverage")
212
267
 
268
+ check_vision(ux, vision)
213
269
  check_links(ux)
214
270
 
215
271
  # --- Report ---
@@ -6,32 +6,49 @@ sync with the code on every interface change.
6
6
  ## The pipeline
7
7
 
8
8
  ```
9
- Personas · JTBD · Journeys · Stories → Flows → Screens → Scenarios → Audits → Plans
10
- foundation.md flows.md screens.md scenarios.md audits/ plans/
9
+ Vision → Personas · JTBD · Journeys · Stories → Flows → Screens → Scenarios → Audits → Plans
10
+ vision.md foundation.md flows.md screens.md scenarios.md audits/ plans/
11
11
  ```
12
12
 
13
13
  | File | Holds |
14
14
  |------|-------|
15
+ | `vision.md` | WHAT IT IS: essence, core idea, principles, the anti-vision, the alignment test. Optional layer; when present, new features are checked against it before anything is designed |
15
16
  | `foundation.md` | WHO & WHY: personas, jobs-to-be-done, journeys, user stories, monetization, Figma on/off + file URL |
16
17
  | `flows.md` | HOW: user-flow diagrams (screens, branches, error paths), referencing screens by `SCR-ID` |
17
18
  | `screens.md` | THE UI MAP: every screen and state with its Figma frame link, wireframe, code coverage, scenarios, resources |
18
- | `scenarios.md` | WHAT EXACTLY: use-case scenarios — the source of truth for behavior |
19
+ | `scenarios.md` | WHAT IT DOES: use-case scenarios — the source of truth for behavior |
19
20
  | `audits/` | audit reports (code vs the chain) |
20
21
  | `plans/` | UX plans (target interface + what to create/modify/delete) |
21
22
  | `wireframes/` | optional low-fi wireframes / storyboards |
22
23
  | `lint.py` | the integrity/drift linter |
24
+ | `doctor.py` | the contract doctor — mixed or stale contract versions across the artifacts |
25
+
26
+ **Two files answer a question shaped like "what".** `vision.md` says what
27
+ the product **is**; `scenarios.md` says what it **does**. A feature can
28
+ satisfy every scenario and still violate the anti-vision.
29
+
30
+ ## Its sibling: `docs/brand/`
31
+
32
+ `docs/ux/` decides what the product does. **`docs/brand/`** decides how it
33
+ speaks — `voice.md`, `terminology.md`, `facts.md`, `channels.md`,
34
+ `strings.md`, `locales/<code>.md`, and its own `lint.py`. It is a separate
35
+ root because the brand also governs surfaces that are not UX: a store
36
+ listing, an ad, a post. Seed it with `/brand-init`.
23
37
 
24
38
  ## Rules
25
39
 
26
40
  1. **Design before you build.** A new feature starts here (job → flow →
27
- screen → scenario), approved, before any UI code.
41
+ screen → scenario), approved, before any UI code. With `vision.md`
42
+ present, it is checked against the anti-vision first.
28
43
  2. **Update in the same change.** Any interface change updates
29
44
  `scenarios.md`, affected flows, the affected screens in `screens.md`,
30
45
  and (Figma on) the Figma frame plus its link — together, not later.
46
+ Any change to public text updates `docs/brand/` the same way.
31
47
  3. **No drift.** Code that diverges from a screen's record, or a stale Figma
32
48
  link, is a bug to fix. The same goes for the look: one style pack is
33
49
  recorded in `screens.md` → Design system (pick it with the
34
50
  **sheleg-design** skill) and every screen obeys it.
35
- 4. **Lint it.** Run `python3 docs/ux/lint.py` after changes and in CI.
51
+ 4. **Lint it.** Run `python3 docs/ux/lint.py` after changes and in CI
52
+ plus `python3 docs/brand/lint.py` after any text change.
36
53
 
37
54
  Maintained with the super-ux plugin. In Claude Code, run `/ux`.
@@ -26,9 +26,11 @@
26
26
  - After any UX change and before calling the work done, run the linter
27
27
  `python3 docs/ux/lint.py` — it must pass (errors are drift/broken
28
28
  structure; wire it into CI/pre-commit).
29
- - Use `/ux` as the entry point; skills: `ux-foundation`, `ux-flows`
30
- (flows + Figma mockups), `ux-scenarios` for maintenance, `ux-audit` for
31
- evidence-backed verification. Full map: the plugin's system-map reference.
29
+ - Use `/ux` as the entry point; skills: `vision` (what the product is and
30
+ refuses to become), `ux-foundation`, `ux-flows` (flows + Figma mockups),
31
+ `ux-scenarios` for maintenance, `ux-audit` for evidence-backed
32
+ verification, `brand-voice` and `copywriting` for everything the user
33
+ reads. Full map: the plugin's system-map reference.
32
34
 
33
35
  ## Brand voice — hard rule (super-ux)
34
36
 
@@ -0,0 +1,17 @@
1
+ ## Vision alignment — hard rule (super-ux)
2
+
3
+ Before planning any new feature, capability or significant change, check it
4
+ against `docs/ux/vision.md` — specifically the **anti-vision** and the
5
+ **alignment test**.
6
+
7
+ **Aligned** → proceed, and say in one line which part of the vision it serves.
8
+
9
+ **Misaligned** → stop and say so before writing code:
10
+ 1. Name the conflict — which layer it contradicts, quoting that layer.
11
+ 2. Offer two paths: (a) reshape the feature to fit, with the specific change;
12
+ (b) amend the vision, saying which layer changes and what that costs.
13
+ 3. Wait for the decision. Do not pick one silently.
14
+
15
+ **Do NOT trigger for:** bug fixes, refactors, dependency work, tests,
16
+ documentation, or anything with no user-facing surface. A vision check on a
17
+ typo fix is how a team learns to skip the check that matters.
@@ -0,0 +1,74 @@
1
+ # <Product> — Vision
2
+
3
+ <!-- Managed with super-ux (ux-contract v4). The layer above the chain. -->
4
+
5
+ <!--
6
+ Owned by the `vision` skill (super-ux). Nine sections, these headings, in
7
+ this order — `python3 docs/ux/lint.py` keys off them.
8
+
9
+ This file says what the product IS. `scenarios.md` says what it DOES.
10
+ A feature can satisfy every scenario and still violate the anti-vision;
11
+ that case is the whole reason this layer exists.
12
+
13
+ Writing this file is half the job. The other half is the
14
+ `## Vision alignment — hard rule (super-ux)` block in the project's own
15
+ instruction file — without it nothing ever reads this document.
16
+ -->
17
+
18
+ **Status:** draft
19
+ **Last reviewed:** <YYYY-MM-DD>
20
+
21
+ ## 1. Essence
22
+
23
+ <!-- One sentence: "[Product] is [type of system] that changes how [user] [does X]".
24
+ No feature names, no UI, no technology. Test: would it survive a total
25
+ rewrite on a different stack? If not, it names an implementation. -->
26
+
27
+ ## 2. Core idea
28
+
29
+ <!-- An observation about the world, not a problem statement:
30
+ [X] is abundant.
31
+ [Y] is scarce.
32
+ -> this product bridges the gap.
33
+ One observation, not a list. -->
34
+
35
+ ## 3. What the system does
36
+
37
+ <!-- Continuous behaviour, verbs not nouns: observes ... understands ...
38
+ transforms ... surfaces ... "Has a dashboard" is not behaviour. -->
39
+
40
+ ## 4. The user's role
41
+
42
+ <!-- What the user becomes. Not "the user clicks" — the user directs, judges,
43
+ decides. If it reads like operating any other tool, go deeper. -->
44
+
45
+ ## 5. Principles
46
+
47
+ <!-- Three to five, each "we do X, not Y", where Y is something a reasonable
48
+ team would genuinely have picked. A principle with no rejected side is a
49
+ slogan. -->
50
+
51
+ ## 6. Anti-vision
52
+
53
+ <!-- What this product refuses to become, explicitly. Name the adjacent
54
+ products it could drift into and say no to each. This is the layer teams
55
+ skip and the only one that ever settles an argument. -->
56
+
57
+ ## 7. Horizon
58
+
59
+ <!-- Two to three years, as a direction. Not a roadmap — roadmaps belong in
60
+ the tracker and go stale in a quarter. -->
61
+
62
+ ## 8. The one sentence
63
+
64
+ <!-- If someone remembers exactly one thing. -->
65
+
66
+ ## 9. The alignment test
67
+
68
+ <!-- Three to five questions any proposed feature must pass. These are what
69
+ the hard rule in the instruction file actually runs. A stranger should be
70
+ able to reject a plausible feature with them. -->
71
+
72
+ 1. <question>
73
+ 2. <question>
74
+ 3. <question>