cbrowser 18.78.0 → 18.80.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.
Files changed (235) hide show
  1. package/dist/analysis/accessibility-empathy.d.ts.map +1 -1
  2. package/dist/analysis/accessibility-empathy.js +395 -66
  3. package/dist/analysis/accessibility-empathy.js.map +1 -1
  4. package/dist/analysis/agent-ready-audit.d.ts.map +1 -1
  5. package/dist/analysis/agent-ready-audit.js +78 -0
  6. package/dist/analysis/agent-ready-audit.js.map +1 -1
  7. package/dist/analysis/page-understanding.d.ts +139 -1
  8. package/dist/analysis/page-understanding.d.ts.map +1 -1
  9. package/dist/analysis/page-understanding.js +179 -12
  10. package/dist/analysis/page-understanding.js.map +1 -1
  11. package/dist/artifact-store.d.ts +19 -5
  12. package/dist/artifact-store.d.ts.map +1 -1
  13. package/dist/artifact-store.js +41 -11
  14. package/dist/artifact-store.js.map +1 -1
  15. package/dist/browser.d.ts +9 -0
  16. package/dist/browser.d.ts.map +1 -1
  17. package/dist/browser.js +81 -5
  18. package/dist/browser.js.map +1 -1
  19. package/dist/cli.js +55 -10
  20. package/dist/cli.js.map +1 -1
  21. package/dist/cognitive/index.d.ts.map +1 -1
  22. package/dist/cognitive/index.js +3 -2
  23. package/dist/cognitive/index.js.map +1 -1
  24. package/dist/config.d.ts +57 -0
  25. package/dist/config.d.ts.map +1 -1
  26. package/dist/config.js +234 -7
  27. package/dist/config.js.map +1 -1
  28. package/dist/index.js +1 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/mcp-server-remote.d.ts.map +1 -1
  31. package/dist/mcp-server-remote.js +108 -4
  32. package/dist/mcp-server-remote.js.map +1 -1
  33. package/dist/mcp-server.d.ts.map +1 -1
  34. package/dist/mcp-server.js +62 -938
  35. package/dist/mcp-server.js.map +1 -1
  36. package/dist/mcp-tools/account-personas.d.ts +41 -0
  37. package/dist/mcp-tools/account-personas.d.ts.map +1 -0
  38. package/dist/mcp-tools/account-personas.js +98 -0
  39. package/dist/mcp-tools/account-personas.js.map +1 -0
  40. package/dist/mcp-tools/base/analysis-tools.d.ts.map +1 -1
  41. package/dist/mcp-tools/base/analysis-tools.js +12 -2
  42. package/dist/mcp-tools/base/analysis-tools.js.map +1 -1
  43. package/dist/mcp-tools/base/artifact-tools.d.ts +3 -0
  44. package/dist/mcp-tools/base/artifact-tools.d.ts.map +1 -0
  45. package/dist/mcp-tools/base/artifact-tools.js +78 -0
  46. package/dist/mcp-tools/base/artifact-tools.js.map +1 -0
  47. package/dist/mcp-tools/base/audit-tools.d.ts +11 -0
  48. package/dist/mcp-tools/base/audit-tools.d.ts.map +1 -1
  49. package/dist/mcp-tools/base/audit-tools.js +595 -340
  50. package/dist/mcp-tools/base/audit-tools.js.map +1 -1
  51. package/dist/mcp-tools/base/browser-management-tools.d.ts.map +1 -1
  52. package/dist/mcp-tools/base/browser-management-tools.js +18 -0
  53. package/dist/mcp-tools/base/browser-management-tools.js.map +1 -1
  54. package/dist/mcp-tools/base/bug-analysis-tools.d.ts.map +1 -1
  55. package/dist/mcp-tools/base/bug-analysis-tools.js +59 -3
  56. package/dist/mcp-tools/base/bug-analysis-tools.js.map +1 -1
  57. package/dist/mcp-tools/base/capture-tools.d.ts +37 -0
  58. package/dist/mcp-tools/base/capture-tools.d.ts.map +1 -1
  59. package/dist/mcp-tools/base/capture-tools.js +267 -6
  60. package/dist/mcp-tools/base/capture-tools.js.map +1 -1
  61. package/dist/mcp-tools/base/cognitive-tools.d.ts +12 -0
  62. package/dist/mcp-tools/base/cognitive-tools.d.ts.map +1 -1
  63. package/dist/mcp-tools/base/cognitive-tools.js +63 -15
  64. package/dist/mcp-tools/base/cognitive-tools.js.map +1 -1
  65. package/dist/mcp-tools/base/extraction-tools.d.ts.map +1 -1
  66. package/dist/mcp-tools/base/extraction-tools.js +33 -2
  67. package/dist/mcp-tools/base/extraction-tools.js.map +1 -1
  68. package/dist/mcp-tools/base/index.d.ts +2 -0
  69. package/dist/mcp-tools/base/index.d.ts.map +1 -1
  70. package/dist/mcp-tools/base/index.js +9 -0
  71. package/dist/mcp-tools/base/index.js.map +1 -1
  72. package/dist/mcp-tools/base/marketing-tools.js +3 -3
  73. package/dist/mcp-tools/base/marketing-tools.js.map +1 -1
  74. package/dist/mcp-tools/base/persona-comparison-tools.d.ts.map +1 -1
  75. package/dist/mcp-tools/base/persona-comparison-tools.js +529 -40
  76. package/dist/mcp-tools/base/persona-comparison-tools.js.map +1 -1
  77. package/dist/mcp-tools/base/site-knowledge-tools.d.ts.map +1 -1
  78. package/dist/mcp-tools/base/site-knowledge-tools.js +36 -3
  79. package/dist/mcp-tools/base/site-knowledge-tools.js.map +1 -1
  80. package/dist/mcp-tools/base/values-tools.d.ts +113 -0
  81. package/dist/mcp-tools/base/values-tools.d.ts.map +1 -1
  82. package/dist/mcp-tools/base/values-tools.js +868 -78
  83. package/dist/mcp-tools/base/values-tools.js.map +1 -1
  84. package/dist/mcp-tools/base/visual-testing-tools.d.ts.map +1 -1
  85. package/dist/mcp-tools/base/visual-testing-tools.js +130 -10
  86. package/dist/mcp-tools/base/visual-testing-tools.js.map +1 -1
  87. package/dist/mcp-tools/index.d.ts +3 -1
  88. package/dist/mcp-tools/index.d.ts.map +1 -1
  89. package/dist/mcp-tools/index.js +3 -28
  90. package/dist/mcp-tools/index.js.map +1 -1
  91. package/dist/mcp-tools/persona-creation-tools.d.ts.map +1 -1
  92. package/dist/mcp-tools/persona-creation-tools.js +363 -20
  93. package/dist/mcp-tools/persona-creation-tools.js.map +1 -1
  94. package/dist/mcp-tools/persona-lifecycle-tools.d.ts +3 -0
  95. package/dist/mcp-tools/persona-lifecycle-tools.d.ts.map +1 -0
  96. package/dist/mcp-tools/persona-lifecycle-tools.js +406 -0
  97. package/dist/mcp-tools/persona-lifecycle-tools.js.map +1 -0
  98. package/dist/mcp-tools/screenshot-utils.d.ts.map +1 -1
  99. package/dist/mcp-tools/screenshot-utils.js +56 -10
  100. package/dist/mcp-tools/screenshot-utils.js.map +1 -1
  101. package/dist/mcp-tools/tier-gate.js +2 -2
  102. package/dist/mcp-tools/tier-gate.js.map +1 -1
  103. package/dist/mcp-tools/tool-categories.d.ts.map +1 -1
  104. package/dist/mcp-tools/tool-categories.js +4 -1
  105. package/dist/mcp-tools/tool-categories.js.map +1 -1
  106. package/dist/mcp-tools/ui-resources.d.ts +167 -0
  107. package/dist/mcp-tools/ui-resources.d.ts.map +1 -0
  108. package/dist/mcp-tools/ui-resources.js +707 -0
  109. package/dist/mcp-tools/ui-resources.js.map +1 -0
  110. package/dist/mcp-tools/widget-kit.d.ts +299 -0
  111. package/dist/mcp-tools/widget-kit.d.ts.map +1 -0
  112. package/dist/mcp-tools/widget-kit.js +2034 -0
  113. package/dist/mcp-tools/widget-kit.js.map +1 -0
  114. package/dist/mcp-ui-resources.d.ts.map +1 -1
  115. package/dist/mcp-ui-resources.js +14 -4
  116. package/dist/mcp-ui-resources.js.map +1 -1
  117. package/dist/persona-questionnaire.d.ts +69 -1
  118. package/dist/persona-questionnaire.d.ts.map +1 -1
  119. package/dist/persona-questionnaire.js +177 -76
  120. package/dist/persona-questionnaire.js.map +1 -1
  121. package/dist/persona-scope.d.ts +20 -0
  122. package/dist/persona-scope.d.ts.map +1 -0
  123. package/dist/persona-scope.js +59 -0
  124. package/dist/persona-scope.js.map +1 -0
  125. package/dist/personas.d.ts +51 -17
  126. package/dist/personas.d.ts.map +1 -1
  127. package/dist/personas.js +947 -286
  128. package/dist/personas.js.map +1 -1
  129. package/dist/recording/capture-player.d.ts +68 -0
  130. package/dist/recording/capture-player.d.ts.map +1 -0
  131. package/dist/recording/capture-player.js +298 -0
  132. package/dist/recording/capture-player.js.map +1 -0
  133. package/dist/recording/engine.d.ts +52 -0
  134. package/dist/recording/engine.d.ts.map +1 -1
  135. package/dist/recording/engine.js +241 -3
  136. package/dist/recording/engine.js.map +1 -1
  137. package/dist/recording/inline-player.d.ts +39 -0
  138. package/dist/recording/inline-player.d.ts.map +1 -0
  139. package/dist/recording/inline-player.js +272 -0
  140. package/dist/recording/inline-player.js.map +1 -0
  141. package/dist/recording/saliency-overlay.d.ts +133 -0
  142. package/dist/recording/saliency-overlay.d.ts.map +1 -0
  143. package/dist/recording/saliency-overlay.js +360 -0
  144. package/dist/recording/saliency-overlay.js.map +1 -0
  145. package/dist/recording/types.d.ts +32 -0
  146. package/dist/recording/types.d.ts.map +1 -1
  147. package/dist/recording/types.js +46 -0
  148. package/dist/recording/types.js.map +1 -1
  149. package/dist/site-model/manager.d.ts +20 -0
  150. package/dist/site-model/manager.d.ts.map +1 -1
  151. package/dist/site-model/manager.js +52 -0
  152. package/dist/site-model/manager.js.map +1 -1
  153. package/dist/trait-reference.d.ts +26 -2
  154. package/dist/trait-reference.d.ts.map +1 -1
  155. package/dist/trait-reference.js +129 -19
  156. package/dist/trait-reference.js.map +1 -1
  157. package/dist/types.d.ts +105 -18
  158. package/dist/types.d.ts.map +1 -1
  159. package/dist/types.js.map +1 -1
  160. package/dist/url-resolution.d.ts +58 -0
  161. package/dist/url-resolution.d.ts.map +1 -0
  162. package/dist/url-resolution.js +143 -0
  163. package/dist/url-resolution.js.map +1 -0
  164. package/dist/values/big-five-inference.d.ts +121 -0
  165. package/dist/values/big-five-inference.d.ts.map +1 -0
  166. package/dist/values/big-five-inference.js +241 -0
  167. package/dist/values/big-five-inference.js.map +1 -0
  168. package/dist/values/big-five-values.d.ts +76 -0
  169. package/dist/values/big-five-values.d.ts.map +1 -0
  170. package/dist/values/big-five-values.js +152 -0
  171. package/dist/values/big-five-values.js.map +1 -0
  172. package/dist/values/index.d.ts +1 -1
  173. package/dist/values/index.d.ts.map +1 -1
  174. package/dist/values/index.js +1 -1
  175. package/dist/values/index.js.map +1 -1
  176. package/dist/values/persona-completeness.d.ts +91 -0
  177. package/dist/values/persona-completeness.d.ts.map +1 -0
  178. package/dist/values/persona-completeness.js +114 -0
  179. package/dist/values/persona-completeness.js.map +1 -0
  180. package/dist/values/persona-diff.d.ts +50 -0
  181. package/dist/values/persona-diff.d.ts.map +1 -0
  182. package/dist/values/persona-diff.js +126 -0
  183. package/dist/values/persona-diff.js.map +1 -0
  184. package/dist/values/persona-values.d.ts +16 -0
  185. package/dist/values/persona-values.d.ts.map +1 -1
  186. package/dist/values/persona-values.js +33 -0
  187. package/dist/values/persona-values.js.map +1 -1
  188. package/dist/values/schwartz-values.d.ts +9 -1
  189. package/dist/values/schwartz-values.d.ts.map +1 -1
  190. package/dist/values/schwartz-values.js +10 -2
  191. package/dist/values/schwartz-values.js.map +1 -1
  192. package/dist/values/value-mappings.d.ts +41 -2
  193. package/dist/values/value-mappings.d.ts.map +1 -1
  194. package/dist/values/value-mappings.js +65 -8
  195. package/dist/values/value-mappings.js.map +1 -1
  196. package/dist/visual/attention-quality.d.ts +27 -0
  197. package/dist/visual/attention-quality.d.ts.map +1 -1
  198. package/dist/visual/attention-quality.js +164 -2
  199. package/dist/visual/attention-quality.js.map +1 -1
  200. package/dist/visual/attention-transport.d.ts +41 -2
  201. package/dist/visual/attention-transport.d.ts.map +1 -1
  202. package/dist/visual/attention-transport.js +98 -26
  203. package/dist/visual/attention-transport.js.map +1 -1
  204. package/dist/visual/barrier-overlay-html.d.ts +10 -0
  205. package/dist/visual/barrier-overlay-html.d.ts.map +1 -1
  206. package/dist/visual/barrier-overlay-html.js.map +1 -1
  207. package/dist/visual/cognitive-models.d.ts +109 -13
  208. package/dist/visual/cognitive-models.d.ts.map +1 -1
  209. package/dist/visual/cognitive-models.js +166 -3
  210. package/dist/visual/cognitive-models.js.map +1 -1
  211. package/dist/visual/cognitive-transport-chain.d.ts +149 -5
  212. package/dist/visual/cognitive-transport-chain.d.ts.map +1 -1
  213. package/dist/visual/cognitive-transport-chain.js +558 -30
  214. package/dist/visual/cognitive-transport-chain.js.map +1 -1
  215. package/dist/visual/cognitive-transport.d.ts +45 -3
  216. package/dist/visual/cognitive-transport.d.ts.map +1 -1
  217. package/dist/visual/cognitive-transport.js +126 -12
  218. package/dist/visual/cognitive-transport.js.map +1 -1
  219. package/dist/visual/fixation-corpus.d.ts +52 -0
  220. package/dist/visual/fixation-corpus.d.ts.map +1 -1
  221. package/dist/visual/fixation-corpus.js +95 -0
  222. package/dist/visual/fixation-corpus.js.map +1 -1
  223. package/dist/visual/llm-relevance.d.ts +193 -0
  224. package/dist/visual/llm-relevance.d.ts.map +1 -0
  225. package/dist/visual/llm-relevance.js +480 -0
  226. package/dist/visual/llm-relevance.js.map +1 -0
  227. package/dist/visual/perceptual-transport.d.ts +60 -0
  228. package/dist/visual/perceptual-transport.d.ts.map +1 -1
  229. package/dist/visual/perceptual-transport.js +228 -19
  230. package/dist/visual/perceptual-transport.js.map +1 -1
  231. package/dist/visual/persona-context.d.ts +23 -0
  232. package/dist/visual/persona-context.d.ts.map +1 -0
  233. package/dist/visual/persona-context.js +44 -0
  234. package/dist/visual/persona-context.js.map +1 -0
  235. package/package.json +2 -1
package/dist/personas.js CHANGED
@@ -14,11 +14,28 @@ import { join } from "path";
14
14
  import { homedir } from "os";
15
15
  import { applyTraitCorrelations } from "./persona-questionnaire.js";
16
16
  import { AGENT_PERSONAS, isAgentPersona, getAgentPersona, listAgentPersonas, isAgentPersonaObject } from "./agent-personas.js";
17
+ import { scopedDataDir } from "./persona-scope.js";
18
+ import { TRAIT_DEFINITIONS, canonicalizeTraits } from "./trait-reference.js";
17
19
  // ============================================================================
18
20
  // Custom Personas Storage
