@redocly/recheck 0.1.0 → 0.3.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 (595) hide show
  1. package/README.md +1023 -56
  2. package/dist/cli.js +40 -7
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/markdoc-schema.d.ts +17 -0
  5. package/dist/commands/markdoc-schema.d.ts.map +1 -0
  6. package/dist/commands/markdoc-schema.js +127 -0
  7. package/dist/commands/markdoc-schema.js.map +1 -0
  8. package/dist/commands/run.d.ts +2 -1
  9. package/dist/commands/run.d.ts.map +1 -1
  10. package/dist/commands/run.js +87 -10
  11. package/dist/commands/run.js.map +1 -1
  12. package/dist/config/load.d.ts +11 -0
  13. package/dist/config/load.d.ts.map +1 -1
  14. package/dist/config/load.js +12 -2
  15. package/dist/config/load.js.map +1 -1
  16. package/dist/config/presets/google.d.ts +3 -0
  17. package/dist/config/presets/google.d.ts.map +1 -0
  18. package/dist/config/presets/google.js +1671 -0
  19. package/dist/config/presets/google.js.map +1 -0
  20. package/dist/config/presets/inclusive-language.d.ts +3 -0
  21. package/dist/config/presets/inclusive-language.d.ts.map +1 -0
  22. package/dist/config/presets/inclusive-language.js +321 -0
  23. package/dist/config/presets/inclusive-language.js.map +1 -0
  24. package/dist/config/presets/index.d.ts +67 -0
  25. package/dist/config/presets/index.d.ts.map +1 -0
  26. package/dist/config/presets/index.js +137 -0
  27. package/dist/config/presets/index.js.map +1 -0
  28. package/dist/config/presets/markdoc.d.ts +21 -0
  29. package/dist/config/presets/markdoc.d.ts.map +1 -0
  30. package/dist/config/presets/markdoc.js +101 -0
  31. package/dist/config/presets/markdoc.js.map +1 -0
  32. package/dist/config/presets/markdown-relaxed.d.ts +3 -0
  33. package/dist/config/presets/markdown-relaxed.d.ts.map +1 -0
  34. package/dist/config/presets/markdown-relaxed.js +98 -0
  35. package/dist/config/presets/markdown-relaxed.js.map +1 -0
  36. package/dist/config/presets/markdown.d.ts +43 -0
  37. package/dist/config/presets/markdown.d.ts.map +1 -0
  38. package/dist/config/presets/markdown.js +132 -0
  39. package/dist/config/presets/markdown.js.map +1 -0
  40. package/dist/config/presets/microsoft.d.ts +3 -0
  41. package/dist/config/presets/microsoft.d.ts.map +1 -0
  42. package/dist/config/presets/microsoft.js +2268 -0
  43. package/dist/config/presets/microsoft.js.map +1 -0
  44. package/dist/config/presets/minimal.d.ts +3 -0
  45. package/dist/config/presets/minimal.d.ts.map +1 -0
  46. package/dist/config/presets/minimal.js +21 -0
  47. package/dist/config/presets/minimal.js.map +1 -0
  48. package/dist/config/presets/plain-language.d.ts +3 -0
  49. package/dist/config/presets/plain-language.d.ts.map +1 -0
  50. package/dist/config/presets/plain-language.js +351 -0
  51. package/dist/config/presets/plain-language.js.map +1 -0
  52. package/dist/config/presets/prose.d.ts +52 -0
  53. package/dist/config/presets/prose.d.ts.map +1 -0
  54. package/dist/config/presets/prose.js +138 -0
  55. package/dist/config/presets/prose.js.map +1 -0
  56. package/dist/config/schema.d.ts +128 -22
  57. package/dist/config/schema.d.ts.map +1 -1
  58. package/dist/config/schema.js +105 -21
  59. package/dist/config/schema.js.map +1 -1
  60. package/dist/config/validate.d.ts +12 -2
  61. package/dist/config/validate.d.ts.map +1 -1
  62. package/dist/config/validate.js +1200 -44
  63. package/dist/config/validate.js.map +1 -1
  64. package/dist/core/auto-fix.d.ts +8 -13
  65. package/dist/core/auto-fix.d.ts.map +1 -1
  66. package/dist/core/auto-fix.js +94 -75
  67. package/dist/core/auto-fix.js.map +1 -1
  68. package/dist/core/case-preserve.d.ts +46 -0
  69. package/dist/core/case-preserve.d.ts.map +1 -0
  70. package/dist/core/case-preserve.js +57 -0
  71. package/dist/core/case-preserve.js.map +1 -0
  72. package/dist/core/directives.d.ts +9 -0
  73. package/dist/core/directives.d.ts.map +1 -0
  74. package/dist/core/directives.js +73 -0
  75. package/dist/core/directives.js.map +1 -0
  76. package/dist/core/files.d.ts +63 -0
  77. package/dist/core/files.d.ts.map +1 -1
  78. package/dist/core/files.js +185 -0
  79. package/dist/core/files.js.map +1 -1
  80. package/dist/core/inline-code.d.ts +87 -0
  81. package/dist/core/inline-code.d.ts.map +1 -0
  82. package/dist/core/inline-code.js +104 -0
  83. package/dist/core/inline-code.js.map +1 -0
  84. package/dist/core/line-endings.d.ts +32 -0
  85. package/dist/core/line-endings.d.ts.map +1 -0
  86. package/dist/core/line-endings.js +65 -0
  87. package/dist/core/line-endings.js.map +1 -0
  88. package/dist/core/markdoc-tags.d.ts +79 -0
  89. package/dist/core/markdoc-tags.d.ts.map +1 -0
  90. package/dist/core/markdoc-tags.js +131 -0
  91. package/dist/core/markdoc-tags.js.map +1 -0
  92. package/dist/core/rule-filters.d.ts +17 -0
  93. package/dist/core/rule-filters.d.ts.map +1 -1
  94. package/dist/core/rule-filters.js +64 -0
  95. package/dist/core/rule-filters.js.map +1 -1
  96. package/dist/core/runner.d.ts +92 -3
  97. package/dist/core/runner.d.ts.map +1 -1
  98. package/dist/core/runner.js +348 -110
  99. package/dist/core/runner.js.map +1 -1
  100. package/dist/core/timing.d.ts.map +1 -1
  101. package/dist/data/markdoc-realm-schema.d.ts +3 -0
  102. package/dist/data/markdoc-realm-schema.d.ts.map +1 -0
  103. package/dist/data/markdoc-realm-schema.js +760 -0
  104. package/dist/data/markdoc-realm-schema.js.map +1 -0
  105. package/dist/data/proper-nouns.d.ts +2 -0
  106. package/dist/data/proper-nouns.d.ts.map +1 -0
  107. package/dist/data/proper-nouns.js +47 -0
  108. package/dist/data/proper-nouns.js.map +1 -0
  109. package/dist/index.d.ts +90 -0
  110. package/dist/index.d.ts.map +1 -0
  111. package/dist/index.js +152 -0
  112. package/dist/index.js.map +1 -0
  113. package/dist/metrics/formulas.d.ts +17 -0
  114. package/dist/metrics/formulas.d.ts.map +1 -0
  115. package/dist/metrics/formulas.js +70 -0
  116. package/dist/metrics/formulas.js.map +1 -0
  117. package/dist/metrics/index.d.ts +5 -0
  118. package/dist/metrics/index.d.ts.map +1 -0
  119. package/dist/metrics/index.js +3 -0
  120. package/dist/metrics/index.js.map +1 -0
  121. package/dist/metrics/statistics.d.ts +27 -0
  122. package/dist/metrics/statistics.d.ts.map +1 -0
  123. package/dist/metrics/statistics.js +56 -0
  124. package/dist/metrics/statistics.js.map +1 -0
  125. package/dist/parser/index.d.ts +18 -0
  126. package/dist/parser/index.d.ts.map +1 -0
  127. package/dist/parser/index.js +168 -0
  128. package/dist/parser/index.js.map +1 -0
  129. package/dist/parser/markdoc/extract-statics.d.ts +45 -0
  130. package/dist/parser/markdoc/extract-statics.d.ts.map +1 -0
  131. package/dist/parser/markdoc/extract-statics.js +139 -0
  132. package/dist/parser/markdoc/extract-statics.js.map +1 -0
  133. package/dist/parser/markdoc/pairing.d.ts +63 -0
  134. package/dist/parser/markdoc/pairing.d.ts.map +1 -0
  135. package/dist/parser/markdoc/pairing.js +94 -0
  136. package/dist/parser/markdoc/pairing.js.map +1 -0
  137. package/dist/parser/markdoc/schema.d.ts +85 -0
  138. package/dist/parser/markdoc/schema.d.ts.map +1 -0
  139. package/dist/parser/markdoc/schema.js +86 -0
  140. package/dist/parser/markdoc/schema.js.map +1 -0
  141. package/dist/parser/markdoc/span.d.ts +64 -0
  142. package/dist/parser/markdoc/span.d.ts.map +1 -0
  143. package/dist/parser/markdoc/span.js +729 -0
  144. package/dist/parser/markdoc/span.js.map +1 -0
  145. package/dist/parser/markdoc/structure.d.ts +28 -0
  146. package/dist/parser/markdoc/structure.d.ts.map +1 -0
  147. package/dist/parser/markdoc/structure.js +153 -0
  148. package/dist/parser/markdoc/structure.js.map +1 -0
  149. package/dist/parser/markdoc/syntax.d.ts +44 -0
  150. package/dist/parser/markdoc/syntax.d.ts.map +1 -0
  151. package/dist/parser/markdoc/syntax.js +317 -0
  152. package/dist/parser/markdoc/syntax.js.map +1 -0
  153. package/dist/parser/types.d.ts +18 -0
  154. package/dist/parser/types.d.ts.map +1 -0
  155. package/dist/parser/types.js.map +1 -0
  156. package/dist/reporter/fixes.d.ts.map +1 -1
  157. package/dist/reporter/fixes.js +22 -1
  158. package/dist/reporter/fixes.js.map +1 -1
  159. package/dist/reporter/statistics.d.ts +1 -8
  160. package/dist/reporter/statistics.d.ts.map +1 -1
  161. package/dist/reporter/statistics.js.map +1 -1
  162. package/dist/rules/registry.d.ts +15 -0
  163. package/dist/rules/registry.d.ts.map +1 -0
  164. package/dist/rules/registry.js +81 -0
  165. package/dist/rules/registry.js.map +1 -0
  166. package/dist/rules/scope/capitalization.d.ts +3 -0
  167. package/dist/rules/scope/capitalization.d.ts.map +1 -0
  168. package/dist/rules/scope/capitalization.js +163 -0
  169. package/dist/rules/scope/capitalization.js.map +1 -0
  170. package/dist/rules/scope/conditional.d.ts +3 -0
  171. package/dist/rules/scope/conditional.d.ts.map +1 -0
  172. package/dist/rules/scope/conditional.js +112 -0
  173. package/dist/rules/scope/conditional.js.map +1 -0
  174. package/dist/rules/scope/consistency.d.ts +3 -0
  175. package/dist/rules/scope/consistency.d.ts.map +1 -0
  176. package/dist/rules/scope/consistency.js +178 -0
  177. package/dist/rules/scope/consistency.js.map +1 -0
  178. package/dist/rules/scope/length.d.ts +3 -0
  179. package/dist/rules/scope/length.d.ts.map +1 -0
  180. package/dist/rules/scope/length.js +69 -0
  181. package/dist/rules/scope/length.js.map +1 -0
  182. package/dist/rules/scope/max-image-size.d.ts +3 -0
  183. package/dist/rules/scope/max-image-size.d.ts.map +1 -0
  184. package/dist/rules/scope/max-image-size.js +65 -0
  185. package/dist/rules/scope/max-image-size.js.map +1 -0
  186. package/dist/rules/scope/metric.d.ts +17 -0
  187. package/dist/rules/scope/metric.d.ts.map +1 -0
  188. package/dist/rules/scope/metric.js +216 -0
  189. package/dist/rules/scope/metric.js.map +1 -0
  190. package/dist/rules/scope/occurrence.d.ts +3 -0
  191. package/dist/rules/scope/occurrence.d.ts.map +1 -0
  192. package/dist/rules/scope/occurrence.js +48 -0
  193. package/dist/rules/scope/occurrence.js.map +1 -0
  194. package/dist/rules/scope/pattern.d.ts +3 -0
  195. package/dist/rules/scope/pattern.d.ts.map +1 -0
  196. package/dist/rules/scope/pattern.js +75 -0
  197. package/dist/rules/scope/pattern.js.map +1 -0
  198. package/dist/rules/scope/repetition.d.ts +3 -0
  199. package/dist/rules/scope/repetition.d.ts.map +1 -0
  200. package/dist/rules/scope/repetition.js +139 -0
  201. package/dist/rules/scope/repetition.js.map +1 -0
  202. package/dist/rules/scope/semantic-line-breaks.d.ts +3 -0
  203. package/dist/rules/scope/semantic-line-breaks.d.ts.map +1 -0
  204. package/dist/rules/scope/semantic-line-breaks.js +213 -0
  205. package/dist/rules/scope/semantic-line-breaks.js.map +1 -0
  206. package/dist/rules/scope/spelling.d.ts +27 -0
  207. package/dist/rules/scope/spelling.d.ts.map +1 -0
  208. package/dist/rules/scope/spelling.js +227 -0
  209. package/dist/rules/scope/spelling.js.map +1 -0
  210. package/dist/rules/scope/swap.d.ts +3 -0
  211. package/dist/rules/scope/swap.d.ts.map +1 -0
  212. package/dist/rules/scope/swap.js +149 -0
  213. package/dist/rules/scope/swap.js.map +1 -0
  214. package/dist/rules/scope/title-case.d.ts +46 -0
  215. package/dist/rules/scope/title-case.d.ts.map +1 -0
  216. package/dist/rules/scope/title-case.js +301 -0
  217. package/dist/rules/scope/title-case.js.map +1 -0
  218. package/dist/rules/token/blanks-around-fences.d.ts +3 -0
  219. package/dist/rules/token/blanks-around-fences.d.ts.map +1 -0
  220. package/dist/rules/token/blanks-around-fences.js +44 -0
  221. package/dist/rules/token/blanks-around-fences.js.map +1 -0
  222. package/dist/rules/token/blanks-around-headings.d.ts +3 -0
  223. package/dist/rules/token/blanks-around-headings.d.ts.map +1 -0
  224. package/dist/rules/token/blanks-around-headings.js +108 -0
  225. package/dist/rules/token/blanks-around-headings.js.map +1 -0
  226. package/dist/rules/token/blanks-around-lists.d.ts +3 -0
  227. package/dist/rules/token/blanks-around-lists.d.ts.map +1 -0
  228. package/dist/rules/token/blanks-around-lists.js +56 -0
  229. package/dist/rules/token/blanks-around-lists.js.map +1 -0
  230. package/dist/rules/token/blanks-around-tables.d.ts +3 -0
  231. package/dist/rules/token/blanks-around-tables.d.ts.map +1 -0
  232. package/dist/rules/token/blanks-around-tables.js +42 -0
  233. package/dist/rules/token/blanks-around-tables.js.map +1 -0
  234. package/dist/rules/token/code-block-style.d.ts +3 -0
  235. package/dist/rules/token/code-block-style.d.ts.map +1 -0
  236. package/dist/rules/token/code-block-style.js +30 -0
  237. package/dist/rules/token/code-block-style.js.map +1 -0
  238. package/dist/rules/token/code-fence-style.d.ts +3 -0
  239. package/dist/rules/token/code-fence-style.d.ts.map +1 -0
  240. package/dist/rules/token/code-fence-style.js +35 -0
  241. package/dist/rules/token/code-fence-style.js.map +1 -0
  242. package/dist/rules/token/commands-show-output.d.ts +3 -0
  243. package/dist/rules/token/commands-show-output.d.ts.map +1 -0
  244. package/dist/rules/token/commands-show-output.js +38 -0
  245. package/dist/rules/token/commands-show-output.js.map +1 -0
  246. package/dist/rules/token/descriptive-link-text.d.ts +3 -0
  247. package/dist/rules/token/descriptive-link-text.d.ts.map +1 -0
  248. package/dist/rules/token/descriptive-link-text.js +53 -0
  249. package/dist/rules/token/descriptive-link-text.js.map +1 -0
  250. package/dist/rules/token/emphasis-style.d.ts +3 -0
  251. package/dist/rules/token/emphasis-style.d.ts.map +1 -0
  252. package/dist/rules/token/emphasis-style.js +51 -0
  253. package/dist/rules/token/emphasis-style.js.map +1 -0
  254. package/dist/rules/token/fenced-code-language.d.ts +3 -0
  255. package/dist/rules/token/fenced-code-language.d.ts.map +1 -0
  256. package/dist/rules/token/fenced-code-language.js +35 -0
  257. package/dist/rules/token/fenced-code-language.js.map +1 -0
  258. package/dist/rules/token/first-line-h1.d.ts +3 -0
  259. package/dist/rules/token/first-line-h1.d.ts.map +1 -0
  260. package/dist/rules/token/first-line-h1.js +107 -0
  261. package/dist/rules/token/first-line-h1.js.map +1 -0
  262. package/dist/rules/token/heading-increment.d.ts +3 -0
  263. package/dist/rules/token/heading-increment.d.ts.map +1 -0
  264. package/dist/rules/token/heading-increment.js +27 -0
  265. package/dist/rules/token/heading-increment.js.map +1 -0
  266. package/dist/rules/token/heading-start-left.d.ts +3 -0
  267. package/dist/rules/token/heading-start-left.d.ts.map +1 -0
  268. package/dist/rules/token/heading-start-left.js +31 -0
  269. package/dist/rules/token/heading-start-left.js.map +1 -0
  270. package/dist/rules/token/heading-style.d.ts +3 -0
  271. package/dist/rules/token/heading-style.d.ts.map +1 -0
  272. package/dist/rules/token/heading-style.js +41 -0
  273. package/dist/rules/token/heading-style.js.map +1 -0
  274. package/dist/rules/token/helpers.d.ts +313 -0
  275. package/dist/rules/token/helpers.d.ts.map +1 -0
  276. package/dist/rules/token/helpers.js +746 -0
  277. package/dist/rules/token/helpers.js.map +1 -0
  278. package/dist/rules/token/hr-style.d.ts +3 -0
  279. package/dist/rules/token/hr-style.d.ts.map +1 -0
  280. package/dist/rules/token/hr-style.js +28 -0
  281. package/dist/rules/token/hr-style.js.map +1 -0
  282. package/dist/rules/token/index.d.ts +75 -0
  283. package/dist/rules/token/index.d.ts.map +1 -0
  284. package/dist/rules/token/index.js +226 -0
  285. package/dist/rules/token/index.js.map +1 -0
  286. package/dist/rules/token/line-length.d.ts +3 -0
  287. package/dist/rules/token/line-length.d.ts.map +1 -0
  288. package/dist/rules/token/line-length.js +120 -0
  289. package/dist/rules/token/line-length.js.map +1 -0
  290. package/dist/rules/token/link-fragments.d.ts +3 -0
  291. package/dist/rules/token/link-fragments.d.ts.map +1 -0
  292. package/dist/rules/token/link-fragments.js +145 -0
  293. package/dist/rules/token/link-fragments.js.map +1 -0
  294. package/dist/rules/token/link-image-reference-definitions.d.ts +3 -0
  295. package/dist/rules/token/link-image-reference-definitions.d.ts.map +1 -0
  296. package/dist/rules/token/link-image-reference-definitions.js +50 -0
  297. package/dist/rules/token/link-image-reference-definitions.js.map +1 -0
  298. package/dist/rules/token/link-image-style.d.ts +3 -0
  299. package/dist/rules/token/link-image-style.d.ts.map +1 -0
  300. package/dist/rules/token/link-image-style.js +131 -0
  301. package/dist/rules/token/link-image-style.js.map +1 -0
  302. package/dist/rules/token/list-indent.d.ts +3 -0
  303. package/dist/rules/token/list-indent.d.ts.map +1 -0
  304. package/dist/rules/token/list-indent.js +60 -0
  305. package/dist/rules/token/list-indent.js.map +1 -0
  306. package/dist/rules/token/list-length.d.ts +3 -0
  307. package/dist/rules/token/list-length.d.ts.map +1 -0
  308. package/dist/rules/token/list-length.js +55 -0
  309. package/dist/rules/token/list-length.js.map +1 -0
  310. package/dist/rules/token/list-marker-space.d.ts +3 -0
  311. package/dist/rules/token/list-marker-space.d.ts.map +1 -0
  312. package/dist/rules/token/list-marker-space.js +52 -0
  313. package/dist/rules/token/list-marker-space.js.map +1 -0
  314. package/dist/rules/token/markdoc-attributes.d.ts +3 -0
  315. package/dist/rules/token/markdoc-attributes.d.ts.map +1 -0
  316. package/dist/rules/token/markdoc-attributes.js +269 -0
  317. package/dist/rules/token/markdoc-attributes.js.map +1 -0
  318. package/dist/rules/token/markdoc-pairing.d.ts +3 -0
  319. package/dist/rules/token/markdoc-pairing.d.ts.map +1 -0
  320. package/dist/rules/token/markdoc-pairing.js +73 -0
  321. package/dist/rules/token/markdoc-pairing.js.map +1 -0
  322. package/dist/rules/token/markdoc-syntax.d.ts +3 -0
  323. package/dist/rules/token/markdoc-syntax.d.ts.map +1 -0
  324. package/dist/rules/token/markdoc-syntax.js +119 -0
  325. package/dist/rules/token/markdoc-syntax.js.map +1 -0
  326. package/dist/rules/token/markdoc-unknown-tag.d.ts +3 -0
  327. package/dist/rules/token/markdoc-unknown-tag.d.ts.map +1 -0
  328. package/dist/rules/token/markdoc-unknown-tag.js +64 -0
  329. package/dist/rules/token/markdoc-unknown-tag.js.map +1 -0
  330. package/dist/rules/token/messages.d.ts +4 -0
  331. package/dist/rules/token/messages.d.ts.map +1 -0
  332. package/dist/rules/token/messages.js +20 -0
  333. package/dist/rules/token/messages.js.map +1 -0
  334. package/dist/rules/token/no-alt-text.d.ts +3 -0
  335. package/dist/rules/token/no-alt-text.d.ts.map +1 -0
  336. package/dist/rules/token/no-alt-text.js +47 -0
  337. package/dist/rules/token/no-alt-text.js.map +1 -0
  338. package/dist/rules/token/no-bare-urls.d.ts +3 -0
  339. package/dist/rules/token/no-bare-urls.d.ts.map +1 -0
  340. package/dist/rules/token/no-bare-urls.js +88 -0
  341. package/dist/rules/token/no-bare-urls.js.map +1 -0
  342. package/dist/rules/token/no-blanks-blockquote.d.ts +3 -0
  343. package/dist/rules/token/no-blanks-blockquote.d.ts.map +1 -0
  344. package/dist/rules/token/no-blanks-blockquote.js +39 -0
  345. package/dist/rules/token/no-blanks-blockquote.js.map +1 -0
  346. package/dist/rules/token/no-duplicate-heading.d.ts +3 -0
  347. package/dist/rules/token/no-duplicate-heading.d.ts.map +1 -0
  348. package/dist/rules/token/no-duplicate-heading.js +101 -0
  349. package/dist/rules/token/no-duplicate-heading.js.map +1 -0
  350. package/dist/rules/token/no-duplicate-link-destinations.d.ts +3 -0
  351. package/dist/rules/token/no-duplicate-link-destinations.d.ts.map +1 -0
  352. package/dist/rules/token/no-duplicate-link-destinations.js +65 -0
  353. package/dist/rules/token/no-duplicate-link-destinations.js.map +1 -0
  354. package/dist/rules/token/no-emphasis-as-heading.d.ts +3 -0
  355. package/dist/rules/token/no-emphasis-as-heading.d.ts.map +1 -0
  356. package/dist/rules/token/no-emphasis-as-heading.js +44 -0
  357. package/dist/rules/token/no-emphasis-as-heading.js.map +1 -0
  358. package/dist/rules/token/no-empty-headings.d.ts +3 -0
  359. package/dist/rules/token/no-empty-headings.d.ts.map +1 -0
  360. package/dist/rules/token/no-empty-headings.js +28 -0
  361. package/dist/rules/token/no-empty-headings.js.map +1 -0
  362. package/dist/rules/token/no-empty-links.d.ts +3 -0
  363. package/dist/rules/token/no-empty-links.d.ts.map +1 -0
  364. package/dist/rules/token/no-empty-links.js +67 -0
  365. package/dist/rules/token/no-empty-links.js.map +1 -0
  366. package/dist/rules/token/no-hard-tabs.d.ts +3 -0
  367. package/dist/rules/token/no-hard-tabs.d.ts.map +1 -0
  368. package/dist/rules/token/no-hard-tabs.js +76 -0
  369. package/dist/rules/token/no-hard-tabs.js.map +1 -0
  370. package/dist/rules/token/no-inline-html.d.ts +3 -0
  371. package/dist/rules/token/no-inline-html.d.ts.map +1 -0
  372. package/dist/rules/token/no-inline-html.js +45 -0
  373. package/dist/rules/token/no-inline-html.js.map +1 -0
  374. package/dist/rules/token/no-missing-space-atx.d.ts +3 -0
  375. package/dist/rules/token/no-missing-space-atx.d.ts.map +1 -0
  376. package/dist/rules/token/no-missing-space-atx.js +36 -0
  377. package/dist/rules/token/no-missing-space-atx.js.map +1 -0
  378. package/dist/rules/token/no-missing-space-closed-atx.d.ts +3 -0
  379. package/dist/rules/token/no-missing-space-closed-atx.d.ts.map +1 -0
  380. package/dist/rules/token/no-missing-space-closed-atx.js +45 -0
  381. package/dist/rules/token/no-missing-space-closed-atx.js.map +1 -0
  382. package/dist/rules/token/no-multiple-blanks.d.ts +3 -0
  383. package/dist/rules/token/no-multiple-blanks.d.ts.map +1 -0
  384. package/dist/rules/token/no-multiple-blanks.js +35 -0
  385. package/dist/rules/token/no-multiple-blanks.js.map +1 -0
  386. package/dist/rules/token/no-multiple-space-atx.d.ts +13 -0
  387. package/dist/rules/token/no-multiple-space-atx.d.ts.map +1 -0
  388. package/dist/rules/token/no-multiple-space-atx.js +50 -0
  389. package/dist/rules/token/no-multiple-space-atx.js.map +1 -0
  390. package/dist/rules/token/no-multiple-space-blockquote.d.ts +3 -0
  391. package/dist/rules/token/no-multiple-space-blockquote.d.ts.map +1 -0
  392. package/dist/rules/token/no-multiple-space-blockquote.js +43 -0
  393. package/dist/rules/token/no-multiple-space-blockquote.js.map +1 -0
  394. package/dist/rules/token/no-multiple-space-closed-atx.d.ts +3 -0
  395. package/dist/rules/token/no-multiple-space-closed-atx.d.ts.map +1 -0
  396. package/dist/rules/token/no-multiple-space-closed-atx.js +19 -0
  397. package/dist/rules/token/no-multiple-space-closed-atx.js.map +1 -0
  398. package/dist/rules/token/no-reversed-links.d.ts +3 -0
  399. package/dist/rules/token/no-reversed-links.d.ts.map +1 -0
  400. package/dist/rules/token/no-reversed-links.js +52 -0
  401. package/dist/rules/token/no-reversed-links.js.map +1 -0
  402. package/dist/rules/token/no-space-in-code.d.ts +3 -0
  403. package/dist/rules/token/no-space-in-code.d.ts.map +1 -0
  404. package/dist/rules/token/no-space-in-code.js +75 -0
  405. package/dist/rules/token/no-space-in-code.js.map +1 -0
  406. package/dist/rules/token/no-space-in-emphasis.d.ts +3 -0
  407. package/dist/rules/token/no-space-in-emphasis.d.ts.map +1 -0
  408. package/dist/rules/token/no-space-in-emphasis.js +79 -0
  409. package/dist/rules/token/no-space-in-emphasis.js.map +1 -0
  410. package/dist/rules/token/no-space-in-links.d.ts +3 -0
  411. package/dist/rules/token/no-space-in-links.d.ts.map +1 -0
  412. package/dist/rules/token/no-space-in-links.js +48 -0
  413. package/dist/rules/token/no-space-in-links.js.map +1 -0
  414. package/dist/rules/token/no-trailing-punctuation.d.ts +3 -0
  415. package/dist/rules/token/no-trailing-punctuation.d.ts.map +1 -0
  416. package/dist/rules/token/no-trailing-punctuation.js +38 -0
  417. package/dist/rules/token/no-trailing-punctuation.js.map +1 -0
  418. package/dist/rules/token/no-trailing-spaces.d.ts +3 -0
  419. package/dist/rules/token/no-trailing-spaces.d.ts.map +1 -0
  420. package/dist/rules/token/no-trailing-spaces.js +89 -0
  421. package/dist/rules/token/no-trailing-spaces.js.map +1 -0
  422. package/dist/rules/token/ol-prefix.d.ts +3 -0
  423. package/dist/rules/token/ol-prefix.d.ts.map +1 -0
  424. package/dist/rules/token/ol-prefix.js +70 -0
  425. package/dist/rules/token/ol-prefix.js.map +1 -0
  426. package/dist/rules/token/proper-names.d.ts +3 -0
  427. package/dist/rules/token/proper-names.d.ts.map +1 -0
  428. package/dist/rules/token/proper-names.js +93 -0
  429. package/dist/rules/token/proper-names.js.map +1 -0
  430. package/dist/rules/token/reference-links-images.d.ts +3 -0
  431. package/dist/rules/token/reference-links-images.d.ts.map +1 -0
  432. package/dist/rules/token/reference-links-images.js +36 -0
  433. package/dist/rules/token/reference-links-images.js.map +1 -0
  434. package/dist/rules/token/required-headings.d.ts +3 -0
  435. package/dist/rules/token/required-headings.d.ts.map +1 -0
  436. package/dist/rules/token/required-headings.js +83 -0
  437. package/dist/rules/token/required-headings.js.map +1 -0
  438. package/dist/rules/token/single-h1.d.ts +3 -0
  439. package/dist/rules/token/single-h1.d.ts.map +1 -0
  440. package/dist/rules/token/single-h1.js +56 -0
  441. package/dist/rules/token/single-h1.js.map +1 -0
  442. package/dist/rules/token/single-trailing-newline.d.ts +3 -0
  443. package/dist/rules/token/single-trailing-newline.d.ts.map +1 -0
  444. package/dist/rules/token/single-trailing-newline.js +25 -0
  445. package/dist/rules/token/single-trailing-newline.js.map +1 -0
  446. package/dist/rules/token/strong-style.d.ts +3 -0
  447. package/dist/rules/token/strong-style.d.ts.map +1 -0
  448. package/dist/rules/token/strong-style.js +51 -0
  449. package/dist/rules/token/strong-style.js.map +1 -0
  450. package/dist/rules/token/table-column-count.d.ts +3 -0
  451. package/dist/rules/token/table-column-count.d.ts.map +1 -0
  452. package/dist/rules/token/table-column-count.js +44 -0
  453. package/dist/rules/token/table-column-count.js.map +1 -0
  454. package/dist/rules/token/table-column-style.d.ts +3 -0
  455. package/dist/rules/token/table-column-style.d.ts.map +1 -0
  456. package/dist/rules/token/table-column-style.js +179 -0
  457. package/dist/rules/token/table-column-style.js.map +1 -0
  458. package/dist/rules/token/table-pipe-style.d.ts +3 -0
  459. package/dist/rules/token/table-pipe-style.d.ts.map +1 -0
  460. package/dist/rules/token/table-pipe-style.js +54 -0
  461. package/dist/rules/token/table-pipe-style.js.map +1 -0
  462. package/dist/rules/token/ul-indent.d.ts +3 -0
  463. package/dist/rules/token/ul-indent.d.ts.map +1 -0
  464. package/dist/rules/token/ul-indent.js +72 -0
  465. package/dist/rules/token/ul-indent.js.map +1 -0
  466. package/dist/rules/token/ul-style.d.ts +3 -0
  467. package/dist/rules/token/ul-style.d.ts.map +1 -0
  468. package/dist/rules/token/ul-style.js +80 -0
  469. package/dist/rules/token/ul-style.js.map +1 -0
  470. package/dist/rules/types.d.ts +81 -0
  471. package/dist/rules/types.d.ts.map +1 -0
  472. package/dist/rules/types.js +2 -0
  473. package/dist/rules/types.js.map +1 -0
  474. package/dist/rules/utils.d.ts +29 -0
  475. package/dist/rules/utils.d.ts.map +1 -0
  476. package/dist/{assertions → rules}/utils.js +27 -0
  477. package/dist/rules/utils.js.map +1 -0
  478. package/dist/scopes/extractor.d.ts +7 -0
  479. package/dist/scopes/extractor.d.ts.map +1 -0
  480. package/dist/scopes/extractor.js +475 -0
  481. package/dist/scopes/extractor.js.map +1 -0
  482. package/dist/scopes/selector.d.ts +51 -0
  483. package/dist/scopes/selector.d.ts.map +1 -0
  484. package/dist/scopes/selector.js +121 -0
  485. package/dist/scopes/selector.js.map +1 -0
  486. package/dist/scopes/sentences.d.ts +15 -0
  487. package/dist/scopes/sentences.d.ts.map +1 -0
  488. package/dist/scopes/sentences.js +124 -0
  489. package/dist/scopes/sentences.js.map +1 -0
  490. package/dist/scopes/types.d.ts +44 -0
  491. package/dist/scopes/types.d.ts.map +1 -0
  492. package/dist/scopes/types.js +2 -0
  493. package/dist/scopes/types.js.map +1 -0
  494. package/dist/scopes/vocabulary.d.ts +16 -0
  495. package/dist/scopes/vocabulary.d.ts.map +1 -0
  496. package/dist/scopes/vocabulary.js +70 -0
  497. package/dist/scopes/vocabulary.js.map +1 -0
  498. package/dist/types/assertions.d.ts +95 -32
  499. package/dist/types/assertions.d.ts.map +1 -1
  500. package/dist/types/problems.d.ts +4 -5
  501. package/dist/types/problems.d.ts.map +1 -1
  502. package/dist/types/rules.d.ts +4 -6
  503. package/dist/types/rules.d.ts.map +1 -1
  504. package/examples/appendices/google.appendix.yaml +91 -0
  505. package/examples/appendices/inclusive-language.appendix.yaml +61 -0
  506. package/examples/appendices/microsoft.appendix.yaml +99 -0
  507. package/examples/appendices/plain-language.appendix.yaml +88 -0
  508. package/examples/google.yaml +1525 -0
  509. package/examples/inclusive-language.yaml +304 -0
  510. package/examples/microsoft.yaml +1542 -0
  511. package/examples/plain-language.yaml +325 -0
  512. package/package.json +49 -16
  513. package/presets/google/PROVENANCE.md +1022 -0
  514. package/presets/google/sources.json +192 -0
  515. package/presets/inclusive-language/PROVENANCE.md +174 -0
  516. package/presets/inclusive-language/sources.json +107 -0
  517. package/presets/microsoft/PROVENANCE.md +1555 -0
  518. package/presets/microsoft/sources.json +494 -0
  519. package/presets/plain-language/PROVENANCE.md +364 -0
  520. package/presets/plain-language/sources.json +108 -0
  521. package/dist/assertions/bullet-style.d.ts +0 -3
  522. package/dist/assertions/bullet-style.d.ts.map +0 -1
  523. package/dist/assertions/bullet-style.js +0 -60
  524. package/dist/assertions/bullet-style.js.map +0 -1
  525. package/dist/assertions/index.d.ts +0 -21
  526. package/dist/assertions/index.d.ts.map +0 -1
  527. package/dist/assertions/index.js +0 -30
  528. package/dist/assertions/index.js.map +0 -1
  529. package/dist/assertions/max-image-size.d.ts +0 -3
  530. package/dist/assertions/max-image-size.d.ts.map +0 -1
  531. package/dist/assertions/max-image-size.js +0 -73
  532. package/dist/assertions/max-image-size.js.map +0 -1
  533. package/dist/assertions/max-line-length.d.ts +0 -3
  534. package/dist/assertions/max-line-length.d.ts.map +0 -1
  535. package/dist/assertions/max-line-length.js +0 -68
  536. package/dist/assertions/max-line-length.js.map +0 -1
  537. package/dist/assertions/no-broken-fragment-links.d.ts +0 -3
  538. package/dist/assertions/no-broken-fragment-links.d.ts.map +0 -1
  539. package/dist/assertions/no-broken-fragment-links.js +0 -79
  540. package/dist/assertions/no-broken-fragment-links.js.map +0 -1
  541. package/dist/assertions/no-duplicate-headings.d.ts +0 -3
  542. package/dist/assertions/no-duplicate-headings.d.ts.map +0 -1
  543. package/dist/assertions/no-duplicate-headings.js +0 -66
  544. package/dist/assertions/no-duplicate-headings.js.map +0 -1
  545. package/dist/assertions/no-hard-tabs.d.ts +0 -3
  546. package/dist/assertions/no-hard-tabs.d.ts.map +0 -1
  547. package/dist/assertions/no-hard-tabs.js +0 -63
  548. package/dist/assertions/no-hard-tabs.js.map +0 -1
  549. package/dist/assertions/no-trailing-spaces.d.ts +0 -3
  550. package/dist/assertions/no-trailing-spaces.d.ts.map +0 -1
  551. package/dist/assertions/no-trailing-spaces.js +0 -72
  552. package/dist/assertions/no-trailing-spaces.js.map +0 -1
  553. package/dist/assertions/pattern.d.ts +0 -3
  554. package/dist/assertions/pattern.d.ts.map +0 -1
  555. package/dist/assertions/pattern.js +0 -39
  556. package/dist/assertions/pattern.js.map +0 -1
  557. package/dist/assertions/semantic-line-breaks.d.ts +0 -3
  558. package/dist/assertions/semantic-line-breaks.d.ts.map +0 -1
  559. package/dist/assertions/semantic-line-breaks.js +0 -152
  560. package/dist/assertions/semantic-line-breaks.js.map +0 -1
  561. package/dist/assertions/swap.d.ts +0 -3
  562. package/dist/assertions/swap.d.ts.map +0 -1
  563. package/dist/assertions/swap.js +0 -39
  564. package/dist/assertions/swap.js.map +0 -1
  565. package/dist/assertions/utils.d.ts +0 -8
  566. package/dist/assertions/utils.d.ts.map +0 -1
  567. package/dist/assertions/utils.js.map +0 -1
  568. package/dist/core/scope-parser.d.ts +0 -26
  569. package/dist/core/scope-parser.d.ts.map +0 -1
  570. package/dist/core/scope-parser.js +0 -110
  571. package/dist/core/scope-parser.js.map +0 -1
  572. package/dist/files.d.ts +0 -2
  573. package/dist/files.d.ts.map +0 -1
  574. package/dist/files.js +0 -39
  575. package/dist/files.js.map +0 -1
  576. package/dist/load-config.d.ts +0 -25
  577. package/dist/load-config.d.ts.map +0 -1
  578. package/dist/load-config.js +0 -104
  579. package/dist/load-config.js.map +0 -1
  580. package/dist/load.d.ts +0 -25
  581. package/dist/load.d.ts.map +0 -1
  582. package/dist/load.js +0 -112
  583. package/dist/load.js.map +0 -1
  584. package/dist/scope.d.ts +0 -26
  585. package/dist/scope.d.ts.map +0 -1
  586. package/dist/scope.js +0 -110
  587. package/dist/scope.js.map +0 -1
  588. package/dist/types.d.ts +0 -109
  589. package/dist/types.d.ts.map +0 -1
  590. package/dist/types.js.map +0 -1
  591. package/dist/validate.d.ts +0 -31
  592. package/dist/validate.d.ts.map +0 -1
  593. package/dist/validate.js +0 -154
  594. package/dist/validate.js.map +0 -1
  595. /package/dist/{types.js → parser/types.js} +0 -0
