super-ux 0.66.3 → 0.67.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,3 +1,58 @@
1
+ ## 0.67.0 (2026-11-09): contract v5, agents and capabilities (opt-in)
2
+
3
+ Packet P-SU of the family run `2026-11-09-ai-native` (hub plan and design:
4
+ `docs/evidence/plans/2026-11-09-ai-native.md`,
5
+ `docs/evidence/specs/2026-11-09-ai-native-pipeline.md` in ssheleg/sshlg-skills).
6
+
7
+ - **Contract v5, opt-in by marker.** `scenario-format.md` gains *Contract v5:
8
+ agents and capabilities*, the home of AN-1 (parity by story) and AN-4 (why a
9
+ person: gates and serving reasons, each with evidence): actors `A-NN` under
10
+ `## Actors`, persona `Agent use`, a journey `Performed by` column,
11
+ `docs/ux/capabilities.md` with one `CAP-NN` per effect set (`Traces`,
12
+ `Actors`, `Agent excluded`, `Roles`, `Gate`, `Tools`, `Emits`, `Data`,
13
+ `Automation`, `Human surface`, `Since`, `Status`), scenario `Channel`, screen
14
+ `Fronts` and `Why human`, and `docs/ux/agent-panel.md`. A chain is on v5 when
15
+ its headers carry `ux-contract v5`; the lowest marker decides, so a v4 chain
16
+ with one v5 seed stays on v4. v4 chains lint exactly as before.
17
+ - **Nine lint codes, warnings, v5 only:** `U087` (a CAP field missing), `U088`
18
+ (a v5 value outside its enum), `U089` (a must/should story with no CAP),
19
+ `U090` (an agent CAP with no tools), `U091` (an agent-channel scenario citing
20
+ no CAP), `U092` (agents left out with no evidenced reason, or debt with no
21
+ board row), `U093` (a gate or serving reason with no evidence), `U094` (a
22
+ screen with no `Fronts` or `Why human`, or fronting an unknown CAP), `U095` (a
23
+ no-agent persona's must story served only by agent scenarios). Under v5 a
24
+ chain with no `screens.md` is not asked `U050`. Each code is planted both
25
+ ways in `test/ux_lint_test.py`; `test/fixtures/contract-v4` exits 0 under
26
+ `--strict` and `test/fixtures/contract-v5` is clean too. `validate.py`
27
+ compares the linter's `V5_ENUMS` with the contract's v5 declarations.
28
+ - **Doctor:** `CURRENT = 5`, `HISTORY[5]`; v4 is supported (no problem
29
+ counted) and is offered the v4 to v5 migration: add actors, capabilities and
30
+ channels, then the marker; nothing else is required.
31
+ - **Doctrine:** `system-map.md` carries AN-2 (the design order: stories,
32
+ capabilities, contract, logic, automation, human surfaces) and AN-6
33
+ (automation by default); `ux-design-principles.md` reorders the pipeline
34
+ (jobs, capabilities, contract, then flows and screens for the human lanes)
35
+ and is the home of AN-5 (the attention budget). The agent surface itself is
36
+ cited by raw URL to agent-stack's `agent-surface.md` and `operability.md`,
37
+ not restated.
38
+ - **Templates:** the seeds `/ux init` writes carry `ux-contract v5`;
39
+ `foundation.md` gains `## Actors`, `Agent use` and `Performed by`; new
40
+ `capabilities.md` and `agent-panel.md`; `scenarios.md` gains `Channel`;
41
+ `screens.md` gains `Fronts`, `Why human` and the missing `blocked` status.
42
+ - **Skills:** `ux-foundation` owns the capabilities layer (description 267 to
43
+ 264 characters); `ux-scenarios` writes agent-channel steps; `ux-flows` designs
44
+ the human lanes and asks the Figma question at the first human surface;
45
+ `ux-audit` accepts an MCP or CLI call receipt as runtime evidence and checks
46
+ the one-contract proxy; `vision` §4 and §9 guidance ask what an agent can do
47
+ without a screen (nine sections unchanged); `copywriting` separates
48
+ agent-facing text from the `human_steps` a person reads. `/ux` routes agent
49
+ requests to the capabilities layer. The hard rule in `templates/claude-rule.md`
50
+ and `/ux-rule` (byte-equal) becomes foundation, capabilities, contract,
51
+ flows, screens, scenarios for human lanes.
52
+ - **Drift fixed on the way (BN-22):** the screens template lacked `blocked`;
53
+ the linter's no-docs message named only `scenarios.md` although it accepts
54
+ `foundation.md` or `vision.md`.
55
+
1
56
  ## 0.66.3 (2026-10-27): route misses in Triggers, ux-scenarios names its subject first
2
57
 
3
58
  - **Route misses join ux-flows' `## Triggers`** (hub board row B-267): "users drop off" /
package/README.md CHANGED
@@ -184,8 +184,9 @@ Installed into your project's `CLAUDE.md` (and as the always-on Cursor rule):
184
184
  Code that diverges from a screen's record, or a stale Figma link, is drift
185
185
  the audit flags.
186
186
  - Any new feature or project **starts** with the chain: which job, which
187
- journey stage, which story, then flows, screens, and scenarios, validated
188
- against the existing base and approved.
187
+ journey stage, which story, which capability and who performs it (a person,
188
+ an agent, an automation), then flows, screens, and scenarios for the human
189
+ lanes, validated against the existing base and approved.
189
190
  - **Do not write interface code until that workflow is done.** Chain designed
190
191
  and approved, and (Figma on, the default) the UI mocked up with every screen
191
192
  linked to its frame. Building UI before this is the mistake super-ux exists
@@ -253,13 +254,13 @@ to is a skill nobody runs.
253
254
  | `/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 |
254
255
  | `docs/ux/lint.py` + `/ux-lint` | The deterministic half: missing Figma frames, a screen with no states or no render axes, a built state with no story, more than one onboarding screen before the first value with no reason on file, art direction approved with no critique cited, unresolved SCR/story traces, orphans, built screens without coverage, index desync, ID gaps, broken links. Stdlib-only, exit 1 on problems, so wire it into CI and drift can't merge |
255
256
  | `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) |