19
21
  // ============================================================================
20
22
  const DATA_DIR = process.env.CBROWSER_DATA_DIR || join(homedir(), ".cbrowser");
21
- const PERSONAS_DIR = join(DATA_DIR, "personas");
23
+ /**
24
+ * Resolved per call, not once at module load.
25
+ *
26
+ * A hosted server process serves many accounts, so the directory cannot be a
27
+ * constant fixed when the file is imported. `scopedDataDir()` returns the
28
+ * account bound to the CURRENT request's async context, or undefined when
29
+ * nothing entered a scope — which is every CLI run, every stdio session and
30
+ * every local install, all of which keep the previous behaviour exactly.
31
+ *
32
+ * Deliberately NOT a module-level `let` reassigned per request: that is the
33
+ * shape that lets two overlapping requests read each other's accounts, and it
34
+ * is the shape this codebase already removed from the billing path. (2026-08-01)
35
+ */
36
+ function personasDirFor() {
37
+ return join(scopedDataDir() ?? DATA_DIR, "personas");
38
+ }
22
39
  // ============================================================================
23
40
  // Runtime Persona Registry (for Enterprise extensions)
24
41
  // ============================================================================
@@ -32,21 +49,34 @@ function ensurePersonasDir() {
32
49
  if (!existsSync(DATA_DIR)) {
33
50
  mkdirSync(DATA_DIR, { recursive: true });
34
51
  }
35
- if (!existsSync(PERSONAS_DIR)) {
36
- mkdirSync(PERSONAS_DIR, { recursive: true });
52
+ if (!existsSync(personasDirFor())) {
53
+ mkdirSync(personasDirFor(), { recursive: true });
37
54
  }
38
55
  }
39
56
  /**
40
57
  * Load all custom personas from disk.
41
58
  */
59
+ /**
60
+ * Normalise a persona read off disk: deprecated trait keys to canonical ones.
61
+ *
62
+ * Without this a persona saved before the mentalModelRigidity rename loads with
63
+ * a key nothing recognises, so that trait silently stops contributing.
64
+ */
65
+ function normalizePersonaOnRead(p) {
66
+ const t = p?.cognitiveTraits;
67
+ if (!t)
68
+ return p;
69
+ const canon = canonicalizeTraits(t);
70
+ return canon === t ? p : { ...p, cognitiveTraits: canon };
71
+ }
42
72
  export function loadCustomPersonas() {
43
73
  ensurePersonasDir();
44
74
  const personas = {};
45
75
  try {
46
- const files = readdirSync(PERSONAS_DIR).filter(f => f.endsWith(".json") || f.endsWith(".yaml") || f.endsWith(".yml"));
76
+ const files = readdirSync(personasDirFor()).filter(f => f.endsWith(".json") || f.endsWith(".yaml") || f.endsWith(".yml"));
47
77
  for (const file of files) {
48
78
  try {
49
- const content = readFileSync(join(PERSONAS_DIR, file), "utf-8");
79
+ const content = readFileSync(join(personasDirFor(), file), "utf-8");
50
80
  let persona;
51
81
  if (file.endsWith(".json")) {
52
82
  // Bare `as Persona` trusted arbitrary user-authored JSON to be complete.
@@ -54,7 +84,10 @@ export function loadCustomPersonas() {
54
84
  // branch the same floor rather than letting a partial file reach
55
85
  // getCognitiveProfile half-formed. (2026-07-28)
56
86
  const parsed = JSON.parse(content);
57
- persona = { ...parsed, behaviors: parsed.behaviors ?? {} };
87
+ // Deprecated trait keys resolved here, at the read boundary, so a
88
+ // persona saved before a rename keeps contributing instead of
89
+ // silently dropping the renamed trait.
90
+ persona = normalizePersonaOnRead({ ...parsed, behaviors: parsed.behaviors ?? {} });
58
91
  }
59
92
  else {
60
93
  // Simple YAML parsing for persona files
@@ -84,11 +117,31 @@ export function loadCustomPersonas() {
84
117
  /**
85
118
  * Save a custom persona to disk.
86
119
  */
120
+ /**
121
+ * Remove run-scoped values a persona record must not carry.
122
+ *
123
+ * `siteFamiliarity` is a persona-SITE pair, not a disposition, so a stored one
124
+ * is always answering "familiar with which site?" about a site nobody named.
125
+ * Worse, the persona builder merged it over the caller's per-run parameter, so
126
+ * the tool discarded the input while reporting that it had applied it.
127
+ *
128
+ * Stripping on the way to disk rather than trusting every caller: this is the
129
+ * single chokepoint every custom persona passes through, and a rule enforced at
130
+ * one write is worth more than the same rule written in six call sites.
131
+ * (2026-08-02)
132
+ */
133
+ export function stripStoredFamiliarity(persona) {
134
+ if (persona?.cognitiveTraits && "siteFamiliarity" in persona.cognitiveTraits) {
135
+ const { siteFamiliarity: _dropped, ...rest } = persona.cognitiveTraits;
136
+ return { ...persona, cognitiveTraits: rest };
137
+ }
138
+ return persona;
139
+ }
87
140
  export function saveCustomPersona(persona) {
88
141
  ensurePersonasDir();
89
142
  const filename = `${persona.name.toLowerCase().replace(/[^a-z0-9-]/g, "-")}.json`;
90
- const filepath = join(PERSONAS_DIR, filename);
91
- writeFileSync(filepath, JSON.stringify(persona, null, 2));
143
+ const filepath = join(personasDirFor(), filename);
144
+ writeFileSync(filepath, JSON.stringify(stripStoredFamiliarity(persona), null, 2));
92
145
  return filepath;
93
146
  }
94
147
  /**
@@ -103,7 +156,7 @@ export function deleteCustomPersona(name) {
103
156
  const baseName = name.toLowerCase().replace(/[^a-z0-9-]/g, "-");
104
157
  const extensions = [".json", ".yaml", ".yml"];
105
158
  for (const ext of extensions) {
106
- const filepath = join(PERSONAS_DIR, `${baseName}${ext}`);
159
+ const filepath = join(personasDirFor(), `${baseName}${ext}`);
107
160
  try {
108
161
  if (existsSync(filepath)) {
109
162
  unlinkSync(filepath);
@@ -560,7 +613,7 @@ function generateCognitiveTraitsFromDescription(descLower, techLevel, isImpatien
560
613
  let emotionalContagion = 0.5;
561
614
  let fearOfMissingOut = 0.5;
562
615
  let socialProofSensitivity = 0.5;
563
- let mentalModelRigidity = 0.5;
616
+ let mentalModelFlexibility = 0.5;
564
617
  // Adjust new traits by tech level
565
618
  if (techLevel === "expert") {
566
619
  informationForaging = 0.9; // Experts follow scent efficiently
@@ -575,7 +628,7 @@ function generateCognitiveTraitsFromDescription(descLower, techLevel, isImpatien
575
628
  emotionalContagion = 0.2; // Mood-stable
576
629
  fearOfMissingOut = 0.2; // Not swayed by urgency
577
630
  socialProofSensitivity = 0.3; // Evaluate on merits
578
- mentalModelRigidity = 0.8; // Adapt to new patterns
631
+ mentalModelFlexibility = 0.8; // Adapt to new patterns
579
632
  }
580
633
  else if (techLevel === "beginner") {
581
634
  informationForaging = 0.3; // Exhaustive search
@@ -590,7 +643,7 @@ function generateCognitiveTraitsFromDescription(descLower, techLevel, isImpatien
590
643
  emotionalContagion = 0.7; // Mood influenced by UI
591
644
  fearOfMissingOut = 0.7; // Susceptible to urgency
592
645
  socialProofSensitivity = 0.8; // Rely on reviews
593
- mentalModelRigidity = 0.3; // Struggle with new patterns
646
+ mentalModelFlexibility = 0.3; // Struggle with new patterns
594
647
  }
595
648
  // Adjust for impatience
596
649
  if (isImpatient) {
@@ -615,7 +668,7 @@ function generateCognitiveTraitsFromDescription(descLower, techLevel, isImpatien
615
668
  proceduralFluency = Math.min(proceduralFluency, 0.35); // Struggle with sequences
616
669
  transferLearning = Math.min(transferLearning, 0.25); // Each UI novel
617
670
  authoritySensitivity = Math.max(authoritySensitivity, 0.7); // Trust authority
618
- mentalModelRigidity = Math.min(mentalModelRigidity, 0.2); // Rigid mental models
671
+ mentalModelFlexibility = Math.min(mentalModelFlexibility, 0.2); // Rigid mental models
619
672
  }
620
673
  // Adjust for vision issues (screen reader users)
621
674
  if (hasVisionIssues) {
@@ -647,7 +700,7 @@ function generateCognitiveTraitsFromDescription(descLower, techLevel, isImpatien
647
700
  emotionalContagion,
648
701
  fearOfMissingOut,
649
702
  socialProofSensitivity,
650
- mentalModelRigidity,
703
+ mentalModelFlexibility,
651
704
  };
652
705
  }
653
706
  /**
@@ -691,8 +744,7 @@ export function createCognitivePersona(name, description, traits, options = {})
691
744
  emotionalContagion: traits.emotionalContagion ?? basePersona.cognitiveTraits?.emotionalContagion ?? 0.5,
692
745
  fearOfMissingOut: traits.fearOfMissingOut ?? basePersona.cognitiveTraits?.fearOfMissingOut ?? 0.5,
693
746
  socialProofSensitivity: traits.socialProofSensitivity ?? basePersona.cognitiveTraits?.socialProofSensitivity ?? 0.5,
694
- mentalModelRigidity: traits.mentalModelRigidity ?? basePersona.cognitiveTraits?.mentalModelRigidity ?? 0.5,
695
- siteFamiliarity: traits.siteFamiliarity ?? basePersona.cognitiveTraits?.siteFamiliarity ?? 0.5,
747
+ mentalModelFlexibility: traits.mentalModelFlexibility ?? basePersona.cognitiveTraits?.mentalModelFlexibility ?? 0.5,
696
748
  };
697
749
  // Backstop for the failure mode above. The enumeration is kept because it
698
750
  // documents each trait's fallback, but a hand-written list silently drops
@@ -738,7 +790,14 @@ export function createCognitivePersona(name, description, traits, options = {})
738
790
  export function getCognitiveProfile(persona) {
739
791
  // Derive attention pattern from humanBehavior.attention.pattern or behaviors
740
792
  let attentionPattern = "f-pattern";
793
+ let attentionPatternSource = "default";
794
+ let attentionPatternMargin;
795
+ let attentionPatternConflict;
796
+ let techLevel;
797
+ let techLevelSource = "derived";
798
+ let techLevelConflict;
741
799
  if (persona.humanBehavior?.attention?.pattern) {
800
+ attentionPatternSource = "declared";
742
801
  const pattern = persona.humanBehavior.attention.pattern;
743
802
  if (pattern === "skim")
744
803
  attentionPattern = "skim";
@@ -758,17 +817,41 @@ export function getCognitiveProfile(persona) {
758
817
  // it. (2026-07-28)
759
818
  if (persona.behaviors?.attentionPattern) {
760
819
  attentionPattern = persona.behaviors.attentionPattern;
820
+ attentionPatternSource = "declared";
761
821
  }
822
+ const declaredAttentionPattern = attentionPatternSource === "declared" ? attentionPattern : undefined;
762
823
  // Derive decision style from traits
763
824
  let decisionStyle = "cautious";
825
+ let decisionStyleSource = "default";
826
+ let decisionStyleMargin;
827
+ let decisionStyleConfidence;
828
+ let decisionStyleRunnerUp;
829
+ let scoresTop;
764
830
  const traits = persona.cognitiveTraits;
765
831
  // v16.7.1: Validate trait completeness
766
832
  if (traits) {
767
- const expectedTraitCount = 25; // 7 required + 18 optional
768
- const actualTraitCount = Object.keys(traits).filter(k => traits[k] !== undefined).length;
833
+ // Derived from the trait definitions rather than hardcoded. This was the
834
+ // literal 25 while the model had 26, so a persona missing exactly one trait
835
+ // counted as complete and the warning could never fire for it. A count that
836
+ // has to be updated by hand in step with a table is a count that will drift
837
+ // from it -- this file, mcp-server.ts, cognitive/index.ts, widget-kit.ts and
838
+ // cognitive-transport.ts had all drifted to 25 by 2026-08-01.
839
+ // Run-scoped and runtime-derived dimensions are excluded from the count.
840
+ //
841
+ // siteFamiliarity is deliberately not stored on personas (it is a
842
+ // persona-SITE pair supplied per run), and readingCapacity/motorCapacity/
843
+ // sustainedAttention are injected at analysis time -- the first two from the
844
+ // formal models, the third resolved from accessibilityTraits.attentionSpan.
845
+ // Counting them as "missing" made every persona in the roster warn on every
846
+ // load, which turns a real signal -- a persona that genuinely skipped traits
847
+ // -- into noise nobody reads. (2026-08-02)
848
+ const RUNTIME_SCOPED = new Set(["siteFamiliarity", "readingCapacity", "motorCapacity", "sustainedAttention"]);
849
+ const expectedTraitCount = Object.keys(TRAIT_DEFINITIONS).filter((t) => !RUNTIME_SCOPED.has(t)).length;
850
+ const actualTraitCount = Object.keys(traits)
851
+ .filter((k) => !RUNTIME_SCOPED.has(k) && traits[k] !== undefined).length;
769
852
  if (actualTraitCount < expectedTraitCount) {
770
853
  console.warn(`[CBrowser] Persona "${persona.name}" has ${actualTraitCount}/${expectedTraitCount} traits. ` +
771
- `Missing traits will use defaults. Consider regenerating the persona for full 25-trait model.`);
854
+ `Missing traits will use defaults. Consider regenerating the persona for the full ${expectedTraitCount}-trait model.`);
772
855
  }
773
856
  }
774
857
  if (traits) {
@@ -777,24 +860,157 @@ export function getCognitiveProfile(persona) {
777
860
  const riskTolerance = traits.riskTolerance ?? 0.5;
778
861
  const comprehension = traits.comprehension ?? 0.5;
779
862
  const readingTendency = traits.readingTendency ?? 0.5;
780
- if (patience < 0.3 && riskTolerance > 0.6) {
781
- decisionStyle = "impulsive";
863
+ // Scored, not branched.
864
+ //
865
+ // An if/else chain is partial by construction: it answers only for the
866
+ // trait combinations someone thought to write a condition for, and every
867
+ // combination nobody covered silently inherits the initial literal. Six of
868
+ // twenty-one personas landed there -- first-timer among them -- reporting
869
+ // "cautious" because no branch matched, not because anything about them is
870
+ // cautious. Adding more branches moves the gap rather than closing it.
871
+ //
872
+ // Every style is scored from the trait vector and the highest wins, so a
873
+ // style is always chosen on evidence and the derivation is total: there is
874
+ // no combination left with no answer. (2026-07-31)
875
+ const t = (k, d = 0.5) => {
876
+ const v = traits[k];
877
+ return typeof v === "number" ? v : d;
878
+ };
879
+ const mean = (...xs) => xs.reduce((a, b) => a + b, 0) / xs.length;
880
+ const patienceV = t("patience");
881
+ const riskV = t("riskTolerance");
882
+ const satisficeV = t("satisficing");
883
+ const readingV = t("readingTendency");
884
+ const scores = [
885
+ // Acts before evaluating: tolerant of risk, short on patience, unplanned,
886
+ // and content with the first adequate option. Satisficing belongs here --
887
+ // without it someone with riskTolerance 0.8 and satisficing 0.25 scored
888
+ // impulsive, when refusing to settle is the opposite of impulsive.
889
+ ["impulsive", mean(riskV, 1 - patienceV, 1 - t("metacognitivePlanning"), satisficeV)],
890
+ // Knows the pattern and takes the shortest path that works.
891
+ ["efficient", mean(t("comprehension"), t("proceduralFluency"), satisficeV, 1 - readingV)],
892
+ // Checks before committing, and does not extend trust by default.
893
+ ["cautious", mean(1 - riskV, 1 - t("trustCalibration"), patienceV)],
894
+ // Reads, plans, and refuses to settle for the first adequate option.
895
+ ["deliberate", mean(readingV, patienceV, 1 - satisficeV, t("metacognitivePlanning"))],
896
+ ];
897
+ // Only reachable on a touch device, where it describes the input mode
898
+ // rather than the disposition.
899
+ if (persona.demographics.device === "mobile") {
900
+ // Weighted to win on mobile unless the trait evidence clearly says
901
+ // otherwise. Device was previously a terminal branch, so every mobile
902
+ // persona was quick-tap; letting it compete unweighted relabelled all of
903
+ // them at once, which is a bigger claim than closing a coverage gap
904
+ // should be making.
905
+ scores.push(["quick-tap", mean(1 - patienceV, satisficeV, riskV) + 0.2]);
782
906
  }
783
- else if (comprehension > 0.8 && patience < 0.4) {
784
- decisionStyle = "efficient";
907
+ scores.sort((a, b) => b[1] - a[1]);
908
+ decisionStyle = scores[0][0];
909
+ scoresTop = scores[0][0];
910
+ decisionStyleSource = "derived";
911
+ // A near-tie is a weak reading, and saying so beats presenting the winner
912
+ // as though it were clear.
913
+ decisionStyleMargin = Math.round((scores[0][1] - scores[1][1]) * 100) / 100;
914
+ // ...but exposing the margin was not enough on its own.
915
+ //
916
+ // The style STRING carried identical authority at a margin of 0.03 as at
917
+ // 0.90, and a consumer reading only `decisionStyle` -- which is most of
918
+ // them -- got a coin flip presented as a finding. A number nobody is
919
+ // required to look at does not qualify the claim sitting next to it.
920
+ //
921
+ // Below 0.05 the two top styles are indistinguishable given the precision of
922
+ // the inputs, so the label is withheld rather than guessed and the runner-up
923
+ // is named so the tie is inspectable. (2026-08-02)
924
+ decisionStyleConfidence = decisionStyleMargin >= 0.20 ? "clear"
925
+ : decisionStyleMargin >= 0.05 ? "weak"
926
+ : "indistinguishable";
927
+ if (decisionStyleConfidence === "indistinguishable") {
928
+ decisionStyleRunnerUp = scores[1][0];
785
929
  }
786
- else if (riskTolerance < 0.3) {
787
- decisionStyle = "cautious";
930
+ // Tech level, derived rather than trusted.
931
+ //
932
+ // demographics.tech_level is a creation-time literal that nothing ever
933
+ // revisits, and the default is "intermediate". Ten of twenty-one personas
934
+ // carried exactly that string while their fluency traits ranged from 0.28
935
+ // to 0.85 -- which is not ten independent judgements, it is one unset
936
+ // default. It is read by persona-comparison, so it affects output rather
937
+ // than only display.
938
+ //
939
+ // The stored value is left alone; someone may have meant it. The derived
940
+ // one is reported alongside, and a disagreement is named. (2026-08-01)
941
+ const fluency = mean(t("proceduralFluency"), t("transferLearning"), t("comprehension"));
942
+ const derivedTech = fluency >= 0.75 ? "expert" : fluency >= 0.5 ? "intermediate" : "beginner";
943
+ const declaredTech = persona.demographics?.tech_level;
944
+ techLevel = declaredTech ?? derivedTech;
945
+ techLevelSource = declaredTech ? "declared" : "derived";
946
+ if (declaredTech && declaredTech !== derivedTech) {
947
+ techLevelConflict =
948
+ `demographics.tech_level is "${declaredTech}", but this persona's fluency traits average ${Math.round(fluency * 100) / 100}, `
949
+ + `which reads as "${derivedTech}" (proceduralFluency ${t("proceduralFluency")}, transferLearning ${t("transferLearning")}, comprehension ${t("comprehension")}). `
950
+ + `The stored value is what persona-comparison uses.`;
788
951
  }
789
- else if (readingTendency > 0.8 && patience > 0.7) {
790
- decisionStyle = "deliberate";
952
+ // Attention pattern, derived the same way decisionStyle is.
953
+ //
954
+ // It was declared-or-default with no derivation at all, so a persona whose
955
+ // traits describe hard skimming could carry attentionPattern "thorough"
956
+ // and nothing anywhere would say the two disagreed -- and the declaration
957
+ // is what drives behaviour at runtime, so the contradiction changed how the
958
+ // persona read a page rather than merely how it was reported.
959
+ //
960
+ // Scored rather than branched, for the reason decisionStyle is: a branch
961
+ // chain answers only for combinations someone wrote a condition for, and
962
+ // everything else silently inherits the initial literal. Only the patterns
963
+ // the trait vector can actually speak to are scored. `sequential` is a
964
+ // navigation MODE (screen reader, tab order) and `z-pattern` is a property
965
+ // of sparse layouts rather than of a person, so neither is inferred from
966
+ // disposition -- they stay declarable and are never guessed. (2026-08-01)
967
+ const attnScores = [
968
+ // Reads everything, slowly: high reading, patient, refuses to settle.
969
+ ["thorough", mean(readingV, patienceV, 1 - satisficeV, t("comprehension"))],
970
+ // Big elements only: low reading, impatient, settles fast.
971
+ ["skim", mean(1 - readingV, 1 - patienceV, satisficeV)],
972
+ // Straight to the expected place: knows the conventions, forages by
973
+ // scent, does not need to read to locate.
974
+ ["targeted", mean(t("proceduralFluency"), t("informationForaging"), satisficeV, 1 - readingV)],
975
+ // Notices everything, follows what is interesting rather than a plan.
976
+ ["exploratory", mean(t("curiosity"), 1 - t("metacognitivePlanning"), t("informationForaging"))],
977
+ // The web-reading default: middling on the axes that separate the others.
978
+ // Scored rather than used as a fallback literal so it has to WIN to be
979
+ // chosen, which is what stops it becoming the silent catch-all the old
980
+ // default was.
981
+ ["f-pattern", mean(0.5 + (0.5 - Math.abs(readingV - 0.5)), 0.5 + (0.5 - Math.abs(patienceV - 0.5))) * 0.9],
982
+ ];
983
+ attnScores.sort((a, b) => b[1] - a[1]);
984
+ const derivedAttentionPattern = attnScores[0][0];
985
+ attentionPatternMargin = Math.round((attnScores[0][1] - attnScores[1][1]) * 100) / 100;
986
+ if (declaredAttentionPattern === undefined) {
987
+ attentionPattern = derivedAttentionPattern;
988
+ attentionPatternSource = "derived";
791
989
  }
792
- else if (persona.demographics.device === "mobile") {
793
- decisionStyle = "quick-tap";
990
+ else if (declaredAttentionPattern !== derivedAttentionPattern) {
991
+ // The declaration still WINS -- someone wrote it deliberately and it may
992
+ // encode something the traits cannot, like a screen reader's sequential
993
+ // pass. But a disagreement this size is reported rather than swallowed,
994
+ // because the runtime behaviour follows the declaration and the numbers
995
+ // say something else.
996
+ // A conflict is only as strong as the derivation behind it. When the
997
+ // top two derived patterns are themselves a near-tie, "the traits say X"
998
+ // overstates what the numbers support -- the honest claim is that the
999
+ // traits do not endorse the declaration, not that they pick a different
1000
+ // winner. Same rule the Maslow tie reporting follows.
1001
+ const decisive = (attentionPatternMargin ?? 0) > 0.05;
1002
+ attentionPatternConflict = decisive
1003
+ ? `declared "${declaredAttentionPattern}", but this persona's traits score "${derivedAttentionPattern}" highest by ${attentionPatternMargin} `
1004
+ + `(reading ${readingV}, patience ${patienceV}, satisficing ${satisficeV}). `
1005
+ + `The declaration is in force at runtime; the trait vector disagrees with it.`
1006
+ : `declared "${declaredAttentionPattern}", which is not what the traits favour — though they do not clearly favour anything either `
1007
+ + `("${derivedAttentionPattern}" leads by only ${attentionPatternMargin}). `
1008
+ + `Read this as the declaration being unsupported rather than contradicted.`;
794
1009
  }
795
1010
  }
796
1011
  if (persona.behaviors?.decisionStyle) {
797
1012
  decisionStyle = persona.behaviors.decisionStyle;
1013
+ decisionStyleSource = "declared";
798
1014
  }
799
1015
  // v16.14.1: Merge traits with defaults to ensure complete CognitiveTraits
800
1016
  // This handles both Persona (full traits) and AccessibilityPersona (partial traits)
@@ -826,13 +1042,44 @@ export function getCognitiveProfile(persona) {
826
1042
  emotionalContagion: 0.5,
827
1043
  fearOfMissingOut: 0.5,
828
1044
  socialProofSensitivity: 0.5,
829
- mentalModelRigidity: 0.5,
830
- siteFamiliarity: 0.5,
1045
+ mentalModelFlexibility: 0.5,
831
1046
  };
832
1047
  return {
833
1048
  traits: traits ? { ...defaultTraits, ...traits } : defaultTraits,
834
1049
  attentionPattern,
835
- decisionStyle,
1050
+ // NULL when suppressed, not omitted.
1051
+ //
1052
+ // Omitting the key left consumers unable to tell suppression from "never
1053
+ // computed", while decisionStyleSource still said "derived" -- claiming a
1054
+ // derivation for a value that was not there. An explicit null plus a
1055
+ // suppression flag and the threshold says what happened. (2026-08-02)
1056
+ decisionStyle: decisionStyleConfidence === "indistinguishable" ? null : decisionStyle,
1057
+ ...(decisionStyleConfidence === "indistinguishable"
1058
+ ? { decisionStyleSuppressed: true, decisionStyleThreshold: 0.05 }
1059
+ : {}),
1060
+ attentionPatternSource,
1061
+ attentionPatternMargin,
1062
+ attentionPatternConflict,
1063
+ techLevel,
1064
+ techLevelSource,
1065
+ techLevelConflict,
1066
+ // Reports SUPPRESSION rather than claiming a derivation whose result was
1067
+ // withheld. It said "derived" beside an absent decisionStyle.
1068
+ decisionStyleSource: decisionStyleConfidence === "indistinguishable" ? "suppressed" : decisionStyleSource,
1069
+ ...(decisionStyleMargin !== undefined ? { decisionStyleMargin } : {}),
1070
+ ...(decisionStyleConfidence ? { decisionStyleConfidence } : {}),
1071
+ // The LABEL is withheld at an indistinguishable margin, not just annotated.
1072
+ // Annotating it leaves the string in place for every consumer that reads
1073
+ // only `decisionStyle`, which is the failure this fixes. The candidate is
1074
+ // still available as decisionStyleRunnerUp plus the winner below, so
1075
+ // nothing is hidden -- it just stops being asserted.
1076
+ ...(decisionStyleConfidence === "indistinguishable"
1077
+ ? { decisionStyleCandidates: [scoresTop, decisionStyleRunnerUp].filter((x) => !!x) }
1078
+ : {}),
1079
+ ...(decisionStyleRunnerUp ? { decisionStyleRunnerUp } : {}),
1080
+ ...(decisionStyleConfidence === "indistinguishable"
1081
+ ? { decisionStyleNote: `Withheld: the top two styles differ by ${decisionStyleMargin}, which is inside the noise of the inputs. Runner-up was ${decisionStyleRunnerUp}. A label at this margin is a coin flip wearing a finding's clothes.` }
1082
+ : {}),
836
1083
  innerVoiceTemplate: persona.behaviors?.innerVoiceTemplate,
837
1084
  };
838
1085
  }
@@ -883,32 +1130,31 @@ export const BUILTIN_PERSONAS = {
883
1130
  },
884
1131
  cognitiveTraits: {
885
1132
  patience: 0.3, // Low - expects things to work
886
- riskTolerance: 0.9, // High - clicks confidently
887
- comprehension: 0.95, // Expert - knows all conventions
1133
+ riskTolerance: 0.8, // High - clicks confidently
1134
+ comprehension: 0.9, // Expert - knows all conventions
888
1135
  persistence: 0.4, // Low - switches approaches quickly
889
1136
  curiosity: 0.2, // Low - stays focused on goal
890
1137
  workingMemory: 0.9, // High - never repeats attempts
891
- readingTendency: 0.1, // Low - scans for shortcuts
892
- resilience: 0.85, // High - shrugs off errors quickly (BRS)
1138
+ readingTendency: 0.2, // Low - scans for shortcuts
1139
+ resilience: 0.8, // High - shrugs off errors quickly (BRS)
893
1140
  selfEfficacy: 0.9, // High - trusts own expertise (Bandura)
894
- satisficing: 0.3, // Low - knows quality, seeks optimal (Simon)
1141
+ satisficing: 0.4, // Low - knows quality, seeks optimal (Simon)
895
1142
  trustCalibration: 0.7, // High - trusts tech but verifies (Fogg)
896
- interruptRecovery: 0.85, // High - uses env cues, resumes fast (Mark)
1143
+ interruptRecovery: 0.9, // High - uses env cues, resumes fast (Mark)
897
1144
  // New traits (v15.0.0)
898
1145
  informationForaging: 0.9, // High - follows scent efficiently
899
- changeBlindness: 0.2, // Low - notices UI changes
1146
+ changeBlindness: 0.3, // Low - notices UI changes
900
1147
  anchoringBias: 0.3, // Low - compares options freely
901
1148
  timeHorizon: 0.3, // Long-term - invests in learning
902
1149
  attributionStyle: 0.2, // Blames system - "this is broken"
903
1150
  metacognitivePlanning: 0.8, // High - plans before acting
904
1151
  proceduralFluency: 0.9, // High - follows procedures easily
905
- transferLearning: 0.95, // High - applies knowledge across UIs
1152
+ transferLearning: 0.9, // High - applies knowledge across UIs
906
1153
  authoritySensitivity: 0.3, // Low - questions authority, verifies
907
- emotionalContagion: 0.2, // Low - mood-stable
1154
+ emotionalContagion: 0.3, // Low - mood-stable
908
1155
  fearOfMissingOut: 0.2, // Low - not swayed by urgency
909
1156
  socialProofSensitivity: 0.3, // Low - evaluates on merits
910
- mentalModelRigidity: 0.8, // High - adapts to new patterns
911
- siteFamiliarity: 0.9, // Very high - daily user, knows everything
1157
+ mentalModelFlexibility: 0.8, // High - adapts to new patterns
912
1158
  },
913
1159
  context: {
914
1160
  viewport: [1920, 1080],
@@ -962,33 +1208,32 @@ export const BUILTIN_PERSONAS = {
962
1208
  },
963
1209
  },
964
1210
  cognitiveTraits: {
965
- patience: 0.6, // Medium - willing to learn
1211
+ patience: 0.7, // Medium - willing to learn
966
1212
  riskTolerance: 0.3, // Low - hesitates before clicking
967
1213
  comprehension: 0.3, // Low - doesn't know conventions
968
1214
  persistence: 0.5, // Medium - tries a few times
969
- curiosity: 0.7, // High - explores the interface
1215
+ curiosity: 0.9, // High - explores the interface
970
1216
  workingMemory: 0.4, // Medium - might repeat mistakes
971
- readingTendency: 0.8, // High - reads tooltips and help
1217
+ readingTendency: 0.6, // High - reads tooltips and help
972
1218
  resilience: 0.4, // Low-medium - new users get discouraged (BRS)
973
1219
  selfEfficacy: 0.4, // Low - uncertain of abilities (Bandura)
974
- satisficing: 0.5, // Medium - unsure what's "good enough" (Simon)
1220
+ satisficing: 0.6, // Medium - unsure what's "good enough" (Simon)
975
1221
  trustCalibration: 0.4, // Low - skeptical, unsure what to trust (Fogg)
976
- interruptRecovery: 0.35, // Low - easily loses place, restarts (Mark)
1222
+ interruptRecovery: 0.4, // Low - easily loses place, restarts (Mark)
977
1223
  // New traits (v15.0.0)
978
- informationForaging: 0.3, // Low - exhaustive search, clicks everything
1224
+ informationForaging: 0.4, // Low - exhaustive search, clicks everything
979
1225
  changeBlindness: 0.6, // High - misses peripheral changes
980
1226
  anchoringBias: 0.7, // High - anchored to first information
981
1227
  timeHorizon: 0.7, // Short-term - wants quick results
982
1228
  attributionStyle: 0.7, // Self-blaming - "I must be doing it wrong"
983
1229
  metacognitivePlanning: 0.3, // Low - trial-and-error approach
984
- proceduralFluency: 0.35, // Low - struggles with multi-step flows
1230
+ proceduralFluency: 0.2, // Low - struggles with multi-step flows
985
1231
  transferLearning: 0.2, // Low - each interface feels new
986
- authoritySensitivity: 0.8, // High - trusts official-looking cues
987
- emotionalContagion: 0.7, // High - influenced by UI tone
1232
+ authoritySensitivity: 0.7, // High - trusts official-looking cues
1233
+ emotionalContagion: 0.6, // High - influenced by UI tone
988
1234
  fearOfMissingOut: 0.6, // Medium-high - susceptible to urgency
989
- socialProofSensitivity: 0.8, // High - relies heavily on reviews
990
- mentalModelRigidity: 0.3, // Low - struggles when patterns break
991
- siteFamiliarity: 0.0, // Zero - brand new, never visited before
1235
+ socialProofSensitivity: 0.7, // High - relies heavily on reviews
1236
+ mentalModelFlexibility: 0.3, // Low - struggles when patterns break
992
1237
  },
993
1238
  context: {
994
1239
  viewport: [1280, 800],
@@ -1036,33 +1281,32 @@ export const BUILTIN_PERSONAS = {
1036
1281
  },
1037
1282
  },
1038
1283
  cognitiveTraits: {
1039
- patience: 0.4, // Low - mobile = quick tasks
1284
+ patience: 0.3, // Low - mobile = quick tasks
1040
1285
  riskTolerance: 0.6, // Medium - taps somewhat freely
1041
1286
  comprehension: 0.6, // Medium - knows mobile patterns
1042
- persistence: 0.3, // Low - gives up if fiddly
1043
- curiosity: 0.3, // Low - wants to complete and go
1287
+ persistence: 0.4, // Low - gives up if fiddly
1288
+ curiosity: 0.4, // Low - wants to complete and go
1044
1289
  workingMemory: 0.5, // Medium
1045
- readingTendency: 0.3, // Low - minimal reading on mobile
1290
+ readingTendency: 0.2, // Low - minimal reading on mobile
1046
1291
  resilience: 0.5, // Medium - recovers if next try works (BRS)
1047
1292
  selfEfficacy: 0.6, // Medium - comfortable in mobile context (Bandura)
1048
1293
  satisficing: 0.8, // High - mobile context demands quick decisions (Simon)
1049
1294
  trustCalibration: 0.6, // Medium - quick decisions, moderate trust (Fogg)
1050
- interruptRecovery: 0.45, // Low-Med - constant phone interruptions (Mark)
1295
+ interruptRecovery: 0.5, // Low-Med - constant phone interruptions (Mark)
1051
1296
  // New traits (v15.0.0)
1052
1297
  informationForaging: 0.7, // High - efficient mobile navigation
1053
- changeBlindness: 0.5, // Medium - small screen limits peripheral
1298
+ changeBlindness: 0.6, // Medium - small screen limits peripheral
1054
1299
  anchoringBias: 0.5, // Medium
1055
1300
  timeHorizon: 0.8, // Very short-term - mobile is quick tasks
1056
1301
  attributionStyle: 0.4, // Medium - blames small screen/touch
1057
1302
  metacognitivePlanning: 0.4, // Low-medium - quick decisions
1058
1303
  proceduralFluency: 0.6, // Medium - knows mobile patterns
1059
- transferLearning: 0.7, // High - mobile UI conventions
1304
+ transferLearning: 0.6, // High - mobile UI conventions
1060
1305
  authoritySensitivity: 0.5, // Medium
1061
1306
  emotionalContagion: 0.5, // Medium
1062
- fearOfMissingOut: 0.7, // High - push notifications, urgency
1307
+ fearOfMissingOut: 0.6, // High - push notifications, urgency
1063
1308
  socialProofSensitivity: 0.6, // Medium-high - app store ratings
1064
- mentalModelRigidity: 0.6, // Medium - adapts to mobile patterns
1065
- siteFamiliarity: 0.4, // Low-medium - used desktop version before
1309
+ mentalModelFlexibility: 0.6, // Medium - adapts to mobile patterns
1066
1310
  },
1067
1311
  context: {
1068
1312
  viewport: [375, 812], // iPhone X dimensions
@@ -1110,33 +1354,41 @@ export const BUILTIN_PERSONAS = {
1110
1354
  },
1111
1355
  },
1112
1356
  cognitiveTraits: {
1113
- patience: 0.8, // High - used to slow navigation
1114
- riskTolerance: 0.5, // Medium - careful but experienced
1357
+ patience: 0.9, // High - used to slow navigation
1358
+ // 0.5 -> 0.2 (2026-08-02). Screen-reader navigation carries a real
1359
+ // disorientation cost: no visual context to recover from, high cognitive
1360
+ // load from remembering shortcuts and prior steps, and interface changes
1361
+ // that go unannounced. The documented behaviour is cautious exploration
1362
+ // to avoid that cost, which is the low end of this scale ('only clicks
1363
+ // obvious, labeled buttons') rather than the middle.
1364
+ // Sources: Information Wayfinding of Screen Reader Users, ASSETS 2024
1365
+ // (dl.acm.org/doi/fullHtml/10.1145/3663548.3688539); Context-Aware
1366
+ // Guidance for Screen Reader Users, CHI 2026 (10.1145/3772318.3790661).
1367
+ riskTolerance: 0.2, // Medium - careful but experienced
1115
1368
  comprehension: 0.8, // High - expert at a11y patterns
1116
1369
  persistence: 0.9, // High - determination required
1117
1370
  curiosity: 0.2, // Low - structured navigation
1118
- workingMemory: 0.9, // High - mental model essential
1119
- readingTendency: 1.0, // Full - ALL content is read aloud
1371
+ workingMemory: 0.7, // High - mental model essential
1372
+ readingTendency: 0.9, // Full - ALL content is read aloud
1120
1373
  resilience: 0.8, // High - experienced with setbacks (CD-RISC)
1121
- selfEfficacy: 0.5, // Medium - high in accessible sites, low otherwise (Bandura)
1374
+ selfEfficacy: 0.7, // Medium - high in accessible sites, low otherwise (Bandura)
1122
1375
  satisficing: 0.6, // Medium - relies on structured review content (Simon)
1123
- trustCalibration: 0.5, // Medium - cautious from past a11y failures (Fogg)
1124
- interruptRecovery: 0.75, // High - strong mental model aids recovery (Mark)
1376
+ trustCalibration: 0.6, // Medium - cautious from past a11y failures (Fogg)
1377
+ interruptRecovery: 0.6, // High - strong mental model aids recovery (Mark)
1125
1378
  // New traits (v15.0.0)
1126
- informationForaging: 0.7, // High - sequential but efficient with landmarks
1379
+ informationForaging: 0.6, // High - sequential but efficient with landmarks
1127
1380
  changeBlindness: 0.3, // Low - ARIA live regions announce changes
1128
1381
  anchoringBias: 0.4, // Medium - sequential exposure to info
1129
1382
  timeHorizon: 0.3, // Long-term - patient, thorough
1130
1383
  attributionStyle: 0.4, // Medium - blames inaccessible design
1131
- metacognitivePlanning: 0.85, // Very high - plans navigation carefully
1384
+ metacognitivePlanning: 0.7, // Very high - plans navigation carefully
1132
1385
  proceduralFluency: 0.8, // High - follows heading structure
1133
- transferLearning: 0.75, // High - knows a11y patterns across sites
1386
+ transferLearning: 0.7, // High - knows a11y patterns across sites
1134
1387
  authoritySensitivity: 0.5, // Medium - verifies with screen reader
1135
1388
  emotionalContagion: 0.3, // Low - focused on structure, not tone
1136
- fearOfMissingOut: 0.2, // Low - methodical, not impulsive
1389
+ fearOfMissingOut: 0.4, // Low - methodical, not impulsive
1137
1390
  socialProofSensitivity: 0.5, // Medium - reviews are accessible info
1138
- mentalModelRigidity: 0.6, // Medium - adapts but needs time
1139
- siteFamiliarity: 0.3, // Low - has visited but forgets layout
1391
+ mentalModelFlexibility: 0.6, // Medium - adapts but needs time
1140
1392
  },
1141
1393
  context: {
1142
1394
  viewport: [1280, 800],
@@ -1184,33 +1436,59 @@ export const BUILTIN_PERSONAS = {
1184
1436
  },
1185
1437
  },
1186
1438
  cognitiveTraits: {
1187
- patience: 0.9, // High - not in a rush
1188
- riskTolerance: 0.1, // Very low - afraid of mistakes
1439
+ patience: 0.8, // High - not in a rush
1440
+ riskTolerance: 0.2, // Very low - afraid of mistakes
1189
1441
  comprehension: 0.2, // Low - unfamiliar with modern UI
1190
- persistence: 0.7, // High - determined but confused
1191
- curiosity: 0.1, // Very low - just wants to finish
1192
- workingMemory: 0.3, // Low - may forget steps
1193
- readingTendency: 0.9, // High - reads everything carefully
1442
+ persistence: 0.6, // High - determined but confused
1443
+ curiosity: 0.4, // Very low - just wants to finish
1444
+ workingMemory: 0.4, // Low - may forget steps
1445
+ readingTendency: 0.8, // High - reads everything carefully
1194
1446
  resilience: 0.3, // Low - frustration lingers longer (BRS age-related)
1195
- selfEfficacy: 0.3, // Low - often blame themselves for tech issues (Bandura)
1196
- satisficing: 0.7, // High - values simplicity over optimization (Simon)
1447
+ selfEfficacy: 0.4, // Low - often blame themselves for tech issues (Bandura)
1448
+ satisficing: 0.6, // High - values simplicity over optimization (Simon)
1197
1449
  trustCalibration: 0.25, // Very low - skeptical of online requests (Fogg)
1198
- interruptRecovery: 0.3, // Low - difficulty resuming, forgets context (Mark)
1450
+ interruptRecovery: 0.4, // Low - difficulty resuming, forgets context (Mark)
1199
1451
  // New traits (v15.0.0)
1200
1452
  informationForaging: 0.25, // Very low - exhaustive, uncertain search
1201
- changeBlindness: 0.7, // High - misses UI updates and changes
1202
- anchoringBias: 0.8, // High - sticks to first information
1453
+ changeBlindness: 0.6, // High - misses UI updates and changes
1454
+ anchoringBias: 0.7, // High - sticks to first information
1203
1455
  timeHorizon: 0.4, // Medium - patient but unsure of value
1204
1456
  attributionStyle: 0.85, // Very high - blames self for all errors
1205
1457
  metacognitivePlanning: 0.25, // Low - doesn't know what to plan for
1206
- proceduralFluency: 0.3, // Low - struggles with multi-step processes
1458
+ // 0.5, corrected 2026-08-03 from 0.4, and it is a REASONED MIDPOINT rather
1459
+ // than an unfilled default -- the two forces genuinely cancel.
1460
+ //
1461
+ // The old value came from the doc's rationale, "slower development of
1462
+ // procedural skills with new interfaces". That describes ACQUISITION
1463
+ // RATE. This trait measures EXECUTION: its own scale runs from "struggles
1464
+ // with sequences, skips steps, confuses order" to "follows procedures
1465
+ // precisely and efficiently". Third instance of the same defect class on
1466
+ // these pages, after timeHorizon and attributionStyle -- a number set for
1467
+ // the construct the rationale describes rather than the one the trait
1468
+ // measures.
1469
+ //
1470
+ // It scored 0.4, IDENTICAL to cognitive-adhd, while carrying higher
1471
+ // working memory (0.40 vs 0.30) and four times the patience (0.80 vs
1472
+ // 0.20). The low end of this scale is impulsive error, and this persona's
1473
+ // own page says older users "make fewer impulsive errors".
1474
+ //
1475
+ // Precision is preserved or improved with age and efficiency is reduced
1476
+ // -- the speed-accuracy tradeoff shifts toward accuracy (Fisk et al.
1477
+ // 2009, Designing for Older Adults). Those net to the middle: the trait
1478
+ // reference's own `medium` band reads "follows most procedures with
1479
+ // occasional errors", which is the honest description.
1480
+ //
1481
+ // Stays BELOW motor-impairment-tremor (0.6), which is cognitively intact
1482
+ // with working memory 0.70 and metacognitive planning 0.70 against this
1483
+ // persona's 0.40 and 0.25. Stays ABOVE first-timer (0.2), which has no
1484
+ // procedural schema at all.
1485
+ proceduralFluency: 0.5,
1207
1486
  transferLearning: 0.2, // Low - each new site is confusing
1208
- authoritySensitivity: 0.85, // Very high - trusts official-looking things
1487
+ authoritySensitivity: 0.7, // Very high - trusts official-looking things
1209
1488
  emotionalContagion: 0.6, // Medium-high - affected by stern warnings
1210
1489
  fearOfMissingOut: 0.3, // Low - not driven by urgency, confused by it
1211
1490
  socialProofSensitivity: 0.7, // High - relies on grandchildren's recommendations
1212
- mentalModelRigidity: 0.2, // Very low - rigid, struggles with novel UIs
1213
- siteFamiliarity: 0.5, // Medium - knows the basics
1491
+ mentalModelFlexibility: 0.2, // Very low - rigid, struggles with novel UIs
1214
1492
  },
1215
1493
  context: {
1216
1494
  viewport: [1280, 800],
@@ -1265,37 +1543,154 @@ export const BUILTIN_PERSONAS = {
1265
1543
  },
1266
1544
  cognitiveTraits: {
1267
1545
  patience: 0.1, // Very low - abandons instantly
1268
- riskTolerance: 0.8, // High - clicks first thing
1269
- comprehension: 0.5, // Medium
1270
- persistence: 0.1, // Very low - one strike and out
1271
- curiosity: 0.1, // Very low - no time to explore
1546
+ riskTolerance: 0.6, // High - clicks first thing
1547
+ comprehension: 0.6, // Medium
1548
+ persistence: 0.2, // Very low - one strike and out
1549
+ curiosity: 0.3, // Very low - no time to explore
1272
1550
  workingMemory: 0.6, // Medium
1273
- readingTendency: 0.05, // Almost none - scanning only
1274
- resilience: 0.2, // Very low - no recovery, abandons (BRS)
1275
- selfEfficacy: 0.5, // Medium - impatient doesn't mean incapable (Bandura)
1551
+ readingTendency: 0.1, // Almost none - scanning only
1552
+ resilience: 0.3, // Very low - no recovery, abandons (BRS)
1553
+ selfEfficacy: 0.6, // Medium - impatient doesn't mean incapable (Bandura)
1276
1554
  satisficing: 0.9, // Very high - definition of satisficing (Simon)
1277
1555
  trustCalibration: 0.7, // High - clicks through without reading (Fogg)
1278
1556
  interruptRecovery: 0.2, // Very low - abandons rather than resumes (Mark)
1279
1557
  // New traits (v15.0.0)
1280
1558
  informationForaging: 0.9, // Very high - follows first scent, moves on
1281
- changeBlindness: 0.5, // Medium - focused on task
1559
+ changeBlindness: 0.7, // Medium - focused on task
1282
1560
  anchoringBias: 0.6, // Medium-high - goes with first option
1283
1561
  timeHorizon: 0.95, // Maximum short-term - no time for later
1284
1562
  attributionStyle: 0.3, // Low - blames slow site
1285
- metacognitivePlanning: 0.1, // Very low - no planning, just clicking
1286
- proceduralFluency: 0.5, // Medium - can follow if fast
1563
+ metacognitivePlanning: 0.3, // Very low - no planning, just clicking
1564
+ proceduralFluency: 0.6, // Medium - can follow if fast
1287
1565
  transferLearning: 0.7, // High - recognizes patterns quickly
1288
1566
  authoritySensitivity: 0.4, // Medium - only if it speeds things up
1289
1567
  emotionalContagion: 0.4, // Medium - focused on goal, not tone
1290
- fearOfMissingOut: 0.9, // Very high - urgency works on them
1291
- socialProofSensitivity: 0.5, // Medium - if it's quick to evaluate
1292
- mentalModelRigidity: 0.7, // High - adapts quickly, expects conventions
1293
- siteFamiliarity: 0.2, // Low - site structure feels different each visit
1568
+ fearOfMissingOut: 0.8, // Very high - urgency works on them
1569
+ socialProofSensitivity: 0.4, // Medium - if it's quick to evaluate
1570
+ mentalModelFlexibility: 0.6, // High - adapts quickly, expects conventions
1294
1571
  },
1295
1572
  context: {
1296
1573
  viewport: [1280, 800],
1297
1574
  },
1298
1575
  },
1576
+ // Four personas that had a values profile in the Schwartz registry and no
1577
+ // definition here, so nothing could be RUN as them: empathy_audit,
1578
+ // cognitive_journey and every other persona-driven tool rejected the name
1579
+ // while persona_values_lookup happily returned a full motivational profile.
1580
+ // The values were authored and coherent -- explorer at selfDirection 0.9 /
1581
+ // stimulation 0.9 / security 0.2 is a deliberate description, not a stub --
1582
+ // so the missing half is written here rather than the authored half deleted.
1583
+ // Traits are chosen to be consistent with each one's existing values.
1584
+ // (2026-07-31)
1585
+ "distracted-user": {
1586
+ name: "distracted-user",
1587
+ description: "Attention repeatedly pulled away mid-task; returns with context lost",
1588
+ demographics: { age_range: "18-55", tech_level: "intermediate", device: "desktop" },
1589
+ behaviors: { loses_place: true, task_switches: true, rereads: true },
1590
+ humanBehavior: {
1591
+ timing: {
1592
+ reactionTime: { min: 250, max: 1400 }, clickDelay: { min: 120, max: 500 },
1593
+ typeSpeed: { min: 25, max: 70 }, readingSpeed: 200,
1594
+ scrollPauseTime: { min: 200, max: 1500 },
1595
+ },
1596
+ errors: { misClickRate: 0.09, doubleClickAccidental: 0.04, typoRate: 0.08, backtrackRate: 0.35 },
1597
+ mouse: { curvature: 0.4, jitter: 8, overshoot: 0.15, speed: "normal" },
1598
+ attention: { pattern: "skim", scrollBehavior: "jump", focusAreas: ["header", "cta"], distractionRate: 0.85 },
1599
+ },
1600
+ cognitiveTraits: {
1601
+ patience: 0.4, riskTolerance: 0.5, comprehension: 0.5, persistence: 0.3,
1602
+ curiosity: 0.6, workingMemory: 0.25, readingTendency: 0.3, resilience: 0.4,
1603
+ selfEfficacy: 0.45, satisficing: 0.7, trustCalibration: 0.5,
1604
+ interruptRecovery: 0.1, // The defining trait: context is not recovered
1605
+ informationForaging: 0.6, changeBlindness: 0.8, anchoringBias: 0.6,
1606
+ timeHorizon: 0.7, attributionStyle: 0.4, metacognitivePlanning: 0.2,
1607
+ proceduralFluency: 0.5, transferLearning: 0.5, authoritySensitivity: 0.5,
1608
+ emotionalContagion: 0.5, fearOfMissingOut: 0.7, socialProofSensitivity: 0.5,
1609
+ mentalModelFlexibility: 0.5,
1610
+ },
1611
+ context: { viewport: [1280, 800] },
1612
+ },
1613
+ "careful-reader": {
1614
+ name: "careful-reader",
1615
+ description: "Reads before acting; verifies details and dislikes surprises",
1616
+ demographics: { age_range: "25-65", tech_level: "intermediate", device: "desktop" },
1617
+ behaviors: { reads_fully: true, verifies_before_submit: true, avoids_risk: true },
1618
+ humanBehavior: {
1619
+ timing: {
1620
+ reactionTime: { min: 600, max: 2500 }, clickDelay: { min: 300, max: 900 },
1621
+ typeSpeed: { min: 30, max: 80 }, readingSpeed: 130,
1622
+ scrollPauseTime: { min: 800, max: 3000 },
1623
+ },
1624
+ errors: { misClickRate: 0.02, doubleClickAccidental: 0.01, typoRate: 0.02, backtrackRate: 0.08 },
1625
+ mouse: { curvature: 0.2, jitter: 3, overshoot: 0.03, speed: "slow" },
1626
+ attention: { pattern: "thorough", scrollBehavior: "continuous", focusAreas: ["text", "prices"], distractionRate: 0.1 },
1627
+ },
1628
+ cognitiveTraits: {
1629
+ patience: 0.9, riskTolerance: 0.15, comprehension: 0.8, persistence: 0.8,
1630
+ curiosity: 0.5, workingMemory: 0.7, readingTendency: 0.95, resilience: 0.7,
1631
+ selfEfficacy: 0.6, satisficing: 0.1, trustCalibration: 0.25,
1632
+ interruptRecovery: 0.7, informationForaging: 0.4, changeBlindness: 0.2,
1633
+ anchoringBias: 0.3, timeHorizon: 0.3, attributionStyle: 0.6,
1634
+ metacognitivePlanning: 0.8, proceduralFluency: 0.6, transferLearning: 0.6,
1635
+ authoritySensitivity: 0.6, emotionalContagion: 0.3, fearOfMissingOut: 0.15,
1636
+ socialProofSensitivity: 0.4, mentalModelFlexibility: 0.4,
1637
+ },
1638
+ context: { viewport: [1280, 800] },
1639
+ },
1640
+ "explorer": {
1641
+ name: "explorer",
1642
+ description: "Wanders the interface by choice, opening things to see what they do",
1643
+ demographics: { age_range: "18-45", tech_level: "expert", device: "desktop" },
1644
+ behaviors: { wanders: true, tries_everything: true, ignores_happy_path: true },
1645
+ humanBehavior: {
1646
+ timing: {
1647
+ reactionTime: { min: 200, max: 900 }, clickDelay: { min: 100, max: 350 },
1648
+ typeSpeed: { min: 45, max: 110 }, readingSpeed: 250,
1649
+ scrollPauseTime: { min: 200, max: 1200 },
1650
+ },
1651
+ errors: { misClickRate: 0.05, doubleClickAccidental: 0.02, typoRate: 0.04, backtrackRate: 0.25 },
1652
+ mouse: { curvature: 0.5, jitter: 6, overshoot: 0.12, speed: "fast" },
1653
+ attention: { pattern: "z-pattern", scrollBehavior: "chunked", focusAreas: ["header", "images", "cta"], distractionRate: 0.5 },
1654
+ },
1655
+ cognitiveTraits: {
1656
+ patience: 0.7, riskTolerance: 0.9, comprehension: 0.75, persistence: 0.7,
1657
+ curiosity: 0.95, workingMemory: 0.65, readingTendency: 0.4, resilience: 0.8,
1658
+ selfEfficacy: 0.85, satisficing: 0.2, trustCalibration: 0.6,
1659
+ interruptRecovery: 0.7, informationForaging: 0.9, changeBlindness: 0.3,
1660
+ anchoringBias: 0.25, timeHorizon: 0.35, attributionStyle: 0.7,
1661
+ metacognitivePlanning: 0.5, proceduralFluency: 0.7, transferLearning: 0.85,
1662
+ authoritySensitivity: 0.2, emotionalContagion: 0.4, fearOfMissingOut: 0.4,
1663
+ socialProofSensitivity: 0.25, mentalModelFlexibility: 0.75,
1664
+ },
1665
+ context: { viewport: [1440, 900] },
1666
+ },
1667
+ "task-focused": {
1668
+ name: "task-focused",
1669
+ description: "Came to finish one thing; ignores everything that is not on the path",
1670
+ demographics: { age_range: "25-55", tech_level: "intermediate", device: "desktop" },
1671
+ behaviors: { goal_directed: true, ignores_promotions: true, resents_detours: true },
1672
+ humanBehavior: {
1673
+ timing: {
1674
+ reactionTime: { min: 200, max: 800 }, clickDelay: { min: 100, max: 300 },
1675
+ typeSpeed: { min: 40, max: 100 }, readingSpeed: 260,
1676
+ scrollPauseTime: { min: 150, max: 600 },
1677
+ },
1678
+ errors: { misClickRate: 0.04, doubleClickAccidental: 0.02, typoRate: 0.04, backtrackRate: 0.12 },
1679
+ mouse: { curvature: 0.2, jitter: 4, overshoot: 0.06, speed: "fast" },
1680
+ attention: { pattern: "f-pattern", scrollBehavior: "jump", focusAreas: ["cta", "text"], distractionRate: 0.15 },
1681
+ },
1682
+ cognitiveTraits: {
1683
+ patience: 0.6, riskTolerance: 0.5, comprehension: 0.7, persistence: 0.9,
1684
+ curiosity: 0.2, workingMemory: 0.7, readingTendency: 0.35, resilience: 0.75,
1685
+ selfEfficacy: 0.8, satisficing: 0.5, trustCalibration: 0.5,
1686
+ interruptRecovery: 0.8, informationForaging: 0.7, changeBlindness: 0.6,
1687
+ anchoringBias: 0.45, timeHorizon: 0.6, attributionStyle: 0.6,
1688
+ metacognitivePlanning: 0.75, proceduralFluency: 0.8, transferLearning: 0.7,
1689
+ authoritySensitivity: 0.4, emotionalContagion: 0.25, fearOfMissingOut: 0.3,
1690
+ socialProofSensitivity: 0.2, mentalModelFlexibility: 0.6,
1691
+ },
1692
+ context: { viewport: [1280, 800] },
1693
+ },
1299
1694
  };
1300
1695
  /**
1301
1696
  * Get a persona by name.
@@ -1329,7 +1724,7 @@ export function listCustomPersonas() {
1329
1724
  * Get the custom personas directory path.
1330
1725
  */
1331
1726
  export function getPersonasDir() {
1332
- return PERSONAS_DIR;
1727
+ return personasDirFor();
1333
1728
  }
1334
1729
  /**
1335
1730
  * Built-in accessibility personas for empathy testing.
@@ -1394,37 +1789,49 @@ export const ACCESSIBILITY_PERSONAS = {
1394
1789
  cognitiveTraits: {
1395
1790
  // Research: Motor impairment + self-efficacy (Lorig et al., 2001 - Chronic Disease Self-Management)
1396
1791
  // Adapted users maintain moderate efficacy despite physical challenges
1397
- selfEfficacy: 0.5,
1792
+ selfEfficacy: 0.6,
1398
1793
  // Research: Physical effort increases satisficing (Kool et al., 2010 - Effort discounting)
1399
1794
  // Motor strain leads to accepting "good enough" to minimize fatigue
1400
- satisficing: 0.7,
1401
- // Research: Chronic condition users develop moderate trust (Lorig 2001)
1402
- trustCalibration: 0.5,
1795
+ // High = first adequate option. Each additional comparison means another
1796
+ // set of pointing operations.
1797
+ satisficing: 0.75,
1798
+ trustCalibration: 0.6,
1403
1799
  // Research: Motor issues make resumption harder but adaptation helps (Mark)
1404
- interruptRecovery: 0.4,
1405
- // New traits (v15.0.0)
1406
- patience: 0.6,
1407
- riskTolerance: 0.4,
1800
+ // Re-acquiring a small target after an interruption repeats the hardest
1801
+ // part of the interaction.
1802
+ interruptRecovery: 0.6,
1803
+ // Every pointing action costs time and repeat attempts, so tolerance for
1804
+ // slow interaction is built in -- the persona's own research note records
1805
+ // 1-15Hz tremor and misclicks as the primary barrier.
1806
+ patience: 0.8,
1807
+ // Low = only clicks obvious, labelled controls. An accidental click is
1808
+ // expensive to undo when re-pointing is itself hard.
1809
+ riskTolerance: 0.3,
1408
1810
  comprehension: 0.7,
1811
+ // A missed target is retried rather than abandoned; the alternative is not
1812
+ // completing the task at all.
1409
1813
  persistence: 0.7,
1410
1814
  curiosity: 0.4,
1411
1815
  workingMemory: 0.7,
1412
1816
  readingTendency: 0.6,
1413
1817
  resilience: 0.6,
1414
- informationForaging: 0.5,
1818
+ // Low = exhaustive rather than scent-following. Exploratory clicking is
1819
+ // expensive, so paths are chosen carefully and few are sampled.
1820
+ informationForaging: 0.35,
1415
1821
  changeBlindness: 0.4,
1416
1822
  anchoringBias: 0.5,
1417
1823
  timeHorizon: 0.4, // Patient due to motor constraints
1418
1824
  attributionStyle: 0.4, // Blames physical constraints, not self
1419
- metacognitivePlanning: 0.7, // Plans to avoid difficult interactions
1825
+ // Target order and approach are planned to minimise pointing: keyboard
1826
+ // where possible, large targets first.
1827
+ metacognitivePlanning: 0.7,
1420
1828
  proceduralFluency: 0.6,
1421
1829
  transferLearning: 0.6,
1422
1830
  authoritySensitivity: 0.5,
1423
1831
  emotionalContagion: 0.4,
1424
1832
  fearOfMissingOut: 0.3, // Not swayed, focused on completing
1425
1833
  socialProofSensitivity: 0.5,
1426
- mentalModelRigidity: 0.5,
1427
- siteFamiliarity: 0.5, // Medium - regular user with motor challenges
1834
+ mentalModelFlexibility: 0.5,
1428
1835
  },
1429
1836
  },
1430
1837
  "low-vision-magnified": {
@@ -1475,6 +1882,17 @@ export const ACCESSIBILITY_PERSONAS = {
1475
1882
  viewport: [1280, 800], // But effectively sees 1/9th at a time
1476
1883
  },
1477
1884
  accessibilityTraits: {
1885
+ // Reading capacity, stated rather than inferred from the persona's NAME.
1886
+ // These are the same values getReadingProfile returned for this persona
1887
+ // via name matching, so behaviour is unchanged -- but they are now
1888
+ // editable, which they were not: no trait change could move WPM, fixation
1889
+ // span or phonological penalty. Source: Legge et al. (2007) -- central
1890
+ // scotoma and crowding. (2026-08-02)
1891
+ orthographicFluency: 0.6,
1892
+ phonologicalDecoding: 0.75,
1893
+ visualSpan: 3,
1894
+ vocabularyBreadth: 0.75,
1895
+ crowdingSensitivity: 0.7,
1478
1896
  visionLevel: 0.3,
1479
1897
  contrastSensitivity: 3.0,
1480
1898
  processingSpeed: 0.7,
@@ -1484,10 +1902,10 @@ export const ACCESSIBILITY_PERSONAS = {
1484
1902
  cognitiveTraits: {
1485
1903
  // Research: Visual impairment + self-efficacy (Brody et al., 2002 - AMD and depression)
1486
1904
  // Reduced vision correlates with lower confidence in task completion
1487
- selfEfficacy: 0.45,
1905
+ selfEfficacy: 0.6,
1488
1906
  // Research: Limited information access increases satisficing (Pachur & Hertwig, 2006)
1489
1907
  // Seeing only 1/9th of screen at a time → accept accessible options
1490
- satisficing: 0.65,
1908
+ satisficing: 0.4,
1491
1909
  // Research: Vision impairment increases caution (Brody 2002)
1492
1910
  trustCalibration: 0.4,
1493
1911
  // Research: Limited visible area makes context recovery harder (Mark)
@@ -1495,26 +1913,25 @@ export const ACCESSIBILITY_PERSONAS = {
1495
1913
  // New traits (v15.0.0)
1496
1914
  patience: 0.7,
1497
1915
  riskTolerance: 0.3,
1498
- comprehension: 0.6,
1499
- persistence: 0.7,
1916
+ comprehension: 0.7,
1917
+ persistence: 0.8,
1500
1918
  curiosity: 0.3,
1501
1919
  workingMemory: 0.6,
1502
- readingTendency: 0.8, // Reads carefully when visible
1503
- resilience: 0.5,
1920
+ readingTendency: 0.9, // Reads carefully when visible
1921
+ resilience: 0.7,
1504
1922
  informationForaging: 0.4, // Limited by zoom - systematic search
1505
1923
  changeBlindness: 0.8, // Very high - can't see peripheral
1506
1924
  anchoringBias: 0.6, // Anchored due to limited comparison
1507
1925
  timeHorizon: 0.4, // Patient, knows it takes time
1508
1926
  attributionStyle: 0.5, // Balanced - knows about limitations
1509
1927
  metacognitivePlanning: 0.7, // Plans navigation with zoom
1510
- proceduralFluency: 0.5, // Harder with limited view
1511
- transferLearning: 0.5, // Standard
1928
+ proceduralFluency: 0.6, // Harder with limited view
1929
+ transferLearning: 0.6, // Standard
1512
1930
  authoritySensitivity: 0.5,
1513
1931
  emotionalContagion: 0.4,
1514
- fearOfMissingOut: 0.3, // Not swayed - focused
1932
+ fearOfMissingOut: 0.4, // Not swayed - focused
1515
1933
  socialProofSensitivity: 0.5,
1516
- mentalModelRigidity: 0.4, // Adapts but needs time
1517
- siteFamiliarity: 0.3, // Low - magnification makes layout unfamiliar
1934
+ mentalModelFlexibility: 0.4, // Adapts but needs time
1518
1935
  },
1519
1936
  },
1520
1937
  "cognitive-adhd": {
@@ -1564,38 +1981,83 @@ export const ACCESSIBILITY_PERSONAS = {
1564
1981
  viewport: [1920, 1080],
1565
1982
  },
1566
1983
  accessibilityTraits: {
1984
+ // Reading capacity, stated rather than inferred from the persona's NAME.
1985
+ // These are the same values getReadingProfile returned for this persona
1986
+ // via name matching, so behaviour is unchanged -- but they are now
1987
+ // editable, which they were not: no trait change could move WPM, fixation
1988
+ // span or phonological penalty. Source: Dhar et al. (2010) -- intact
1989
+ // decoding, reduced sustained attention. (2026-08-02)
1990
+ orthographicFluency: 0.8,
1991
+ phonologicalDecoding: 0.75,
1992
+ visualSpan: 6,
1993
+ vocabularyBreadth: 0.7,
1994
+ crowdingSensitivity: 0.2,
1567
1995
  processingSpeed: 0.6, // Variable - can be fast when interested
1568
1996
  attentionSpan: 0.3, // Short sustained attention
1569
1997
  fatigueSusceptibility: 0.4,
1570
1998
  },
1999
+ // Reconciled against Persona-ADHD.md, the persona's own published trait
2000
+ // table, on 2026-08-02. Nineteen of twenty-five values disagreed. Each was
2001
+ // arbitrated against trait-reference.ts -- which names specific personas as
2002
+ // typically high or low per trait and defines what each end of the scale
2003
+ // MEANS -- rather than by preferring one file over the other.
2004
+ //
2005
+ // Fifteen adopted the doc's value; the doc was corrected on the other four,
2006
+ // which are recorded in Persona-ADHD.md. The four are worth naming because
2007
+ // they are all one failure: a number written against the plain-English
2008
+ // reading of a trait's NAME instead of its documented scale.
2009
+ //
2010
+ // timeHorizon doc said 0.3, meaning "long-term focused"
2011
+ // attributionStyle doc said 0.3, meaning "blames the system"
2012
+ // mentalModelFlexibility doc said 0.4, meaning "somewhat rigid"
2013
+ // trustCalibration doc said 0.5, meaning "neutral"
2014
+ //
2015
+ // In each case the doc's own RATIONALE argued for the opposite number. It
2016
+ // said "strong preference for immediate rewards" beside 0.3 on a scale
2017
+ // whose 1.0 end is literally "Immediate gratification, wants results NOW",
2018
+ // and "may blame self" beside 0.3 on a scale whose 1.0 end is "Blames
2019
+ // self". The prose was right every time; the digit was inverted.
1571
2020
  cognitiveTraits: {
1572
- workingMemory: 0.25, // Research: large magnitude impairment (d=1.63-2.03)
1573
- patience: 0.25, // Impulsivity: low tolerance for waiting
1574
- persistence: 0.2, // Low task persistence before switching
1575
- curiosity: 0.8, // High novelty-seeking
2021
+ workingMemory: 0.3, // Research: large magnitude impairment (d=1.63-2.03)
2022
+ patience: 0.2, // Impulsivity: low tolerance for waiting
2023
+ persistence: 0.3, // Low for unengaging tasks; can hyperfocus on interesting ones
2024
+ curiosity: 0.9, // High novelty-seeking
1576
2025
  riskTolerance: 0.7, // Impulsive clicking
1577
2026
  readingTendency: 0.2, // Skims rather than reads
1578
- resilience: 0.55, // Medium - recovers emotionally but not focus (BRS)
2027
+ resilience: 0.4, // Emotional dysregulation is common (doc; code had 0.55)
1579
2028
  selfEfficacy: 0.4, // Variable - can feel capable when engaged (Bandura)
1580
- satisficing: 0.85, // High - ADHD drives "good enough" decisions (Simon)
1581
- trustCalibration: 0.65, // Medium-high - impulsive clicks without reading (Fogg)
1582
- interruptRecovery: 0.2, // Very low - WM deficits make recovery difficult (Mark)
2029
+ satisficing: 0.7, // High - ADHD drives "good enough" decisions (Simon)
2030
+ // KEPT over the doc's 0.5: trait-reference lists cognitive-adhd under
2031
+ // typicalScores.high, and 1.0 on this scale is "highly trusting, accepts
2032
+ // claims at face value" -- which is what impulsive clicking without
2033
+ // reading looks like.
2034
+ trustCalibration: 0.65,
2035
+ interruptRecovery: 0.3, // Very low - WM deficits make recovery difficult (Mark)
1583
2036
  // New traits (v15.0.0)
1584
- comprehension: 0.5,
1585
- informationForaging: 0.6, // Follows interesting scents, not systematic
1586
- changeBlindness: 0.7, // High - focus elsewhere when changes happen
1587
- anchoringBias: 0.4, // Lower - novelty-seeking overrides anchoring
1588
- timeHorizon: 0.9, // Very short-term - immediate gratification
1589
- attributionStyle: 0.6, // Slightly self-blaming
1590
- metacognitivePlanning: 0.15, // Very low - impulsive, no planning
1591
- proceduralFluency: 0.3, // Low - struggles with sequences
1592
- transferLearning: 0.6, // Medium - recognizes patterns when focused
2037
+ comprehension: 0.6, // Capable when engaged; this is UI-convention grasp, not reading
2038
+ informationForaging: 0.4, // Distractible; goes down tangential paths (doc; code had 0.6)
2039
+ changeBlindness: 0.6, // High - focus elsewhere when changes happen
2040
+ anchoringBias: 0.6, // First option favored, decision-fatigue avoidance (doc; code had 0.4)
2041
+ // KEPT over the doc's 0.3. 1.0 on this scale is "Immediate gratification,
2042
+ // wants results NOW" -- the name reads like "how far ahead do you look",
2043
+ // and it means the opposite. trait-reference lists cognitive-adhd under
2044
+ // typicalScores.high alongside impatient-user and mobile-user.
2045
+ timeHorizon: 0.9,
2046
+ // KEPT over the doc's 0.3. 1.0 is "Blames self (internal attribution)",
2047
+ // and the doc's own rationale for 0.3 was "may blame self".
2048
+ attributionStyle: 0.6,
2049
+ metacognitivePlanning: 0.3, // Very low - impulsive, no planning
2050
+ proceduralFluency: 0.4, // Inconsistent; routines help but are hard to establish
2051
+ transferLearning: 0.5, // Variable; depends on engagement level
1593
2052
  authoritySensitivity: 0.4, // Low - impulsive, ignores authority
1594
2053
  emotionalContagion: 0.6, // Medium-high - emotional variability
1595
- fearOfMissingOut: 0.85, // Very high - ADHD correlates with FOMO
2054
+ fearOfMissingOut: 0.7, // High - ADHD correlates with FOMO
1596
2055
  socialProofSensitivity: 0.5, // Medium - if interesting
1597
- mentalModelRigidity: 0.7, // High - adapts easily, novelty-seeking
1598
- siteFamiliarity: 0.3, // Low - doesn't retain site structure well
2056
+ // KEPT over the doc's 0.4. 1.0 is "Highly adaptive - quickly forms new
2057
+ // mental models"; the doc's rationale was "flexible thinking, sometimes
2058
+ // too flexible". A fourth instance of the rigidity/flexibility inversion
2059
+ // that commit 086d519 fixed in three other pages.
2060
+ mentalModelFlexibility: 0.7,
1599
2061
  },
1600
2062
  },
1601
2063
  "dyslexic-user": {
@@ -1645,38 +2107,81 @@ export const ACCESSIBILITY_PERSONAS = {
1645
2107
  viewport: [1280, 800],
1646
2108
  },
1647
2109
  accessibilityTraits: {
2110
+ // Reading capacity, stated rather than inferred from the persona's NAME.
2111
+ // These are the same values getReadingProfile returned for this persona
2112
+ // via name matching, so behaviour is unchanged -- but they are now
2113
+ // editable, which they were not: no trait change could move WPM, fixation
2114
+ // span or phonological penalty. Source: Perry, Zorzi & Ziegler (2019),
2115
+ // Psychological Science. (2026-08-02)
2116
+ orthographicFluency: 0.4,
2117
+ phonologicalDecoding: 0.35,
2118
+ visualSpan: 4,
2119
+ vocabularyBreadth: 0.65,
2120
+ crowdingSensitivity: 0.55,
1648
2121
  processingSpeed: 0.5, // Slower text processing
1649
2122
  attentionSpan: 0.5,
1650
2123
  fatigueSusceptibility: 0.6, // Reading requires more effort
1651
2124
  },
1652
2125
  cognitiveTraits: {
1653
- workingMemory: 0.6, // WM typically unaffected
1654
- patience: 0.5,
2126
+ // WM deficit persists into adulthood across phonological AND visuospatial
2127
+ // modalities; a cognitive marker distinguishing dyslexic from typical
2128
+ // readers in every European orthography (de Jong 2006; n-back fNIRS,
2129
+ // PMC3496727). Was 0.6 -- above average -- which inverted one of the best-
2130
+ // established findings in the literature.
2131
+ workingMemory: 0.3,
2132
+ // Slower reading is one of three documented compensatory mechanisms,
2133
+ // alongside text-structure use and verbal ability (Annals of Dyslexia,
2134
+ // 10.1007/s11881-020-00195-w). Taking extra time with text is the
2135
+ // strategy, not a failure of tolerance.
2136
+ patience: 0.75,
1655
2137
  curiosity: 0.7,
1656
- readingTendency: 0.4, // Avoids heavy text, prefers visuals
1657
- resilience: 0.6, // Medium-high - adapted to text challenges (BRS)
1658
- selfEfficacy: 0.5, // Medium - effective with accommodations (Bandura)
1659
- satisficing: 0.6, // Medium - avoids text-heavy paths (Simon)
2138
+ // Relies on structure and skimming over sustained prose. Note this is a
2139
+ // STRATEGY, not decoding ability -- decoding now enters the chain via
2140
+ // readingCapacity, bridged from getReadingProfile.
2141
+ readingTendency: 0.35,
2142
+ // Pulled down from 0.6 for documented secondary anxiety (Kapur & Agrawal
2143
+ // 2026), but not lower: the compensation literature describes adults
2144
+ // achieving academic success, so learned helplessness would overstate it.
2145
+ resilience: 0.6,
2146
+ // Dyslexia is a strong predictor of lower academic self-efficacy in higher
2147
+ // education (PMC11488781; Nigerian undergraduate cohort).
2148
+ selfEfficacy: 0.3,
2149
+ // Reading is the expensive operation, so the first adequate option is
2150
+ // strongly preferred over an exhaustive comparison.
2151
+ satisficing: 0.7,
1660
2152
  trustCalibration: 0.5, // Medium - standard trust evaluation (Fogg)
1661
- interruptRecovery: 0.55, // Medium - visual cues help, text harder (Mark)
2153
+ // Follows from the WM deficit: resuming a task means reloading context
2154
+ // that is expensive to hold.
2155
+ interruptRecovery: 0.35,
1662
2156
  // New traits (v15.0.0)
1663
- riskTolerance: 0.5,
2157
+ riskTolerance: 0.4,
1664
2158
  comprehension: 0.5, // Comprehension fine for visuals
1665
- persistence: 0.6,
1666
- informationForaging: 0.5, // Standard - prefers visual cues
2159
+ // The same compensation literature: strategies are built and sustained
2160
+ // across years of schooling, and mastery experience feeds back into
2161
+ // persistence.
2162
+ persistence: 0.75,
2163
+ // Text-structure sensitivity is the second documented compensator:
2164
+ // headings and layout are used as scent rather than reading everything,
2165
+ // which is the high end of this scale.
2166
+ informationForaging: 0.65,
1667
2167
  changeBlindness: 0.4, // Lower - visual learner
1668
- anchoringBias: 0.5,
2168
+ anchoringBias: 0.6,
1669
2169
  timeHorizon: 0.5,
1670
- attributionStyle: 0.5,
1671
- metacognitivePlanning: 0.5,
1672
- proceduralFluency: 0.5, // Fine with visual instructions
2170
+ // High = blames self. Psychoeducation framing dyslexia as
2171
+ // neurodevelopmental rather than personal inadequacy measurably REDUCES
2172
+ // self-blame, which places the untreated baseline above neutral (Kapur &
2173
+ // Agrawal 2026).
2174
+ attributionStyle: 0.7,
2175
+ // Deliberate compensatory strategy use IS metacognition -- choosing
2176
+ // structure over linear prose is a planned approach, not a reflex.
2177
+ metacognitivePlanning: 0.7,
2178
+ proceduralFluency: 0.4,
1673
2179
  transferLearning: 0.6, // Recognizes visual patterns
1674
2180
  authoritySensitivity: 0.5,
1675
2181
  emotionalContagion: 0.5,
1676
- fearOfMissingOut: 0.5,
2182
+ fearOfMissingOut: 0.4,
1677
2183
  socialProofSensitivity: 0.6, // Relies on visual reviews
1678
- mentalModelRigidity: 0.6, // Good adaptation
1679
- siteFamiliarity: 0.5, // Medium - knows sites but reading is hard
2184
+ mentalModelFlexibility: 0.6, // Good adaptation
1680
2185
  },
1681
2186
  },
1682
2187
  "deaf-user": {
@@ -1729,33 +2234,32 @@ export const ACCESSIBILITY_PERSONAS = {
1729
2234
  attentionSpan: 0.8,
1730
2235
  },
1731
2236
  cognitiveTraits: {
1732
- patience: 0.7, // Accustomed to finding captions/text alternatives
1733
- riskTolerance: 0.5, // Standard caution
2237
+ patience: 0.6, // Accustomed to finding captions/text alternatives
2238
+ riskTolerance: 0.4, // Standard caution
1734
2239
  readingTendency: 0.9, // High - reads all text since can't hear audio
1735
- resilience: 0.75, // High - adapted to audio-free experience (BRS)
1736
- selfEfficacy: 0.6, // Medium-high - confident in visual navigation (Bandura)
1737
- satisficing: 0.55, // Medium - seeks captioned/text content (Simon)
1738
- trustCalibration: 0.55, // Medium - standard visual trust evaluation (Fogg)
1739
- interruptRecovery: 0.65, // Medium-high - visual cues assist recovery (Mark)
2240
+ resilience: 0.7, // High - adapted to audio-free experience (BRS)
2241
+ selfEfficacy: 0.7, // Medium-high - confident in visual navigation (Bandura)
2242
+ satisficing: 0.5, // Medium - seeks captioned/text content (Simon)
2243
+ trustCalibration: 0.6, // Medium - standard visual trust evaluation (Fogg)
2244
+ interruptRecovery: 0.7, // Medium-high - visual cues assist recovery (Mark)
1740
2245
  // New traits (v15.0.0)
1741
- comprehension: 0.7,
2246
+ comprehension: 0.8,
1742
2247
  persistence: 0.7,
1743
- curiosity: 0.5,
1744
- workingMemory: 0.7,
2248
+ curiosity: 0.6,
2249
+ workingMemory: 0.6,
1745
2250
  informationForaging: 0.6, // Good visual navigation
1746
- changeBlindness: 0.2, // Very low - visually attentive
2251
+ changeBlindness: 0.3, // Very low - visually attentive
1747
2252
  anchoringBias: 0.5,
1748
2253
  timeHorizon: 0.5,
1749
2254
  attributionStyle: 0.4, // Blames lack of captions
1750
- metacognitivePlanning: 0.6, // Plans for captioned content
2255
+ metacognitivePlanning: 0.7, // Plans for captioned content
1751
2256
  proceduralFluency: 0.7, // Good with visual instructions
1752
2257
  transferLearning: 0.7, // Recognizes visual patterns
1753
2258
  authoritySensitivity: 0.5,
1754
- emotionalContagion: 0.4, // Less affected by audio tone
2259
+ emotionalContagion: 0.6, // Less affected by audio tone
1755
2260
  fearOfMissingOut: 0.4,
1756
2261
  socialProofSensitivity: 0.6, // Visual reviews/ratings
1757
- mentalModelRigidity: 0.65, // Good visual adaptation
1758
- siteFamiliarity: 0.5, // Medium - visual learner, remembers layout
2262
+ mentalModelFlexibility: 0.65, // Good visual adaptation
1759
2263
  },
1760
2264
  },
1761
2265
  "elderly-low-vision": {
@@ -1806,6 +2310,21 @@ export const ACCESSIBILITY_PERSONAS = {
1806
2310
  viewport: [1280, 800],
1807
2311
  },
1808
2312
  accessibilityTraits: {
2313
+ // This persona is BOTH elderly and low-vision, and the name lookup
2314
+ // resolved it by accident: it tests 'low-vision' before 'elderly', so it
2315
+ // silently returned the low-vision profile and the aging half was never
2316
+ // applied. Rather than preserve an ordering accident, each dimension
2317
+ // takes the MORE impaired of the two profiles -- both conditions impair
2318
+ // reading -- except vocabulary, where age is an advantage: crystallized
2319
+ // vocabulary grows across the lifespan (Rayner et al. 2006), so the
2320
+ // elderly value of 0.85 is kept over low vision's 0.75.
2321
+ // orthographic 0.6 (lowVision) / phonological 0.65 (elderly) /
2322
+ // span 3 (lowVision) / vocabulary 0.85 (elderly) / crowding 0.7 (lowVision).
2323
+ orthographicFluency: 0.6,
2324
+ phonologicalDecoding: 0.65,
2325
+ visualSpan: 3,
2326
+ vocabularyBreadth: 0.85,
2327
+ crowdingSensitivity: 0.7,
1809
2328
  motorControl: 0.5,
1810
2329
  tremor: false, // Not necessarily present
1811
2330
  reachability: 0.6,
@@ -1816,33 +2335,32 @@ export const ACCESSIBILITY_PERSONAS = {
1816
2335
  fatigueSusceptibility: 0.7,
1817
2336
  },
1818
2337
  cognitiveTraits: {
1819
- workingMemory: 0.35, // Research: inhibitory control decline limits WM
1820
- patience: 0.7, // High: more methodical than young users
1821
- persistence: 0.6, // Determined but confused
2338
+ workingMemory: 0.3, // Research: inhibitory control decline limits WM
2339
+ patience: 0.9, // High: more methodical than young users
2340
+ persistence: 0.8, // Determined but confused
1822
2341
  curiosity: 0.3, // Low: prefers familiar patterns
1823
- riskTolerance: 0.15, // Very cautious with unfamiliar interfaces
2342
+ riskTolerance: 0.1, // Very cautious with unfamiliar interfaces
1824
2343
  comprehension: 0.3, // Unfamiliar with modern UI conventions
1825
- resilience: 0.25, // Low - frustration compounds with physical challenges (BRS)
1826
- selfEfficacy: 0.25, // Very low - often blame selves for tech issues (Bandura)
1827
- satisficing: 0.75, // High - avoids complex decision trees (Simon)
1828
- trustCalibration: 0.2, // Very low - very skeptical of online requests (Fogg)
1829
- interruptRecovery: 0.25, // Very low - WM + vision decline makes recovery hard (Mark)
2344
+ resilience: 0.6, // Low - frustration compounds with physical challenges (BRS)
2345
+ selfEfficacy: 0.3, // Very low - often blame selves for tech issues (Bandura)
2346
+ satisficing: 0.7, // High - avoids complex decision trees (Simon)
2347
+ trustCalibration: 0.4, // Very low - very skeptical of online requests (Fogg)
2348
+ interruptRecovery: 0.3, // Very low - WM + vision decline makes recovery hard (Mark)
1830
2349
  // New traits (v15.0.0)
1831
- readingTendency: 0.85,
1832
- informationForaging: 0.2, // Very low - exhaustive, uncertain
1833
- changeBlindness: 0.75, // High - vision issues
2350
+ readingTendency: 0.9,
2351
+ informationForaging: 0.4, // Very low - exhaustive, uncertain
2352
+ changeBlindness: 0.7, // High - vision issues
1834
2353
  anchoringBias: 0.8, // High - sticks to first info
1835
2354
  timeHorizon: 0.4, // Medium - patient but unsure
1836
2355
  attributionStyle: 0.9, // Very high - blames self
1837
2356
  metacognitivePlanning: 0.2, // Low - doesn't know what to plan
1838
- proceduralFluency: 0.25, // Low - struggles with sequences
1839
- transferLearning: 0.15, // Very low - each UI is confusing
1840
- authoritySensitivity: 0.85, // Very high - trusts official things
2357
+ proceduralFluency: 0.4, // Low - struggles with sequences
2358
+ transferLearning: 0.4, // Very low - each UI is confusing
2359
+ authoritySensitivity: 0.7, // Very high - trusts official things
1841
2360
  emotionalContagion: 0.6, // Medium-high
1842
- fearOfMissingOut: 0.25, // Low - not driven by urgency
2361
+ fearOfMissingOut: 0.2, // Low - not driven by urgency
1843
2362
  socialProofSensitivity: 0.7, // High - relies on family advice
1844
- mentalModelRigidity: 0.15, // Very low - rigid, struggles
1845
- siteFamiliarity: 0.2, // Low - forgets between visits
2363
+ mentalModelFlexibility: 0.15, // Very low - rigid, struggles
1846
2364
  },
1847
2365
  },
1848
2366
  "color-blind-deuteranopia": {
@@ -1900,31 +2418,46 @@ export const ACCESSIBILITY_PERSONAS = {
1900
2418
  cognitiveTraits: {
1901
2419
  patience: 0.6, // Accustomed to extra verification
1902
2420
  persistence: 0.7, // Used to working around color issues
1903
- riskTolerance: 0.5, // Cautious with color-only indicators
2421
+ // Low = only clicks the obvious. A control whose state is colour-coded is
2422
+ // ambiguous, so the unambiguous one is chosen.
2423
+ riskTolerance: 0.4,
1904
2424
  resilience: 0.7, // High - adapted to workarounds, recovers quickly (BRS)
1905
- selfEfficacy: 0.7, // High - adapted strategies work well (Bandura)
2425
+ // Deliberately ABOVE neutral. Deuteranopia is lifelong, common (about 8%
2426
+ // of men of Northern European descent) and compensated from childhood; it
2427
+ // is a perceptual difference, not a confidence deficit, and modelling it
2428
+ // as one would be wrong.
2429
+ selfEfficacy: 0.65,
1906
2430
  satisficing: 0.5, // Medium - needs extra verification (Simon)
1907
- trustCalibration: 0.55, // Medium - cautious with color-coded security indicators (Fogg)
2431
+ // Low = sceptical. Interfaces routinely signal validity and error through
2432
+ // colour alone, so a page's own status signals are not reliably readable
2433
+ // and get verified rather than believed.
2434
+ trustCalibration: 0.4,
1908
2435
  interruptRecovery: 0.6, // Medium-high - good adaptation, unaffected cognition (Mark)
1909
2436
  // New traits (v15.0.0)
1910
2437
  comprehension: 0.7,
1911
- curiosity: 0.5,
2438
+ curiosity: 0.6,
1912
2439
  workingMemory: 0.7,
1913
2440
  readingTendency: 0.7, // Relies on text labels
1914
- informationForaging: 0.6, // Good - unaffected cognition
1915
- changeBlindness: 0.45, // Medium - misses color-only changes
2441
+ // Scent-following depends on non-colour cues -- position, label, icon --
2442
+ // which is a learned and efficient strategy.
2443
+ informationForaging: 0.65,
2444
+ // High = misses changes. A state change signalled by colour alone -- a
2445
+ // green valid border, a red error border -- is invisible without a second
2446
+ // cue (MDPI 13/2/53; simulated-interface study).
2447
+ changeBlindness: 0.7,
1916
2448
  anchoringBias: 0.5,
1917
2449
  timeHorizon: 0.5,
1918
2450
  attributionStyle: 0.3, // Blames poor design, not self
1919
- metacognitivePlanning: 0.6, // Plans for color verification
2451
+ // Compensation is deliberate: read the label, look for the icon, re-check
2452
+ // the text rather than trusting the hue.
2453
+ metacognitivePlanning: 0.7,
1920
2454
  proceduralFluency: 0.7, // Good with non-color cues
1921
2455
  transferLearning: 0.7, // Recognizes non-color patterns
1922
2456
  authoritySensitivity: 0.5,
1923
2457
  emotionalContagion: 0.5,
1924
2458
  fearOfMissingOut: 0.4,
1925
2459
  socialProofSensitivity: 0.5,
1926
- mentalModelRigidity: 0.65, // Adapts with workarounds
1927
- siteFamiliarity: 0.6, // Medium-high - functional user
2460
+ mentalModelFlexibility: 0.65, // Adapts with workarounds
1928
2461
  },
1929
2462
  },
1930
2463
  // =========================================================================
@@ -2013,8 +2546,7 @@ export const ACCESSIBILITY_PERSONAS = {
2013
2546
  emotionalContagion: 0.3, // Low - less influenced by emotional UI tone
2014
2547
  fearOfMissingOut: 0.2, // Low - not driven by social urgency
2015
2548
  socialProofSensitivity: 0.25, // Low - evaluates independently
2016
- mentalModelRigidity: 0.2, // Very low (rigid) - needs consistent patterns
2017
- siteFamiliarity: 0.4, // Low-medium - remembers structure but needs consistency
2549
+ mentalModelFlexibility: 0.2, // Very low (rigid) - needs consistent patterns
2018
2550
  },
2019
2551
  },
2020
2552
  "intellectual-disability": {
@@ -2100,8 +2632,7 @@ export const ACCESSIBILITY_PERSONAS = {
2100
2632
  emotionalContagion: 0.7, // High - influenced by UI emotional tone
2101
2633
  fearOfMissingOut: 0.3, // Low - not driven by urgency
2102
2634
  socialProofSensitivity: 0.6, // Medium - influenced by social cues
2103
- mentalModelRigidity: 0.1, // Extremely rigid - needs consistency
2104
- siteFamiliarity: 0.15, // Very low - limited retention between visits
2635
+ mentalModelFlexibility: 0.1, // Extremely rigid - needs consistency
2105
2636
  },
2106
2637
  },
2107
2638
  "aphasia-receptive": {
@@ -2186,8 +2717,7 @@ export const ACCESSIBILITY_PERSONAS = {
2186
2717
  emotionalContagion: 0.5,
2187
2718
  fearOfMissingOut: 0.3,
2188
2719
  socialProofSensitivity: 0.4,
2189
- mentalModelRigidity: 0.4, // Medium - adapts visually, not linguistically
2190
- siteFamiliarity: 0.25, // Low - text-based nav makes sites feel unfamiliar each visit
2720
+ mentalModelFlexibility: 0.4, // Medium - adapts visually, not linguistically
2191
2721
  },
2192
2722
  },
2193
2723
  "dyscalculia": {
@@ -2249,32 +2779,54 @@ export const ACCESSIBILITY_PERSONAS = {
2249
2779
  fatigueSusceptibility: 0.4, // Moderate - number-heavy pages are tiring
2250
2780
  },
2251
2781
  cognitiveTraits: {
2252
- workingMemory: 0.5, // Normal verbal, impaired numerical
2782
+ // Working memory underpins number processing, and math anxiety reduces WM
2783
+ // capacity further -- the two compound (PMC8946289, numerical activities
2784
+ // of daily living).
2785
+ workingMemory: 0.35,
2253
2786
  patience: 0.5, // Medium - frustrated by numbers, fine otherwise
2254
2787
  persistence: 0.6, // Medium-high - persistent on non-numerical tasks
2255
2788
  curiosity: 0.6, // Normal - explores freely until numbers appear
2256
2789
  riskTolerance: 0.4, // Low-medium - cautious with purchases/quantities
2257
2790
  readingTendency: 0.65, // Medium-high - reads text normally, skips number-heavy sections
2258
2791
  comprehension: 0.65, // Normal for text, low for numerical content
2259
- resilience: 0.5, // Medium - used to numerical difficulty
2260
- selfEfficacy: 0.5, // Medium - confident except with numbers
2261
- satisficing: 0.6, // Medium - avoids comparing numerical options
2262
- trustCalibration: 0.5, // Medium - can evaluate text-based trust, not numerical
2792
+ // Math anxiety is documented as intense and self-reinforcing: errors raise
2793
+ // anxiety, which lowers WM, which produces more errors.
2794
+ resilience: 0.35,
2795
+ // Years of number-task failure produce intense anxiety around bills,
2796
+ // budgeting and any quantity judgement.
2797
+ selfEfficacy: 0.3,
2798
+ // High = takes the first adequate option. Comparison shopping means
2799
+ // comparing numbers, which is the expensive operation.
2800
+ satisficing: 0.75,
2801
+ // High = trusting. A displayed total cannot be sanity-checked mentally, so
2802
+ // it has to be taken on faith.
2803
+ trustCalibration: 0.65,
2263
2804
  interruptRecovery: 0.6, // Normal
2264
2805
  informationForaging: 0.55, // Medium - normal except for numerical scent
2265
2806
  changeBlindness: 0.5, // Normal
2266
- anchoringBias: 0.6, // Medium - numerical anchoring is especially strong
2267
- timeHorizon: 0.5, // Medium
2268
- attributionStyle: 0.5, // Medium - knows it's a specific difficulty
2269
- metacognitivePlanning: 0.6, // Normal - good planning for non-numerical tasks
2270
- proceduralFluency: 0.55, // Medium - struggles when steps involve numbers
2807
+ // High = the first number dominates. Unable to evaluate quantities
2808
+ // independently, the first price or figure seen becomes the reference.
2809
+ anchoringBias: 0.75,
2810
+ // High = present-focused. Difficulty judging time and distance is a core
2811
+ // symptom, so future consequences are hard to weigh against an immediate
2812
+ // task.
2813
+ timeHorizon: 0.75,
2814
+ // High = blames self. Numeric failure is culturally read as personal
2815
+ // deficiency rather than a processing difference, so self-attribution runs
2816
+ // high.
2817
+ attributionStyle: 0.7,
2818
+ // Adults describe deliberate workarounds: calculators, written steps,
2819
+ // avoiding mental arithmetic. That is planned strategy, not reflex.
2820
+ metacognitivePlanning: 0.65,
2821
+ // Multi-step numeric flows -- quantities, totals, percentages, dates --
2822
+ // are exactly where slow and error-prone processing bites.
2823
+ proceduralFluency: 0.4,
2271
2824
  transferLearning: 0.6, // Normal for non-numerical patterns
2272
2825
  authoritySensitivity: 0.5,
2273
2826
  emotionalContagion: 0.5,
2274
2827
  fearOfMissingOut: 0.5,
2275
2828
  socialProofSensitivity: 0.5,
2276
- mentalModelRigidity: 0.6, // Medium - adapts well to non-numerical patterns
2277
- siteFamiliarity: 0.5, // Medium - normal retention
2829
+ mentalModelFlexibility: 0.6, // Medium - adapts well to non-numerical patterns
2278
2830
  },
2279
2831
  },
2280
2832
  };
@@ -2350,33 +2902,32 @@ export const EMOTIONAL_PERSONAS = {
2350
2902
  },
2351
2903
  },
2352
2904
  cognitiveTraits: {
2353
- patience: 0.3, // Low - anxiety makes waiting feel worse
2354
- riskTolerance: 0.15, // Very low - avoids uncertain outcomes
2905
+ patience: 0.8, // Low - anxiety makes waiting feel worse
2906
+ riskTolerance: 0.2, // Very low - avoids uncertain outcomes
2355
2907
  comprehension: 0.6, // Medium - understands but worries
2356
- persistence: 0.4, // Low-medium - gives up when anxious
2908
+ persistence: 0.6, // Low-medium - gives up when anxious
2357
2909
  curiosity: 0.3, // Low - prefers safe known paths
2358
2910
  workingMemory: 0.5, // Medium - anxiety can impair WM
2359
- readingTendency: 0.8, // High - reads everything for reassurance
2360
- resilience: 0.2, // Very low - slow emotional recovery
2361
- selfEfficacy: 0.25, // Very low - doubts own abilities
2362
- satisficing: 0.4, // Low-medium - wants certainty
2363
- trustCalibration: 0.3, // Low - skeptical and worried
2364
- interruptRecovery: 0.3, // Low - anxiety compounds on interruption
2911
+ readingTendency: 0.9, // High - reads everything for reassurance
2912
+ resilience: 0.3, // Very low - slow emotional recovery
2913
+ selfEfficacy: 0.3, // Very low - doubts own abilities
2914
+ satisficing: 0.3, // Low-medium - wants certainty
2915
+ trustCalibration: 0.2, // Low - skeptical and worried
2916
+ interruptRecovery: 0.4, // Low - anxiety compounds on interruption
2365
2917
  // New traits (v15.0.0)
2366
2918
  informationForaging: 0.4, // Low - overthinks each path
2367
2919
  changeBlindness: 0.3, // Low - hypervigilant, notices changes
2368
2920
  anchoringBias: 0.7, // High - sticks to first safe option
2369
2921
  timeHorizon: 0.3, // Long-term - worries about consequences
2370
- attributionStyle: 0.8, // Very high - blames self for everything
2922
+ attributionStyle: 0.7, // Very high - blames self for everything
2371
2923
  metacognitivePlanning: 0.6, // Medium-high - plans to avoid anxiety
2372
2924
  proceduralFluency: 0.5, // Medium - follows carefully
2373
- transferLearning: 0.5, // Medium
2925
+ transferLearning: 0.4, // Medium
2374
2926
  authoritySensitivity: 0.7, // High - seeks reassurance from authority
2375
- emotionalContagion: 0.8, // Very high - absorbs UI emotional tone
2927
+ emotionalContagion: 0.7, // Very high - absorbs UI emotional tone
2376
2928
  fearOfMissingOut: 0.6, // Medium-high - anxiety about missing out
2377
- socialProofSensitivity: 0.7, // High - seeks validation from others
2378
- mentalModelRigidity: 0.4, // Low-medium - struggles with unexpected
2379
- siteFamiliarity: 0.5, // Medium
2929
+ socialProofSensitivity: 0.8, // High - seeks validation from others
2930
+ mentalModelFlexibility: 0.4, // Low-medium - struggles with unexpected
2380
2931
  },
2381
2932
  context: {
2382
2933
  viewport: [1280, 800],
@@ -2428,33 +2979,32 @@ export const EMOTIONAL_PERSONAS = {
2428
2979
  },
2429
2980
  },
2430
2981
  cognitiveTraits: {
2431
- patience: 0.5, // Medium - not impatient but efficient
2982
+ patience: 0.3, // Medium - not impatient but efficient
2432
2983
  riskTolerance: 0.8, // High - willing to try things
2433
2984
  comprehension: 0.7, // High - understands quickly
2434
- persistence: 0.8, // High - doesn't give up easily
2985
+ persistence: 0.7, // High - doesn't give up easily
2435
2986
  curiosity: 0.6, // Medium-high - willing to explore
2436
2987
  workingMemory: 0.7, // High - focused and clear
2437
- readingTendency: 0.3, // Low - scans efficiently
2438
- resilience: 0.9, // Very high - bounces back immediately
2988
+ readingTendency: 0.2, // Low - scans efficiently
2989
+ resilience: 0.8, // Very high - bounces back immediately
2439
2990
  selfEfficacy: 0.9, // Very high - trusts own abilities
2440
- satisficing: 0.5, // Medium - balances speed and quality
2991
+ satisficing: 0.7, // Medium - balances speed and quality
2441
2992
  trustCalibration: 0.7, // High - appropriate trust
2442
- interruptRecovery: 0.8, // High - resumes easily
2993
+ interruptRecovery: 0.7, // High - resumes easily
2443
2994
  // New traits (v15.0.0)
2444
2995
  informationForaging: 0.8, // High - efficient scent-following
2445
- changeBlindness: 0.3, // Low - notices changes
2996
+ changeBlindness: 0.6, // Low - notices changes
2446
2997
  anchoringBias: 0.3, // Low - evaluates options freely
2447
2998
  timeHorizon: 0.4, // Medium-long - thinks ahead
2448
2999
  attributionStyle: 0.2, // Low - blames system issues
2449
- metacognitivePlanning: 0.7, // High - plans efficiently
2450
- proceduralFluency: 0.8, // High - follows procedures well
2451
- transferLearning: 0.85, // High - applies knowledge
2452
- authoritySensitivity: 0.4, // Low-medium - independent judgment
2453
- emotionalContagion: 0.2, // Low - emotionally stable
3000
+ metacognitivePlanning: 0.4, // High - plans efficiently
3001
+ proceduralFluency: 0.7, // High - follows procedures well
3002
+ transferLearning: 0.8, // High - applies knowledge
3003
+ authoritySensitivity: 0.3, // Low-medium - independent judgment
3004
+ emotionalContagion: 0.3, // Low - emotionally stable
2454
3005
  fearOfMissingOut: 0.3, // Low - not swayed by urgency
2455
- socialProofSensitivity: 0.4, // Low-medium - evaluates on merits
2456
- mentalModelRigidity: 0.8, // High - adapts easily
2457
- siteFamiliarity: 0.5, // Medium
3006
+ socialProofSensitivity: 0.3, // Low-medium - evaluates on merits
3007
+ mentalModelFlexibility: 0.8, // High - adapts easily
2458
3008
  },
2459
3009
  context: {
2460
3010
  viewport: [1920, 1080],
@@ -2511,28 +3061,27 @@ export const EMOTIONAL_PERSONAS = {
2511
3061
  comprehension: 0.5, // Medium
2512
3062
  persistence: 0.4, // Low-medium - gives up when frustrated
2513
3063
  curiosity: 0.6, // Medium-high - excitement drives exploration
2514
- workingMemory: 0.45, // Medium - emotions can interfere
2515
- readingTendency: 0.4, // Medium
2516
- resilience: 0.15, // Very low - emotions linger long
3064
+ workingMemory: 0.5, // Medium - emotions can interfere
3065
+ readingTendency: 0.6, // Medium
3066
+ resilience: 0.4, // Very low - emotions linger long
2517
3067
  selfEfficacy: 0.5, // Medium - mood dependent
2518
3068
  satisficing: 0.6, // Medium-high - wants quick resolution
2519
3069
  trustCalibration: 0.5, // Medium - emotional judgment
2520
- interruptRecovery: 0.35, // Low - emotional state persists
3070
+ interruptRecovery: 0.4, // Low - emotional state persists
2521
3071
  // New traits (v15.0.0)
2522
3072
  informationForaging: 0.5, // Medium - mood dependent
2523
3073
  changeBlindness: 0.5, // Medium - emotions distract
2524
- anchoringBias: 0.6, // Medium-high - first impressions stick
3074
+ anchoringBias: 0.7, // Medium-high - first impressions stick
2525
3075
  timeHorizon: 0.7, // Short-term - emotional decisions
2526
3076
  attributionStyle: 0.5, // Medium - variable based on mood
2527
- metacognitivePlanning: 0.3, // Low - emotional impulsivity
2528
- proceduralFluency: 0.45, // Medium - emotions interfere
3077
+ metacognitivePlanning: 0.4, // Low - emotional impulsivity
3078
+ proceduralFluency: 0.5, // Medium - emotions interfere
2529
3079
  transferLearning: 0.5, // Medium
2530
3080
  authoritySensitivity: 0.5, // Medium - mood dependent
2531
3081
  emotionalContagion: 0.9, // Maximum - highly influenced by UI tone
2532
- fearOfMissingOut: 0.75, // High - emotional FOMO
2533
- socialProofSensitivity: 0.7, // High - seeks emotional validation
2534
- mentalModelRigidity: 0.5, // Medium
2535
- siteFamiliarity: 0.5, // Medium
3082
+ fearOfMissingOut: 0.7, // High - emotional FOMO
3083
+ socialProofSensitivity: 0.8, // High - seeks emotional validation
3084
+ mentalModelFlexibility: 0.5, // Medium
2536
3085
  },
2537
3086
  context: {
2538
3087
  viewport: [1280, 800],
@@ -2584,33 +3133,32 @@ export const EMOTIONAL_PERSONAS = {
2584
3133
  },
2585
3134
  },
2586
3135
  cognitiveTraits: {
2587
- patience: 0.9, // Very high - doesn't rush
3136
+ patience: 0.7, // Very high - doesn't rush
2588
3137
  riskTolerance: 0.5, // Medium - balanced judgment
2589
3138
  comprehension: 0.7, // High - clear thinking
2590
- persistence: 0.9, // Very high - never gives up
3139
+ persistence: 0.7, // Very high - never gives up
2591
3140
  curiosity: 0.4, // Medium - methodical exploration
2592
- workingMemory: 0.8, // High - unaffected by emotion
2593
- readingTendency: 0.6, // Medium-high - thorough
2594
- resilience: 0.95, // Maximum - emotions barely register
2595
- selfEfficacy: 0.7, // High - quiet confidence
3141
+ workingMemory: 0.7, // High - unaffected by emotion
3142
+ readingTendency: 0.7, // Medium-high - thorough
3143
+ resilience: 0.8, // Maximum - emotions barely register
3144
+ selfEfficacy: 0.6, // High - quiet confidence
2596
3145
  satisficing: 0.4, // Low - willing to be thorough
2597
- trustCalibration: 0.6, // Medium-high - rational evaluation
2598
- interruptRecovery: 0.8, // High - returns to task easily
3146
+ trustCalibration: 0.7, // Medium-high - rational evaluation
3147
+ interruptRecovery: 0.7, // High - returns to task easily
2599
3148
  // New traits (v15.0.0)
2600
- informationForaging: 0.6, // Medium-high - methodical
2601
- changeBlindness: 0.3, // Low - attentive, focused
3149
+ informationForaging: 0.8, // Medium-high - methodical
3150
+ changeBlindness: 0.4, // Low - attentive, focused
2602
3151
  anchoringBias: 0.3, // Low - evaluates all options
2603
3152
  timeHorizon: 0.2, // Very long-term - patient deliberation
2604
3153
  attributionStyle: 0.4, // Medium - balanced evaluation
2605
- metacognitivePlanning: 0.8, // High - plans carefully
2606
- proceduralFluency: 0.8, // High - follows procedures precisely
3154
+ metacognitivePlanning: 0.7, // High - plans carefully
3155
+ proceduralFluency: 0.6, // High - follows procedures precisely
2607
3156
  transferLearning: 0.7, // High - applies experience
2608
3157
  authoritySensitivity: 0.4, // Low-medium - independent judgment
2609
- emotionalContagion: 0.1, // Minimum - emotionally unaffected
2610
- fearOfMissingOut: 0.1, // Minimum - not driven by urgency
2611
- socialProofSensitivity: 0.3, // Low - evaluates independently
2612
- mentalModelRigidity: 0.7, // High - adapts methodically
2613
- siteFamiliarity: 0.5, // Medium
3158
+ emotionalContagion: 0.2, // Minimum - emotionally unaffected
3159
+ fearOfMissingOut: 0.2, // Minimum - not driven by urgency
3160
+ socialProofSensitivity: 0.4, // Low - evaluates independently
3161
+ mentalModelFlexibility: 0.7, // High - adapts methodically
2614
3162
  },
2615
3163
  context: {
2616
3164
  viewport: [1280, 800],
@@ -2646,6 +3194,30 @@ export function listEmotionalPersonas() {
2646
3194
  * @param name Persona name to look up
2647
3195
  * @returns Persona object or undefined if not found
2648
3196
  */
3197
+ /**
3198
+ * Resolve a persona name tolerantly: case and punctuation do not have to match.
3199
+ *
3200
+ * Resolution was exact-match only, so "POWER-USER" and "power user" both missed
3201
+ * a persona that plainly exists. Before unknown names started throwing, a miss
3202
+ * silently fabricated an all-midpoint persona named after whatever string was
3203
+ * passed -- so a caller who capitalised a name got clean, plausible, invented
3204
+ * data. Now it throws, which is better but still refuses a request anyone would
3205
+ * call valid.
3206
+ *
3207
+ * Exact match is tried FIRST and always wins, so this can only ever add
3208
+ * resolutions, never redirect one that already worked. (2026-08-02)
3209
+ */
3210
+ function resolveLoosely(name) {
3211
+ const norm = (v) => v.toLowerCase().replace(/[^a-z0-9]/g, "");
3212
+ const target = norm(name);
3213
+ if (!target)
3214
+ return undefined;
3215
+ for (const key of listAllPersonas()) {
3216
+ if (norm(key) === target)
3217
+ return key;
3218
+ }
3219
+ return undefined;
3220
+ }
2649
3221
  export function getAnyPersona(name) {
2650
3222
  // 1. Check runtime-registered personas first (Enterprise extensions)
2651
3223
  if (RUNTIME_PERSONAS[name]) {
@@ -2672,6 +3244,11 @@ export function getAnyPersona(name) {
2672
3244
  if (AGENT_PERSONAS[name]) {
2673
3245
  return AGENT_PERSONAS[name];
2674
3246
  }
3247
+ // Exact match failed. Try again ignoring case and punctuation -- only after
3248
+ // every exact lookup above has missed, so this can never shadow a real key.
3249
+ const loose = resolveLoosely(name);
3250
+ if (loose && loose !== name)
3251
+ return getAnyPersona(loose);
2675
3252
  return undefined;
2676
3253
  }
2677
3254
  /**
@@ -2679,6 +3256,90 @@ export function getAnyPersona(name) {
2679
3256
  *
2680
3257
  * @returns Combined list of all available persona names
2681
3258
  */
3259
+ /**
3260
+ * Raised when a persona name resolves to nothing.
3261
+ *
3262
+ * A distinct class so callers can tell "you asked for someone who does not
3263
+ * exist" apart from "the page would not load", and so the message can carry
3264
+ * suggestions instead of a bare failure.
3265
+ */
3266
+ export class UnknownPersonaError extends Error {
3267
+ requested;
3268
+ suggestions;
3269
+ code = "unknown_persona";
3270
+ constructor(requested, suggestions) {
3271
+ super(`Unknown persona "${requested}".` +
3272
+ (suggestions.length ? ` Did you mean: ${suggestions.join(", ")}?` : "") +
3273
+ ` Run list_cognitive_personas for the full roster. Nothing was measured — a persona that does not exist cannot be simulated.`);
3274
+ this.requested = requested;
3275
+ this.suggestions = suggestions;
3276
+ this.name = "UnknownPersonaError";
3277
+ }
3278
+ }
3279
+ /** Closest roster names to a miss, for the error message. */
3280
+ export function suggestPersonaNames(requested, limit = 3) {
3281
+ const roster = listAllPersonas();
3282
+ const q = requested.toLowerCase().replace(/[^a-z0-9]/g, "");
3283
+ if (!q)
3284
+ return roster.slice(0, limit);
3285
+ const scored = roster.map((name) => {
3286
+ const n = name.toLowerCase().replace(/[^a-z0-9]/g, "");
3287
+ // Exact-after-normalising ranks first: it catches the case and punctuation
3288
+ // misses ("POWER-USER", "power user") that are the commonest way to miss.
3289
+ if (n === q)
3290
+ return { name, score: 0 };
3291
+ if (n.startsWith(q) || q.startsWith(n))
3292
+ return { name, score: 1 };
3293
+ if (n.includes(q) || q.includes(n))
3294
+ return { name, score: 2 };
3295
+ // Cheap edit distance, bounded — the roster is small enough that an exact
3296
+ // Levenshtein is affordable and a typo is the other common miss.
3297
+ const d = (a, b) => {
3298
+ const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
3299
+ for (let i = 1; i <= a.length; i++) {
3300
+ let last = prev[0];
3301
+ prev[0] = i;
3302
+ for (let j = 1; j <= b.length; j++) {
3303
+ const tmp = prev[j];
3304
+ prev[j] = Math.min(prev[j] + 1, prev[j - 1] + 1, last + (a[i - 1] === b[j - 1] ? 0 : 1));
3305
+ last = tmp;
3306
+ }
3307
+ }
3308
+ return prev[b.length];
3309
+ };
3310
+ return { name, score: 3 + d(q, n) };
3311
+ });
3312
+ return scored.sort((a, b) => a.score - b.score).filter((x) => x.score < 3 + Math.max(4, q.length * 0.5))
3313
+ .slice(0, limit).map((x) => x.name);
3314
+ }
3315
+ /**
3316
+ * Resolve a persona name, or refuse.
3317
+ *
3318
+ * Every persona-accepting tool used to fall back to
3319
+ * `createCognitivePersona(name, name, {})` on a miss, which builds a complete
3320
+ * persona with every trait at its 0.5 default and names it after whatever
3321
+ * string was passed. The result was a full six-layer breakdown, an abandonment
3322
+ * risk, a rendered motor overlay and an interpretation sentence quoting the
3323
+ * typo as if it were a subject — in range, plausible, and indistinguishable
3324
+ * from a real measurement at analysis time.
3325
+ *
3326
+ * That is silent data corruption: it does not fail, it lies quietly, and a
3327
+ * typo inside a batch run enters a results table having passed eyeballing and
3328
+ * range checks. So a miss now throws.
3329
+ *
3330
+ * `syntheticTraits` is the one legitimate synthesis: a caller defining an
3331
+ * ad-hoc persona by supplying its traits outright. Supplying NO traits and no
3332
+ * known name is not defining a persona, it is a typo. (2026-08-02)
3333
+ */
3334
+ export function resolvePersonaOrThrow(name, syntheticTraits) {
3335
+ const found = getAnyPersona(name);
3336
+ if (found)
3337
+ return found;
3338
+ if (syntheticTraits && Object.keys(syntheticTraits).length > 0) {
3339
+ return createCognitivePersona(name, name, syntheticTraits);
3340
+ }
3341
+ throw new UnknownPersonaError(name, suggestPersonaNames(name));
3342
+ }
2682
3343
  export function listAllPersonas() {
2683
3344
  const runtimeNames = Object.keys(RUNTIME_PERSONAS);
2684
3345
  const builtinNames = Object.keys(BUILTIN_PERSONAS);