@@ -0,0 +1,1555 @@
1
+ # Provenance: `recheck/microsoft`
2
+
3
+ Source: [Microsoft Writing Style Guide](https://learn.microsoft.com/en-us/style-guide/welcome/)
4
+ (canonical URL: `https://learn.microsoft.com/en-us/style-guide/welcome/`).
5
+ Upstream status: **archived** as of 2024-11-13 — Microsoft stopped actively
6
+ maintaining the guide on that date, but the content remains published (and
7
+ still receives occasional copyedits: page "Last updated on" dates in the
8
+ verification passes ranged from 2018 to 2026-07-06). License: CC BY 4.0 (see
9
+ below — the grant is not stated on any `learn.microsoft.com` page itself).
10
+ Sync date: **2026-07-29**.
11
+
12
+ Modification note: rules are adapted to Recheck's assertion vocabulary
13
+ (`swap`, `pattern`, `capitalization`, `length`, `occurrence`, plus a handful
14
+ of markdownlint-parity token rules); wording is paraphrased into each rule's
15
+ `message`, not quoted verbatim from the guide. Every rule carries a `link:`
16
+ to its source page.
17
+
18
+ ## Licence — cite GitHub, not Microsoft Learn
19
+
20
+ **No `learn.microsoft.com` style-guide page states a licence anywhere.**
21
+ Verifier E grepped every fetched raw HTML page for "creative commons",
22
+ "cc-by", "cc by 4", and "licensed under" — zero hits on every page. The only
23
+ copyright-adjacent text on the site is a sitewide footer: "© 2024 Microsoft.
24
+ All rights reserved."
25
+
26
+ The grant instead lives one hop away, in the backing GitHub repository every
27
+ style-guide page's `content_git_url` page metadata points at:
28
+ `https://github.com/MicrosoftDocs/microsoft-style-guide/blob/main/LICENSE`.
29
+ Confirmed two independent ways:
30
+
31
+ - GitHub's own license-detection API:
32
+ `"license": {"key": "cc-by-4.0", "name": "Creative Commons Attribution 4.0 International", "spdx_id": "CC-BY-4.0"}`.
33
+ - The raw `LICENSE` file itself is the full CC BY 4.0 legal code.
34
+
35
+ The site's Terms of Use (`https://learn.microsoft.com/en-us/legal/termsofuse`)
36
+ explicitly defers to this: *"Certain documentation may be subject to
37
+ explicit license terms separate from the terms contained here. To the
38
+ extent the terms conflict, the explicit license terms control."*
39
+
40
+ **Bottom line, and the reason this matters for the preset:** CC-BY-4.0
41
+ attribution is factually correct to ship, but every `link:` in `microsoft.ts`
42
+ points at the `learn.microsoft.com` page the rule's TEXT comes from — never
43
+ at a page claiming to state the licence, because none does. The repo's
44
+ `LICENSE` file is the citation for the licence claim itself, separate from
45
+ any individual rule's source link. There is also a separate `LICENSE-CODE`
46
+ file (MIT, for embedded code samples) — not relevant to prose rules.
47
+
48
+ This is a materially different situation from `recheck/google`, whose
49
+ `developers.google.com/style` pages carry an on-page CC BY 4.0 footer notice
50
+ directly. The two presets' licence-attribution code paths must not be
51
+ copy-pasted from one to the other.
52
+
53
+ ## How this table was produced
54
+
55
+ Four independent verification passes fetched the LIVE guide directly (`curl`
56
+ with a real browser User-Agent, `html5lib`/BeautifulSoup — never a
57
+ summarizing fetch tool) and confirmed or rejected each candidate rule
58
+ against the raw page text/HTML:
59
+
60
+ ```
61
+ task-10-verify-E.md §2 (voice/word-choice/grammar, V1-V42) + §6 (self-contradictions) + LICENCE
62
+ task-10-verify-F.md §3 (capitalization/headings/lists/tables/punctuation/UI/structure, C/L/T/P/N/U/A/W/S) + numeric claims + 17 omitted exceptions
63
+ task-10-verify-G.md §5a A-Z word list, avoid-terms A-L (~100 entries), 7 contradictions
64
+ task-10-verify-H.md §5a A-Z word list, avoid-terms M-Z + Tiers 2/3/4 (~165 entries), tier-boundary audit
65
+ ```
66
+
67
+ Together they checked ~490 rules/entries across ~340 live page fetches and
68
+ found: 7 self-contradictions, 1 fabrication ("afflicted with"), 3 crossed
69
+ accessibility-table pairings, 1 inverted verdict ("shaded"), 1 wrong
70
+ direction/target ("boot" → "open"), 1 non-guidance rule ("corrupted" →
71
+ "damaged"), and 5 confirmed Tier-1/Tier-4 audience-conditional conflicts
72
+ (plus more found on closer reading during authoring — see "Additional
73
+ tier-boundary findings" below). This preset is built **only** from entries
74
+ those four reports marked `CONFIRMED`, with every one of the defects above
75
+ corrected or excluded rather than shipped. Nothing here was sourced from
76
+ `scratchpad/research-microsoft-style.md` (the pre-verification draft)
77
+ directly — that file is a candidate checklist, not a source of truth.
78
+
79
+ ## Shipped rules
80
+
81
+ Severity policy: structural/document-mechanics rules (heading, list, table,
82
+ alt-text, link-text mechanics) are `error`, matching `recheck/google`'s
83
+ convention. The A-Z word list's three "unconditional" tiers (general
84
+ terminology, accessibility/people-first language, spelling/hyphenation) also
85
+ ship at `error`, per the research's own "ship at error" framing for those
86
+ tiers (§5a/§5b/§5c headers) — once Tier 4's audience-conditional entries are
87
+ removed, every verifier confirmed the remainder as genuinely unconditional.
88
+ Everything else (voice, contractions, punctuation conventions, UI-verb
89
+ terminology, and any rule whose detection mechanism is a narrowed heuristic
90
+ rather than a complete test) is `warn`.
91
+
92
+ ### Structural (heading, list, table, alt-text, link mechanics) — `error` unless noted
93
+
94
+ | Rule id | Source URL | Quote | Verdict |
95
+ |---|---|---|---|
96
+ | `microsoft/heading-sentence-case` | [capitalization](https://learn.microsoft.com/en-us/style-guide/capitalization) | "Microsoft style uses sentence-style capitalization." | CONFIRMED |
97
+ | `microsoft/capitalize-after-heading-colon` (`warn`) | [colons](https://learn.microsoft.com/en-us/style-guide/punctuation/colons) | "Always capitalize the word after the colon." | CONFIRMED, scoped to headings only — the mid-sentence-colon form is merely "Acceptable" per the guide's own table, not shipped |
98
+ | `microsoft/no-trailing-punctuation` | [headings](https://learn.microsoft.com/en-us/style-guide/scannable-content/headings) | "Don't end headings with a period." | CONFIRMED — default punctuation set (`.,;:` with `?` stripped) already matches Microsoft's own `?`-allowed/`!`-rarely exceptions with no override |
99
+ | `microsoft/no-ampersand-in-headings` (`warn`) | [headings](https://learn.microsoft.com/en-us/style-guide/scannable-content/headings) | "Don't use ampersands (&) or plus signs (+) in headings unless..." | CONFIRMED, `exceptions.lines` for `C++`/`A+`/`.NET`; `&` pattern excludes `&`/` `/`<`/`>`/`"`/numeric entities |
100
+ | `microsoft/vs-in-headings` (`warn`) | [versus-vs](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/v/versus-vs) | "In headings, use the abbreviation vs., all lowercase." | CONFIRMED, scoped to `heading` |
101
+ | `microsoft/versus-in-text` (`warn`) | same | "In text, spell out as versus." | CONFIRMED, scoped to `[paragraph, list-item, table.cell]` — two scoped rules with opposite directions so they don't fight each other |
102
+ | `microsoft/no-multiple-blanks` | [headings](https://learn.microsoft.com/en-us/style-guide/scannable-content/headings) | "Don't use extra line breaks to increase heading spacing." | CONFIRMED |
103
+ | `microsoft/no-emphasis-as-heading` | [writing-all-abilities](https://learn.microsoft.com/en-us/style-guide/accessibility/writing-all-abilities) | "Use heading levels instead of text formatting to communicate... hierarchy." | CONFIRMED |
104
+ | `microsoft/no-apostrophe-plural-decade` (`warn`) | [apostrophes](https://learn.microsoft.com/en-us/style-guide/punctuation/apostrophes) | "Don't use an apostrophe... to form the plural of a singular noun." | CONFIRMED, scoped to the unambiguous decade case (`1990's`); a bare `[A-Z]{2,}'s` (e.g. `API's`) is excluded — frequently a legitimate possessive |
105
+ | `microsoft/article-before-acronym` (`warn`) | [acronyms](https://learn.microsoft.com/en-us/style-guide/acronyms) | "a DLL / an ISP / a URL / a SQL database" | CONFIRMED |
106
+ | `microsoft/list-item-capital` | [lists](https://learn.microsoft.com/en-us/style-guide/scannable-content/lists) | "Begin each item in a list with a capital letter." | CONFIRMED, custom-regex `capitalization` (not `$sentence`, which would overshoot) |
107
+ | `microsoft/no-trailing-conjunction-list` (`warn`) | [lists](https://learn.microsoft.com/en-us/style-guide/scannable-content/lists) | "Don't use semicolons, commas, or conjunctions... at the end of list items." | CONFIRMED |
108
+ | `microsoft/no-ellipsis-column-header` (`warn`) | [tables](https://learn.microsoft.com/en-us/style-guide/scannable-content/tables) | "Don't use ellipses at the end of column headers." | CONFIRMED |
109
+ | `microsoft/no-blank-table-cell` (`warn`) | [tables](https://learn.microsoft.com/en-us/style-guide/scannable-content/tables) | "Don't leave a cell blank or use an em dash. Instead, use Not applicable or None." | CONFIRMED, **em dash only** — the guide's other half ("don't leave a cell blank") is NOT enforced: a truly blank `table.cell` segment's content is the empty string, and every `pattern` token can only ever produce a zero-width match against `''`, which pattern.ts deliberately skips (no real text to report) — an architectural limit of the `pattern` assertion, not a missed narrowing pass. See the rule's doc comment in `config/presets/microsoft.ts` for the full reasoning and what a real fix would require (a token rule or an extractor-level change). The draft's original broader pattern also matched an en dash/hyphen, which the guide never names for this rule either. |
110
+ | `microsoft/single-space-after-punctuation` (`warn`) | [periods](https://learn.microsoft.com/en-us/style-guide/punctuation/periods) + [top-10-tips](https://learn.microsoft.com/en-us/style-guide/top-10-tips-style-voice) | "Put one space, not two, after a period" — top-10-tips broadens to "periods, question marks, and colons" | CONFIRMED, widened scope per verifier F's note |
111
+ | `microsoft/no-space-around-em-dash` (`warn`) | [dashes-hyphens](https://learn.microsoft.com/en-us/style-guide/punctuation/dashes-hyphens/) | "Don't use spaces around em dashes." | CONFIRMED, **narrowed to em dash only** — the en-dash rule has an explicit worked exception for UI timestamps/date ranges ("2:15 PM – 4:45 PM") a blind regex can't distinguish from the ordinary case; en-dash spacing is not enforced at all |
112
+ | `microsoft/no-from-before-en-dash-range` (`warn`) | [numbers](https://learn.microsoft.com/en-us/style-guide/numbers) | "Don't use from before a range indicated by an en dash." | CONFIRMED |
113
+ | `microsoft/straight-quotes` (`warn`) | [quotation-marks](https://learn.microsoft.com/en-us/style-guide/punctuation/quotation-marks) | "Use straight quotation marks. Segoe Sans... does not have a curly quotation mark option." | CONFIRMED |
114
+ | `microsoft/spell-out-ordinals` (`warn`) | [numbers](https://learn.microsoft.com/en-us/style-guide/numbers) | "Always spell out ordinal numbers." | CONFIRMED |
115
+ | `microsoft/ordinal-no-ly` (`warn`) | [numbers](https://learn.microsoft.com/en-us/style-guide/numbers) | "Don't add -ly to an ordinal number, as in firstly or secondly." | CONFIRMED |
116
+ | `microsoft/noon-midnight` (`warn`) | [numbers](https://learn.microsoft.com/en-us/style-guide/numbers) | "Don't use numerals for 12:00. Use noon or midnight instead." | CONFIRMED |
117
+ | `microsoft/no-alt-text` | [alternative-text](https://learn.microsoft.com/en-us/style-guide/accessibility/alternative-text) | "Add alt text to all images that convey important meaning." | CONFIRMED |
118
+ | `microsoft/alt-text-length` (`warn`) | same | "Limit the length to 150 characters." | CONFIRMED — structural number |
119
+ | `microsoft/alt-text-format` (`warn`) | same | "Begin alt text with a capital letter. End it with a period." | CONFIRMED, incomplete — the guide's own carve-out ("even if it's just a fragment, if doing so is practical for the image type") is not modeled; see "Known limitations" |
120
+ | `microsoft/alt-text-generic-opener` (`warn`) | same | "Don't start alt text with 'Image.'" / "Don't start... with 'Button' or 'Link.'" | CONFIRMED — `Screenshot`/`Diagram`/`Photograph`/`Chart`/`Drawing` are prescribed openers, deliberately absent from the pattern |
121
+ | `microsoft/alt-text-no-filename` (`warn`) | same | "Don't use the file name of an image as alt text." | CONFIRMED |
122
+ | `microsoft/descriptive-link-text` | [urls-web-addresses](https://learn.microsoft.com/en-us/style-guide/urls-web-addresses) | "rather than a generic phrase like click here" | CONFIRMED |
123
+
124
+ ### Structural numbers (`warn`) — see the dedicated section below
125
+
126
+ `microsoft/paragraph-length`, `microsoft/list-length`, `microsoft/comma-density`.
127
+
128
+ ### Voice, contractions — `warn`
129
+
130
+ | Rule id | Source URL | Quote | Verdict |
131
+ |---|---|---|---|
132
+ | `microsoft/use-contractions` | [use-contractions](https://learn.microsoft.com/en-us/style-guide/word-choice/use-contractions) + [top-10-tips](https://learn.microsoft.com/en-us/style-guide/top-10-tips-style-voice) | "Use contractions like it's, you'll, you're, we're, and let's." | CONFIRMED — the signature Microsoft rule and the sharpest voice difference from other guides. Every pair is a clean word-for-word contraction; `applyMatchCase` verified to handle sentence-initial capitalized matches correctly ("Do not" → "Don't"). |
133
+ | `microsoft/no-awkward-contractions` | [use-contractions](https://learn.microsoft.com/en-us/style-guide/word-choice/use-contractions) | "Avoid ambiguous or awkward contractions, such as there'd, it'll, and they'd." | CONFIRMED |
134
+ | `microsoft/contraction-consistency` | same | "don't use can't and cannot in the same UI." | CONFIRMED — textbook fit for first-seen-wins `consistency` |
135
+ | `microsoft/no-weak-phrasing` | [top-10-tips](https://learn.microsoft.com/en-us/style-guide/top-10-tips-style-voice) | "Avoid weak phrasing like there is, there are, and there were." | CONFIRMED |
136
+ | `microsoft/avoid-please` | [a-z/please](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/p/please) | "Avoid please except in situations where the customer is asked to do something inconvenient..." | CONFIRMED |
137
+
138
+ ### US spelling, Latin abbreviations, simple words — `error` (mechanical, per the research's "ship at error" framing)
139
+
140
+ | Rule id | Source URL | Quote | Verdict |
141
+ |---|---|---|---|
142
+ | `microsoft/us-spelling` | [use-us-spelling](https://learn.microsoft.com/en-us/style-guide/word-choice/use-us-spelling-avoid-non-english-words) | "use the US spelling. For example, use license, not licence." | CONFIRMED — a subset not already covered by `recheck/prose`'s `consistency` rule (behavior/color/license/organize) |
143
+ | `microsoft/no-latin-abbreviations` | same | "Avoid Latin abbreviations for common English phrases." | CONFIRMED for e.g./i.e./viz./ergo/de facto/ad hoc/vis-a-vis. `via` deliberately dropped (see self-contradictions section) |
144
+ | `microsoft/simple-words` | [use-simple-words](https://learn.microsoft.com/en-us/style-guide/word-choice/use-simple-words-concise-sentences) | "Choose simple verbs without modifiers." / "Don't use two or three words when one will do." | CONFIRMED |
145
+ | `microsoft/leverage` | [avoid-jargon](https://learn.microsoft.com/en-us/style-guide/word-choice/avoid-jargon) | "using leverage to mean take advantage of" | CONFIRMED — split into its own rule so its `link:` points at the page that actually states it |
146
+ | `microsoft/glyph` | same | "such as symbol instead of glyph" | CONFIRMED |
147
+ | `microsoft/bucketize` | [dont-use-common-words](https://learn.microsoft.com/en-us/style-guide/word-choice/dont-use-common-words-in-new-ways) | "Don't create a new word from an existing word." | CONFIRMED |
148
+ | `microsoft/impact-verb` (`warn`) | same | "Don't use verbs as nouns or nouns as verbs" | CONFIRMED, narrowly anchored to `impact` immediately followed by a specific object noun (performance/productivity/quality/reliability/availability/latency/throughput) — bare "impact" is also a common, correct noun ("the impact of this change") per verifier E's own caution |
149
+ | `microsoft/the-ask` (`warn`) | [dont-use-common-words](https://learn.microsoft.com/en-us/style-guide/word-choice/dont-use-common-words-in-new-ways) | "respond to the request" vs. "respond to the ask" | CONFIRMED |
150
+
151
+ ### Bias-free, militaristic, derogatory language — `error` (sensitive category)
152
+
153
+ | Rule id | Source URL | Quote | Verdict |
154
+ |---|---|---|---|
155
+ | `microsoft/bias-free-terms` | [bias-free](https://learn.microsoft.com/en-us/style-guide/bias-free-communication) | chairman→chair; mankind→humanity; manmade→synthetic; manpower→workforce; salesman→sales representative; DMZ→perimeter network | CONFIRMED. `master/slave` deliberately NOT included in this rule's pairs — it ships separately as `microsoft/master-slave`, see next row and the self-contradictions section |
156
+ | `microsoft/master-slave` | [bias-free](https://learn.microsoft.com/en-us/style-guide/bias-free-communication) + [a-z/master-slave](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/m/master-slave) | "primary/subordinate ← master/slave" (bias-free); "Don't use master/slave. Use primary/replica or alternatives such as primary/secondary, principal/agent, controller/worker" (a-z) | CONFIRMED both pages ban the term; the two pages disagree only on the replacement — added in the task-10 fix wave as `pattern` (detection-only), naming both candidates in its message rather than shipping neither direction. See "Self-contradictions" |
157
+ | `microsoft/cyberattack-spelling` | [militaristic-language](https://learn.microsoft.com/en-us/style-guide/militaristic-language) | "add cyber- in front of threat so it reads cyberthreat, all one word no space no hyphen" | CONFIRMED — spelling normalization only; the guide's separate "needs a qualifier in front of it" test is not mechanically decidable and is not enforced |
158
+ | `microsoft/no-derogatory-slang` | [bias-free](https://learn.microsoft.com/en-us/style-guide/bias-free-communication) | "Don't use profane or derogatory terms, such as pimp or bitch." / "spirit animal" | CONFIRMED, detection-only (no safe fixed replacement for any of these) |
159
+ | `microsoft/racial-ethnic-capitalization` | same | "Use title-style capitalization for Asian, Black and African American, Hispanic and Latinx..." | CONFIRMED, case-sensitive match (only genuinely-lowercase forms flagged); `white`/`multiracial` (which the guide says to LOWERCASE) deliberately excluded — a bare capitalized "White" collides constantly with unrelated proper nouns |
160
+
161
+ **Note on the militaristic-language word bundle:** the broader "kill chain /
162
+ blast radius / locked down / threat intel / defense-in-depth approach /
163
+ frontline analysts / first line of defense" bundle from the research draft
164
+ is **not shipped**. Verifier E found the draft's "12 never-use terms" count
165
+ factually wrong (the live list has 9 bullet groups / 17 individual terms)
166
+ and "threat intel" mis-sourced within the page — the underlying content is
167
+ real, but no verifier independently confirmed exact replacement text for
168
+ each multi-word term, and guessing at replacement values for a bundle
169
+ already shown to contain counting/sourcing errors was judged too risky.
170
+ Only the one mechanically clean, precisely quoted sub-rule (`cyber attack`
171
+ → `cyberattack` spelling) ships.
172
+
173
+ ### Accessibility term collection (Tier 2) — `error`, detection-only
174
+
175
+ `microsoft/accessibility-terms` — see the dedicated "Tier 2 design" section
176
+ below.
177
+
178
+ ### Spelling and hyphenation normalization (Tier 3) — `error`
179
+
180
+ `microsoft/spelling-hyphenation`, `microsoft/tooltip-capitalization` — pure
181
+ mechanics (email/database/endpoint/website/webpage/workstation/screenshot/
182
+ taskbar/namespace/plugin/e-commerce/e-learning/e-book/cybersecurity/
183
+ coauthor/dial-up/read-only/context-sensitive/single sign-on/multifactor/
184
+ multicloud/multitenant/wellbeing/tooltip normalization). CONFIRMED per the
185
+ research's own Tier 3 table; the "ToolTip" config-mechanics bug (see
186
+ "Section 3" below) is fixed by splitting it into its own case-sensitive
187
+ rule rather than sharing the case-insensitive "tool tip" pattern's flag.
188
+
189
+ ### CASE-ONLY (`fix: false`) — `error`, detection-only
190
+
191
+ `microsoft/az-case-only`: `Internet`/`Intranet`/`Extranet` → lowercase,
192
+ `Big Data` → `big data`, `Euro` → `euro`, `Dark Mode`/`darkmode` →
193
+ `dark mode`, `Devops`/`devops` → `DevOps`, `bluetooth` → `Bluetooth`,
194
+ `boolean` → `Boolean`, `Javascript`/`javascript` → `JavaScript`,
195
+ `World Wide Web` → `web`, `WWW` → `www`, `Registry` → `registry`, `Spam` →
196
+ `spam`. Every pair here is confirmed by at least one verifier as CASE-ONLY:
197
+ `applyMatchCase` reapplies the MATCH's observed casing to the replacement,
198
+ so fixing (say) sentence-initial "Internet" reproduces "Internet" — a
199
+ silent, permanent no-op. `fix: false` ships instead of a fix that appears
200
+ to work but never changes anything.
201
+
202
+ ### VERB-ABLE (`fix: false`, message says "rewrite") — `error`, detection-only
203
+
204
+ `microsoft/az-verb-able`: `blacklist` → `block list`, `whitelist` →
205
+ `allow list`, `allowlist` → `allow list`, `blocklist` → `block list`. Each
206
+ avoid-term is documented as also usable as a verb ("whitelist an email
207
+ address," "add-list the address"); the replacement is a noun phrase, so a
208
+ blind fix produces ungrammatical output ("allow list an email address").
209
+
210
+ ### A-Z word list (Tier 1), thematic groups — `error`
211
+
212
+ `microsoft/az-state-failure`, `microsoft/az-lifecycle-verbs`,
213
+ `microsoft/az-judgment-words`, `microsoft/actionable` (detection-only —
214
+ adjective/relative-clause mismatch, see below), `microsoft/az-geography`,
215
+ `microsoft/az-direction-layout`, `microsoft/az-ui-nouns`,
216
+ `microsoft/az-typography`, `microsoft/az-filesystem`,
217
+ `microsoft/az-grammar-usage`, `microsoft/az-abbreviations-names`,
218
+ `microsoft/az-navigation`, `microsoft/az-no-replacement` (detection-only —
219
+ no fixed replacement given anywhere for these terms), `microsoft/az-real-replacements`.
220
+
221
+ Every pair in these fourteen rules is CONFIRMED unconditional by at least
222
+ one verifier, with Tier 4 conflicts, developer-audience carve-outs, and
223
+ substring/homograph-risk terms excluded per the sections below. `actionable`
224
+ ships as `pattern`, not `swap`: "actionable" is an adjective and its
225
+ replacement ("that you can act on") is a relative clause — a direct
226
+ substitution is ungrammatical in most positions ("actionable insights" →
227
+ "that you can act on insights"), so this is detection-only with a
228
+ "rewrite" message, the same class of fix-safety issue as VERB-ABLE, just
229
+ adjective-for-clause instead of verb-for-noun.
230
+
231
+ ### UI verbs and checkbox/dialog terminology — `warn`
232
+
233
+ | Rule id | Source URL | Quote | Verdict |
234
+ |---|---|---|---|
235
+ | `microsoft/no-click` | [describing-interactions-with-ui](https://learn.microsoft.com/en-us/style-guide/procedures-instructions/describing-interactions-with-ui) | "Don't use input-specific verbs, such as click or swipe." | CONFIRMED — **the single sharpest divergence from `recheck/google`**, which allows "click". Anchored with a negative lookbehind/lookahead to exclude the hyphen-joined compounds `double-click`/`right-click` (a real substring risk: `\bclick\b` DOES match inside "double-click" once a hyphen precedes it, since a hyphen is a non-word character) and the unrelated compounds `clickstream`/`clickthrough`. Verified via a dedicated fix-safety test. |
236
+ | `microsoft/press-key-verb` | [a-z/hit](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/h/hit) | "Don't use press, depress, hit, or strike [to describe pressing a key]. Use select instead." | CONFIRMED, narrowly anchored to recognizable key-press phrasing (Enter/Tab/Esc/Delete/etc., or "the ... key") — bare "press"/"hit"/"strike" are far too polysemous (press releases, press charges, hit a milestone, strike a balance) to match unconditionally |
237
+ | `microsoft/checkbox-verbs` | [describing-interactions-with-ui](https://learn.microsoft.com/en-us/style-guide/procedures-instructions/describing-interactions-with-ui) | "Clear \| Clearing the selection from a checkbox." | CONFIRMED for `uncheck`/`unmark`/`unselect` → `clear`. Bare `check`/`deselect` deliberately excluded: "check" is extremely polysemous, and "deselect"'s replacement differs by UI-element type ("clear" for checkboxes, "cancel the selection" elsewhere) in a way a blind swap can't resolve |
238
+ | `microsoft/dialog-terminology` | [formatting-text-in-instructions](https://learn.microsoft.com/en-us/style-guide/procedures-instructions/formatting-text-in-instructions) | "Don't use pop-up window, dialog box, or dialogue box." | CONFIRMED |
239
+ | `microsoft/mouse-over` | [mouse-and-mouse-interaction-terms](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/mouse-and-mouse-interaction-terms) | "Don't use mouse over or move the mouse pointer to." | CONFIRMED (conditionally OK for beginner-skill content per the same page — low risk for reference documentation) |
240
+ | `microsoft/keyboard-shortcut-plus-spacing` | [formatting-text-in-instructions](https://learn.microsoft.com/en-us/style-guide/procedures-instructions/formatting-text-in-instructions) | "Don't put a space around the plus sign (+) in keyboard shortcuts." | CONFIRMED, detection-only — `swap` replacements are literal (Constraint 2), and the fix would need to reproduce whichever modifier key matched, which needs capture-group interpolation the engine does not support |
241
+ | `microsoft/sign-in-sign-out` | [log-on-log-off](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/l/log-on-log-off) | "Don't use log in, login, log into, log on... unless it appears in the UI (and you're writing instructions)." | CONFIRMED, `fix: false` — "login"/"logon" are frequently used as NOUNS or adjectives ("the login page", "your login credentials"), where "sign in" (a verb phrase) does not slot in grammatically. The guide's exception is also a two-part test (UI match AND instructions context) neither this nor the research draft attempts to model — flagged here as a known limitation, not silently dropped |
242
+
243
+ ## Excluded candidates
244
+
245
+ Every candidate below was checked by one of the four verification passes
246
+ (or, where marked, judged independently by this preset's author) and is
247
+ **not** shipped, with the reason. This section exists so "why doesn't
248
+ `recheck/microsoft` check X?" has a documented answer instead of looking
249
+ like an oversight.
250
+
251
+ ### Tier 4 (audience-conditional / UI-conditional) — never ships
252
+
253
+ See "Rule 0" below for the full ten-plus-list with citations.
254
+
255
+ ### Self-contradictions — enforced in neither direction
256
+
257
+ See the dedicated section below: `%`/percent, `etc.`, forced line breaks.
258
+ (`master/slave` was also a self-contradiction, but ships detection-only as
259
+ `microsoft/master-slave` as of the task-10 fix wave — it is no longer in
260
+ the "neither direction" set; see the section's own subsection.)
261
+
262
+ ### Inverted, wrong, or non-guidance entries — corrected or dropped
263
+
264
+ See "Section 3" below: `shaded`, `boot`→`open`, `corrupted`→`damaged`,
265
+ `invalid`→`not valid`, "afflicted with" (fabrication), the `ToolTip`
266
+ config bug.
267
+
268
+ ### Developer-audience carve-outs (Redocly-specific) — never ships
269
+
270
+ `header`, `context menu`, `disk`, `directory` — see the dedicated section below.
271
+
272
+ ### TOO-RISKY (guide confirms it, but the avoid-term is too ordinary/polysemous or audience-conditional to blind-match)
273
+
274
+ Fix wave B / Step 6.3: every row below now carries its own source URL(s) —
275
+ the stated bar for this table is rule + URL + reason, and a reason without
276
+ a citation isn't verifiable by a future reader.
277
+
278
+ | Candidate | Why excluded | Source(s) |
279
+ |---|---|---|
280
+ | `deprecated` → `obsolete`; `user` → `customer`; `client` (a person) → `customer`; `utility` → `tool`; `cursor`; `machine` → `computer`; `start` (an app) → `open` | Each carries an explicit or de-facto developer/technical-audience carve-out that directly applies to Redocly's own documentation — see "Additional tier-boundary findings" above. | [d/deprecated](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/d/deprecated), [u/user-end-user](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/u/user-end-user), [c/client](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/c/client), [u/utility](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/u/utility), [mouse-mouse-interaction-terms](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/mouse-mouse-interaction-terms), [computer-device-terms](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/computer-device-terms), [o/open](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/o/open) |
281
+ | `freeze`/`frozen` (as system-hang synonyms) | Sense-ambiguous: "freeze an account," "freeze a value," "deep freeze" are all common, unrelated, correct uses; the guide's own carve-out ("as a synonym for stop responding") names a specific sense a blind swap can't isolate. | [f/freeze-frozen](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/f/freeze-frozen) |
282
+ | `header`/`head` → `heading`; `context menu` → `shortcut menu`; `disk` → `hard drive`; `directory` → `folder`; `field`/`entry field` → `box`; `attribute` (meaning property) → `property`; `issue` (meaning problem) → `problem` | Each has a common, correct, unrelated technical sense in API/developer documentation (HTTP header, context menu in a sample app, managed disk, working directory, JSON field, XML attribute, GitHub issue / "issue a token") that a blind match would corrupt. | [h/header](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/h/header), [c/context-menu](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/c/context-menu), [d/disk](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/d/disk), [d/directory](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/d/directory), [f/field](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/f/field), [a/attribute](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/a/attribute), [i/issue](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/i/issue) |
283
+ | `radio button`, `scroll`, `Microsoft's` (possessive), x-multiplication, `shortcut key` | Tier-4/Tier-1 crossings — see Rule 0. | [r/radio-button](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/r/radio-button), [s/scroll](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/s/scroll), [m/microsoft](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/m/microsoft), [m/multiplication-sign](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/m/multiplication-sign), [keys-keyboard-shortcuts](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/keys-keyboard-shortcuts) |
284
+ | `may` (ability modal) | Homograph collision with the proper noun "May" (the month) — `ignoreCase: true` (needed to catch sentence-initial "May") would flag "released in May 2024" as a modal verb. | [c/can-may](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/c/can-may) |
285
+ | `that` (referring to people) → `who` | One of the most common words in English; only a small fraction of occurrences refer to people. A literal `swap` would be extraordinarily noisy; the guide's own quote scopes the objection to a use a `pattern`/`swap` can't isolate. | [w/who-vs-that](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/w/who-vs-that) |
286
+ | `star` → `asterisk` | Ordinary English word (star rating, star performer) with a narrow, Microsoft-documented exception (OK for a phone-keypad key) a blanket swap would miss in both directions. | [keys-keyboard-shortcuts](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/keys-keyboard-shortcuts) |
287
+ | `+` (plus sign, in text) | Single punctuation character; collides with `C++`, query strings, and ordinary arithmetic in code-adjacent prose. | [p/plus-sign](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/p/plus-sign) |
288
+ | `field`, `attribute`, `issue` | See row above — TOO-RISKY for Redocly's schema/API documentation specifically. | (same as above) |
289
+ | `foo`/`foobar`/`fubar` (as placeholders) | "OK to use... in content for a technical audience" — Redocly's docs qualify. | [f/foo-foobar-fubar](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/f/foo-foobar-fubar) |
290
+ | `Internet Service Provider`/`Key Performance Indicator` (C20 illustrative pairs) | The general principle (lowercase spelled-out acronym forms) is CONFIRMED, but these specific phrase pairs were not independently re-quoted as live-page examples by any verifier — dropped rather than shipped on the author's own unverified guess. | [acronyms](https://learn.microsoft.com/en-us/style-guide/acronyms) (general principle only; no per-phrase page exists to cite) |
291
+ | `API's`/`URL's`/`SDK's` → `APIs`/`URLs`/`SDKs` (C18) | The general "add lowercase s, not an apostrophe" principle is CONFIRMED, but a bare `[A-Z]{2,}'s` is frequently a legitimate POSSESSIVE ("the API's response"), not an attempted plural — verifier G's own note flags this exact ambiguity. Excluded entirely; only the unambiguous decade-plural case (`microsoft/no-apostrophe-plural-decade`) ships. | [acronyms](https://learn.microsoft.com/en-us/style-guide/acronyms) |
292
+ | `property sheet`/`property page` → "dialog box or tab"; `application developer` → "software developer, web developer, developer, or programmer" | Multiple, meaningfully different acceptable replacements — no single canonical swap target can be chosen without guessing which the guide's authors intended for a given context. | [p/property-sheet-property-page](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/p/property-sheet-property-page), [a/application-developer-app-developer](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/a/application-developer-app-developer) |
293
+ | Broader militaristic-language bundle (`kill chain`, `blast radius`, `locked down`, `threat intel`, `defense-in-depth approach`, `frontline analysts`, `first line of defense`) | See the note under "Bias-free, militaristic, derogatory language" above — content confirmed real, but no verifier independently quoted exact replacement text for each multi-word term, and the same page's "12 never-use terms" count was already shown to be wrong. | [militaristic-language](https://learn.microsoft.com/en-us/style-guide/militaristic-language) |
294
+ | `corrupt`/`corrupted` (detection-only) | Considered as a `pattern` fallback after ruling out the `swap` to "damaged"; ultimately not shipped at all — the guide's own instruction ("offer help fixing it if possible") is a request for an empathetic, context-dependent rewrite, not a term to flag mechanically. | [c/corrupted](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/c/corrupted) |
295
+ | C21 acronym first-mention expansion | See "Known limitations" #5. | [acronyms](https://learn.microsoft.com/en-us/style-guide/acronyms) |
296
+ | Broader "spell out & / + / ~ in prose" (A9) | NOISY in developer docs outside heading/list-item/alt scope (query strings, HTML entities, code-adjacent `&&`); the one high-value case (ampersands in HEADINGS specifically) is already covered by `microsoft/no-ampersand-in-headings`. | [headings](https://learn.microsoft.com/en-us/style-guide/scannable-content/headings) |
297
+ | `no-possessive-product-names` (C16, "Don't use Microsoft's") | **Not shippable at all**, conditionally or not — see below. | [m/microsoft](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/m/microsoft) |
298
+ | `SMB` → "small or medium-sized business" | Fix wave B / Step 4 drop, added here in fix wave C to give it a URL + reason row (it previously existed only in the narrative "Fix wave B / Step 4" prose below, which this table's own stated purpose — "why doesn't `recheck/microsoft` check X?" — doesn't cover). Bare, case-shouted acronym with a common, correct, unrelated technical sense in exactly Redocly's domain: SMB as the Server Message Block network protocol ("mount the SMB share"). No syntactic anchor distinguishes either sense — both are just the bare acronym in ordinary noun position. | [s/smb](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/s/smb) |
299
+ | `SKU` → "subscription, edition, version, or tier" | Fix wave B / Step 4 drop, same table-completeness reason as `SMB` above. Common, correct, unrelated e-commerce/inventory sense ("the SKU field") with no syntactic anchor separating it from Microsoft's intended sense. Carries a second, independent hazard even where the guide's sense IS intended: the guide names FOUR acceptable alternatives, not one, so no single canonical swap target exists either. | [s/sku](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/s/sku) |
300
+ | `terminate` → "close" | Fix wave B / Step 4 drop, same table-completeness reason as `SMB`/`SKU` above. "Terminate the instance/process/session/connection" is standard, correct cloud-infrastructure vocabulary throughout Redocly's own API-documentation domain (a genuine meaning change, not a false match: the guide's sense is "close an app or window"), and no reliable positional anchor separates that sense from the guide's UI sense the way `exit`'s determiner-based anchor does. | [t/terminate](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/t/terminate) |
301
+
302
+ Every URL in the "Source(s)" column above was re-verified live (`curl`,
303
+ HTTP 200) during this fix wave — see `sources.json` for the ones this
304
+ preset also hashes; the handful used only for citation here (not hashed,
305
+ since no shipped rule links to them) are noted inline instead.
306
+
307
+ ### C16 ("Don't use the possessive form of a product/trademark name") — excluded entirely, not just softened
308
+
309
+ The live guide's actual rule is narrower than the research draft's config:
310
+ "Don't use Microsoft's" specifically means don't use the possessive
311
+ immediately before a TRADEMARK/product name (e.g. "Microsoft's Azure
312
+ services" should be "Azure services"), while "it's OK to use Microsoft's
313
+ occasionally when referring to the company itself" — with the guide's own
314
+ worked example, "Microsoft's privacy policies." A linter cannot
315
+ mechanically distinguish "Microsoft's <trademark>" from "Microsoft's
316
+ <general reference to the company>" — the operative distinction is
317
+ semantic, not syntactic. Shipping either an unconditional ban (flags the
318
+ guide's own approved example) or a heuristic scoped to a hardcoded product
319
+ list (requires a project-specific brand vocabulary this preset has
320
+ otherwise declined to ship — see `proper-names` below) was judged worse
321
+ than not shipping the rule at all. `microsoft-clean.md`'s test asserts
322
+ "Microsoft's" (used of the company) produces zero findings — trivially
323
+ true here, since no rule targets the string at all.
324
+
325
+ ### Project-specific brand vocabulary — not shipped, matching `recheck/google`'s precedent
326
+
327
+ `recheck/google` does not ship a `proper-names` rule (brand/product-name
328
+ casing is left to the user's own config, not baked into the style-fidelity
329
+ preset). `recheck/microsoft` follows the same precedent: C15 ("product/
330
+ service names get consistent, exact casing") is CONFIRMED content, but no
331
+ `microsoft/proper-names` rule ships — a hardcoded product-name list is
332
+ Redocly's own vocabulary, not something the Microsoft guide provides.
333
+
334
+ ### NOT-ENFORCEABLE (real guide content, requires human judgment or structure Recheck doesn't have)
335
+
336
+ | Candidate | Why excluded |
337
+ |---|---|
338
+ | V19-V22, V37-V39 (noun+verb contractions, active voice, subjunctive mood, verb-first sentences, modifier stacks, that/who omission, -ing word care) | Each requires POS tagging or grammatical-role judgment beyond regex/AST primitives. |
339
+ | C10 (second-level headings need ≥2 siblings), C11 (no two headings in a row), L4 (periods only on complete sentences), L5 (per-list punctuation consistency), L7 (list parallelism), T1 (table sentence-case — NOISY, reference tables are dense with identifiers/proper nouns), T4 ("Name" is a bad column header — NOISY, "Name" is genuinely correct in many API reference tables), T5/T6/T10 (table intro-sentence rules, minimum table dimensions), P3/P4/P6/P8/P9/P10-P12 (multiple-hyphens-for-dash, en-dash-for-minus, from/through ranges, closing-quote placement, mid-sentence colon rules, colon-before-block), N1/N2/N6/N7/N9/N11 (spell out 0-9, no sentence-initial numerals, no K/M/B abbreviation, comma grouping, spell out months, decimal leading zero) | Each is confirmed content, but every one carries either a documented exception too common to ignore (space-limited UI text, "if one item requires a numeral," page numbers/addresses/decimals) or needs structural context (paragraph→table/list/heading adjacency) Recheck's segment model doesn't expose — matching the research draft's own NOISY/NOT-ENFORCEABLE verdicts, independently re-confirmed rather than re-litigated. |
340
+ | U5 (don't name the UI element type unless it adds clarity), U8 (file extensions lowercase / device names uppercase), U9 (bold not italic for work titles), A7 (directional terms as the only clue to location), W1-W4 (URL formatting/bare-URL rules) | "Unless it adds needed clarity" and "as the only clue" are judgment calls; the URL rules were separately judged too weakly cited (W4) or too noisy in a docs site full of real clickable links (W1/W2) to attribute to Microsoft specifically — available generically via `recheck/markdown`'s `no-bare-urls` if wanted. |
341
+ | S1-S4 (link fragments, fenced-code-language, single-H1, consistent bullet/fence/emphasis/table-pipe style) | The research draft itself labels these "House rule; no Microsoft statement" — no live-page citation exists for any of them, so none ship under a Microsoft attribution (unlike `recheck/google`, whose equivalent structural rules ARE independently Google-cited). |
342
+
343
+ ## Deliberation and development history
344
+
345
+ Everything from here on records how the shipped and excluded lists above
346
+ were reached: the audience-conditional and developer-specific carve-outs
347
+ worked out along the way, the self-contradictions and wrong entries an
348
+ earlier draft introduced, the successive fix waves that corrected or
349
+ excluded specific pairs, and the axes a pair must clear before auto-fix is
350
+ safe. **A reader who only wants provenance can stop reading above this
351
+ point** — everything below is for someone auditing or extending this
352
+ preset.
353
+
354
+ ### Rule 0 — Tier 4 (audience-conditional / UI-conditional) never ships
355
+
356
+ Spec §5.6: **never** enforce the audience-conditional tier. The original
357
+ research draft's config shipped five terms unconditionally at `error` while
358
+ those SAME five also appeared, verbatim, on its own Tier-4 "never enforce"
359
+ list:
360
+
361
+ | Term | Live page carve-out |
362
+ |---|---|
363
+ | `execute` | "Don't use except to follow the UI. Use run instead." |
364
+ | `reboot` | "If the UI or API uses reboot in a label... it's OK to refer to the label." |
365
+ | `navigate` | "You don't need to find an alternative to navigation when it's the most precise word in the context." |
366
+ | `ZIP Code` | "It's OK to use ZIP Code in content that's intended for a US audience only." |
367
+ | `disjoint selection` | "Don't use... except for a technical audience, and only if the term appears in the UI or API." |
368
+
369
+ None of the five ship. Their siblings sharing the exact same conditional
370
+ sentence (`contiguous selection`, `nonadjacent selection`, `noncontiguous
371
+ selection` — same sentence as `disjoint selection`) are excluded too.
372
+
373
+ #### The five additional mis-filed entries the corrections doc flagged for evaluation
374
+
375
+ | Term | Carve-out found | Disposition |
376
+ |---|---|---|
377
+ | `shortcut key` | Bundled in the same Tier-1 row as `accelerator key, fast key, hot key, quick key, speed key`, but `keys-keyboard-shortcuts` grants it, alone, the identical developer-audience carve-out already accepted for `access key` (correctly Tier 4). | Removed from the row; `accelerator key`/`fast key`/`hot key`/`quick key`/`speed key` still ship (no carve-out for those). |
378
+ | `radio button` | "Use radio button **only** in content for developers in which the API includes the term." | Excluded entirely. |
379
+ | `scroll` | "It's OK to use scroll in content that teaches beginning skills." | Excluded entirely. |
380
+ | x-multiplication (`x` → `×`) | "Use an asterisk (\*) **if you need to match the UI**" — Tier 4's own defining phrase, verbatim. Also SUBSTRING-RISK (bare `x` collides with `x-axis`/`x-ray`/`x-coordinate`, all hyphen-joined so `\bx\b` DOES match inside them). | Excluded entirely (double reason). |
381
+ | `Microsoft's` (possessive ban) | "Exception: it's OK to use Microsoft's occasionally when referring to the company itself" — with the guide's own worked example, "Microsoft's privacy policies." | Excluded entirely — see "C16" above; not shippable at all, conditional or not (see reasoning). |
382
+
383
+ #### Additional tier-boundary findings made while authoring (beyond the ten above)
384
+
385
+ Verifier G/H's own tables carry a `(cond.)` marker on several more Tier-1
386
+ rows that were not individually named by the corrections doc but share the
387
+ identical shape. Found by re-reading the verifier tables while authoring,
388
+ not pre-flagged:
389
+
390
+ | Term | Carve-out | Disposition |
391
+ |---|---|---|
392
+ | `deprecated` → `obsolete` | "Avoid in content for a **technical audience**. Don't use in content for a general audience." | **Excluded.** Redocly's docs are technical-audience API documentation, and "deprecated" is itself load-bearing OpenAPI vocabulary (the `deprecated: true` field). Shipping this unconditionally would misfire on Redocly's own domain, the same shape as `header`/`context menu`/`disk`/`directory` below. |
393
+ | `and/or` → `or` | "Don't use **unless it helps you avoid lengthy, complex wording**." | Excluded (not in the shipped word list). |
394
+ | `freeze`/`frozen` | "Don't use freeze **as a synonym for stop responding**" — i.e. only in that one sense; "freeze" has many unrelated senses (freeze an account, freeze a value). | Excluded from `az-state-failure`. **Correction (fix wave B / Step 9):** this row previously said "not verifier-flagged" — false. Verifier G:76 marks it `CONFIRMED (cond.)` with the exact quote above; the exclusion is a real, verifier-confirmed sense-ambiguity call (the same class as `marquee`, which verifier H explicitly calls "context-flipped"), not an unflagged author guess. |
395
+ | `user` → `customer`/`person`/etc. | "It's OK to use user in content for **developers**." | Excluded — Redocly's docs are developer content, and "user" is one of the most common words in any documentation. |
396
+ | `cursor` (as opposed to `insertion point`) | "Use cursor only for a **technical audience**." | Only `insertion point` → `pointer` ships (unconditional per the same page); bare `cursor` excluded. |
397
+ | `machine` → `computer` | "It's OK to use machine in content for a **technical audience** and... virtualization." | Excluded — "virtual machine", "state machine", "build machine" are all extremely common in Redocly's domain. |
398
+ | `foo, foobar, fubar` | "OK to use these words as placeholders... in content for a **technical audience**." | Excluded (Redocly's docs qualify). |
399
+ | `client` (a person) → `customer` | Not marked `(cond.)` by the verifier — G:93 states it plainly ("Don't use client to refer to a person. Use customer instead."), no carve-out at all. | Excluded anyway (see the fix-wave-B note directly below — this is a considered "anchoring isn't reliable" call, not silent inference). |
400
+ | `utility` → `tool` | Not marked `(cond.)` either — H:55 is also unconditional ("Use tool, not utility, to describe a feature..."). | Excluded anyway (same fix-wave-B note). |
401
+ | `start` (an app) → `open` | Not marked `(cond.)` — G:89 bundles it with `launch` under one unconditional CONFIRMED row. The row bundles `launch, start (an app), boot`; "start" alone is one of the most common words in English with countless unrelated correct senses (start a server, start a request, from the start). | Excluded; only `launch` → `open` and `boot` → `turn on` ship from this row. |
402
+ | `deinstall`, `machine`, `user`, `client`, `utility` cross-checked against Redocly's own domain vocabulary | — | See individual rows above. |
403
+
404
+ **Fix wave B / Step 8 — these three are inference, not a documented guide
405
+ exception, and that distinction matters.** Unlike `header`/`context
406
+ menu`/`disk`/`directory` (Section 5 below), where Microsoft's OWN page
407
+ states "use X only for developers/technical audience," none of `client`,
408
+ `utility`, or `start` carry a live, verifier-quoted conditional for the
409
+ developer-audience sense — G:93 and H:55 (above) are flatly unconditional,
410
+ and G:89 doesn't carve out `start` either. The exclusion is entirely this
411
+ preset's own inference about Redocly's domain, and `PROVENANCE.md` said so
412
+ plainly before this note — candor that was good but stopped one step short
413
+ of a real disposition. Per the brief's instruction ("ship them anchored, or
414
+ document a real guide exception"), anchoring was evaluated for all three
415
+ and rejected for a concrete reason, not skipped:
416
+
417
+ - **`client`** — the person-sense ("the client requested...", "our
418
+ clients") and the software sense ("API client," "client library," "the
419
+ client sends a request") are both extremely common NOUNS in identical
420
+ syntactic positions, with no reliable preceding/following word that
421
+ separates them the way `exit code`/`the product launch`/`boot disk`
422
+ separate Step 4's noun compounds from their verb senses. A negative-
423
+ lookahead anchor (exclude "client" near "library," "SDK," "API," "HTTP,"
424
+ etc.) would still leave bare "Configure the client to retry requests" —
425
+ ordinary, correct API-docs prose — flagged and rewritten to "Configure
426
+ the customer to retry requests." Anchoring isn't reliable; dropped.
427
+ - **`utility`** — same shape: "utility function," "utility script," "a
428
+ small utility for X" are all common, bare, developer-docs nouns with no
429
+ positional cue distinguishing them from the guide's intended sense
430
+ (avoiding "utility" as a vague synonym for "tool" in end-user UI copy).
431
+ Anchoring isn't reliable; dropped.
432
+ - **`start`** — far higher risk than either of the above: "start" is one of
433
+ the most common verbs in English technical writing ("start the server,"
434
+ "start a request," "restart," "from the start," "get started"), used
435
+ correctly in an enormous range of senses having nothing to do with
436
+ "opening an app." Unlike `launch`/`boot`, which have specific noun-
437
+ compound tells to anchor against, "start" has no comparable narrow
438
+ signature — almost every occurrence would need to be excluded. Anchoring
439
+ isn't reliable; dropped.
440
+
441
+ All three remain excluded, same as before this wave — but now as an
442
+ explicit, reasoned "anchoring considered and rejected" disposition, per
443
+ Step 8's own request, rather than a candid-but-open inference note.
444
+
445
+ ### Section 5 — developer-audience carve-outs specific to Redocly
446
+
447
+ Four Tier-1 candidates carry a Microsoft-documented exception for exactly
448
+ the developer/API-documentation audience Redocly serves. **None of the four
449
+ ship**, unconditionally or otherwise — running this preset on Redocly's own
450
+ docs must not flag any of them:
451
+
452
+ | Term | Carve-out |
453
+ |---|---|
454
+ | `header` (meaning heading) → `heading` | Microsoft's own page confirms "it's OK to use header as a short form of file header, as in HTML header" — the standard, correct term for an HTTP/API request/response header throughout API documentation. |
455
+ | `context menu` → `shortcut menu` | "Use context menu only in content for developers." |
456
+ | `disk`/`fixed disk`/`hard disk`/`disk drive` → `hard drive` | "Use disk only in the context of Azure cloud storage and virtual machines" — extremely common, correct in cloud/infra API docs. |
457
+ | `directory` → `folder` | "Use directory only in content for developers... to match the API" — CLI paths, working directory, root directory. |
458
+
459
+ `microsoft-clean.md` includes a paragraph using all four terms in exactly
460
+ this developer sense, and the test suite asserts zero findings on it.
461
+
462
+ ### Section 3 — wrong, inverted, and non-guidance entries corrected
463
+
464
+ | Entry | Problem found | Resolution |
465
+ |---|---|---|
466
+ | `shaded` | **Inverted.** The draft bundled it into an avoid-term row (`gray, grayed out, dimmed, shaded, unavailable, disabled`); the live pages state Microsoft **recommends** "shaded" for the appearance of a checkbox representing a mixture of settings. | `shaded` does not appear anywhere in this preset, in either direction. **Nor does the rest of that row** — no `grayed out` or `dimmed` rule ships here either (an earlier revision of this table claimed one did; it never existed). `recheck/inclusive-language` covers `grayed out` → `unavailable`, sourced from Google. |
467
+ | `boot` → `open` | Wrong direction AND target. Live page: *"Don't use as a verb. Use turn on to refer to turning on power to a device."* | Shipped separately as `boot` → `turn on` (`microsoft/az-lifecycle-verbs`), not bundled with `launch`/`open`. |
468
+ | `corrupt`/`corrupted` → `damaged` | Not guidance — the source asks for an empathetic sentence **rewrite** ("Try to use a more empathetic statement... offer help"); its own worked example never uses "damaged". | Not shipped as a swap. (Detection-only was considered and also dropped for scope reasons — see "Excluded candidates".) |
469
+ | `invalid` → `not valid` | Soft preference: *"Both terms are OK to use, but try to use more specific terms instead"* — a machine-translation-safety tip, not a "never use" rule. | Not shipped. |
470
+ | "afflicted with" (Tier 2) | **Fabricated** — appears nowhere on the live `accessibility-terms` page; the actual Row 5 avoid-list is "Affected by, stricken with, suffers from, a victim of, an epileptic." | Not shipped anywhere. `affected by`/`stricken with`/`suffers from`/`a victim of` (the real Row 5 terms, minus "an epileptic", which needs a sentence rewrite no pattern/swap can safely target) ship in `microsoft/accessibility-terms`. |
471
+ | `ToolTip` regex (Tier 3) | Config-mechanics bug: a shared `ignoreCase: true` flag on the combined "tool tip"/"ToolTip" pattern made the "ToolTip"-only branch also match the already-correct lowercase "tooltip". | `microsoft/tooltip-capitalization` ships as its OWN case-sensitive (`ignoreCase: false`) rule, separate from `microsoft/spelling-hyphenation`'s case-insensitive "tool tip" (spacing) pattern. |
472
+ | Row-1452 "(all don't use)" bundle | **Under-shipped.** The draft filed the whole bundle as detection-only; six of its members have an explicit, single-value replacement stated on their own page. | `friendly name` → `display name`, `print queue`/`printer queue` → `list of documents`, `data record` → `record`, `e-form` → `form`, `upsize` → `scale up` ship in `microsoft/az-real-replacements` (plus `working memory`, `soft copy`, `print out`, `search and replace`, `target drive`/`target file`, which had replacements but weren't singled out by the corrections doc). `property sheet`/`property page` (multi-option: "dialog box or tab") and `application developer` (four very different alternatives: "software developer, web developer, developer, or programmer") are excluded — no single canonical replacement can be chosen without guessing which the guide's authors intended. |
473
+ | Tier 2 accessibility table | **Three crossed pairings.** The draft merged two separate live rows into one, producing wrong swap targets: `handicapped` → `person with a disability` (wrong; that word belongs to Row 3, whose target is "person with limited mobility..."); `differently abled` → `person with a disability` (wrong; Row 9's target is "person with cognitive disabilities, developmental disabilities, learning disabilities, or dyslexia"). | See "Tier 2 design" below — resolved by shipping **detection-only**, sidestepping the wrong-target risk entirely rather than attempting to re-derive every row's exact target from partial verifier quotes. |
474
+
475
+ ### Self-contradictions — enforced in NEITHER direction (three), plus one escalated
476
+
477
+ Four cases where two live Microsoft pages give opposite guidance for the
478
+ same case were found. Three are still enforced in neither direction (no
479
+ rule ships for either side); the fourth, `master/slave`, was escalated in
480
+ the task-10 fix wave to ship detection-only — see the dedicated subsection
481
+ below the table. Both citations for all four are recorded here regardless,
482
+ per the task's instruction:
483
+
484
+ | Case | Page A | Page B |
485
+ |---|---|---|
486
+ | **`%` vs. spelled-out "percent"** | [numbers](https://learn.microsoft.com/en-us/style-guide/numbers) (`ms.date` 2022-05-13): "Use a numeral plus percent to specify a percentage." | [a-z/percent-percentage](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/p/percent-percentage) (`ms.date` 2023-11-15, newer): "Use the percent sign (\"%\") with numerals, rather than spelling out \"percent.\"" |
487
+ | **`master/slave` replacement target** — ESCALATED, ships detection-only, see below | [bias-free](https://learn.microsoft.com/en-us/style-guide/bias-free-communication): "primary/subordinate ← master/slave" | [a-z/master-slave](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/m/master-slave): "Don't use master/slave. Use primary/replica or alternatives such as primary/secondary, principal/agent, controller/worker" — and separately rejects `primary/subordinate` as a synonym for parent/child. |
488
+ | **`etc.`** | [use-us-spelling](https://learn.microsoft.com/en-us/style-guide/word-choice/use-us-spelling-avoid-non-english-words): "It's OK to use etc., in situations where space is limited." | [a-z/etc](https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/e/etc): "Don't use. Instead be specific. When space is limited, use such as or like." |
489
+ | **Forced line breaks** | [writing-all-abilities](https://learn.microsoft.com/en-us/style-guide/accessibility/writing-all-abilities): "Don't force line breaks... within sentences and paragraphs." | [headings](https://learn.microsoft.com/en-us/style-guide/scannable-content/headings): "Break two-line headings carefully... (Shift + Enter inserts a manual line break)." |
490
+
491
+ Also note: `via` is dropped from the Latin-abbreviation swap list on
492
+ verifier E's own recommendation ("Could not independently confirm 'via'
493
+ appears as an example to avoid on any fetched page... Microsoft's own prose
494
+ uses it... low confidence either way, drop it").
495
+
496
+ #### `master/slave` — escalated to detection-only (task-10 fix wave, 2026-07-29)
497
+
498
+ The original authoring pass read the task brief's Step 3 ("the 4
499
+ self-contradictions... enforce NEITHER direction") literally and shipped
500
+ `master/slave` in neither direction, flagging in the final report that
501
+ verifier E's own resolution note was softer (it suggested the A-Z page's
502
+ replacement could still be used as "primary guidance," since the two pages
503
+ agree the TERM should be banned and disagree only on the REPLACEMENT). That
504
+ escalation was correct to raise, and the project owner's decision is: ship
505
+ it, detection-only. Both pages agree `master/slave` must be avoided; they
506
+ disagree only on which replacement to name. Shipping nothing would silently
507
+ permit `master/slave` in a preset that carries an inclusive-language
508
+ mandate — a worse outcome than either replacement choice. `pattern`
509
+ (`microsoft/master-slave`, no `swap`, so no fix of any kind is possible)
510
+ imposes no replacement at all, so "enforce neither direction" still holds
511
+ for the part the guide's two pages actually dispute; its message names
512
+ BOTH candidates (`primary/subordinate` from bias-free-communication;
513
+ `primary/replica`, or the guide's own further alternatives
514
+ `primary/secondary`/`principal/agent`/`controller/worker`, from
515
+ a-z/master-slave) so a human picks the one that fits the context, rather
516
+ than the tool guessing.
517
+
518
+ ### No `metric` rule
519
+
520
+ Microsoft's style guide publishes no readability formula or grade-level
521
+ target anywhere in the ~340 pages fetched across all four verification
522
+ passes. The 7-8 grade / 60-70 Flesch figures some contributors cite come
523
+ from `learn.microsoft.com` Q&A threads about Word's **Editor** feature, not
524
+ the Writing Style Guide itself (confirmed absent on every fetched page).
525
+ Shipping a `metric` rule under a Microsoft citation would misattribute an
526
+ invented numeric mandate. Instead, this preset ships the guide's own real
527
+ structural numbers:
528
+
529
+ | Rule | Assertion | Guide's number | Source |
530
+ |---|---|---|---|
531
+ | `microsoft/paragraph-length` | `length`, `unit: sentences`, `max: 7` | "Three to seven lines is about the right length for a paragraph." | [scannable-content](https://learn.microsoft.com/en-us/style-guide/scannable-content/) |
532
+ | `microsoft/list-length` | `list-length`, `min: 2, max: 7` | "at least two items but (if possible) no more than seven items" | [lists](https://learn.microsoft.com/en-us/style-guide/scannable-content/lists) |
533
+ | `microsoft/comma-density` | `occurrence`, pattern `,`, `max: 2`, `scope: sentence` | "If a sentence contains more than a comma or two and ending punctuation, consider rewriting it" | [punctuation](https://learn.microsoft.com/en-us/style-guide/punctuation/) |
534
+ | `microsoft/alt-text-length` | `length`, `unit: characters`, `max: 150`, `scope: alt` | "Limit the length to 150 characters." | [alternative-text](https://learn.microsoft.com/en-us/style-guide/accessibility/alternative-text) |
535
+
536
+ **Two deliberate deviations from the brief's literal numbers, both found
537
+ empirically while building this preset's own clean fixture, both discussed
538
+ in the final report:**
539
+
540
+ 1. **"Lines" → "sentences" proxy for `paragraph-length`.** Recheck has no
541
+ concept of a rendered "line" in Markdown — line length depends on
542
+ viewport/font, which is meaningless for a structural check. `length`'s
543
+ `sentences` unit is the closest countable proxy for the guide's stated
544
+ range. This is an intentional substitution, not a literal reading of
545
+ "lines" — flagged so a future reader doesn't mistake it for an exact
546
+ quote.
547
+ 2. **`paragraph-length` ships only `max: 7`, not `min: 3`.** The brief's
548
+ Step 4 lists "length with unit: sentences for paragraphs" without
549
+ spelling out both bounds explicitly; a `min: 3` floor was tried first
550
+ (matching the guide's "three to seven" range literally) but flagged more
551
+ than a dozen ordinary, correct paragraphs in a realistic
552
+ API-documentation-shaped clean fixture — single-sentence paragraphs
553
+ (a one-line lead-in to a code block, an image caption, a short
554
+ introductory sentence before a list) are commonplace and entirely
555
+ correct in reference documentation. This is the same class of
556
+ over-firing `recheck/google`'s own `list-length` rule avoided by
557
+ shipping only `min` with no `max` (Google states no upper bound, so only
558
+ the floor is enforced). Shipping only the guide's upper bound here still
559
+ enforces its one genuinely actionable direction (a paragraph that has
560
+ grown too long to scan) without penalizing normal short paragraphs.
561
+
562
+ ### Tier 2 design: detection-only by construction, not merely by caution
563
+
564
+ `microsoft/accessibility-terms` ships as a `pattern` rule (no fixed
565
+ replacement target at all), not a `swap`. This is deliberate, not a
566
+ fallback: the research draft crossed two of the live table's rows (mapping
567
+ "handicapped" and "differently abled" to the WRONG row's replacement, per
568
+ verifier H's finding above) and fabricated a third avoid-phrase
569
+ ("afflicted with"). Detection-only sidesteps that risk entirely: every
570
+ avoid-term shipped is independently confirmed as a genuine term to avoid,
571
+ but no specific replacement is ever prescribed by the rule itself, so
572
+ there is no wrong-target pairing possible — a property that holds
573
+ regardless of how many terms the rule covers.
574
+
575
+ #### Original shipment (task-10, initial pass) — 12 terms, partial table
576
+
577
+ Only the rows/terms directly quoted in `task-10-verify-H.md` were shipped
578
+ at first: `crippled`, `handicapped`, `the handicapped`, `people with
579
+ handicaps`, `slow learner`, `mentally handicapped`, `differently abled`,
580
+ `special needs`, `affected by`, `stricken with`, `suffers from`, `a victim
581
+ of`. Re-deriving exact per-row replacement targets from a table only
582
+ PARTIALLY re-quoted in the verification reports (verifier H's report
583
+ excerpts the errors it found, not a full re-transcription of every row)
584
+ risked reproducing the same crossed-row defect on a row nobody had
585
+ independently re-checked, so the remaining rows were excluded rather than
586
+ guessed at, per Rule 0's discipline. This was flagged in the original
587
+ final report as a known gap for a future pass to close.
588
+
589
+ #### Fix wave (task-10, 2026-07-29) — extended to the complete, independently re-verified 11-row table
590
+
591
+ `task-10-verify-accessibility.md` re-extracted the ENTIRE live table with
592
+ `BeautifulSoup(html5lib)`, reading each `<tr>`'s cells via
593
+ `find_all(["td","th"], recursive=False)` — one atomic tuple per row, never
594
+ three separately-collected column-wide lists zipped back together
595
+ afterward. That is exactly the shape that let the original research draft
596
+ cross two rows; reading a `<tr>` as one tuple makes it structurally
597
+ impossible here, regardless of which terms end up shipped. **Row count
598
+ correction**: the table has **11** rows, not the "ten rows" the original
599
+ Tier 2 design note (above) and `task-10-verify-H.md` described — Rows 8
600
+ and 10 both list "special needs" mapped to two DIFFERENT preferred
601
+ replacements ("person with cognitive disabilities, developmental
602
+ disabilities, learning disabilities, or dyslexia" for Row 8 vs. "functional
603
+ needs (or paraphrase according to the specific disability)" for Row 10) —
604
+ a genuine self-contradiction on Microsoft's own page. The original count
605
+ folded that duplicate into one logical entry instead of counting DOM rows
606
+ literally; both readings describe the same page content, and since
607
+ `special needs` ships as a single already-existing token either way (not
608
+ two separately-targeted rules), the miscount never affected what shipped.
609
+
610
+ All six previously-excluded named terms are now `CONFIRMED` on their own
611
+ distinct row, and eight further rows/terms were found that no earlier
612
+ pass had covered. `microsoft/accessibility-terms` is extended from 12 to
613
+ 30 tokens, still 100% `pattern` (detection-only) — the extension adds
614
+ NO swap pairs and therefore carries none of the original crossed-row risk.
615
+ Per-row treatment, from the verified table:
616
+
617
+ - **Specific, low collision risk — shipped without extra guards:**
618
+ `sight-impaired`, `vision-impaired` (Row 0), `hearing-impaired` (Row 1),
619
+ `non-verbal` (Row 3), `maimed`, `missing a limb`, `birth defect` (Row 6),
620
+ `Special Ed person` (Row 8), `normal person`/`healthy person` (Row 5,
621
+ matched as the guide's own two-word PHRASE — see the technical-meaning
622
+ collision note below), `Asperger's` (Row 9, matched with BOTH the
623
+ verified straight U+0027 apostrophe and the curly U+2019 form, since a
624
+ curly one would not match a straight-only pattern).
625
+ - **Needs a human, not a substitution — shipped anyway, since this is
626
+ `pattern` and no substitution is ever offered:** `dumb`/`mute` (Row 3 —
627
+ the row's own ONLY avoid-terms; no Acceptable-column alternative exists
628
+ for this row at all), `lame` (Row 2), `stupid` (Row 8), `an epileptic`
629
+ (Row 4 — the guide's own replacement is a condition-specific sentence
630
+ rewrite, "has multiple sclerosis, cerebral palsy, a seizure disorder, or
631
+ muscular dystrophy," not a term any `pattern`/`swap` rule can respell).
632
+
633
+ **Reversed decision: bare `lame` is no longer excluded.** The original
634
+ design excluded `lame` even from detection-only ("at least as common in
635
+ the unrelated idiom 'lame excuse'/'lame joke' as in any disability
636
+ reference, and Recheck has no way to distinguish the senses"). The fix
637
+ wave re-decided to ship it: since this rule is `pattern`-only, flagging
638
+ `lame` never risks an auto-rewrite of "lame excuse" — only a flag a human
639
+ can dismiss — and the same tolerance already applies to `stupid` (also
640
+ shipped, also a common general-purpose pejorative). `stupid` was not
641
+ previously excluded by name; it simply had not been added.
642
+
643
+ #### Technical-meaning collisions, scope-limited rather than shipped bare
644
+
645
+ - **`mute`** has an extremely common, entirely correct, unrelated
646
+ technical sense as an audio/UI control ("mute the microphone," "mute
647
+ notifications," a mute button/icon). A preset that rewrites "mute the
648
+ audio track" would be worse than one that stays quiet — and even a bare
649
+ `pattern` flag on every "mute" in a technical corpus would be mostly
650
+ noise. Scoped instead to the shapes real ableist usage and the guide's
651
+ own phrasing actually take: predicate-adjective position
652
+ (`is`/`was`/`are`/`were`/`being`/`been` + `mute`) and the compound forms
653
+ `deaf and mute`/`deaf-mute`. This deliberately does NOT match "mute the
654
+ X," "on mute," or "mute button/icon/notifications" — the dominant
655
+ audio-control phrasings — because none of those put "mute" in predicate
656
+ position or in the disability-specific compound.
657
+ - **`normal`** (Row 5, as "normal person"/"healthy person") is matched as
658
+ the guide's own two-word PHRASE, never the bare word. Bare `\bnormal\b`
659
+ would also match a statistical "normal distribution" or "normalize a
660
+ value" — senses the guide never addresses — while the phrase-level match
661
+ is very unlikely to collide with either.
662
+ - **`dumb`** carries a secondary, weaker collision (a dated technical sense
663
+ in "dumb terminal"/"dumb pipe") and the much more common "not smart"
664
+ insult sense. Shipped bare anyway, on the same reasoning as `lame`/
665
+ `stupid` above: this is detection-only, so the cost of an occasional
666
+ false-positive flag is far lower than the cost of leaving a genuinely
667
+ offensive, guide-confirmed term completely unflagged.
668
+ - **`an epileptic`** is guarded with a negative lookahead excluding
669
+ `seizure`/`episode`/`fit`/`attack`/`event` immediately after — "an
670
+ epileptic seizure" is ordinary, correct medical usage (an adjective
671
+ describing the EVENT), not the guide's objection (calling a PERSON "an
672
+ epileptic" instead of "a person with... a seizure disorder").
673
+ - **`non-verbal`** has a narrower, secondary technical sense too ("non-verbal
674
+ communication," "non-verbal cues" in UX/behavioral-design writing) — a
675
+ real but low-probability collision, accepted here per the "specific, low
676
+ collision risk" classification above; unlike `mute`, it was not judged to
677
+ need a scope guard.
678
+
679
+ ### Known limitations
680
+
681
+ Verifier-confirmed guide-sanctioned exceptions or two-part tests the shipped
682
+ rules do not fully implement — recorded here, not just in each rule's
683
+ own comment, per the same discipline `recheck/google`'s provenance uses:
684
+
685
+ 1. **`microsoft/alt-text-format` doesn't model the "if practical for the
686
+ image type" carve-out** (verifier F, A3). Some alt text is legitimately
687
+ a fragment that doesn't end in a period "if doing so is practical" —
688
+ the shipped rule requires the period unconditionally.
689
+ 2. **`microsoft/sign-in-sign-out`'s guide exception is a two-part test**
690
+ (verifier F, U7): "unless it appears in the UI" AND "you're writing
691
+ instructions" — both conditions are required, but Recheck has no way to
692
+ know whether a matched string is a literal UI label being quoted.
693
+ 3. **`microsoft/no-space-around-em-dash` doesn't distinguish contexts for
694
+ en dashes at all** — deliberately: the guide's spaced-en-dash exception
695
+ for UI timestamps is common enough, and structurally indistinguishable
696
+ from the ordinary prohibited case, that this preset does not attempt to
697
+ enforce en-dash spacing in either direction. See the self-contradictions
698
+ section.
699
+ 4. **`microsoft/accessibility-terms` still doesn't offer any replacement
700
+ text (it's `pattern`, not `swap`), even after the task-10 fix wave
701
+ extended it to the complete, re-verified 11-row table** — a deliberate
702
+ choice, not a residual gap: no wrong-target pairing is possible only
703
+ because no target is ever prescribed. A reader wanting the specific
704
+ replacement for a flagged term should consult "Tier 2 design" above or
705
+ the live `accessibility-terms` page directly.
706
+ 5. **No rule enforces C21 (first-mention acronym expansion).** The
707
+ research draft's `conditional`-based approach (`microsoft/expand-sso`,
708
+ one rule per acronym) needs a project-specific list of acronym/expansion
709
+ pairs the guide itself doesn't provide, and the live page carries three
710
+ documented carve-outs (don't introduce a parenthetical for a single-use
711
+ acronym; some acronyms like USB/FAQ/URL should never be spelled out at
712
+ all; avoid first use in a heading/title unless needed for SEO) that the
713
+ draft's rule shape ignores entirely. `conditional` remains a documented
714
+ opt-in (`DOCUMENTED_OPT_IN_ASSERTIONS`), unchanged from before this task.
715
+
716
+ ### Author's judgment calls
717
+
718
+ Decisions this preset's author made that go beyond a verifier's literal
719
+ verdict, recorded per the task's instruction to flag (not silently
720
+ resolve) anything not settled by the inputs:
721
+
722
+ 1. **`master/slave` — originally shipped in NEITHER direction per the
723
+ brief's literal Step 3 instruction, then ESCALATED to detection-only in
724
+ the task-10 fix wave (2026-07-29) after the project owner's review.**
725
+ The original judgment call (flagging the tension between the brief's
726
+ literal wording and verifier E's softer resolution note) was confirmed
727
+ correct to raise; the settled decision is `microsoft/master-slave` as a
728
+ `pattern` rule naming both candidate replacements — see the
729
+ "Self-contradictions" section's dedicated subsection above for the full
730
+ reasoning.
731
+ 2. **Several additional Tier-1 entries excluded on audience-collision
732
+ grounds beyond the ten the corrections doc named** (`deprecated`,
733
+ `user`, `client`, `utility`, `cursor`, `machine`, `start`, `and/or`,
734
+ `freeze`/`frozen`) — see "Additional tier-boundary findings" and
735
+ "TOO-RISKY" above. Each is defensible individually, but collectively
736
+ this is a broader exclusion than the corrections doc's own five-plus-
737
+ five enumeration, reflecting Redocly's specific developer-documentation
738
+ audience.
739
+ 3. **`paragraph-length` ships with only `max: 7`, dropping the guide's
740
+ `min: 3`** — an empirical finding from building this preset's own clean
741
+ fixture, not a verifier's instruction. See the "No `metric` rule"
742
+ section's deviation note.
743
+ 4. **Tier 2 (`microsoft/accessibility-terms`) ships as `pattern`
744
+ (detection-only), not `swap`** — a stricter design choice than the
745
+ draft's swap-based approach, made specifically to make the crossed-row
746
+ defect class structurally impossible rather than merely fixed for the
747
+ three rows verifier H happened to catch. Extended in the task-10 fix
748
+ wave (2026-07-29) from 12 to 30 tokens (11 rows, complete) per
749
+ `task-10-verify-accessibility.md`'s bounded, atomic-per-`<tr>`
750
+ re-extraction — still entirely `pattern`, so the extension carries none
751
+ of the crossed-row risk the original design was built to avoid. See
752
+ "Tier 2 design" above for the per-term treatment and the two
753
+ technical-meaning collisions (`mute`, `normal`) scoped rather than
754
+ shipped bare.
755
+ 5. **`microsoft/actionable` and the VERB-ABLE/CASE-ONLY buckets use
756
+ "rewrite" language in their messages, not "replace"** — matching
757
+ `recheck/google`'s own established convention for the same fix-safety
758
+ classes.
759
+ 6. **No `microsoft/proper-names` rule ships**, matching `recheck/google`'s
760
+ own precedent of leaving brand/product vocabulary to the user's config.
761
+ 7. **The broader militaristic-language word bundle and C20's illustrative
762
+ phrase pairs are excluded** even though the underlying principle is
763
+ CONFIRMED, because no verifier independently quoted exact replacement
764
+ text for the specific multi-word examples — see the relevant sections
765
+ above.
766
+
767
+ ### Engine/registry changes this preset required
768
+
769
+ - **`occurrence` moved from `DOCUMENTED_OPT_IN_ASSERTIONS` to "shipped in a
770
+ preset"**, the same way `length` moved when `recheck/google` shipped it.
771
+ `microsoft/comma-density` gives it a real, guide-sourced default
772
+ (`max: 2`), so its bounds are no longer "no one right answer for
773
+ everyone." `conditional` and `metric` remain opt-in — see "Known
774
+ limitations" #5 and the "No `metric` rule" section for why neither ships
775
+ here either.
776
+ - **`recheck/microsoft` registered in `src/config/presets/index.ts`** and
777
+ added to the six-preset list `presets.test.ts` pins.
778
+ - **README.md's preset list and "Opt-in prose assertions" section updated**
779
+ to describe the new preset and reflect `occurrence`'s move out of the
780
+ opt-in list (mirroring how `length`'s move was documented for
781
+ `recheck/google`).
782
+ - No `schema.ts` or `case-preserve.ts` changes were needed: Task 9 already
783
+ widened the rule-key pattern to `^[a-z][a-z0-9-]*/[a-z0-9-_]+$` and fixed
784
+ `applyMatchCase`'s multi-word-replacement shouting bug before this task
785
+ began (see `task-10-resolutions.md`'s Constraints 1 and 3) — both were
786
+ verified against the LIVE code, not assumed, before authoring any rule
787
+ here (see the final report's "Constraint verification" section).
788
+
789
+ ### Fix wave (2026-07-29)
790
+
791
+ Base commit `8d7b32cb506`, branch `aa/recheck-style-guides`. Three items
792
+ from the fix brief (`.superpowers/sdd/task-10-fix-brief.md`):
793
+
794
+ 1. **`master/slave` escalated from "ships in neither direction" to
795
+ detection-only.** New rule `microsoft/master-slave` (`pattern`, no
796
+ `swap`) — see the "Self-contradictions" section's dedicated
797
+ subsection and the "Bias-free, militaristic, derogatory language"
798
+ table row.
799
+ 2. **`microsoft/accessibility-terms` extended from 12 to 30 tokens** —
800
+ the complete, independently re-verified 11-row table from
801
+ `task-10-verify-accessibility.md` (a bounded, atomic-per-`<tr>`
802
+ re-extraction that supersedes `task-10-verify-H.md`'s partial one for
803
+ this rule). Row-count correction (10 → 11) and per-term treatment are
804
+ in "Tier 2 design" above. Still 100% `pattern` — no swap pairs were
805
+ added, so the extension carries none of the crossed-row risk the
806
+ original detection-only design exists to avoid. Two technical-meaning
807
+ collisions (`mute`, `normal`) were scoped rather than shipped bare;
808
+ `an epileptic` is guarded against the legitimate "epileptic
809
+ seizure/episode/fit/attack/event" medical phrasing; bare `lame` (fully
810
+ excluded before this wave, even from detection-only) and `stupid`
811
+ ship, accepting their ordinary-usage collision on the same reasoning
812
+ the rule already applied to other pattern-only entries.
813
+ 3. **`microsoft/paragraph-length`'s `min: 3` deviation** — re-checked
814
+ against the fix brief's Item 3 and found ALREADY fully documented (the
815
+ "No `metric` rule" section's "Two deliberate deviations" note and
816
+ "Author's judgment calls" #3, both present before this wave): the
817
+ empirical reason (a dozen ordinary short paragraphs over-firing) and
818
+ the source URL (scannable-content) were already recorded. No edit was
819
+ needed for this item; noted here so the fix wave's own scope is
820
+ auditable.
821
+
822
+ Plus two documentation carry-overs from `recheck/google` (Item 4, touching
823
+ files outside this preset — not duplicated here, see those files
824
+ directly):
825
+
826
+ - `presets/google/PROVENANCE.md`'s "Known limitations" section gained a
827
+ third entry noting `google/gcp-name` is case-sensitive (no
828
+ `ignoreCase`), so `gcp`/`Gcp` are neither flagged nor fixed.
829
+ - `src/core/case-preserve.ts`'s `applyMatchCase` doc comment gained a
830
+ "KNOWN EDGE" paragraph naming the hyphen/dot-only "multi-word" gap
831
+ (`ECOMMERCE` → `E-COMMERCE`, `NODEJS` → `NODE.JS`) — comment only, no
832
+ behavior change; the behavior is under separate review approval.
833
+
834
+ Verification: `pnpm build` (dist re-emitted after deleting
835
+ `tsconfig.tsbuildinfo`/`tsconfig.typecheck.tsbuildinfo`), `pnpm test`,
836
+ `pnpm parity` (27425 = 27425, unchanged), `npx nx run recheck:lint
837
+ --max-warnings=0`, plus both preset acceptance gates (every rule and pair
838
+ fires on the violations fixture; the clean fixture reports zero, extended
839
+ with near-misses for a legitimate audio "mute" and a statistical "normal
840
+ distribution") — see `.superpowers/sdd/task-10-report.md`'s "## Fix wave"
841
+ section for the full command output.
842
+
843
+ ### Fix wave B (2026-07-30)
844
+
845
+ Base commit `e629b0f141f`, branch `aa/recheck-style-guides`. Fix brief:
846
+ `.superpowers/sdd/task-10-fixB-brief.md`. An independent review found the
847
+ same defect class that took `recheck/google` three fix waves: the clean
848
+ fixture and per-token gate had no A–Z Tier-1 near-miss coverage, so ~15
849
+ Tier-1 pairs corrupted correct prose (and `us-spelling` collapsed
850
+ inflections onto a single literal replacement) behind an all-green suite.
851
+
852
+ #### Step 1 — clean fixture extended with A–Z Tier-1 near-misses
853
+
854
+ `microsoft-clean.md` gained an "Avoid-term near-misses" section covering
855
+ all 13 minimum-named phrases plus a 14th (`hangs on to`, the phrasal-verb
856
+ collision Step 4 also names). Before any Step 3/4 fix, this section alone
857
+ produced 14 false-positive findings — every one of the corrupting pairs
858
+ below, confirming the gate is real (see the report for the exact list).
859
+
860
+ #### Step 2 — per-token gate inverted to derive from the live preset
861
+
862
+ `preset-microsoft.test.ts`'s pattern-token coverage test used to iterate
863
+ `Object.entries(PATTERN_TOKEN_EXAMPLES)` — the map's own keys — so a
864
+ `pattern` rule with no entry in the map was invisible to it. It now
865
+ iterates `Object.entries(preset)` and requires every rule with a `pattern`
866
+ assertion to have a registered entry, the same inversion Task 10's
867
+ original fix wave applied to the per-pair gate. Before this fix wave, the
868
+ map covered 2 of 24 `pattern` rules (32 of 67 tokens); it now covers all of
869
+ them (67 tokens pre-wave, growing to more as Step 4 moved some `swap` pairs
870
+ to detection-only `pattern` siblings — see the report for the final
871
+ count).
872
+
873
+ #### Step 3 — `us-spelling` inflection collapse fixed
874
+
875
+ Every alternation-group key (`(s)?`, `(ed|ing)`, `(e|es|ed|ing|ation)`) was
876
+ split into one literal pair per inflection — `centre`/`centres`,
877
+ `cancelled`/`cancelling`, `authoris(e/es/ed/ing/ation)`,
878
+ `customis(e/es/ed/ing/ation)`, `labelled`/`labelling`,
879
+ `modelled`/`modelling` — each mapped to its own correct US-spelling target
880
+ (`canceling`, not just `canceled`, for the `-ing` form; matches G:174/G:175,
881
+ which the previous single-target replacement contradicted for both
882
+ `cancelling`→`canceling` and `labelling`→`labeling`). `favou?rite` (not an
883
+ alternation group, but the same class of bug: the optional `u` matched the
884
+ already-correct `favorite` spelling too) narrowed to `favourite` only.
885
+ `dialogue box` removed from this rule (see Step 7 — it already ships in
886
+ `microsoft/dialog-terminology`).
887
+
888
+ #### Step 4 — ~15 Tier-1 pairs no longer rewrite correct prose
889
+
890
+ | Pair | Disposition | Reasoning |
891
+ |---|---|---|
892
+ | `SMB` → `small or medium-sized business` | **Dropped** | Homograph with the Server Message Block protocol, common in exactly Redocly's domain ("mount the SMB share"); no positional anchor distinguishes the senses. |
893
+ | `SKU` → `edition` | **Dropped** | `SKU` is standard e-commerce/inventory vocabulary in API docs; also carried an independent ALL-CAPS-shout hazard (single-word replacement) and a multi-target guide quote (subscription/edition/version/tier). |
894
+ | `terminate` → `close` | **Dropped** | "Terminate the instance/process/session" is standard, correct cloud-infrastructure vocabulary in Redocly's own domain — a genuine meaning change, not a false match, with no reliable anchor. |
895
+ | `exit` → `close` | **Anchored, stays fixable** | Lookaround excludes preceding "the/an/no/emergency" and following "code/status/button/sign/strategy/interview/poll/ramp/velocity/row" (noun-compound senses). |
896
+ | `launch` → `open` | **Anchored, stays fixable** | Excludes preceding "product/software/game/website/app/feature/rocket/mission" and following "date/event/party/window/site/pad/day/plan/schedule/announcement". |
897
+ | `boot` → `turn on` | **Anchored, stays fixable** | Excludes following "disk/sector/loader/sequence/process/time/options/record/partition/menu/order/camera". |
898
+ | `crash` → `fail` | **Moved to detection-only** (`az-state-failure-detect`) | G:73 gives a hardware/software target split ("fail" vs. "stop responding") a single `swap` replacement can't express; also anchored against "crash dump/report/log/course/test/site". |
899
+ | `lock up` → `fail` | **Moved to detection-only** (`az-state-failure-detect`) | Same hardware/software split (G:77). |
900
+ | `hangs`/`hang` → `stops responding`/`stop responding` | **Anchored, stays fixable** | Excludes "hang(s) on/up/around/out/together" (retain, end a call, loiter — unrelated phrasal verbs). |
901
+ | `roman` → `regular type` | **Anchored, stays fixable** | Excludes the civilization/proper-noun sense ("Roman numerals/Empire/alphabet/calendar/..."), a homograph collision of the `aka`-inside-`Akamai` shape. |
902
+ | `italics`/`italicized` → `italic` | **Moved to detection-only** (`microsoft/italic-as-noun`) | The guide's own rule ("use only as an adjective, not a noun") makes a direct swap ungrammatical in exactly the position the avoid-term occupies — the same class as `actionable`. |
903
+ | `blade` → `pane` | **Anchored, stays fixable** | Excludes following "server(s)/enclosure/chassis/center(s)/centre(s)" (physical hardware sense). |
904
+ | `beta` → `preview` | **Anchored, stays fixable** | Excludes following "distribution/function/coefficient/particle/blocker/decay" (statistical/physics/medical senses). |
905
+ | `visit` → `go to` | **Anchored, stays fixable** | Excludes following "count(s)/duration/frequency/history/log/data" (analytics-metric noun sense). |
906
+ | `in addition` → `also` | **Anchored, stays fixable** | Excludes following "to" (the standard, grammatically necessary preposition phrase "in addition to X", distinct from the guide's stand-alone-adverb target). |
907
+ | `print out` → `print` | **Anchored, stays fixable** | Excludes following "of" (noun sense — "a print out of X" — distinct from the guide's verb-scoped rule). |
908
+
909
+ #### Step 5 — the "CONFIRMED unconditional" comment corrected
910
+
911
+ The A-Z word list section's header comment claimed every pair was
912
+ "CONFIRMED unconditional by at least one verifier." Fifteen pairs
913
+ contradicted it; each was moved to a `*-detect` `pattern` sibling,
914
+ `fix: false`, or dropped (`terminate`, covered under Step 4):
915
+
916
+ - **Moved to detection-only:** `quit`/`deinstall`/`reinitialize`
917
+ (`az-lifecycle-verbs-detect`; G:88 multi-target, G:156/H:52 "(cond.)"),
918
+ `crash`/`lock up` (`az-state-failure-detect`; Step 4), `bottom
919
+ left`/`bottom right` (`az-direction-layout-detect`; G:106, the
920
+ BottomLeft/BottomRight API-property carve-out), `thank you`
921
+ (`az-geography-detect`; H:62), `hierarchical menu`/`secondary
922
+ menu`/`running head`/`running foot` (`az-ui-nouns-detect`; G:146/H:93),
923
+ `pound sign` (`az-abbreviations-names-detect`; H:115).
924
+ - **`fix: false`:** `left-hand`/`right-hand` (new rule
925
+ `microsoft/left-hand-right-hand`; G:108 marks these DETECT-ONLY outright
926
+ — no replacement is stated on the live page for the modifier sense, so
927
+ shipping one fixable was itself the defect).
928
+ - **Already detection-only, no change needed:** `backbone`/`natural user
929
+ interface` (G:187/G:190) already ship as `pattern` in
930
+ `microsoft/az-no-replacement`. `indices` is excluded entirely (see Step
931
+ 5's own TOO-RISKY note below), so its math-carve-out marker (G:168) never
932
+ reached a shipped pair either.
933
+
934
+ Every OTHER pair in the A-Z word list remains CONFIRMED unconditional by at
935
+ least one verifier, and the section header comment now says so precisely
936
+ instead of unconditionally.
937
+
938
+ #### Step 6 — provenance defects (links, sources.json)
939
+
940
+ 1. **17 of 82 `link:` fields 404'd.** Every rule that cited bare `AZ_BASE`
941
+ (16 rules pre-wave, more after Step 4/5 added new rules) now cites the
942
+ real, live, term-specific page for one of its pairs (verified via
943
+ `curl`, HTTP 200, for every one — see the table below). `microsoft/
944
+ mouse-over`'s slug carried a spurious "-and-"
945
+ (`mouse-and-mouse-interaction-terms`, 404) corrected to
946
+ `mouse-mouse-interaction-terms` (200) — verifier G's own fetch log
947
+ named the correct slug at G:200; the verifier never cited the broken
948
+ form itself.
949
+
950
+ | Rule | New link slug |
951
+ |---|---|
952
+ | `spelling-hyphenation` | `e/email` |
953
+ | `az-case-only` | `i/internet-intranet-extranet` |
954
+ | `az-verb-able` | `b/blacklist` |
955
+ | `az-state-failure` | `h/hang` |
956
+ | `az-state-failure-detect` | `c/crash` |
957
+ | `az-lifecycle-verbs` | `b/boot` |
958
+ | `az-lifecycle-verbs-detect` | `q/quit` |
959
+ | `az-judgment-words` | `f/finalize` |
960
+ | `az-geography` | `f/far-east` |
961
+ | `az-geography-detect` | `t/thanks-thank-you` |
962
+ | `az-direction-layout` | `f/far-left-far-right` |
963
+ | `az-direction-layout-detect` | `b/bottom-left-bottom-right` |
964
+ | `left-hand-right-hand` | `l/left-leftmost-left-hand` |
965
+ | `az-ui-nouns` | `b/blade` |
966
+ | `az-ui-nouns-detect` | `h/hierarchical-menu` |
967
+ | `az-typography` | `r/roman` |
968
+ | `az-filesystem` | `c/child-folder` |
969
+ | `az-grammar-usage` | `a/as-well-as` |
970
+ | `az-abbreviations-names` | `h/hexadecimal` |
971
+ | `az-abbreviations-names-detect` | `n/number-sign` |
972
+ | `az-navigation` | `v/visit` |
973
+ | `bookmark-favorite` | `b/bookmark` |
974
+ | `az-no-replacement` | `b/black-box` |
975
+ | `az-real-replacements` | `f/friendly-name` |
976
+
977
+ All 60 distinct URLs this preset's rules now cite were verified live
978
+ (`curl -A "Mozilla/5.0..."`) at HTTP 200 as of 2026-07-30.
979
+
980
+ 2. **Two `sources.json` entries hashed Microsoft's 404 page.** The bare
981
+ `a-z-word-list-term-collections/` index and the broken `mouse-and-mouse`
982
+ slug both hashed `bytes: 1898` / `sha256: 0ea4f717...` — the same 404
983
+ page. Both entries are gone from `sources.json` (no rule links to the
984
+ bare index anymore; the mouse-over rule now links to the corrected,
985
+ working slug, hashed at its real content: 24140 bytes). An `httpStatus`
986
+ field was added to every entry so a dead URL is visible at a glance
987
+ instead of only discoverable by noticing two entries collide.
988
+ `sources.json` now hashes 60 pages (was 35), all `httpStatus: 200`, all
989
+ independently reproducible (fetched twice, byte-identical both times).
990
+ 3. **`## Excluded candidates` now carries URLs.** The TOO-RISKY table's 18
991
+ rows each gained a "Source(s)" column with the live, verified page(s)
992
+ backing that exclusion — see the table itself, above.
993
+
994
+ #### Step 7 — double-reporting spans resolved
995
+
996
+ - `labelled`/`labelling` used to fire both `us-spelling` ("labeled"/
997
+ "labeling", now correct per Step 3) and `az-grammar-usage` ("labeled"/
998
+ "labeling") — removed from `az-grammar-usage`, matching the precedent
999
+ that rule's own comment already documented for `multi-factor`.
1000
+ - `dialogue box` used to fire both `us-spelling` and `dialog-terminology`
1001
+ (same target, "dialog", in both) — removed from `us-spelling` (Step 3);
1002
+ `dialog-terminology` is the more specific, complete owner (it already
1003
+ bundles `dialog box`/`pop-up window` in the same rule).
1004
+
1005
+ #### Step 8 — `client`/`utility`/`start` given a real disposition, not just candor
1006
+
1007
+ All three remain excluded (unchanged from before this wave), but the
1008
+ PROVENANCE note is now a considered "anchoring was evaluated and rejected"
1009
+ call, not an open inference aside — see "Additional tier-boundary
1010
+ findings" above for the per-term reasoning (each fails for a different,
1011
+ specific reason: `client`/`utility` have no positional anchor separating
1012
+ the person/vague sense from the dominant technical one; `start` is too
1013
+ polysemous for any anchor to meaningfully narrow).
1014
+
1015
+ #### Step 9 — accuracy fixes
1016
+
1017
+ - `case-preserve.ts`'s "KNOWN EDGE" note claimed no shipped pair hit the
1018
+ hyphen/dot-joined-replacement shouting gap — false; `spelling-
1019
+ hyphenation`'s `ecommerce`→`e-commerce`/`elearning`→`e-learning`/
1020
+ `ebook`→`e-book` all do (comment corrected, no behavior change).
1021
+ - `az-case-only`'s claim that all 17 pairs would "silently no-op" was
1022
+ verified against the live `applyMatchCase` function directly (not
1023
+ re-reasoned by eye): true for only 7 (`Internet`, `Intranet`,
1024
+ `Extranet`, `Euro`, `WWW`, `Registry`, `Spam`). The other 10 (`Big
1025
+ Data`, `Dark Mode`, `darkmode`, `Devops`, `devops`, `bluetooth`,
1026
+ `boolean`, `Javascript`, `javascript`, `World Wide Web`) now ship
1027
+ fixable in a new rule, `microsoft/az-case-fixable`, instead of
1028
+ reporting forever for no reason.
1029
+ - `bookmark`→`favorite` moved to `fix: false` (new rule
1030
+ `microsoft/bookmark-favorite`) — resolutions C4 names it VERB-ABLE.
1031
+ - `all right` added to the `okay`/`alright`→`OK` pair (H:120 names three
1032
+ terms; only two shipped).
1033
+ - `freeze`/`frozen`'s exclusion note corrected — it previously said "not
1034
+ verifier-flagged"; G:76 marks it `CONFIRMED (cond.)`. The exclusion
1035
+ itself was already correct; only the note's claim about its sourcing
1036
+ was wrong.
1037
+
1038
+ #### Gates — command and output
1039
+
1040
+ See `.superpowers/sdd/task-10-report.md`'s "## Fix wave B" section for the
1041
+ full command output (`pnpm build`, `pnpm test`, `pnpm parity` — unchanged
1042
+ at 27425 = 27425 — `npx nx run recheck:lint --max-warnings=0`) and the four
1043
+ acceptance-gate results (every Step 3/4 string fixed twice without
1044
+ corruption; the nine collision probes still clean; every rule/pair/token
1045
+ firing; the clean fixture, extended with the new near-misses, reports
1046
+ zero).
1047
+
1048
+ ### Fix wave C (2026-07-30)
1049
+
1050
+ Base commit `b29fa202046`, branch `aa/recheck-style-guides`. Fix brief:
1051
+ `.superpowers/sdd/task-10-fixC-brief.md`. A reviewer probed four pairs in
1052
+ one rule family nobody had named (`az-grammar-usage`) and found two more
1053
+ corrupting pairs — both marked plain `CONFIRMED` by a verifier, with the
1054
+ correct quote attached:
1055
+
1056
+ - `as well as` → `and`: a/as-well-as says *"Don't use as a synonym for
1057
+ and"* — a caution against treating the two as interchangeable, not an
1058
+ instruction to replace the text.
1059
+ - `or greater`/`or higher`/`or lower` → `or later`/`or earlier`:
1060
+ g/greater-better and h/higher scope this to *"identifying multiple
1061
+ versions of programs or apps"* — a version-number rule, not a
1062
+ general-magnitude rule.
1063
+
1064
+ #### The root cause, restated precisely
1065
+
1066
+ A `CONFIRMED` verdict means *the live page genuinely discusses this term*.
1067
+ It does **not** mean *a blind textual substitution of that term is safe*.
1068
+ Those are two different properties, and this task's verification passes
1069
+ (E/F/G/H/accessibility) only ever checked the first one. Fixing the two
1070
+ named pairs without addressing the conflation would have left the rest of
1071
+ the class shipped, so Step 1 was an audit of **every** fixable pair against
1072
+ the substitution question, not a two-line patch.
1073
+
1074
+ #### Step 1 — full audit of every fixable pair
1075
+
1076
+ **230 fixable pairs audited** (225 `swap` pairs across 40 rules with `fix`
1077
+ not set to `false`, plus 5 `consistency` `either` pairs — the complete set
1078
+ `Object.keys(preset)` produces when filtered to fixable `swap`/`consistency`
1079
+ assertions). Method: re-read each pair's verifier-row quote in
1080
+ `task-10-verify-{E,F,G,H}.md`; where the quote was ambiguous as to shape
1081
+ (narrower scope, multi-target, or a caution-vs-instruction distinction), the
1082
+ live page was re-fetched with `curl` + a local `html5lib`/BeautifulSoup
1083
+ parse (never inferred from the row alone) — 29 pages re-fetched this wave,
1084
+ all HTTP 200.
1085
+
1086
+ **10 pairs reclassified** from fixable to detection-only or `fix: false`:
1087
+
1088
+ | Pair | Rule (before → after) | Guidance shape (live quote) | Why unsafe as a blind swap |
1089
+ |---|---|---|---|
1090
+ | `as well as` → `and` | `az-grammar-usage` → `az-grammar-usage-detect` (`pattern`) | "Don't use **as a synonym for** and." | Caution against conflation, not an instruction to replace — "As well as being fast, the API is reliable." → "And being fast, the API is reliable." is not grammatical; "and" can't head a sentence the way a subordinating phrase can. |
1091
+ | `or greater` → `or later` | `az-grammar-usage` → `az-grammar-usage-detect` | "Don't use greater or better to mean or later **when identifying multiple versions of programs or apps**." | Scoped to version numbers; "a score of 80 or greater" → "a score of 80 or later" is nonsensical for a magnitude. |
1092
+ | `or higher` → `or later` | `az-grammar-usage` → `az-grammar-usage-detect` | Same page, PLUS: "It's OK to use higher to refer to **display resolution**. Don't use higher to refer to **processor speed**. Use **faster** instead." | Scoped to version numbers AND multi-target (resolution: unchanged; processor speed: "faster"; version: "later") — no single literal replacement covers all three senses the live page itself names. |
1093
+ | `or lower` → `or earlier` | `az-grammar-usage` → `az-grammar-usage-detect` | "Don't use **to indicate product version numbers**. Use earlier instead." | Same version-number scoping; "lower" is an ordinary magnitude word (price, temperature, priority) in every other context. |
1094
+ | `leverage`/`leveraging`/`leveraged` → `use`/`using`/`used` | `microsoft/leverage`, `fix: false` | "Don't use **as a verb** to mean take advantage of. Use take advantage of, use, or **another more appropriate word or phrase**." | Verb-sense-scoped AND multiple-alternatives. "Leverage"/"leveraged" are also common, correct nouns/adjectives this page never addresses ("financial leverage", "a highly leveraged company", "a leveraged buyout") — unlike `impact-verb`, there's no small enumerable set of following objects to anchor on (leverage takes almost any direct object), so detection-only is the fallback, not an abandoned first attempt. |
1095
+ | `glyph` → `symbol` | `microsoft/glyph`, `fix: false` | "Don't use to refer generically to a graphic... on a button, on an icon, or in a message box. Use symbol instead. **It's OK to use glyph in a technical discussion of fonts and characters.**" | Context-scoped exactly like the developer-audience carve-outs already excluded for `header`/`disk`/`directory`/`context menu` — font/Unicode documentation (plausible in Redocly's own docs) uses "glyph" as a precise technical term this page's objection doesn't cover, and no anchor tells "technical discussion of fonts" apart from the generic-icon sense. |
1096
+ | `de facto` → `in practice` | `no-latin-abbreviations` → `no-latin-abbreviations-detect` (`pattern`) | use-us-spelling's ONLY sentence: "Avoid non-English words or phrases, **such as** de facto or ad hoc." | No replacement given at all — an example list of avoid-terms, not a "use Y instead of X" table row (unlike `e.g./i.e./viz./ergo`, which come from this same page's own table). "in practice" was the author's own reasonable-sounding guess. |
1097
+ | `ad hoc` → `as needed` | `no-latin-abbreviations` → `no-latin-abbreviations-detect` | Same sentence as `de facto`. | Same — no replacement stated. |
1098
+ | `vis-a-vis` → `compared with` | `no-latin-abbreviations` → `no-latin-abbreviations-detect` | **Not named on any fetched page at all** — confirmed via a live 404 on `v/vis-a-vis`. | Not just "no replacement given" — the term itself isn't sourced anywhere; "compared with" is entirely the author's own invention. |
1099
+ | `visit` → `go to` | `az-navigation` → `az-navigation-detect` (`pattern`) | "use go to in **most cases**... It's OK to use visit... if you're using a tone that's meant to imply [a suggestion, or browsing]." Worked example: "**Visit** the product website to learn about offerings..." | The live page's own approved example uses "visit" — a blind fix would rewrite Microsoft's own sanctioned usage. Also a second, independent hazard the wave-B anchor never covered: "visit" preceded by an article ("Schedule a visit", "during my visit") is a common noun sense the noun-**compound** anchor (which only excluded specific FOLLOWING words) never excluded — "Schedule a go to with the doctor" is not English. |
1100
+
1101
+ `hot link` (same `az-navigation` rule) and `e.g./i.e./viz./ergo` (same
1102
+ `no-latin-abbreviations` rule) were audited alongside their reclassified
1103
+ siblings and are unconditional, single-target, direct substitutions per
1104
+ their own live pages — they stay fixable.
1105
+
1106
+ #### Anchor gaps found and closed (not reclassifications — same bucket, wider anchor)
1107
+
1108
+ Auditing the pairs wave B already anchored surfaced three anchors that were
1109
+ real but incomplete — the guidance shape was correctly identified as
1110
+ sense-scoped, but the exclusion list didn't cover every common noun/idiom
1111
+ sense:
1112
+
1113
+ | Pair | Rule | Gap found | Fix |
1114
+ |---|---|---|---|
1115
+ | `hang`/`hangs` → `stop(s) responding` | `az-state-failure` | Exclusion list (`on/up/around/out/together`) missed "get the hang **of** it" (a knack, not a system), "hang **in** there"/"hang **tight**"/"hang **loose**"/"hang **fire**" (encouragement/waiting idioms), and the literal suspension sense ("hangs **from** the ceiling"/"hangs **over** the door"). | Added `of/in/tight/loose/fire/from/over` to the exclusion list. Not exhaustive — "hang" is as broad a word as `crash`/`lock up`, which were moved to detection-only in wave B for the identical breadth reason — but this closes the specific gap a plausible Redocly onboarding sentence ("Once you get the hang of the API...") would have hit. |
1116
+ | `print out` → `print` | `az-real-replacements` | The noun-compound anchor only excluded "print out **of** X" — "Keep the print out safe"/"Attach the print out to the ticket" (a determiner-preceded noun, no "of" following) still corrupted to "Keep the print safe"/"Attach the print to the ticket" (register shift toward "print" = photograph). | Added a negative lookbehind excluding a preceding determiner/possessive (`a/an/the/this/that/your/my/its/his/her/their/our`) — the verb sense is never preceded by a determiner directly. |
1117
+ | `click`/`clicks` → `select(s)` | `no-click` | The live page's ban is verb-scoped ("Avoid this **verb**"); the anchor excluded hyphen-joined compounds and letter-adjacent compounds (`double-click`, `clickstream`) but not the ordinary noun sense ("click count", "clicks per session") — plausible in analytics/UI-event documentation, exactly Redocly's domain. | Added a follow-word exclusion for noun-compound risk (`count(s)/rate(s)/event(s)/tracking/data/metrics/history/id(s)/per`). Residual, accepted risk (not anchored): the live page's own carve-out "OK to use click when you need to describe mouse actions specifically" isn't mechanically detectable — same class as `hex`'s mechanical-fastener sense, low practical likelihood in Redocly's API-documentation domain. |
1118
+
1119
+ #### Bug fix found during the audit (not a substitution-safety issue — a detection bug)
1120
+
1121
+ `microsoft/article-before-acronym` shipped `'a SQL': 'a SQL'` — a
1122
+ same-to-same self-mapping, not a wrong→right correction like its three
1123
+ sibling pairs (`'an URL': 'a URL'`, `'a ISP': 'an ISP'`, `'an SQL database':
1124
+ 'a SQL database'`). Because `swap`'s `execute()` reports every regex match
1125
+ as a violation regardless of whether match equals replacement, this flagged
1126
+ the ALREADY-CORRECT "a SQL" (e.g. "Write a SQL query") as if it were wrong,
1127
+ and `--fix` reproduced identical text — a permanent, silent false-positive
1128
+ DETECTION on correct prose (not a corruption, but squarely inside this
1129
+ audit's "every fixable pair" scope). Corrected to `'an SQL': 'a SQL'`,
1130
+ mirroring the `'an SQL database'` pair's own pronunciation rule for the bare acronym
1131
+ without a following noun.
1132
+
1133
+ #### Step 2 — near-miss coverage extended
1134
+
1135
+ `microsoft-clean.md` had no near-miss at all for `az-grammar-usage` — the
1136
+ gap this wave's two named defects came from. A new "Fix wave C
1137
+ near-misses" section adds the `hang`/`print out`/`click`/SQL anchor-gap
1138
+ near-misses (the ones that remain FIXABLE, so a zero-findings clean-fixture
1139
+ check is meaningful for them). The 10 reclassified pairs above are
1140
+ DETECTION-only or `fix: false` — by construction they flag every occurrence
1141
+ including a compliant one (that's what detection-only means: a human
1142
+ reviews it instead of the tool guessing), so a zero-findings clean-fixture
1143
+ entry for them would be structurally impossible, not a gap. Their
1144
+ protection instead lives in `preset-microsoft.test.ts`'s new "fix wave C"
1145
+ `describe` blocks: an `unchanged` list (fixed twice, byte-identical) proving
1146
+ no corruption, and a `stillDetects` list (per acceptance item 3) proving the
1147
+ rule still reports the violation rather than going silently dead.
1148
+
1149
+ #### Step 3 — `SMB`/`SKU`/`terminate` added to the canonical registry
1150
+
1151
+ All three were genuinely absent from every rule (correctly dropped in fix
1152
+ wave B) but existed only in that wave's narrative "Fix wave B / Step 4"
1153
+ table above, which has no URL column. Added to "Excluded candidates" —
1154
+ the table whose stated purpose is answering "why doesn't
1155
+ `recheck/microsoft` check X?" — with rule, live URL, and reason, matching
1156
+ the format already used for the 18 pre-existing TOO-RISKY rows. See that
1157
+ table for the three new rows.
1158
+
1159
+ #### Step 4 — `centred`/`centring` and `catalogued`/`cataloguing` added
1160
+
1161
+ `us-spelling` covered `centre`/`centres` and `catalogue`/`catalogues` (the
1162
+ noun/plural forms) but not the verb inflections. Not a regression — the
1163
+ original alternation groups fix wave B / Step 3 replaced never covered
1164
+ these verb forms either — but "enumerate every inflection" is the standard
1165
+ Step 3 established, so this finishes it: `centred`→`centered`,
1166
+ `centring`→`centering`, `catalogued`→`cataloged`, `cataloguing`→`cataloging`.
1167
+
1168
+ #### Step 5 — the CONFIRMED-vs-safe-to-fix distinction, written down for reuse
1169
+
1170
+ Recorded here and mirrored in `presets/google/PROVENANCE.md`: a verifier
1171
+ `CONFIRMED` verdict establishes only that the live guide page discusses a
1172
+ term. It does **not** establish that a blind textual (`swap`) substitution
1173
+ of that term is safe — that is a separate question, requiring the same
1174
+ per-pair check Step 1's table above applies (direct instruction vs.
1175
+ synonym-conflation caution vs. verb/context-scoped vs. multi-target vs. no
1176
+ replacement given). Two future presets will be authored against these same
1177
+ verifier reports; this distinction, not any single fixed pair, is the most
1178
+ transferable thing this task has produced.
1179
+
1180
+ **Superseded by the posture change below.** This distinction turned out to
1181
+ be necessary but not sufficient: fix waves A/B/C each fixed the specific
1182
+ pairs a probe happened to find (2, then 2, then 6), and each probe found
1183
+ more. See "Fix-posture change" for why CONFIRMED-vs-safe-to-fix was
1184
+ replaced with a second, orthogonal axis and a structural (not reactive)
1185
+ posture.
1186
+
1187
+ #### Gates — command and output
1188
+
1189
+ See `.superpowers/sdd/task-10-report.md`'s "## Fix wave C" section for the
1190
+ full command output (`pnpm build`, `pnpm test`, `pnpm parity` — unchanged at
1191
+ 27425 = 27425 — `npx nx run recheck:lint --max-warnings=0`) and the four
1192
+ acceptance-gate results (all 10 reclassified pairs plus the two named
1193
+ corruptions fixed twice, unchanged; the 20 wave-B corruption strings still
1194
+ clean; every retained/anchored pair still catches its genuine violation;
1195
+ every rule/pair/token still firing, clean fixture zero with the new
1196
+ near-misses).
1197
+
1198
+ ### Fix-posture change (2026-07-30)
1199
+
1200
+ > **RETIRED 2026-07-30 — see "Detection-only" at the end of this file.**
1201
+ > This section's criterion (same-word normalization) no longer determines
1202
+ > which pairs are fixable in this preset: none are. Kept as historical
1203
+ > record only.
1204
+
1205
+ Base commit `eb4f8b11dac`, branch `aa/recheck-style-guides`. Brief:
1206
+ `.superpowers/sdd/preset-fix-posture-brief.md`. Report:
1207
+ `.superpowers/sdd/preset-fix-posture-report.md`.
1208
+
1209
+ #### Why fix waves A/B/C never converged
1210
+
1211
+ Three fix waves on this preset each fixed the NAMED corrupting pairs an
1212
+ independent probe found, and each probe found more (2, then 2, then 6).
1213
+ Step 5 above ("CONFIRMED means discussed, not safe-to-fix") explained WHY
1214
+ each individual pair was missed, but it did not stop the next probe from
1215
+ finding another one, because it is still a per-pair judgement call —
1216
+ exactly the kind of call that is easy to get right nine times and wrong
1217
+ the tenth. The last probe's six findings, all reproducible and stably
1218
+ wrong under `--fix` twice:
1219
+
1220
+ ```
1221
+ "Tensions remain high near the DMZ dividing North and South Korea."
1222
+ -> "...near the perimeter network dividing North and South Korea."
1223
+ "Traders watched the ask tick higher throughout the session."
1224
+ -> "Traders watched the request tick higher throughout the session."
1225
+ "...so the CLI can find the user's home directory for its config files."
1226
+ -> "...find the user's root directory..." (semantically wrong)
1227
+ "Use the API to unmark a conversation as read..."
1228
+ -> "Use the API to clear a conversation as read..."
1229
+ "The contractor built the connector on spec..."
1230
+ -> "...built the connector on specification..."
1231
+ "The click-through rate improved after the redesign."
1232
+ -> "The select-through rate improved after the redesign."
1233
+ ```
1234
+
1235
+ #### The two orthogonal axes, and the posture that makes both moot for fixing
1236
+
1237
+ 1. **Guidance-shape axis** (Step 5 above): does the guide's own wording
1238
+ authorize a direct substitution, or is it a caution / scoped / multi-
1239
+ alternative / no-replacement statement?
1240
+ 2. **Homograph axis** (new this wave): does the avoid-term have a
1241
+ legitimate, unrelated sense in ordinary or technical English? All six
1242
+ corruption strings above pass axis 1 (each guide page states a plain
1243
+ "use Y instead of X" rule) and fail on axis 2 — `DMZ` is also the
1244
+ Korean border zone; `the ask` is also the bid/ask market term; `home
1245
+ directory` always means the Unix `$HOME` sense in the corrupted
1246
+ sentence, and the guide's "root directory" target is a genuinely
1247
+ different concept (the filesystem's `/`, not a per-user directory);
1248
+ `unmark` collides with no listed acceptable alternative for non-
1249
+ checkbox UI (the guide's own carve-out); `spec` is also the "on spec"
1250
+ bid/contract idiom; `click-through` is a compound analytics term the
1251
+ bare-verb ban was never meant to reach.
1252
+ 3. **The posture**: auto-fix is retained **only** where a replacement
1253
+ cannot be wrong — the same word, normalized (spelling, hyphenation,
1254
+ casing, or a non-standard written form of the identical word). A pair
1255
+ that substitutes a *different* word or phrase — even one that always
1256
+ looks safe, like `alright` → `OK` — moves to detection-only, because
1257
+ "this one looks safe" is exactly the per-pair judgement call that
1258
+ produced three rounds of misses. Mechanical classification, not
1259
+ another round of judgement, is what stops the class.
1260
+
1261
+ #### Result
1262
+
1263
+ **Fixable `swap`/`consistency` pairs: 230 → 126**, across 45 rules with a
1264
+ `swap`/`consistency` assertion (up from 41 — 4 new rule ids from
1265
+ splitting bundles that mixed same-word and different-word pairs; `fix`
1266
+ is a whole-RULE flag, not per-pair, so a mixed bundle has to split). The
1267
+ 16 rules that remain fixable are exhaustively enumerated with before/after
1268
+ fix output in the fix-posture report; four representative examples:
1269
+
1270
+ | Rule | Pair | Why it stays fixable |
1271
+ |---|---|---|
1272
+ | `microsoft/us-spelling` | `centre` → `center` | Spelling variant of the identical word (the brief's own textbook example). |
1273
+ | `microsoft/spelling-hyphenation` | `e-mail` → `email` | Hyphenation of the identical word. |
1274
+ | `microsoft/az-case-fixable` | `javascript` → `JavaScript` | Casing of the identical token; empirically re-verified against `applyMatchCase` this wave (case-sensitive matching means an ALL-CAPS input like `JAVASCRIPT` can never reach these keys in the first place, so the ALL-CAPS-shout no-op class `az-case-only` exists to avoid doesn't recur here). |
1275
+ | `microsoft/az-grammar-usage` | `broadcasted` → `broadcast` | Non-standard inflection of the identical word (the guide's own irregular past tense), the same class as "alot" → "a lot". |
1276
+
1277
+ Two rules flipped for a reason found DURING this wave, not named in the
1278
+ brief:
1279
+
1280
+ - **`microsoft/racial-ethnic-capitalization` was re-examined and correctly
1281
+ stays fixable, reversing an initial assumption.** It looked like the
1282
+ `az-case-only` no-op shape (case-only replacement), but hand-tracing
1283
+ `applyMatchCase` and confirming empirically shows it is not: every key
1284
+ is matched case-sensitively (`ignoreCase: false`) and is always
1285
+ all-lowercase, so `applyMatchCase`'s "already-Capitalized match" no-op
1286
+ branch can never trigger — the fix genuinely inserts the configured
1287
+ Title-Case replacement. Case-only pairs are not automatically a no-op;
1288
+ each has to be checked against the actual function, not just the shape
1289
+ of the rule.
1290
+ - **`MSFT` → `Microsoft` (`az-abbreviations-names`) moved to detection-only
1291
+ for a fix-*mechanism* defect, not a word-choice one.** `MSFT` is
1292
+ virtually always written all-caps (it's a stock ticker); with
1293
+ `ignoreCase: true`, `applyMatchCase`'s ALL-CAPS branch shouts a
1294
+ single-word replacement, so the "fix" would produce `"MICROSOFT"` (wrong
1295
+ casing for a trademark), not the configured `"Microsoft"`. Confirmed
1296
+ empirically (`applyMatchCase('MSFT', 'Microsoft')` → `'MICROSOFT'`).
1297
+
1298
+ #### Severity follows fixability
1299
+
1300
+ Every rule that became detection-only in this wave, and is word-choice/
1301
+ phrasing guidance (not structural), moved from `error` to `warn` — a hard
1302
+ CI failure a user cannot auto-resolve is disproportionate for a style
1303
+ nit. Exception, deliberately not changed: `bias-free-terms` (bundles
1304
+ `DMZ`) and `racial-ethnic-capitalization` stay at `error` — the existing
1305
+ "sensitive category" carve-out already applied to `master-slave`,
1306
+ `no-derogatory-slang`, and `accessibility-terms` (all detection-only at
1307
+ `error` before this wave) is a considered, pre-existing exception to
1308
+ "detection-only implies warn", not an oversight.
1309
+
1310
+ #### Step 4 (brief) — scoping the four headline false-positive terms
1311
+
1312
+ `DMZ`, `the ask`, `home directory`, and `spec` are the terms most likely
1313
+ to visibly misfire on ordinary prose now that they're detection-only
1314
+ (detection-only rules cannot corrupt, so this is a noise concern, not a
1315
+ correctness one). Each gained a narrow anchor against its most common
1316
+ unrelated sense — `DMZ(?!\s+(?:dividing|between|separating))`, `the
1317
+ ask(?!\s+(?:tick|price|spread|size|quote))`, `home
1318
+ directory(?!\s+for\s+(?:its|the|...)?\s*config)`, `(?<!\bon\s)\bspec\b` —
1319
+ and all four sentences from the brief's corruption list now appear
1320
+ verbatim in `microsoft-clean.md` under "Fix-posture near-misses",
1321
+ producing zero findings. The other ~15 pairs this preset already
1322
+ reclassified to detection-only in fix waves B/C (`hang`, `click`, `print
1323
+ out`, ...) are deliberately NOT given this treatment — `microsoft-clean.md`
1324
+ already documents that flagging every occurrence, including a compliant
1325
+ one, is what detection-only means, and the brief only asks for the four
1326
+ headline terms.
1327
+
1328
+ #### Gates
1329
+
1330
+ `pnpm build` (dist re-emitted), `pnpm test` (105 files, 1467 passed / 5
1331
+ skipped), `pnpm parity --corpus monorepo-docs` (unchanged: 27425 = 27425,
1332
+ 0 unexplained), `npx nx run recheck:lint --max-warnings=0` (clean). Plus:
1333
+ every one of the six corruption strings above and the four anchor
1334
+ near-misses (`hang by a thread`, `hang back`, sentence-initial `Print out
1335
+ is required`, `click depth`) through `--fix` twice — unchanged; every one
1336
+ of the 126 remaining fixable pairs (across both presets, 240 combined)
1337
+ verified programmatically — real change, idempotent, violation gone — not
1338
+ sampled; every rule (including the 4 new ones from splitting bundles)
1339
+ still fires on `microsoft-violations.md`; `microsoft-clean.md` still zero
1340
+ findings with the four new near-misses added. Full command output and the
1341
+ exhaustive fixable-rule table are in
1342
+ `.superpowers/sdd/preset-fix-posture-report.md`.
1343
+
1344
+ ### Fix-posture change, wave 2 — the proper-noun axis (2026-07-30)
1345
+
1346
+ > **RETIRED 2026-07-30 — see "Detection-only" at the end of this file.**
1347
+ > This section's third axis (proper-noun collision) no longer determines
1348
+ > which pairs are fixable in this preset: none are. Kept as historical
1349
+ > record only.
1350
+
1351
+ Base commit `25a62c3d1f1`, branch `aa/recheck-style-guides`. Brief:
1352
+ `.superpowers/sdd/preset-posture-fix2-brief.md`. Report:
1353
+ `.superpowers/sdd/preset-fix-posture-report.md`'s "Wave 2" section.
1354
+
1355
+ #### The gap the first two axes didn't cover
1356
+
1357
+ An independent probe of the ~230 pairs the previous wave left fixable
1358
+ found four more corruptions, all sharing one cause neither axis above
1359
+ catches:
1360
+
1361
+ ```
1362
+ "The store offered a markdown of thirty percent."
1363
+ -> "...a Markdown of thirty percent."
1364
+ "FinTech Group AG reported strong earnings."
1365
+ -> "fintech Group AG reported strong earnings."
1366
+ "...processed by U.S. Bank on Tuesday"
1367
+ -> "...processed by US Bank"
1368
+ "The USA Gymnastics team announced its roster."
1369
+ -> "The US Gymnastics team announced its roster."
1370
+ ```
1371
+
1372
+ `Markdown`, `FinTech`, `U.S. Bank`, `USA Gymnastics` are **proper nouns**
1373
+ that happen to contain the very token a same-word normalization rule
1374
+ targets. The guidance-shape axis is satisfied (each really is the same
1375
+ word, just re-cased or re-punctuated) and the homograph axis, as
1376
+ previously scoped ("does the avoid-term have an unrelated sense in
1377
+ ordinary English"), doesn't ask the right question either — "U.S." has
1378
+ no unrelated ordinary-English sense, yet "U.S. Bank" still breaks.
1379
+
1380
+ #### The third axis
1381
+
1382
+ A pair keeps `fix: true` only if **all three** hold:
1383
+
1384
+ 1. **Same word, not a different one** (spelling/hyphenation/casing/
1385
+ non-standard form — never a substitution, and an abbreviation
1386
+ EXPANDED INTO A PHRASE — `aka` -> `also known as`, `spec` ->
1387
+ `specification` — counts as a substitution, not a respelling).
1388
+ 2. **No unrelated legitimate sense** (the homograph axis, wave 1).
1389
+ 3. **NEW — cannot occur as part of a real organization, product, brand,
1390
+ or place name.** Acronyms and single capitalizable words are the
1391
+ highest-risk shapes, because a rule that ignores case or normalizes
1392
+ punctuation will match a proper noun's own official spelling just as
1393
+ readily as ordinary prose.
1394
+
1395
+ #### Sweep and results
1396
+
1397
+ Every fixable `swap` pair in both presets (113 pairs across 15 rules in
1398
+ this preset, plus 5 `consistency` pairs left untouched — 118 total; the
1399
+ parallel sweep in `recheck/google` is documented in that preset's own
1400
+ PROVENANCE.md) was checked against question 3. This preset's flips:
1401
+
1402
+ | Rule | Pair(s) flipped | Real proper noun that collides |
1403
+ |---|---|---|
1404
+ | `microsoft/usa-abbreviation` | `USA`/`U.S.A.`/`U.S.` -> `US` (all 3) | "USA Gymnastics" (US national governing body), "U.S. Bank" (top-10 US bank), "U.S.A. Track and Field" (national governing body), "U.S. Steel" — named brief case. |
1405
+ | `microsoft/us-spelling` | `centre`/`centres` -> `center`/`centers` | "Bell Centre" (Montreal Canadiens' arena, official English spelling) and "Centre County, Pennsylvania" (a real US county whose OWN official name keeps the British "re"). |
1406
+ | `microsoft/us-spelling` | `catalogue`/`catalogues` -> `catalog`/`catalogs` | "Catalogue of Life" (a real, commonly-cited global species database whose own name is spelled with the British "ue"). |
1407
+ | `microsoft/az-case-fixable` | `boolean` -> `Boolean` | Not proper-noun (question 2, found during this sweep): lowercase `boolean` is the REQUIRED spelling of the OpenAPI/JSON Schema type name (`"type": "boolean"`) — exactly Redocly's own domain — and auto-capitalizing it would corrupt extremely common, completely correct schema prose. |
1408
+
1409
+ **4 pairs swept out of the 113 live `swap` pairs, all moved to a
1410
+ detection-only sibling rather than anchored** — `microsoft/us-spelling`
1411
+ keeps its remaining 21 pairs (the verb inflections `centred`/`centring`/
1412
+ `catalogued`/`cataloguing` stay fixable: a participle doesn't head a
1413
+ proper noun the way the bare noun does), and the new siblings
1414
+ `microsoft/us-spelling-detect` and `microsoft/az-case-fixable-detect`
1415
+ carry the four reclassified pairs at the SAME severity as their parent
1416
+ rule (`error` — the guidance itself is still unconditionally correct;
1417
+ only the auto-fix is unsafe, the same reasoning `bias-free-terms`/
1418
+ `racial-ethnic-capitalization` already established for a "sensitive
1419
+ category" carve-out). `microsoft/usa-abbreviation` itself flips whole-rule
1420
+ to `fix: false`; severity drops `error` -> `warn`, matching this file's
1421
+ policy for every other rule that becomes detection-only for a word-
1422
+ choice/phrasing reason rather than a structural one.
1423
+
1424
+ **Fixable pairs: 126 -> 118** (113 `swap` + 5 `consistency`, unchanged).
1425
+
1426
+ #### Why `fix: false`, not another anchor
1427
+
1428
+ Every anchor added in fix waves B and C (`hang`, `print out`, `click`,
1429
+ ...) has independently leaked exactly one near-miss beyond wherever it
1430
+ was tested (documented in those waves' own sections above). A casing/
1431
+ abbreviation fix is low-value enough that detection alone is a fine
1432
+ outcome — the user still learns the term needs a second look; a fourth
1433
+ round of anchor-then-leak was not worth it for pairs this marginal.
1434
+ `fix: false` was preferred over an anchor for every pair this wave found.
1435
+
1436
+ #### `aka` was misclassified, not newly found unsafe (recorded here for
1437
+ cross-reference; the pair itself lives in `recheck/google`)
1438
+
1439
+ Expanding an abbreviation into a phrase is a substitution, not a
1440
+ respelling — `aka` -> `also known as` should have flipped in wave 1
1441
+ alongside `e.g.`/`i.e.` (the identical shape), not stayed fixable. See
1442
+ `recheck/google`'s own PROVENANCE.md for the fix; noted here because this
1443
+ file's wave-1 section states the two-axis criterion this correction
1444
+ applies to.
1445
+
1446
+ #### Gates
1447
+
1448
+ `pnpm build` (dist re-emitted), `pnpm test` (105 files, 1490 passed / 5
1449
+ skipped — 23 new tests this wave), `pnpm parity --corpus monorepo-docs
1450
+ --profile default` (unchanged: 27425 = 27425, 0 unexplained), `npx nx run
1451
+ recheck:lint --max-warnings=0` (clean). Every sentence in the brief's
1452
+ acceptance set 1 (both presets) verified unchanged through `--fix` twice
1453
+ and still detected against the correct new rule name; the ten wave-1
1454
+ regression strings still inert; every rule (including the 2 new ones
1455
+ here) still fires on `microsoft-violations.md`. Full output in
1456
+ `.superpowers/sdd/preset-fix-posture-report.md`'s "Wave 2" section.
1457
+
1458
+ ### Detection-only (2026-07-30)
1459
+
1460
+ Base commit `006c026a1f0`, branch `aa/recheck-style-guides`. Brief:
1461
+ `.superpowers/sdd/preset-detection-only-brief.md`. Report:
1462
+ `.superpowers/sdd/preset-detection-only-report.md`.
1463
+
1464
+ **This section REPLACES the fixability criterion described in every
1465
+ "Fix wave" and "Fix-posture change" section above — it does not sit
1466
+ alongside them as a fourth, stricter axis.** Those sections are kept below
1467
+ (above this one), unedited, as the historical record of the criteria that
1468
+ were tried and superseded; do not read any of them as current guidance. As
1469
+ of this section, **`recheck/microsoft` ships zero fixable rules. Every rule
1470
+ in this file is `fix: false`, unconditionally** — set structurally, once,
1471
+ by a loop at the end of `buildMicrosoftPreset()`
1472
+ (`src/config/presets/microsoft.ts`), not by auditing pairs against a
1473
+ sharper rule. A dedicated test (`preset-microsoft.test.ts`'s
1474
+ "is detection-only" describe block) reads the live preset object and fails
1475
+ if any rule is ever fixable again — the same derive-from-the-preset shape
1476
+ the per-pair coverage gate already uses, so this cannot regress silently
1477
+ the way three prior narrowing passes did.
1478
+
1479
+ #### Why a fourth axis wasn't the answer
1480
+
1481
+ Three prior fix waves (A, B, C above) each patched the specific corrupting
1482
+ pairs one probe found — 2, then 2, then 6 — and each following probe found
1483
+ MORE, a rising rate, not a shrinking one. The fix-posture change and its
1484
+ wave 2 (above) then replaced "patch what's found" with two, then three,
1485
+ structural axes: same-word-normalized (not a substitution), no unrelated
1486
+ homograph sense, and no real-proper-noun collision. Each pass shipped clean
1487
+ against its own criterion and each was then probed again. The fifth
1488
+ adversarial probe — against this preset and `recheck/google` together —
1489
+ found **18 of 29 probed pairs (62%) still corrupting correct prose**, a
1490
+ RISING hit rate after three rounds of narrowing, spanning every category
1491
+ the three axes above treated as clean:
1492
+
1493
+ - **Spelling**, believed the safest category of all: Hemingway's real,
1494
+ correctly spelled published title *A Moveable Feast* is corrected to "A
1495
+ Movable Feast" by this preset's own `microsoft/az-grammar-usage`'s
1496
+ `moveable` → `movable` pair — a genuine same-word normalization by every
1497
+ axis above (axis 1 passes, axis 2 finds no unrelated sense, axis 3 finds
1498
+ no proper-noun collision on the word "moveable" itself), corrupted anyway
1499
+ because the collision is with a specific, individually unforeseeable
1500
+ literary title rather than a common name pattern the axis could
1501
+ generalize against.
1502
+ - **Hyphenation**: `microsoft/spelling-hyphenation`'s `dial up` →
1503
+ `dial-up` pair turns "Dial up the treble until the mix sounds right" (a
1504
+ phrasal verb — turn a knob up) into "Dial-up the treble..." (1990s modem
1505
+ technology used as an adjective). Same-word by every axis, wrong because
1506
+ the axes check the WORDS, not the GRAMMATICAL ROLE those words are
1507
+ playing in the sentence being fixed.
1508
+ - **A correct verb conjugation broken outright**: `microsoft/az-grammar-usage`'s
1509
+ `zeroes` → `zeros` pair turns "the counter zeroes out" (a correctly
1510
+ conjugated verb) into "the counter zeros out" (the plural noun form used
1511
+ as a verb) — same word-pair, same axis clearance, grammatically wrong
1512
+ regardless of context.
1513
+
1514
+ Full round-5 acceptance evidence (these sentences and more, run through
1515
+ `--fix` twice and confirmed byte-identical) lives in
1516
+ `src/config/__tests__/preset-detection-only-acceptance.test.ts`, plus the
1517
+ per-preset regression suites in `preset-microsoft.test.ts` and
1518
+ `preset-google-fix-wave-c.test.ts` (both rewritten by this change to assert
1519
+ "unchanged" where they used to assert a real rewrite). The `consistency`
1520
+ engine bug this same change fixed — `it's`/`it is` collapsing into one
1521
+ meaning via first-seen-wins, live since Phase 1 and reproduced exactly by
1522
+ this preset's `microsoft/contraction-consistency` rule with
1523
+ `microsoft/use-contractions` set to `severity: off` — is documented at the
1524
+ engine level in `src/rules/scope/consistency.ts`'s `wordCount()` doc
1525
+ comment, not here: it is independent of this preset and applies to any
1526
+ config shipping a `consistency` rule, including a hypothetical future
1527
+ preset.
1528
+
1529
+ #### The conclusion this decision rests on
1530
+
1531
+ A rule's *category* — spelling, hyphenation, casing, word-choice — does not
1532
+ predict fix-safety at this scale. A style guide states *intent* ("use X to
1533
+ mean Y"); a `swap`/`consistency`/`pattern` rule matches *tokens* (literal
1534
+ text, regardless of the grammatical role or referent that text has in a
1535
+ given sentence). That gap is not closable by inventing a fourth, fifth, or
1536
+ sixth axis: axis 1 (guidance-shape) closed the space of pairs where the
1537
+ guide's own wording was ambiguous; axis 2 (homograph) closed the space of
1538
+ words with an unrelated common sense; axis 3 (proper-noun) closed the space
1539
+ of words that double as real names. Each closure found the NEXT gap, not
1540
+ zero gap. The project decision is to stop narrowing and remove fixing
1541
+ capability from both style-guide presets entirely: users get every finding
1542
+ (detection is completely unaffected — every rule still runs `execute()` and
1543
+ reports) and apply the judgment a style guide has always required, same as
1544
+ before either preset existed and same as Vale (the tool these presets
1545
+ replace), which never shipped an auto-fixer and never had this class of
1546
+ bug.
1547
+
1548
+ #### What did not change
1549
+
1550
+ Detection. Every rule's `execute()` path, message, severity, and scope are
1551
+ untouched — only `fix()` is gated off (`core/runner.ts`'s
1552
+ `rule.fix !== false` check). The per-pair and per-token coverage gates
1553
+ (`preset-microsoft.test.ts`) still require every rule to fire on its own
1554
+ clean fixture, so a rule that neither fixes nor reports is still caught as
1555
+ dead weight, same as before this change.