256
- | `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. The tree is mirrored into `plugins/super-ux/templates/` (and the seeds each skill names into that skill's own directory) by `test/sync_references.py`, so installed channels carry the seeds their texts point at; the validator refuses drift between the copies |
257
+ | `templates/` | Seeds for `docs/ux/`: the vision skeleton, foundation, capabilities, flows, screens, scenario base, the agent test panel, 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. The tree is mirrored into `plugins/super-ux/templates/` (and the seeds each skill names into that skill's own directory) by `test/sync_references.py`, so installed channels carry the seeds their texts point at; the validator refuses drift between the copies |
257
258
 
258
259
  The contracts every skill reads:
259
260
 
260
261
  | Reference | Holds |
261
262
  |---|---|
262
- | [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 |
263
+ | [scenario-format.md](plugins/super-ux/skills/references/scenario-format.md) | **The contract (ux-contract v5; v4 chains stay valid).** File layout, every field name, stable IDs (`P` `A` `JTBD` `JRN` `ST` `CAP` `FLW` `SCR` `SCN`), the opt-in v5 layer for agents (actors, capabilities, channels, why a person is needed), completeness checklists, the `draft → validated → implemented` lifecycle, audit verdicts and severities, the UX-plan format |
263
264
  | [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 |
264
265
  | [ux_doctor.py](plugins/super-ux/scripts/ux_doctor.py) | Contract doctor. It 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` |
265
266
  | [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 |
@@ -6,10 +6,11 @@ alwaysApply: true
6
6
  # UX scenarios — hard rule (super-ux)
7
7
 
8
8
  - `docs/ux/scenarios.md` is the source of truth for all user-facing behavior;
9
- `docs/ux/foundation.md` (personas, JTBD, journeys, stories) and
10
- `docs/ux/flows.md` (user flows) are the WHY and HOW layers scenarios trace
11
- to. Above all three sits `docs/ux/vision.md` — what the product is and
12
- refuses to become — when the project has written one.
9
+ `docs/ux/foundation.md` (personas, actors, JTBD, journeys, stories),
10
+ `docs/ux/capabilities.md` (what people and agents can do, contract v5) and
11
+ `docs/ux/flows.md` (user flows) are the layers scenarios trace to. Above
12
+ them sits `docs/ux/vision.md` (what the product is and refuses to
13
+ become), when the project has written one.
13
14
  - Any change that touches user-facing behavior or interface MUST update, in
14
15
  the same change: `docs/ux/scenarios.md`; affected flows; the affected
15
16
  screens in `docs/ux/screens.md` (the UI map — states, elements, coverage);
@@ -20,14 +21,17 @@ alwaysApply: true
20
21
  path must exist and the line range must be inside the file. Seven of this
21
22
  pack's own citations were pre-shift ranges nothing re-resolved.
22
23
  - Any new feature or project STARTS with the chain: which job does it serve,
23
- which journey stage, which story — then flows and scenarios, validated
24
+ which journey stage, which story, which capability and who performs it (a
25
+ person, an agent, an automation); then flows and scenarios, validated
24
26
  against the existing base, approved. An idea serving no job is challenged,
25
27
  not silently accepted.
26
- - **Do NOT write interface code until the UX workflow is done first:** the
27
- foundation → flows → screens → scenarios chain is designed and approved,
28
- and — when Figma is enabled (default) — the UI is mocked up in Figma with
29
- every screen linked to its frame. Building UI before this is the exact
30
- mistake super-ux exists to prevent.
28
+ - **Do NOT write interface code until the UX workflow is done first:**
29
+ foundation → capabilities → contract → flows → screens → scenarios for
30
+ human lanes, designed and approved (capabilities and contract under
31
+ contract v5), and, when Figma is enabled (default), the UI mocked up in
32
+ Figma with every screen linked to its frame. A screen is designed where a
33
+ person is needed; every other capability stays reachable by agents.
34
+ Building UI before this is the exact mistake super-ux exists to prevent.
31
35
  - Visual identity is ONE locked style pack, recorded in `docs/ux/screens.md`
32
36
  → Design system and obeyed by every Figma frame and every built screen —
33
37
  picked with the **sheleg-design** companion skill when the project has no
@@ -14,7 +14,7 @@ reused. Any interface change updates `screens.md` (and the Figma frame when
14
14
  enabled) in the same change — a screen whose code diverges from its record
15
15
  is `drifted`.
16
16
 
17
- ## Flow entry (ux-contract v4)
17
+ ## Flow entry (ux-contract v5; a v4 chain stays valid)
18
18
 
19
19
  Fields: `Traces` (story/job IDs), `Goal` (observable end state), `Entry
20
20
  points` (ALL of them), `Success exit`, `Task analysis` (numbered
@@ -29,7 +29,10 @@ Story | Behavior |` — a row per default/loading/empty/error/offline/
29
29
  long-content/keyboard-up/first-run state that applies), `Axes`
30
30
  (viewport; theme; text; locale — or the Design system default),
31
31
  `Wireframe`, `Coverage` (file:line), `Scenarios`, `Resources`, `Status`
32
- (designed|blocked|built|drifted|retired). A flow that onboards records
32
+ (designed|blocked|built|drifted|retired); under v5 also `Fronts` (the
33
+ `CAP-NN` it fronts, or none) and `Why human` (a serving reason with
34
+ evidence), because flows and screens are drawn for the human lanes only.
35
+ A flow that onboards records
33
36
  `Onboarding:` and `First value:`; the budget is one screen before the first
34
37
  value unless the director record says why.
35
38
 
@@ -10,7 +10,7 @@ alwaysApply: false
10
10
  stories (ST-NNN)**. IDs sequential, never reused; dropped entries kept, not
11
11
  deleted.
12
12
 
13
- ## Formats (ux-contract v4)
13
+ ## Formats (ux-contract v5; a v4 chain stays valid)
14
14
 
15
15
  - **Persona `P-NN`:** 1–3 sentences, grounded in data/observation,
16
16
  recognizable by a real user.
@@ -26,6 +26,14 @@ deleted.
26
26
  <benefit>` + Traces (JTBD, JRN/stage) + Given/When/Then acceptance
27
27
  criteria + priority (must|should|could) + status
28
28
  (proposed|validated|delivered|dropped). Quality bar: INVEST.
29
+ - **v5 (opt-in; new projects):** persona `Agent use: none|some|most` with
30
+ evidence; actors `A-NN` under `## Actors` (`Kind: agent|system`, `Acts
31
+ for: P-NN`, `Grant`); a journey `Performed by` column; and
32
+ `docs/ux/capabilities.md`, one `CAP-NN` per must/should story with
33
+ `Traces, Actors, Agent excluded, Roles, Gate, Tools, Emits, Data,
34
+ Automation, Human surface, Since, Status`. A person enters at a gate
35
+ (`body|approval|legal`) or for a serving reason, each with evidence;
36
+ everything else stays reachable by agents.
29
37
 
30
38
  ## Workflows
31
39
 
@@ -8,7 +8,7 @@ alwaysApply: false
8
8
  `docs/ux/scenarios.md` describes everything the user can do, see, and hit —
9
9
  every feature, button, state, error, result. Keep it true.
10
10
 
11
- ## File structure (ux-contract v4)
11
+ ## File structure (ux-contract v5; a v4 chain stays valid)
12
12
 
13
13
  Title, then header comment, then: **Index** table
14
14
  (`| ID | Title | Feature | Persona | Traces | Status | Last audit |`),
@@ -28,7 +28,10 @@ when none exist), `UI elements` (every button/field/link/dialog/toast involved
28
28
  this is what audits check), `States covered` (loading|empty|error|success),
29
29
  `Errors & recovery` (each failure: what the user sees, how they recover),
30
30
  `Status` (draft|validated|implemented|retired), `Coverage` (file:line or
31
- `none yet`).
31
+ `none yet`). Under v5, `Channel` (human|agent|both; absent reads human): an
32
+ agent step is `A-NN -> tool -> observable response`, the scenario cites the
33
+ `CAP-NN` it exercises, and `UI elements` is required on the human channel
34
+ only.
32
35
 
33
36
  Rules: IDs `SCN-NNN`, sequential, never reused; retired entries are kept,
34
37
  never deleted. Lifecycle: draft → validated (human approval) → implemented
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "super-ux",
3
- "version": "0.66.3",
3
+ "version": "0.67.0",
4
4
  "scripts": {
5
5
  "test": "python3 test/validate.py && python3 test/family_gate.py && python3 test/family_gate.py --self-test && python3 test/brand_lint_test.py && python3 test/display_copy_test.py && python3 test/ux_lint_test.py && python3 docs/ux/lint.py && python3 docs/brand/lint.py && node test/installer_test.js && npm run test:audit",
6
6
  "test:audit": "for t in test/audit_regressions/*.py; do python3 \"$t\" || exit 1; done"
@@ -21,7 +21,11 @@ import re
21
21
  import sys
22
22
  from pathlib import Path
23
23
 
24
- CURRENT = 4
24
+ CURRENT = 5
25
+ # The oldest contract still read as current work rather than as debt. v5 is
26
+ # opt-in (agents and capabilities), so a v4 chain is supported, not behind:
27
+ # the doctor names the migration and counts no problem for it.
28
+ SUPPORTED = 4
25
29
 
26
30
  # What each contract version introduced, so a report can say what is missing
27
31
  # rather than only that something is old.
@@ -29,8 +33,19 @@ HISTORY = {
29
33
  2: "`Traces:` on every scenario + traceability rules; `foundation.md` (personas, JTBD, journeys, stories)",
30
34
  3: "scenarios become use cases (`user action -> system response`), `Alt paths`, `Traces` includes `FLW-NN`; `flows.md`; `plans/`",
31
35
  4: "`screens.md` — one `SCR-NN` entry per screen with per-state Figma frames; flows reference screens by id instead of respecifying them",
36
+ 5: "agents and capabilities, opt-in: actors (`A-NN`) and `Agent use` in `foundation.md`, `capabilities.md` (`CAP-NN`), `Channel` on scenarios, `Fronts` and `Why human` on screens, `agent-panel.md`",
32
37
  }
33
38
 
39
+ # The v4 -> v5 step, named so a v4 chain is told what opting in costs. Nothing
40
+ # else is required: every v5 lint rule is a warning, and only under the marker.
41
+ V5_MIGRATION = (
42
+ "v4 -> v5 is opt-in, best taken on the first capability task: add "
43
+ "`## Actors` (`A-NN`) and `Agent use` to foundation.md, write "
44
+ "capabilities.md with one `CAP-NN` per must/should story, add `Channel` "
45
+ "to scenarios and `Fronts` with `Why human` to screens, then change every "
46
+ "header marker to `ux-contract v5`. Nothing else is required."
47
+ )
48
+
34
49
  # Additive since v4 — absence is not a version problem, but it is worth naming.
35
50
  ADDITIVE = [
36
51
  ("foundation.md", "## Product mechanics", "Product mechanics section (personalization, engagement, a11y regime) — 0.26.1"),
@@ -39,6 +54,8 @@ ADDITIVE = [
39
54
  ]
40
55
 
41
56
  ARTIFACTS = ["vision.md", "foundation.md", "flows.md", "screens.md", "scenarios.md"]
57
+ # v5 files: their markers count, and their absence is reported only on v5.
58
+ V5_ARTIFACTS = ["capabilities.md", "agent-panel.md"]
42
59
 
43
60
  # `screens.md` format 2 (0.58.0): the screen matrix — a state list and render
44
61
  # axes per screen, a frame for every declared state, a story per state once
@@ -82,6 +99,7 @@ def marker(path: Path) -> int | None:
82
99
  def diagnose(ux: Path) -> dict:
83
100
  present = {a: (ux / a).exists() for a in ARTIFACTS}
84
101
  markers = {a: marker(ux / a) for a in ARTIFACTS if present[a]}
102
+ markers.update({a: marker(ux / a) for a in V5_ARTIFACTS if (ux / a).exists()})
85
103
  known = [v for v in markers.values() if v is not None]
86
104
 
87
105
  misnamed = {
@@ -135,9 +153,13 @@ def report(ux: Path, d: dict) -> int:
135
153
  elif eff is None:
136
154
  problems += 1
137
155
  print(" NO CONTRACT MARKER on any artifact — the chain is unmanaged.")
138
- elif eff < CURRENT:
156
+ elif eff < SUPPORTED:
139
157
  problems += 1
140
158
  print(f" BEHIND: contract v{eff}, current v{CURRENT}")
159
+ elif eff < CURRENT:
160
+ print(f" contract v{eff}, supported; current v{CURRENT} is opt-in")
161
+ else:
162
+ print(f" contract v{eff}")
141
163
 
142
164
  if unmarked and not d["mixed"]:
143
165
  problems += 1
@@ -153,6 +175,8 @@ def report(ux: Path, d: dict) -> int:
153
175
  print(f" MISNAMED: {wrong} -> {right} (the tooling looks for the contract name and finds nothing)")
154
176
 
155
177
  missing_core = [a for a, ok in d["present"].items() if not ok]
178
+ if eff is not None and eff >= 5 and "capabilities.md" not in markers:
179
+ missing_core.append("capabilities.md")
156
180
  if missing_core:
157
181
  print(f" ABSENT: {', '.join(missing_core)}")
158
182
 
@@ -170,7 +194,15 @@ def report(ux: Path, d: dict) -> int:
170
194
  if d.get("screens_format") == 1:
171
195
  print(f" migration offered: {SCREENS_MIGRATION}")
172
196
 
173
- print(" OK — on the current contract" if problems == 0 else f" {problems} problem(s)")
197
+ if eff == 4 and not d["mixed"]:
198
+ print(f" migration offered: {V5_MIGRATION}")
199
+
200
+ if problems:
201
+ print(f" {problems} problem(s)")
202
+ elif eff is not None and eff < CURRENT:
203
+ print(" OK: on a supported contract")
204
+ else:
205
+ print(" OK: on the current contract")
174
206
  return problems
175
207
 
176
208
 
@@ -1038,6 +1038,265 @@ def check_vision(ux: Path, vision: str) -> None:
1038
1038
  f"the `vision` skill's step 4, or keep the edit deliberately")
1039
1039
 
1040
1040
 
1041
+ # --- Contract v5: agents and capabilities (opt-in) -------------------------
1042
+ #
1043
+ # v5 models who performs a story (a person, an agent acting for one, an
1044
+ # automation), the capabilities a story needs, and why a person is needed
1045
+ # where a screen exists. Every v4 chain stays valid: nothing below runs unless
1046
+ # the chain declares `ux-contract v5`, the marker form `doctor.py` already
1047
+ # reads. The LOWEST marker decides, as the doctor's `effective` does, so one
1048
+ # v5 seed dropped into a v4 chain does not switch it onto rules it never
1049
+ # opted into. Every finding here is a warning: the layer is new, and a gate
1050
+ # that failed builds on day one is the gate that gets switched off.
1051
+ CONTRACT_MARKER = re.compile(r"ux-contract v(\d+)")
1052
+ CONTRACT_FILES = ("vision.md", "foundation.md", "flows.md", "screens.md",
1053
+ "scenarios.md", "capabilities.md", "agent-panel.md")
1054
+ V5 = 5
1055
+
1056
+ # The capability row, in the contract's order. One row is one effect set.
1057
+ CAP_FIELDS = ("Traces", "Actors", "Agent excluded", "Roles", "Gate", "Tools",
1058
+ "Emits", "Data", "Automation", "Human surface", "Since", "Status")
1059
+
1060
+ # Every v5 enum, keyed by (layer, field). `validate.py` compares this table
1061
+ # with the contract's v5 declarations, the same parity `STATUS_ENUMS` has.
1062
+ V5_ENUMS = {
1063
+ ("CAP", "Actors"): ("human", "agent", "automation"),
1064
+ ("CAP", "Agent excluded"): ("presence", "legal", "debt"),
1065
+ ("CAP", "Gate"): ("none", "body", "approval", "legal"),
1066
+ ("CAP", "Data"): ("public", "internal", "personal", "payment", "secret"),
1067
+ ("CAP", "Status"): ("proposed", "specified", "exposed", "deprecated", "retired"),
1068
+ ("A", "Kind"): ("agent", "system"),
1069
+ ("P", "Agent use"): ("none", "some", "most"),
1070
+ ("SCN", "Channel"): ("agent", "human", "both"),
1071
+ ("SCR", "Why human"): ("oversight", "exception-queue", "trust", "presence",
1072
+ "audience", "manual", "taste"),
1073
+ }
1074
+ BOARD_ID = re.compile(r"\b[A-Z][A-Z0-9]*-\d+\b")
1075
+
1076
+
1077
+ def contract_version(ux: Path) -> int | None:
1078
+ """The lowest `ux-contract vN` among the chain's files, read raw.
1079
+
1080
+ The marker lives in an HTML comment and `read()` strips comments, so the
1081
+ head of each file is read here directly, as `doctor.py` reads it.
1082
+ """
1083
+ found = []
1084
+ for name in CONTRACT_FILES:
1085
+ try:
1086
+ head = (ux / name).read_text(encoding="utf-8", errors="ignore")[:2000]
1087
+ except OSError:
1088
+ continue
1089
+ m = CONTRACT_MARKER.search(head)
1090
+ if m:
1091
+ found.append(int(m.group(1)))
1092
+ return min(found) if found else None
1093
+
1094
+
1095
+ def v5_field(body: str, name: str) -> str | None:
1096
+ """A v5 field's value on one line; None when the field is absent."""
1097
+ m = re.search(rf"\*\*{re.escape(name)}:\*\*[ \t]*([^\n]*)", body)
1098
+ return None if m is None else m.group(1).strip()
1099
+
1100
+
1101
+ def v5_token(value: str) -> tuple[str, str]:
1102
+ """`(first word, the rest)`: the enum value and the evidence beside it."""
1103
+ m = re.match(r"\s*([A-Za-z][\w-]*)\s*(.*)$", value or "", re.DOTALL)
1104
+ if m is None:
1105
+ return ("", "")
1106
+ return (m.group(1).lower(), m.group(2).strip())
1107
+
1108
+
1109
+ def evidenced(rest: str) -> bool:
1110
+ """Does the text beside an enum value say anything? Brackets and
1111
+ separators alone are not evidence."""
1112
+ return stated(re.sub(r"[()\[\];:,.\s]+", " ", rest or "").strip() or None)
1113
+
1114
+
1115
+ def v5_enum(name: str, sid: str, field: str, value: str,
1116
+ allowed: tuple[str, ...]) -> bool:
1117
+ """Warn U088 on a value outside its enum; True when the value is legal."""
1118
+ if value in allowed:
1119
+ return True
1120
+ warn(f"[U088] {name}: {sid} declares {field} '{value or '(nothing)'}', "
1121
+ f"which is not one of {' | '.join(allowed)}. An unrecognised value "
1122
+ f"reads as no value, and every v5 rule keyed on it stops applying")
1123
+ return False
1124
+
1125
+
1126
+ def actors_of(value: str) -> list[str]:
1127
+ """The actor kinds a CAP names, before any bracketed note."""
1128
+ head = re.split(r"[(;]", value or "", maxsplit=1)[0]
1129
+ return [a.strip().lower() for a in head.split(",") if a.strip()]
1130
+
1131
+
1132
+ def check_v5(foundation: str, scenarios: str, screens: str,
1133
+ capabilities: str) -> None:
1134
+ """The v5 layers: actors, capabilities, channels, and why a person.
1135
+
1136
+ The doctrine of each rule lives in `scenario-format.md`, section
1137
+ "Contract v5: agents and capabilities"; this function only checks it.
1138
+ """
1139
+ caps = entry_blocks(capabilities, "CAP")
1140
+ cap_ids = set(caps)
1141
+ for prefix, name, text in (("CAP", "capabilities.md", capabilities),
1142
+ ("A", "foundation.md/actors", foundation)):
1143
+ entry_ids = ids(text, prefix)
1144
+ if entry_ids:
1145
+ check_unique_and_gaps(entry_ids, name)
1146
+ for sid, colon, title in entry_names(text, prefix):
1147
+ if not (colon and title):
1148
+ err(f"[U073] {name}: `### {sid}` carries no name: the contract's "
1149
+ f"header is `### {sid}: <short name>`")
1150
+ if cap_ids:
1151
+ idx = index_ids(capabilities, "CAP")
1152
+ for missing in sorted(cap_ids - idx):
1153
+ warn(f"[U003] capabilities.md: {missing} has no index row")
1154
+ for ghost in sorted(idx - cap_ids):
1155
+ err(f"[U004] capabilities.md: index lists {ghost} but no entry exists")
1156
+
1157
+ traced_by_caps: set[str] = set()
1158
+ for cid, body in sorted(caps.items()):
1159
+ status, _ = v5_token(v5_field(body, "Status") or "")
1160
+ if status == "retired":
1161
+ continue
1162
+ for field in CAP_FIELDS:
1163
+ if v5_field(body, field) is None:
1164
+ warn(f"[U087] capabilities.md: {cid} is missing **{field}:**. The "
1165
+ f"row is {', '.join(CAP_FIELDS)}; `none` is a value, an "
1166
+ f"absent field is not")
1167
+ traced_by_caps |= refs(v5_field(body, "Traces") or "", "ST")
1168
+ if status:
1169
+ v5_enum("capabilities.md", cid, "Status", status, V5_ENUMS[("CAP", "Status")])
1170
+ actors = actors_of(v5_field(body, "Actors") or "")
1171
+ for actor in actors:
1172
+ v5_enum("capabilities.md", cid, "Actors", actor, V5_ENUMS[("CAP", "Actors")])
1173
+ data, _ = v5_token(v5_field(body, "Data") or "")
1174
+ if data:
1175
+ v5_enum("capabilities.md", cid, "Data", data, V5_ENUMS[("CAP", "Data")])
1176
+
1177
+ tools = v5_field(body, "Tools")
1178
+ if "agent" in actors and (not stated(tools) or v5_token(tools or "")[0] == "none"):
1179
+ warn(f"[U090] capabilities.md: {cid} names agent among its Actors and no "
1180
+ f"**Tools:**. An agent reaches a capability through a tool id, and "
1181
+ f"one with none listed is a capability only a screen can reach")
1182
+
1183
+ excluded, why = v5_token(v5_field(body, "Agent excluded") or "")
1184
+ if actors and "agent" not in actors:
1185
+ if excluded in ("", "none"):
1186
+ warn(f"[U092] capabilities.md: {cid} leaves agents out and names no "
1187
+ f"**Agent excluded:** reason (presence | legal | debt, with "
1188
+ f"evidence). Parity is by story: an exception is named per "
1189
+ f"capability or it is a gap")
1190
+ elif v5_enum("capabilities.md", cid, "Agent excluded", excluded,
1191
+ V5_ENUMS[("CAP", "Agent excluded")]):
1192
+ if excluded == "debt" and not BOARD_ID.search(why):
1193
+ warn(f"[U092] capabilities.md: {cid} records parity debt with "
1194
+ f"no board row. Debt is listed and on the board "
1195
+ f"(`debt (B-nnn)`), never hidden")
1196
+ elif excluded != "debt" and not evidenced(why):
1197
+ warn(f"[U092] capabilities.md: {cid} excludes agents for "
1198
+ f"'{excluded}' and cites no evidence: a JTBD-, ST- or "
1199
+ f"RE- id, a policy or a decision")
1200
+
1201
+ gate, gate_why = v5_token(v5_field(body, "Gate") or "")
1202
+ if gate and v5_enum("capabilities.md", cid, "Gate", gate,
1203
+ V5_ENUMS[("CAP", "Gate")]):
1204
+ if gate != "none" and not evidenced(gate_why):
1205
+ warn(f"[U093] capabilities.md: {cid} is gated '{gate}' and cites no "
1206
+ f"evidence: the policy, the decision or the law that puts a "
1207
+ f"person there. No product-level waiver exists")
1208
+
1209
+ reason_m = re.search(r"Why human:\s*(.*)", v5_field(body, "Human surface") or "")
1210
+ if reason_m:
1211
+ reason, reason_why = v5_token(reason_m.group(1))
1212
+ if v5_enum("capabilities.md", cid, "Why human", reason,
1213
+ V5_ENUMS[("SCR", "Why human")]) and not evidenced(reason_why):
1214
+ warn(f"[U093] capabilities.md: {cid} serves a person for "
1215
+ f"'{reason}' and cites no evidence beside it")
1216
+
1217
+ # AN-1, structurally: every must/should story has a capability tracing to
1218
+ # it, and the capability's row names any exclusion.
1219
+ for sid, body in sorted(entry_blocks(foundation, "ST").items()):
1220
+ if declared_status(body) == "dropped":
1221
+ continue
1222
+ if not re.search(r"\*\*Priority:\*\*\s*(must|should)", body, re.IGNORECASE):
1223
+ continue
1224
+ if sid not in traced_by_caps:
1225
+ warn(f"[U089] foundation.md: {sid} (must/should) has no capability "
1226
+ f"tracing to it in capabilities.md. Parity is by story: name the "
1227
+ f"CAP that serves it, and any exclusion in that CAP's row")
1228
+
1229
+ for aid, body in sorted(entry_blocks(foundation, "A").items()):
1230
+ kind, _ = v5_token(v5_field(body, "Kind") or "")
1231
+ if kind:
1232
+ v5_enum("foundation.md", aid, "Kind", kind, V5_ENUMS[("A", "Kind")])
1233
+
1234
+ # Channel: an absent field reads as `human`, the scenario every v4 chain
1235
+ # already wrote. An agent-channel scenario is the contract's acceptance
1236
+ # test, so it names the capability it accepts.
1237
+ scn_channel: dict[str, str] = {}
1238
+ scn_traces: dict[str, set[str]] = {}
1239
+ for sid, body in sorted(entry_blocks(scenarios, "SCN").items()):
1240
+ channel, _ = v5_token(v5_field(body, "Channel") or "human")
1241
+ scn_traces[sid] = refs(v5_field(body, "Traces") or body, "ST")
1242
+ if not v5_enum("scenarios.md", sid, "Channel", channel,
1243
+ V5_ENUMS[("SCN", "Channel")]):
1244
+ continue
1245
+ scn_channel[sid] = channel
1246
+ if declared_status(body) == "retired":
1247
+ continue
1248
+ if channel in ("agent", "both") and not refs(body, "CAP"):
1249
+ warn(f"[U091] scenarios.md: {sid} is an {channel}-channel scenario and "
1250
+ f"cites no CAP in its Traces. An agent step calls a capability, "
1251
+ f"and the scenario is that capability's acceptance test")
1252
+
1253
+ for sid, body in sorted(screen_blocks(screens).items()):
1254
+ if declared_status(body) == "retired":
1255
+ continue
1256
+ fronts = v5_field(body, "Fronts")
1257
+ why_value = v5_field(body, "Why human")
1258
+ missing = [f for f, v in (("Fronts", fronts), ("Why human", why_value))
1259
+ if not stated(v)]
1260
+ if missing:
1261
+ warn(f"[U094] screens.md: {sid} has no **{':** or **'.join(missing)}:**. "
1262
+ f"A screen fronts capabilities (`Fronts: CAP-NN`, or `none`) and "
1263
+ f"says why a person is needed there")
1264
+ for ghost in sorted(refs(fronts or "", "CAP") - cap_ids):
1265
+ warn(f"[U094] screens.md: {sid} fronts {ghost}, which capabilities.md "
1266
+ f"does not define")
1267
+ if stated(why_value):
1268
+ reason, reason_why = v5_token(why_value)
1269
+ if v5_enum("screens.md", sid, "Why human", reason,
1270
+ V5_ENUMS[("SCR", "Why human")]) and not evidenced(reason_why):
1271
+ warn(f"[U093] screens.md: {sid} serves a person for '{reason}' and "
1272
+ f"cites no evidence beside it: a JTBD-, ST- or RE- id, a "
1273
+ f"policy, a decision or the typed error that routes here")
1274
+
1275
+ # A persona whose people use no agent still gets a whole human path for
1276
+ # every must story: the audience decides the human path's size.
1277
+ jobs = entry_blocks(foundation, "JTBD")
1278
+ for pid, body in sorted(entry_blocks(foundation, "P").items()):
1279
+ use_value = v5_field(body, "Agent use")
1280
+ if use_value is None:
1281
+ continue
1282
+ use, _ = v5_token(use_value)
1283
+ if not v5_enum("foundation.md", pid, "Agent use", use,
1284
+ V5_ENUMS[("P", "Agent use")]) or use != "none":
1285
+ continue
1286
+ for st, sbody in sorted(entry_blocks(foundation, "ST").items()):
1287
+ if not re.search(r"\*\*Priority:\*\*\s*must", sbody, re.IGNORECASE):
1288
+ continue
1289
+ via_jobs = any(pid in refs(v5_field(jobs.get(j, ""), "Personas") or "", "P")
1290
+ for j in refs(sbody, "JTBD"))
1291
+ if pid not in refs(sbody, "P") and not via_jobs:
1292
+ continue
1293
+ serving = [s for s, t in scn_traces.items() if st in t and s in scn_channel]
1294
+ if serving and not any(scn_channel[s] in ("human", "both") for s in serving):
1295
+ warn(f"[U095] foundation.md: {pid} declares Agent use: none and "
1296
+ f"must story {st} has only agent-channel scenarios. People "
1297
+ f"who use no agent need the whole human path for it")
1298
+
1299
+
1041
1300
  def check_links(ux: Path) -> None:
1042
1301
  link_re = re.compile(r"\[[^\]]*\]\(([^)\s]+)\)")
1043
1302
  for md in sorted(ux.rglob("*.md")):
@@ -1055,7 +1314,8 @@ def main() -> int:
1055
1314
  strict = "--strict" in sys.argv[1:]
1056
1315
  ux = find_ux_dir(args[0] if args else None)
1057
1316
  if ux is None:
1058
- print("no UX docs found (docs/ux/scenarios.md). Run /ux to set up.")
1317
+ print("no UX docs found (docs/ux/ with scenarios.md, foundation.md or "
1318
+ "vision.md). Run /ux to set up.")
1059
1319
  return 2
1060
1320
 
1061
1321
  vision = read(ux / "vision.md")
@@ -1063,6 +1323,8 @@ def main() -> int:
1063
1323
  flows = read(ux / "flows.md")
1064
1324
  screens = read(ux / "screens.md")
1065
1325
  scenarios = read(ux / "scenarios.md")
1326
+ capabilities = read(ux / "capabilities.md")
1327
+ v5 = (contract_version(ux) or 0) >= V5
1066
1328
 
1067
1329
  # Cited paths are project-relative, so they resolve against the tree the
1068
1330
  # ux directory sits in — the same derivation `check_links` already uses.
@@ -1263,9 +1525,14 @@ def main() -> int:
1263
1525
  check_jobs(foundation, scenarios, flows, screens)
1264
1526
  check_journey_owners(foundation)
1265
1527
  check_vision(ux, vision)
1266
- check_web_surface(screens, flows)
1528
+ # Under v5 a product may have no human surface at all, so a chain with no
1529
+ # `screens.md` has no screen to ask the web question about.
1530
+ if not (v5 and not (ux / "screens.md").exists()):
1531
+ check_web_surface(screens, flows)
1267
1532
  check_onboarding(flows, ux, project_root)
1268
1533
  check_art_direction(flows, ux, project_root)
1534
+ if v5:
1535
+ check_v5(foundation, scenarios, screens, capabilities)
1269
1536
  check_links(ux)
1270
1537
 
1271
1538
  # --- Report ---
@@ -6,14 +6,19 @@ sync with the code on every interface change.
6
6
  ## The pipeline
7
7
 
8
8
  ```
9
- Vision → Personas · JTBD · Journeys · Stories → Flows → Screens → Scenarios → Audits → Plans
10
- vision.md foundation.md flows.md screens.md scenarios.md audits/ plans/
9
+ Vision → Personas · Actors · JTBD · Journeys · Stories → Capabilities → Flows → Screens → Scenarios → Audits → Plans
10
+ vision.md foundation.md capabilities.md flows.md screens.md scenarios.md audits/ plans/
11
11
  ```
12
12
 
13
+ Flows and screens are drawn for the human lanes: a capability an agent or an
14
+ automation performs needs no screen unless a person is needed there.
15
+
13
16
  | File | Holds |
14
17
  |------|-------|
15
18
  | `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 |
16
- | `foundation.md` | WHO & WHY: personas, jobs-to-be-done, journeys, user stories, monetization, Figma on/off + file URL |
19
+ | `foundation.md` | WHO & WHY: personas, actors (the agents and systems acting for them), jobs-to-be-done, journeys, user stories, monetization, Figma on/off + file URL |
20
+ | `capabilities.md` | WHAT PEOPLE AND AGENTS CAN DO: one `CAP-NN` per effect set, with its actors, gate, tools and the human surface that fronts it |
21
+ | `agent-panel.md` | optional: the agents an agent-tester walks the agent-channel scenarios as |
17
22
  | `flows.md` | HOW: user-flow diagrams (screens, branches, error paths), referencing screens by `SCR-ID` |
18
23
  | `screens.md` | THE UI MAP: every screen and state with its Figma frame link, wireframe, code coverage, scenarios, resources |
19
24
  | `scenarios.md` | WHAT IT DOES: use-case scenarios — the source of truth for behavior |
@@ -0,0 +1,40 @@
1
+ # Agent test panel
2
+
3
+ <!-- Managed with super-ux (ux-contract v5). The agents an agent-tester walks
4
+ the agent-channel scenarios as (task-pipeline reads this file). Separate from
5
+ test-panel.md, the human panel, on purpose. Five agents, each with its grant,
6
+ its model class and the scenarios it walks. A grant here is a test-tenant
7
+ grant: the credential comes from the project's secret store, never this file. -->
8
+
9
+ ## Panel
10
+
11
+ <!-- ### AG-01: Full-grant agent
12
+ - **Acts for:** P-01
13
+ - **Grant:** <every scope its principal holds>
14
+ - **Model class:** <the verification model the project uses>
15
+ - **Walks:** <SCN-… agent-channel scenarios>
16
+
17
+ ### AG-02: Narrowly delegated agent
18
+ - **Acts for:** P-01
19
+ - **Grant:** <one scope, e.g. read-only billing>
20
+ - **Model class:** <…>
21
+ - **Walks:** <SCN-…: expects a typed refusal where its grant ends>
22
+
23
+ ### AG-03: Automation with no conversation
24
+ - **Acts for:** <A-…, Kind: system>
25
+ - **Grant:** <the automation's own scope>
26
+ - **Model class:** none
27
+ - **Walks:** <SCN-… for CAP rows with an Automation trigger>
28
+
29
+ ### AG-04: Cheap model
30
+ - **Acts for:** P-01
31
+ - **Grant:** <as AG-01>
32
+ - **Model class:** <a small, inexpensive model; advisory>
33
+ - **Walks:** <SCN-…: can the tool descriptions alone guide it>
34
+
35
+ ### AG-05: Adversarial agent
36
+ - **Acts for:** P-01
37
+ - **Grant:** <as AG-02>
38
+ - **Model class:** <…>
39
+ - **Walks:** <SCN-… with injected content in what it reads; owed on a public surface>
40
+ -->
@@ -0,0 +1,29 @@
1
+ # Capabilities
2
+
3
+ <!-- Managed with super-ux (ux-contract v5). What people and agents can do:
4
+ one CAP-NN per effect set. Between the stories (foundation.md) and the flows
5
+ (flows.md): every must/should story has a capability tracing to it, and a
6
+ capability found while designing a later layer comes back here in the same
7
+ change. Field rules: the contract's "Contract v5: agents and capabilities". -->
8
+
9
+ ## Index
10
+
11
+ | ID | Capability | Actors | Gate | Status |
12
+ |----|------------|--------|------|--------|
13
+
14
+ ## Capabilities
15
+
16
+ <!-- ### CAP-01: <verb + object, e.g. Cancel a subscription>
17
+ - **Traces:** <ST-…>
18
+ - **Actors:** <human, agent, automation: who performs it>
19
+ - **Agent excluded:** none | presence (<evidence>) | legal (<evidence>) | debt (<board id>)
20
+ - **Roles:** <principal roles allowed to invoke it; an agent never exceeds its principal>
21
+ - **Gate:** none | body | approval | legal (<policy, decision or law>)
22
+ - **Tools:** <tool ids an agent calls, as the contract source names them>
23
+ - **Emits:** <events or notifications an agent can subscribe to, or none>
24
+ - **Data:** public | internal | personal | payment | secret
25
+ - **Automation:** none | <trigger; schedule; kill switch>
26
+ - **Human surface:** none | <SCR-…>; Why human: <serving reason> (<evidence>)
27
+ - **Since:** <contract version that introduced it>
28
+ - **Status:** proposed | specified | exposed | deprecated | retired
29
+ -->
@@ -1,8 +1,9 @@
1
1
  ## UX scenarios — hard rule (super-ux)
2
2
 
3
3
  - `docs/ux/scenarios.md` is the source of truth for all user-facing
4
- behavior; `docs/ux/foundation.md` (personas, JTBD, journeys, stories) and
5
- `docs/ux/flows.md` (user flows) are the WHY and HOW layers scenarios
4
+ behavior; `docs/ux/foundation.md` (personas, actors, JTBD, journeys,
5
+ stories), `docs/ux/capabilities.md` (what people and agents can do,
6
+ contract v5) and `docs/ux/flows.md` (user flows) are the layers scenarios
6
7
  trace to.
7
8
  - Any change that touches user-facing behavior or interface MUST update, in
8
9
  the same change: `docs/ux/scenarios.md`; affected flows; the affected
@@ -11,13 +12,16 @@
11
12
  links in `screens.md`. A screen whose code diverges from its record, or a
12
13
  stale Figma link, is drift — the exact thing this system prevents.
13
14
  - Any new feature or project STARTS with the chain: which job does it
14
- serve, which journey stage, which story — then flows and scenarios,
15
- validated against the existing base, approved.
16
- - **Do NOT write interface code until the UX workflow is done first:** the
17
- foundation → flows → screens → scenarios chain is designed and approved,
18
- and — when Figma is enabled (default) — the UI is mocked up in Figma with
19
- every screen linked to its frame. Building UI before this is the exact
20
- mistake super-ux exists to prevent.
15
+ serve, which journey stage, which story, which capability and who
16
+ performs it (a person, an agent, an automation); then flows and
17
+ scenarios, validated against the existing base, approved.
18
+ - **Do NOT write interface code until the UX workflow is done first:**
19
+ foundation → capabilities → contract → flows → screens → scenarios for
20
+ human lanes, designed and approved (capabilities and contract under
21
+ contract v5), and, when Figma is enabled (default), the UI mocked up in
22
+ Figma with every screen linked to its frame. A screen is designed where a
23
+ person is needed; every other capability stays reachable by agents.
24
+ Building UI before this is the exact mistake super-ux exists to prevent.
21
25
  - Visual identity is ONE locked style pack, recorded in `docs/ux/screens.md`
22
26
  → Design system and obeyed by every Figma frame and every built screen —
23
27
  picked with the **sheleg-design** companion skill when the project has no
@@ -1,7 +1,7 @@
1
1
  # User Flows
2
2
 
3
- <!-- Managed with super-ux (ux-contract v4). The HOW layer: task analysis
4
- and user flows. Flows reference screens by SCR-ID (full specs live in
3
+ <!-- Managed with super-ux (ux-contract v5). The HOW layer: task analysis
4
+ and user flows for the human lanes. Flows reference screens by SCR-ID (full specs live in
5
5
  screens.md). Scenarios in scenarios.md trace to FLW-IDs and must cover every
6
6
  node and edge. -->
7
7
 
@@ -1,14 +1,27 @@
1
1
  # UX Foundation
2
2
 
3
- <!-- Managed with super-ux (ux-contract v4). The WHY layer: personas, jobs
4
- to be done, customer journeys, user stories. Update when the understanding
5
- of users changes; scenarios in scenarios.md trace to the IDs defined here. -->
3
+ <!-- Managed with super-ux (ux-contract v5). The WHY layer: personas, actors,
4
+ jobs to be done, customer journeys, user stories. Update when the understanding
5
+ of users changes; capabilities.md and scenarios.md trace to the IDs defined
6
+ here. -->
6
7
 
7
8
  ## Personas
8
9
 
9
10
  <!-- ### P-01: <name>
10
11
  1-3 sentences: who they are, what they know, what they want. Grounded in
11
- data/observation, recognizable by a real user. -->
12
+ data/observation, recognizable by a real user.
13
+ - **Agent use:** none | some | most (<evidence: RE-NN, the brief, an interview>)
14
+ -->
15
+
16
+ ## Actors
17
+
18
+ <!-- The non-human performers: agents acting for a persona, automations and
19
+ outside systems. People stay in Personas.
20
+ ### A-01: <name>
21
+ - **Kind:** agent | system
22
+ - **Acts for:** P-01
23
+ - **Grant:** <the scope it holds; never wider than its principal's role>
24
+ -->
12
25
 
13
26
  ## Jobs to Be Done
14
27
 
@@ -24,8 +37,9 @@ data/observation, recognizable by a real user. -->
24
37
 
25
38
  <!-- ### JRN-01: <persona> — <job> (JTBD-01)
26
39
  - **Owner:** unknown
27
- | # | Stage | User action | Touchpoint | Emotion (1-5) | Pain | Opportunity |
28
- |---|-------|------------|------------|---------------|------|-------------|
40
+ | # | Stage | User action | Performed by | Touchpoint | Emotion (1-5) | Pain | Opportunity |
41
+ |---|-------|------------|--------------|------------|---------------|------|-------------|
42
+ Performed by: the P- and A- ids that do this stage.
29
43
  -->
30
44
 
31
45
  ## Monetization
@@ -1,6 +1,6 @@
1
1
  # UX Scenarios
2
2
 
3
- <!-- Managed with super-ux (ux-contract v4). Update in the same change as any user-facing behavior change. -->
3
+ <!-- Managed with super-ux (ux-contract v5). Update in the same change as any user-facing behavior change. -->
4
4
 
5
5
  ## Index
6
6
 
@@ -16,20 +16,23 @@ know, what they want. -->
16
16
  ## Scenarios
17
17
 
18
18
  <!-- Group scenarios by feature under "## <feature>" headings. Entry shape
19
- (see ux-contract v4 for field rules); steps are use-case style — one user
20
- action per step paired with the observable system response:
19
+ (see ux-contract v5 for field rules); steps are use-case style: one user
20
+ action per step paired with the observable system response. An agent-channel
21
+ step is `A-NN -> tool -> observable response`, and its scenario cites the
22
+ CAP-NN it exercises in Traces:
21
23
 
22
24
  ### SCN-001: <Title>
23
25
  - **Persona:** <persona-id>
24
26
  - **Feature:** <feature>
25
27
  - **Traces:** <ST-…, FLW-… (JTBD-…, JRN-…/#stage) — required when those layers exist>
28
+ - **Channel:** human | agent | both
26
29
  - **Entry point:** <where the user starts>
27
30
  - **Preconditions:** <state required, or "none">
28
31
  - **Steps:**
29
32
  1. <user action> -> <observable system response>
30
33
  - **Expected result:** <what the user observes on success>
31
34
  - **Alt paths:** <meaningful non-error deviations and system responses, or omit>
32
- - **UI elements:** <every button, field, link, dialog, toast involved>
35
+ - **UI elements:** <every button, field, link, dialog, toast involved; human channel only>
33
36
  - **States covered:** <loading | empty | error | success>
34
37
  - **Errors & recovery:** <each failure: what the user sees, how they recover>
35
38
  - **Telemetry:** <`object_action` event + params, or omit if none>
@@ -4,7 +4,7 @@
4
4
  <!-- The line above puts this registry on format 2: the screen matrix
5
5
  (states, axes, a frame per state, a story per built state) is a lint gate.
6
6
  Keep it. -->
7
- <!-- Managed with super-ux (ux-contract v4). The design map: every screen and
7
+ <!-- Managed with super-ux (ux-contract v5). The design map: every screen and
8
8
  state with its Figma frame, wireframe, code coverage, and related UX/UI
9
9
  resources. Update in the same change as any interface change; when Figma is
10
10
  enabled, update the frame and re-verify its link in the same change. A screen
@@ -44,11 +44,13 @@ the problem it can no longer fix. -->
44
44
 
45
45
  ## Screens
46
46
 
47
- <!-- One entry per screen (see ux-contract v4 for field rules):
47
+ <!-- One entry per screen (see ux-contract v5 for field rules):
48
48
 
49
49
  ### SCR-01: <name>
50
50
  - **Used by:** <FLW-… and the step(s)>
51
51
  - **Purpose:** <the job step this screen serves>
52
+ - **Fronts:** <CAP-… this screen fronts, or none>
53
+ - **Why human:** <oversight | exception-queue | trust | presence | audience | manual | taste> (<evidence>)
52
54
  - **Elements:** <each element; mark the ONE primary action>
53
55
  - **States:** (every one that applies: default, loading, empty, error,
54
56
  offline, long-content, keyboard-up, first-run, product-specific ones)
@@ -69,5 +71,5 @@ the problem it can no longer fix. -->
69
71
  - **Coverage:** <file:line, or "none yet">
70
72
  - **Scenarios:** <SCN-… touching this screen>
71
73
  - **Resources:** <related components, shared assets, API/data deps, links>
72
- - **Status:** designed | built | drifted | retired
74
+ - **Status:** designed | blocked | built | drifted | retired
73
75
  -->
@@ -1,6 +1,6 @@
1
1
  # <Product> — Vision
2
2
 
3
- <!-- Managed with super-ux (ux-contract v4). The layer above the chain. -->
3
+ <!-- Managed with super-ux (ux-contract v5). The layer above the chain. -->
4
4
 
5
5
  <!--
6
6
  Owned by the `vision` skill (super-ux). Nine sections, these headings, in
@@ -39,8 +39,10 @@
39
39
 
40
40
  ## 4. The user's role
41
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. -->
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. Say
44
+ which work the person keeps and which their agent or an automation does
45
+ for them; the person is where a gate or a decision needs them. -->
44
46
 
45
47
  ## 5. Principles
46
48
 
@@ -67,7 +69,9 @@
67
69
 
68
70
  <!-- Three to five questions any proposed feature must pass. These are what
69
71
  the hard rule in the instruction file actually runs. A stranger should be
70
- able to reject a plausible feature with them. -->
72
+ able to reject a plausible feature with them. Include one for agents:
73
+ can an agent do this without a screen, and if a person must, which gate
74
+ or serving reason says so? -->
71
75
 
72
76
  1. <question>
73
77
  2. <question>