@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,1022 @@
1
+ # Provenance: `recheck/google`
2
+
3
+ Source: [Google developer documentation style guide](https://developers.google.com/style)
4
+ (canonical URL: `https://developers.google.com/style`). License:
5
+ [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). Sync date: **2026-07-29**.
6
+
7
+ Modification note: rules are adapted to Recheck's assertion vocabulary
8
+ (`swap`, `pattern`, `capitalization`, `length`, plus a handful of
9
+ markdownlint-parity/Recheck-original token rules); wording is paraphrased
10
+ into each rule's `message`, not quoted verbatim from the guide. Every rule
11
+ carries a `link:` to its source page.
12
+
13
+ ## How this table was produced
14
+
15
+ Five independent verification passes fetched the live guide directly
16
+ (`curl`, not a summarizing fetch) and confirmed or rejected each candidate
17
+ rule against the raw page text/HTML: four slice verifiers (`task-9-verify-A.md`
18
+ covering style guide §2.1-2.5, `task-9-verify-B.md` §2.6-2.10,
19
+ `task-9-verify-C.md` §3.1-3.5, `task-9-verify-D.md` §3.6-3.9) plus one
20
+ cross-check pass that re-parsed the word-list page with a stricter HTML5
21
+ parser and diffed every replacement value quoted by the other four
22
+ (`task-9-verify-crosscheck.md`). Together they checked ~380 candidate
23
+ rules/entries and found **6 fabrications** — rules that appeared in an
24
+ earlier research draft but do not exist anywhere on the live guide (see
25
+ "Fabrications" below). This preset is built **only** from entries those
26
+ five reports marked `CONFIRMED`; nothing here was sourced from the research
27
+ draft directly. Fetch date for the verification passes and for
28
+ `sources.json`'s hashes is the same day this preset was authored: 2026-07-29.
29
+
30
+ Every quote below is the verifier's own quote (or fetch-log citation),
31
+ reproduced here at second hand — see the verifier reports themselves for
32
+ the full text and additional context.
33
+
34
+ ## Shipped rules
35
+
36
+ Severity policy: `error` is reserved for rules checking pure document
37
+ STRUCTURE (heading hierarchy/uniqueness, list-item mechanics, table
38
+ mechanics, link placement, alt-text presence, sentence length) where a
39
+ violation is unambiguous and mechanical; every word-choice, terminology,
40
+ punctuation-convention, and phrasing rule is `warn`. This is a
41
+ simplification of spec §2's severity table (which gives a shorter, purely
42
+ illustrative example list) applied uniformly here for predictability, at
43
+ the cost of a few punctuation-mechanics rules (Oxford comma, en dash,
44
+ single-space-between-sentences) that could arguably also be `error` — see
45
+ "Author's judgment calls" at the end of this document.
46
+
47
+ Four shipped rules are named exceptions to that split, not silent
48
+ inconsistencies with it: `no-code-in-heading` is heading-scoped (in the
49
+ STRUCTURE family above by location) but ships at `warn` because the guide
50
+ states it with hedged wording ("Avoid code items in headings," not
51
+ "don't"). `no-numbered-headings` is also heading-scoped and the guide
52
+ states it unconditionally ("Don't use numbers in headings..."), but ships
53
+ at `warn` because the shipped pattern is a narrowed heuristic (bare
54
+ leading ordinals and `Step N`/`Part N` markers only, to keep the
55
+ false-positive rate low), not a complete detector of every way a heading
56
+ could number a sequence — the rule's confidence is in what it does flag,
57
+ not full coverage of the guide's stated principle. `emphasis-style` and
58
+ `strong-style` ship at `warn` because the guide states its markup
59
+ preference as a recommendation ("we recommend underscores," "it's best to
60
+ use double asterisk"), not an unconditional "don't." So the actual policy
61
+ is: STRUCTURE, stated by the guide as an unconditional, completely
62
+ detectable rule → `error`; everything else — including these four
63
+ nominally-structural rules the guide itself hedges, or that need an
64
+ intentionally incomplete detection heuristic → `warn`.
65
+
66
+ ### Structural (heading, list, table, link, alt-text, sentence mechanics) — `error`
67
+
68
+ | Rule id | Source URL | Quote | Verdict |
69
+ |---|---|---|---|
70
+ | `google/heading-sentence-case` | [headings](https://developers.google.com/style/headings) | "Use sentence case for all headings and titles." | CONFIRMED |
71
+ | `google/heading-increment` | [headings](https://developers.google.com/style/headings) | "put an `<h3>` tag only under an `<h2>` tag" | CONFIRMED |
72
+ | `google/single-h1` | [headings](https://developers.google.com/style/headings) | "only use a level-1 heading once on a page" | CONFIRMED |
73
+ | `google/first-line-h1` | [headings](https://developers.google.com/style/headings) | "only use a level-1 heading once on a page" (same statement as `single-h1`; see "Author's judgment calls") | CONFIRMED |
74
+ | `google/no-duplicate-heading` | [headings](https://developers.google.com/style/headings) | "easier to jump between pages and sections...if the headings...are unique" | CONFIRMED |
75
+ | `google/no-trailing-punctuation` | [periods](https://developers.google.com/style/periods) | "Don't end headings with periods." | CONFIRMED |
76
+ | `google/no-empty-headings` | [headings](https://developers.google.com/style/headings) | "Don't use empty headings. Make sure headings are followed by content." | CONFIRMED |
77
+ | `google/no-emphasis-as-heading` | [accessibility](https://developers.google.com/style/accessibility) | "Tag headings using heading elements." | CONFIRMED (nuance: the engine's own token rule excludes a bold/italic lead-in followed by more text in the SAME paragraph, and a bold/italic-only paragraph that ends in required punctuation — but NOT a bold/italic-only paragraph with no ending punctuation whose description follows in a later paragraph, which the guide's own "run-in heading" pattern also permits; see verifier A row 12 and "Known limitations" below) |
78
+ | `google/no-link-in-heading` | [headings](https://developers.google.com/style/headings) | "Don't put links in headings." | CONFIRMED |
79
+ | `google/list-item-capital` | [lists](https://developers.google.com/style/lists) | "Start each list item with a capital letter" | CONFIRMED |
80
+ | `google/no-alt-text` | [accessibility](https://developers.google.com/style/accessibility) | "For every image, provide an alt attribute" | CONFIRMED |
81
+ | `google/no-merged-cells` | [accessibility](https://developers.google.com/style/accessibility) (also [tables](https://developers.google.com/style/tables)) | "Don't merge cells. Don't use colspan or rowspan attributes" | CONFIRMED |
82
+ | `google/sentence-length` | [accessibility](https://developers.google.com/style/accessibility) | "Try to use fewer than 26 words per sentence." | CONFIRMED — mapped to the new `length` assertion (`unit: words`, `max: 25`) per spec §5.6's stated-numbers table; this is the first non-prose preset to ship a `length`-backed rule (see "Engine/registry changes" below) |
83
+
84
+ ### Headings (residual) / lists — `warn`
85
+
86
+ | Rule id | Source URL | Quote | Verdict |
87
+ |---|---|---|---|
88
+ | `google/no-code-in-heading` | [headings](https://developers.google.com/style/headings) | "Avoid code items in headings." | CONFIRMED |
89
+ | `google/no-numbered-headings` | [headings](https://developers.google.com/style/headings) | "Don't use numbers in headings to indicate a sequence" | CONFIRMED |
90
+ | `google/list-length` | [lists](https://developers.google.com/style/lists) | "a single item isn't really a list" | CONFIRMED, but the verifier marked this specific line NOT-ENFORCEABLE (descriptive aside, not an imperative rule) — downgraded from the generic "list mechanics" `error` class to `warn` for that reason; ships via `list-length`'s own `min: 2` default, no `max` (Google states no upper bound; the 2-7 range is Microsoft's, spec §5.6) |
91
+
92
+ ### Voice, person, tense, contractions — `warn`
93
+
94
+ | Rule id | Source URL | Quote | Verdict |
95
+ |---|---|---|---|
96
+ | `google/second-person` | [person](https://developers.google.com/style/person) | "use `you` or `your` instead of `we`, `our`, or `us`" | CONFIRMED (organizational-reference exception noted in the rule's message; detection-only). Fix wave A: `ignoreCase: true` replaced with an explicit `We/we/Our/our/Us/us` alternation so it stops also matching the all-caps abbreviation "US" — see "Fix wave A corrections" |
97
+ | `google/use-contractions` | [contractions](https://developers.google.com/style/contractions) | "we recommend using negation contractions such as isn't, don't, and can't" | CONFIRMED (`fix: false`: guide's own "is *not*" emphasis exception) |
98
+ | `google/no-triple-contractions` | [contractions](https://developers.google.com/style/contractions) | "Don't use three-word contractions such as mightn't've." | CONFIRMED |
99
+ | `google/no-lets` | [word-list#lets](https://developers.google.com/style/word-list#lets) | "Don't use if at all possible." | CONFIRMED — citation corrected: the draft cited "contractions", but `let's` is never mentioned there; the quote lives on the word-list page (verifier A row 14) |
100
+ | `google/no-please-note` | [word-list#please](https://developers.google.com/style/word-list#please) | "Don't use please in the normal course of explaining how to use a product" (the phrase `please note` specifically has no documented exception) | CONFIRMED. Fix wave A: `fix: false` added — the delete-swap left a capitalization/fragment mess behind ("Please note that X." -> "that X."); see "Fix wave A corrections" |
101
+ | `google/no-please` | [word-list#please](https://developers.google.com/style/word-list#please) | "Use please only when you're asking for permission or forgiveness...Recommended: If the issue persists, please contact your account representative." | CONFIRMED, **must be DETECT-ONLY, never a swap** — the guide's own recommended example sentence uses "please"; a delete-swap would rewrite text the guide endorses (per task-9-author-corrections.md) |
102
+
103
+ ### Timeless documentation — `warn`
104
+
105
+ | Rule id | Source URL | Quote | Verdict |
106
+ |---|---|---|---|
107
+ | `google/no-timeless-phrases` | [timeless-documentation](https://developers.google.com/style/timeless-documentation) | "as of this writing, currently, does not yet, eventually, existing, future...latest, new, newer, now, old, older, presently, at present, soon" (full confirmed list) | CONFIRMED, but only 5 of the ~15 confirmed terms are shipped (`as of this writing`, `at present`, `presently`, `does not yet`, `currently`) — the rest are ordinary high-frequency words (`existing`, `future`, `latest`, `new`, `newer`, `now`, `old`, `older`, `soon`, `eventually`, `in the future`) with legitimate everyday uses that would make a blind pattern unusably noisy; see "Author's judgment calls" |
108
+
109
+ ### Latinisms, abbreviations, slang — `warn`
110
+
111
+ | Rule id | Source URL | Quote | Verdict |
112
+ |---|---|---|---|
113
+ | `google/no-latinisms` | [word-list](https://developers.google.com/style/word-list) | "Don't use i.e. or e.g."; "Don't use vs. as an abbreviation for versus" | CONFIRMED (i.e./e.g./vs. — independently reconfirmed in verifier C's 3.1 slice). Fix wave A: carries only the 3 period-terminated keys now (see "Fix wave A corrections" below for why `aka`/`vice versa` moved out) |
114
+ | `google/no-latinisms-plain` | [word-list](https://developers.google.com/style/word-list) | "aka: Don't use. Instead, write out also known as"; "vice versa: ...use...the other way around" | CONFIRMED, same verification as `google/no-latinisms` above. Split out in Fix wave A (2026-07-29) so these two non-period keys get full `\b...\b` anchoring instead of the period-terminated group's unanchored match — see "Fix wave A corrections" |
115
+ | `google/no-internet-slang` | [abbreviations](https://developers.google.com/style/abbreviations) | "Don't use internet slang abbreviations such as tl;dr, ymmv, RTFM" | CONFIRMED (each also has its own word-list replacement: "To summarize" / "Your results might vary" / "For more information, see...") |
116
+ | `google/no-via` | [word-list#via](https://developers.google.com/style/word-list#via) | "Don't use." | CONFIRMED, DETECT-ONLY (no replacement given) — this is the exact term the research file's own provenance note flagged a summarizer once inverted; independently reconfirmed by verifier C and the cross-check |
117
+ | `google/abbrev-no-periods` | [abbreviations](https://developers.google.com/style/abbreviations) | "Don't use periods with acronyms or initialisms." | CONFIRMED |
118
+ | `google/us-abbreviation` | [word-list#US](https://developers.google.com/style/word-list#US) | "OK to use as an abbreviation for United States. Don't use U.S. or U.S.A." | CONFIRMED |
119
+ | `google/no-slash-abbrev` | [slashes](https://developers.google.com/style/slashes) | "Slashes with abbreviations... Recommended: care of, with / Not recommended: c/o, w/" | CONFIRMED. Fix wave A: both keys gained a leading-only `\b` (were matching inside "src/output", "www/static", "show/hide", "new/old"); Fix wave C: both also gained a trailing `(?![A-Za-z])` (were matching inside "w/o", "c/oscillator") — see "Fix wave A corrections" / "Fix wave C corrections" |
120
+
121
+ ### Numbers, dates, units — `warn`
122
+
123
+ | Rule id | Source URL | Quote | Verdict |
124
+ |---|---|---|---|
125
+ | `google/spell-out-ordinals` | [numbers](https://developers.google.com/style/numbers) | "Not recommended: 1st, 5th, 12th, 43rd" | CONFIRMED |
126
+ | `google/number-format` | [numbers](https://developers.google.com/style/numbers) (also [hyphens](https://developers.google.com/style/hyphens) for the "from N-M" token) | "use numerals and the percent sign (%), without a space"; "place a zero in front of the decimal point"; "Recommended: 192x192 / Not recommended: 192 x 192"; "Not recommended: from 8-20 files" | CONFIRMED |
127
+ | `google/date-format` | [dates-times](https://developers.google.com/style/dates-times) | "Not recommended: 12/02/2017" | CONFIRMED |
128
+ | `google/time-format` | [word-list#AM,\_PM](https://developers.google.com/style/word-list#AM,_PM) | "use all caps, no periods, and a space before" (AM/PM); "Remove the minutes from round hours" (dates-times page) | CONFIRMED — citation corrected: the AM/PM quote lives on the word-list page, not dates-times (verifier B row 63) |
129
+ | `google/rfc-spacing` | [word-list#RFC](https://developers.google.com/style/word-list#RFC) | "use a space between RFC and the number (for example, RFC 2318)" | CONFIRMED |
130
+ | `google/data-rate-units` | [word-list#GBps](https://developers.google.com/style/word-list#GBps) | "By convention, we don't use KB/s" (and the other 5 unit pairs) | CONFIRMED — the master research table's "8 pairs" claim was wrong; verified exactly 6 (verifier B row 67, cross-check) |
131
+
132
+ ### Punctuation — `warn`
133
+
134
+ | Rule id | Source URL | Quote | Verdict |
135
+ |---|---|---|---|
136
+ | `google/no-ampersand` | [text-formatting](https://developers.google.com/style/text-formatting) | "Don't use ampersands (&) as conjunctions or shorthand for and." | CONFIRMED (UI-element exception preserved: pattern requires spaces on both sides, so `AT&T`/`&amp;` are untouched) |
137
+ | `google/dash-style` | [dashes](https://developers.google.com/style/dashes) | "En dashes...Don't use. Instead, use a hyphen or the word to."; "Don't put a space before or after it [the em dash]." | CONFIRMED |
138
+ | `google/single-space-sentences` | [periods](https://developers.google.com/style/periods) | "Leave only one space between sentences." | CONFIRMED |
139
+ | `google/conjunctive-adverb-comma` | [commas](https://developers.google.com/style/commas) | "put a comma after the conjunctive adverb" | CONFIRMED — the draft's fourth example ("Nonetheless") is not named on the live page; only the three confirmed examples (otherwise/however/therefore) are shipped |
140
+ | `google/comma-before-that` | [pronouns](https://developers.google.com/style/pronouns) | "That introduces a restrictive clause. It isn't preceded by a comma." | CONFIRMED |
141
+ | `google/neither-nor` | [word-list#neither](https://developers.google.com/style/word-list#neither) | "Write neither A nor B, not neither A or B." | CONFIRMED |
142
+ | `google/no-and-or` | [slashes](https://developers.google.com/style/slashes) | "avoid writing and/or except when space is limited, such as in tables" | CONFIRMED — the table exception is not separately carved out in the rule's scope (see "Known simplifications") |
143
+
144
+ ### Links — `warn`
145
+
146
+ | Rule id | Source URL | Quote | Verdict |
147
+ |---|---|---|---|
148
+ | `google/vague-link-text` | [cross-references](https://developers.google.com/style/cross-references) | "Don't use phrases such as this document, this article, or click here" | CONFIRMED (page renamed from `link-text`; content unchanged) |
149
+ | `google/no-url-as-link-text` | [cross-references](https://developers.google.com/style/cross-references) | "don't use a URL as link text" | CONFIRMED (legal/ToS exception noted, not separately modeled — see "Known limitations" below) |
150
+ | `google/link-intro-about` | [cross-references](https://developers.google.com/style/cross-references) | "Don't use on instead of about" | CONFIRMED |
151
+ | `google/link-punctuation` | [cross-references](https://developers.google.com/style/cross-references) | "don't put the link text in quotation marks" | CONFIRMED |
152
+ | `google/no-target-blank` | [cross-references](https://developers.google.com/style/cross-references) | "Don't force links to open in a new tab or window" | CONFIRMED |
153
+ | `google/self-reference-terms` | [word-list#documentation](https://developers.google.com/style/word-list#documentation) | "use this document, and not this article, this topic, this doc" | CONFIRMED (general terminology preference, independent of link text — distinct from `vague-link-text` above, which is scoped to the `link` segment) |
154
+
155
+ ### Text formatting — `warn`
156
+
157
+ | Rule id | Source URL | Quote | Verdict |
158
+ |---|---|---|---|
159
+ | `google/emphasis-style` | [text-formatting](https://developers.google.com/style/text-formatting) | "we recommend underscores" | CONFIRMED |
160
+ | `google/strong-style` | [text-formatting](https://developers.google.com/style/text-formatting) | "best to use the double asterisk for bold" | CONFIRMED |
161
+ | `google/no-underline` | [text-formatting](https://developers.google.com/style/text-formatting) | "Reserve underlining for link text" | CONFIRMED |
162
+ | `google/no-casing-style-names` | [capitalization](https://developers.google.com/style/capitalization) | "Don't use a casing style name, such as camel case or snake case" | CONFIRMED |
163
+
164
+ ### Code in text — `warn`
165
+
166
+ | Rule id | Source URL | Quote | Verdict |
167
+ |---|---|---|---|
168
+ | `google/no-inflected-code` | [code-in-text](https://developers.google.com/style/code-in-text) | "Don't inflect the name of a code element" | CONFIRMED |
169
+
170
+ ### UI elements and verbs — `warn`
171
+
172
+ | Rule id | Source URL | Quote | Verdict |
173
+ |---|---|---|---|
174
+ | `google/ui-element-quotes` | [ui-elements](https://developers.google.com/style/ui-elements) | 'click the "Next" button' (Not recommended example) | CONFIRMED |
175
+ | `google/no-click-on` | [word-list#click](https://developers.google.com/style/word-list#click) | "Don't use click on." | CONFIRMED |
176
+ | `google/no-hover` | [word-list#hover](https://developers.google.com/style/word-list#hover) | "Don't use. Instead use hold the pointer over." | CONFIRMED — DETECT-ONLY, not a swap (inflected forms "hovering"/"hovered" don't slot into the replacement phrase) |
177
+ | `google/no-uncheck` | [word-list#uncheck](https://developers.google.com/style/word-list#uncheck) | "use clear for checkboxes" | CONFIRMED. Bare `check` and `deselect` are deliberately NOT shipped — see "Author's judgment calls" |
178
+ | `google/scroll-to` | [word-list#scroll](https://developers.google.com/style/word-list#scroll) | "write go to the section, instead of scroll to the section" | CONFIRMED (preference, not a flat ban — `fix: false`) |
179
+ | `google/no-toggle-verb` | [ui-elements](https://developers.google.com/style/ui-elements) | "Don't use the word toggle as a verb. Describe the action." | CONFIRMED — citation correction: not a word-list entry as the draft implied, but is on the `ui-elements` page (verifier D) |
180
+ | `google/keyboard-keys` | [ui-elements](https://developers.google.com/style/ui-elements) | "Spell out the names of modifier keys"; "use uppercase instead of lowercase" | CONFIRMED |
181
+ | `google/chapter-terminology` | [word-list#chapter](https://developers.google.com/style/word-list#chapter) | "Instead, refer to documents, pages, or sections" | CONFIRMED, DETECT-ONLY |
182
+
183
+ ### Plain language and wordiness — `warn`
184
+
185
+ | Rule id | Source URL | Quote | Verdict |
186
+ |---|---|---|---|
187
+ | `google/plain-language-swaps` | [word-list](https://developers.google.com/style/word-list) | "allows_you_to: Don't use. Instead, use lets you"; "enable: Not recommended: ...enables you to..."; "comprise: Don't use. Instead, use consist of..."; "desire: Don't use. Instead, use a word like want or need"; "learnings: Don't use. Instead, refer to knowledge..."; "agnostic: Don't use. Instead, use...platform-independent" | CONFIRMED |
188
+ | `google/in-order-to` | [word-list#in_order_to](https://developers.google.com/style/word-list#in_order_to) | "Avoid in order to; instead, use to. Use in order to when needed to clarify meaning." | CONFIRMED (conditional — `fix: false`) |
189
+ | `google/utilize` | [word-list#utilize](https://developers.google.com/style/word-list#utilize) | "Use with caution. Don't use utilize when you mean use. It's OK to use utilize... when referring to the quantity of a resource being used." | CONFIRMED, DETECT-ONLY given the documented exception |
190
+ | `google/leverage` | [word-list#leverage](https://developers.google.com/style/word-list#leverage) | "Avoid using if you mean use... use, build on, or take advantage of." | CONFIRMED (`fix: false` — three valid alternatives given) |
191
+ | `google/performant` | [word-list#performant](https://developers.google.com/style/word-list#performant) | "Avoid where possible. Instead, use a more precise term." | CONFIRMED, DETECT-ONLY (no fixed replacement) |
192
+ | `google/copy-and-paste` | [word-list#Copy\_and\_paste](https://developers.google.com/style/word-list#Copy_and_paste) | "Avoid using. Instead, explain what to enter into a field and not how." | CONFIRMED |
193
+ | `google/create-a-new` | [word-list#Create\_a\_new](https://developers.google.com/style/word-list#Create_a_new) | "Avoid using unless you need to distinguish... Instead, use Create a ..." | CONFIRMED (exception noted; `fix: false`) |
194
+ | `google/no-run-the-following-command` | [procedures](https://developers.google.com/style/procedures) | "Avoid using run the following command to introduce code. Instead, focus on what the command does." | CONFIRMED |
195
+ | `google/cons-and-pros` | [word-list#pros](https://developers.google.com/style/word-list#pros) | "cons: Don't use. Instead, use a more precise term, such as disadvantages." / "pros: ...such as advantages." | CONFIRMED — only the compound phrase "pros and cons" ships; bare "pros"/"cons" are excluded, see "Author's judgment calls" |
196
+
197
+ ### Product and brand names — `warn`
198
+
199
+ | Rule id | Source URL | Quote | Verdict |
200
+ |---|---|---|---|
201
+ | `google/product-names` | [word-list](https://developers.google.com/style/word-list) | "Cloud SDK: Not Google Cloud SDK"; "APIs Explorer: Not API explorer..."; "API key: Not developer key or dev key"; "account name: ...use username"; "curated roles: ...use predefined roles"; "network IP address: ...use internal IP address"; "media type: ...Don't use MIME type"; "curl: Not cURL"; "interconnect type: ...use connection type"; "peering zone: Not peer zone"; "Android-powered device: Not Android device"; "Not...Cloud Platform, or Cloud" (Google Cloud); "Cloud console: Not...Developers Console" | CONFIRMED (each entry individually confirmed in verifier D's §3.4). Fix wave A: `'Cloud console'` gained a `(?<!Google )` lookbehind (self-compounding fix — see "Fix wave A corrections"); `GCP` moved to `google/gcp-name` below. Fix wave C: the lookbehind widened to `(?<![Gg][Oo][Oo][Gg][Ll][Ee]\s+)` (case-insensitive, whitespace-tolerant — see "Fix wave C corrections") |
202
+ | `google/gcp-name` | [word-list](https://developers.google.com/style/word-list) | "Not GCP...(Google Cloud)" | CONFIRMED, same verification as `google/product-names` above. Split out in Fix wave A (2026-07-29), `fix: false` — see "Fix wave A corrections". Fix wave C: `applyMatchCase` fixed at the engine, `fix: false` REMOVED — see "Fix wave C corrections" |
203
+ | `google/brand-capitalization` | [word-list](https://developers.google.com/style/word-list) | "Google Play services: Write services in lowercase."; "Google Account, Google Accounts: Capitalize Account."; "Markdown: Always capitalized."; "Material Design: Capitalize each word."; "Search Console: Capitalize each word." | CONFIRMED |
204
+
205
+ ### Compound and one-word forms — `warn`
206
+
207
+ | Rule id | Source URL | Quote | Verdict |
208
+ |---|---|---|---|
209
+ | `google/compound-forms` | [word-list](https://developers.google.com/style/word-list) (webpage via [hyphens](https://developers.google.com/style/hyphens)) | ~68 individual "Not X" / "X: Not Y" entries, e.g. "email: Not e-mail, Email, or E-mail."; "checkbox: Not check box."; "frontend: Not front-end or front end." | CONFIRMED (verifier C's 3.3 slice: ~68/70 confirmed; see "Fabrications"/"Dropped as unverified" for the 2 that didn't make it). Fix wave A: `colo` moved to `google/colo-form` below; `'in line': 'inline'` dropped (see "Dropped in Fix wave A" above) |
210
+ | `google/colo-form` | [word-list](https://developers.google.com/style/word-list) | "colocate: ...Not co-locate or colo." | CONFIRMED, same verification as `google/compound-forms` above. Split out in Fix wave A (2026-07-29), `fix: false` (noun/verb mismatch) — see "Fix wave A corrections" |
211
+ | `google/acronym-forms` | [word-list](https://developers.google.com/style/word-list) | "HTTPS: Not HTTPs."; "IPsec: Not IPSec."; "NoSQL: Not No-SQL or No SQL."; "OAuth 2.0: Not OAuth 2, OAuth2, or Oauth."; "microservices: Not micro-services."; "fintech: ...Don't use FinTech or fin-tech."; "ad tech: ...Don't use adtech or ad-tech." | CONFIRMED. `I-O`/`IO` → `I/O` confirmed individually. Fix wave A: `UNICODE`/`IPSEC` moved to `google/acronym-caps-detect-only` below; `SHA1` moved to `google/sha1-form` below; `Microservices` (capitalized form) dropped (see "Dropped in Fix wave A" above) |
212
+ | `google/acronym-caps-detect-only` | [word-list](https://developers.google.com/style/word-list) | "Unicode: Not UNICODE."; "IPsec: Not...IPSEC." | CONFIRMED, same verification as `google/acronym-forms` above. Split out in Fix wave A (2026-07-29), `fix: false` (case-preservation round-trip made the fix a permanent no-op) — see "Fix wave A corrections" |
213
+ | `google/sha1-form` | [word-list](https://developers.google.com/style/word-list) | "SHA-1: Not SHA1, except in string literals or enum values, and in hyphenated phrases such as HMAC-SHA1." | CONFIRMED (verifier C row 202). Split out in Fix wave A (2026-07-29), `fix: false` per this file's own header policy for rules with a documented guide exception — see "Fix wave A corrections" |
214
+
215
+ ### Inclusive language / ableist language / jargon with people references — `warn`
216
+
217
+ | Rule id | Source URL | Quote | Verdict |
218
+ |---|---|---|---|
219
+ | `google/master-slave` | [word-list#slave](https://developers.google.com/style/word-list#slave) | "Don't use. Instead, use alternative terms... such as worker or replica." | CONFIRMED. Only "slave" ships; bare "master" is deliberately excluded — see "Author's judgment calls" |
220
+ | `google/blacklist-whitelist` | [word-list#blacklist](https://developers.google.com/style/word-list#blacklist) | "For the noun blacklist, consider... denylist, excludelist, or blocklist" / "whitelist...consider...allowlist, trustlist, or safelist" | CONFIRMED. Noun forms only — the guide itself says a word-for-word swap isn't the best fix for verb forms |
221
+ | `google/black-white-hat` | [word-list#blackhat](https://developers.google.com/style/word-list#blackhat) | "Don't use. Instead, use precise terms... such as illegal, unethical, or in violation of rules." | CONFIRMED |
222
+ | `google/black-white-box-testing` | [word-list#black-box](https://developers.google.com/style/word-list#black-box) | "For monitoring, use synthetic monitoring. For testing, use opaque-box testing." | CONFIRMED |
223
+ | `google/grayed-out` | [word-list#grayed-out](https://developers.google.com/style/word-list#grayed-out) | "Don't use. Instead, use unavailable." | CONFIRMED |
224
+ | `google/grandfathered` | [word-list#grandfathered](https://developers.google.com/style/word-list#grandfathered) | "Instead, use an adjective like legacy or exempt or a verb like made an exception." | CONFIRMED |
225
+ | `google/gendered-terms` | [word-list#man\_hours](https://developers.google.com/style/word-list#man_hours) | "man hours...Instead use terms like person hours"; "male adapter...use plug"; "he/she...use...they" | CONFIRMED |
226
+ | `google/jargon-with-people-references` | [word-list#ninja](https://developers.google.com/style/word-list#ninja) | "ninja: Don't use to refer to a person. Instead, use a term such as expert."; "DMZ...use...perimeter network"; and others | CONFIRMED |
227
+ | `google/ableist-figurative-terms` | [word-list#crazy](https://developers.google.com/style/word-list#crazy) | "use complicated, complex, baffling, strange, or unexpected...only for inanimate objects" | CONFIRMED. Restricted to the figurative/inanimate-object sense; "mad" and "hang"/"hung" excluded (see "Author's judgment calls") |
228
+ | `google/dummy-variable` | [word-list#dummy-variable](https://developers.google.com/style/word-list#dummy-variable) | "Don't use to refer to placeholders. Instead, use placeholder." | CONFIRMED |
229
+ | `google/blind-figurative` | [word-list#blind](https://developers.google.com/style/word-list#blind) | "blind to, blind eye to...use more precise terms like ignore, unaware of, disregard, avoid, or reject" | CONFIRMED, restricted to the figurative sense only (the SAME entry also covers "blind writes"/"blind change", real technical terms, deliberately not matched) |
230
+ | `google/unsighted-visually-challenged` | [word-list#unsighted](https://developers.google.com/style/word-list#unsighted) | "Don't use. See blind." | CONFIRMED — resolved to the PERSON-REFERENCE sense of "blind" (person who is blind/visually impaired/low-vision), NOT the figurative sense above, per task-9-author-corrections.md's explicit correction |
231
+ | `google/disability-language` | [inclusive-documentation](https://developers.google.com/style/inclusive-documentation) | "avoid terms such as the disabled or a quadriplegic"; "such as victim of, suffering from... instead, use... experiencing, living with" | CONFIRMED |
232
+ | `google/technical-jargon-precision` | [word-list#fat](https://developers.google.com/style/word-list#fat) | "use a precise modifier... high-capacity network connection instead of fat connection" | CONFIRMED — framed as technical-jargon precision, NOT ableist language, per task-9-author-corrections.md: Google's own entries for "chubby"/"fat" never mention people |
233
+
234
+ ## Excluded candidates
235
+
236
+ Every candidate below was checked by one of the five verification passes
237
+ (or, where marked, judged independently by this preset's author) and is
238
+ **not** shipped, with the reason. This section exists so "why doesn't
239
+ `recheck/google` check X?" has a documented answer instead of looking like
240
+ an oversight.
241
+
242
+ ### Fabrications (found nowhere on the live guide) — never ship
243
+
244
+ | Candidate | Why excluded |
245
+ |---|---|
246
+ | `flesch-reading-ease` metric threshold | No basis anywhere in the guide — zero hits for flesch/reading-ease/readability/grade-level across every fetched page. The guide states only the qualitative principle ("use simpler words and shorter sentences"). Spec §5.6 and spec:215 independently rule out a `metric` rule in the flagship presets; shipping this would attribute an invented numeric mandate to Google under a CC-BY citation. |
247
+ | `respective`/`respectively` → rewrite | No such headword or rule exists anywhere in the word list or any topic page fetched (verifier C, independently re-confirmed by the cross-check). |
248
+ | `makes use of` → `uses` | Zero occurrences anywhere in the fetched pages (verifier C, cross-check). |
249
+ | `query string (not querystring)` | No entry anywhere on the word-list page or any cited topic page; a targeted search of the guide also returns nothing (verifier C, cross-check). |
250
+ | `real time`/`real-time` noun/verb pair | Zero hits in the parsed text or the raw HTML (verifier B, cross-check). |
251
+ | `robust` as a caution term | Does not appear anywhere in the current word list (verifier B, cross-check). |
252
+
253
+ ### Dropped as unverified (real principle, no dedicated confirmable entry)
254
+
255
+ | Candidate | Why excluded |
256
+ |---|---|
257
+ | `user name` → `username` | No dedicated word-list entry; only inferable from `account name → username` and the general closed-compound principle. Per the verification contract, unverified means dropped, not shipped at a lower confidence. |
258
+ | N/A first-reference spell-out | The word-list entry only requires spelling out `N/A`/`NA` on FIRST reference, not a blanket ban — Recheck has no reliable way to track "is this the first reference" positionally across a document, so it's excluded rather than shipped as a (wrong) blanket swap. |
259
+
260
+ ### TOO-RISKY (guide confirms it, but the "avoid" token is too ordinary/polysemous to blind-match)
261
+
262
+ These are all real, guide-stated preferences; none are shipped because the
263
+ literal string is common enough in unrelated, correct usage that a blind
264
+ `swap`/`pattern` would generate far more false positives than true ones.
265
+
266
+ | Candidate | Why excluded |
267
+ |---|---|
268
+ | `abort`, `kill`, `terminate` → stop/exit/cancel/end | Standard, correct technical vocabulary (process signals, transaction aborts, connection termination) with a guide-documented command-line-syntax carve-out a blind swap can't detect. |
269
+ | `execute` → run; `access` (verb); `possible`/`impossible`; `impact` (verb); `interface` (verb); `exploit`; `scale` (bare); `review` (="first read"); `each` (="all") | Each has a real but conditional/sense-scoped ruling; the bare word is common enough in the OTHER (unflagged) sense that a blind match would misfire constantly. |
270
+ | `simply`/`simple`, `easy`/`easily`, `quick`/`quickly`, `just` | The guide's own "try eliminating this word" framing has documented legitimate uses ("just" is explicitly OK "to convey that one approach is simpler"); a blind delete-swap fights the guide's own exception. |
271
+ | `America`/`American` (=USA sense) | Scoped to the USA-sense only; "American" (American English, American Express, Latin American) has far more legitimate uses than violations. |
272
+ | `Cloud` (bare, capitalized, standing for Google Cloud); `portal`/`dashboard` (meaning the Google Cloud console) | The guide explicitly permits lowercase "the cloud" generically, and "portal"/"dashboard" are ordinary words in enormously common non-Google-Cloud technical writing. |
273
+ | `PostgreSQL`/`Postgres`, `directory`/`folder`, `plain text`/`plaintext` | Context-conditional (match the UI's own wording; CLI vs. GUI; cryptography context only), not a free either-or `consistency` pair — shipping as unconditional would mis-enforce legitimate context-appropriate variation. |
274
+ | `firewalls` → `firewall rules` | Scoped to Compute Engine/networking documentation only; "outside of [that], the term firewalls is acceptable" per the guide's own text — Recheck can't detect document subject matter. |
275
+ | `deselect` (bare, banned) | **Wrong to ship at all**: `deselect` is Google's own correct/recommended term for NON-checkbox UI elements ("use clear for checkboxes, and deselect for other UI elements"). Only `uncheck` (unambiguously checkbox-only in meaning) is shipped. |
276
+ | bare `check` (verb) | Extremely polysemous ("check the logs", "check that X is true") — only the unambiguous `uncheck` ships. |
277
+ | `hit` (UI click synonym) | "hit" is extremely common in unrelated technical senses (cache hit, rate limit hit); the guide's UI-click sense can't be isolated by a blind pattern. |
278
+ | `type` → `enter` | "type" is one of the most common words in technical prose in unrelated senses (data type, type of X); too ambiguous to match blindly. |
279
+ | `menu item`/`choice`/`option` → `command` | "option"/"choice" are common ordinary words far outside the menu-item sense the guide targets. |
280
+ | bare `master` | Google's own quote scopes the objection to the master/slave PAIRING ("Never use in conjunction with slave"), not the standalone word, which has many unrelated legitimate senses (master's degree, master key, master bedroom). Only `slave` ships. |
281
+ | `drag and drop` (as opposed to `click and drag`) | Commonly and correctly used as a noun/adjective ("drag-and-drop interface"); only the unambiguous verb phrase `click and drag` ships. |
282
+ | `hang`/`hung` (of a system) | Extremely polysemous outside the system sense ("hung the picture", "hung jury", "hang up the phone"). |
283
+ | `healthy` (of a system) → responsive | Polysemous even in technical prose ("a healthy amount of caution", "healthy competition"). |
284
+ | `mad` (ableist figurative sense) | At least as commonly used to mean "angry", a sense the guide never objects to. |
285
+ | `abnormal`, `deficient`, `deformed` (of a person) | Explicitly "OK to refer to a condition of a computer system"; unscoped, these would misfire on ordinary error-handling prose ("abnormal termination", "abnormal exit code"). |
286
+ | `gimp`/`gimpy`, `lame` | `gimp` has an explicit carve-out for the GIMP image editor and similarly-named tools; neither has a fixed replacement token, and both are DETECT-ONLY at best. |
287
+ | `native` (of people); `target` (verb, of people) | Both are advisory ("avoid... when possible") and the words themselves are ubiquitous in unrelated, correct senses ("native app", "cloud-native", "target audience"). |
288
+ | `above`/`below`/`higher`/`lower`/`older` (version-range words) | Explicitly OK when non-directional ("below average", describing a hierarchy) and reversed for Android docs; only safe when anchored to an unambiguous version-number context, which a blind pattern can't establish. |
289
+ | `tap`/`click` (touch vs. desktop) | Requires knowing whether the document targets a touch device, which Recheck cannot infer from text alone. |
290
+
291
+ ### DETECT-ONLY entries not shipped
292
+
293
+ Real, confirmed guide content with no fixed replacement, excluded here
294
+ because a generic "avoid this" pattern for these specific terms was judged
295
+ lower-value than the DETECT-ONLY rules that did ship (`no-via`, `no-hover`,
296
+ `utilize`, `performant`, `chapter-terminology`, `blind-figurative`):
297
+
298
+ | Candidate | Why excluded |
299
+ |---|---|
300
+ | `persist` (transitive verb) | "Don't use as a transitive verb... best to avoid using as a verb at all" — no fixed replacement token, needs a rewrite. |
301
+ | `comply`/`compliant` | Advisory caution only ("a claim that a product is compliant... is a strong statement"), no replacement at all — not a lint-shaped rule. |
302
+ | `CLI` (bare, generic) | Needs "the specific CLI", which varies by context; no fixed swap target. |
303
+ | `gray-box`/`graybox` testing | "describe exactly what it's doing" — no fixed term, and `translucent-box testing` is only an example, not a mandate. |
304
+ | `tribal knowledge`/`wisdom` | "use a less figurative term" — no fixed replacement. |
305
+ | `anti-pattern`, `canary`/`canarying` (verb), `best effort`, `out of the box` (figurative), `reservation, off the`, `voila` | Each is "avoid"/"don't use" with no fixed replacement token; the underlying content is confirmed but not independently high-value enough to ship as a detection-only pattern rule in this pass. |
306
+ | `physically challenged`, `special`, `differently abled`, `handi-capable` | DETECT-ONLY per `inclusive-documentation`, no replacement offered. |
307
+ | `right-hand side` (directional language, generally) | DETECT-ONLY, no fixed replacement — see also the TOO-RISKY note on `above`/`below` above; directional language generally was judged too broad to ship as its own rule (see "Author's judgment calls"). |
308
+
309
+ ### NOISY (verifier-confirmed content, judged too broad to enforce)
310
+
311
+ **Accounting note, so the count below is checkable.** The task-9 corrections
312
+ brief referred to "the 32 NOISY candidates from the research"
313
+ (`research-google-style.md`'s own §4 tally). That figure is a count over a
314
+ *different* population, using a *different* taxonomy, than this file's own
315
+ four-row table below. The research draft numbered 146 candidate "rule
316
+ ideas" and gave each one its own provisional, pre-verification classification
317
+ (CLEAN/NOISY/NOT-ENFORCEABLE); 32 of those 146 rows carried NOISY as their
318
+ first-stated verdict. This preset was not built by re-litigating those 146
319
+ rows one-by-one — the five verification passes instead checked candidate
320
+ content directly against the live guide's word-list and topic pages
321
+ (~380 distinct entries, a finer unit than the 146 rows: e.g. one draft row
322
+ can bundle a dozen word-list headwords). Cross-referencing the specific
323
+ 32 research-draft NOISY rows against what this file actually contains:
324
+
325
+ - **6 ended up shipped anyway**, because a narrower scope, `fix: false`,
326
+ or a detection-only rule shape resolved the original noise concern
327
+ instead of requiring exclusion: `heading-sentence-case` (row 1, `fix:
328
+ false` + the `exceptions` mechanism), `no-code-in-heading` (row 9,
329
+ scoped to headings), `second-person` (row 13, detection-only),
330
+ `use-contractions` (row 17, `fix: false`), the timeless-documentation
331
+ words (row 29, 5 of ~15 shipped; see "Author's judgment calls" below for
332
+ the rest), and `copy-and-paste` (row 91, the copy/paste half only — the
333
+ same row's keyboard-shortcut suggestion was not shipped or excluded
334
+ anywhere, see below).
335
+ - **5 correspond exactly to this table's own four rows** below (row 6 →
336
+ `no-gerund-headings`; row 52 → `no-slashes-general`; row 95 → table
337
+ sentence-case; rows 100 and 102 → the shared `no-quotes-around-code` /
338
+ `no-angle-brackets-around-code` row).
339
+ - **2 correspond to NOT-ENFORCEABLE entries** below (row 39 → `oxford-comma`;
340
+ row 123 → gendered pronouns used generically).
341
+ - **1 splits across two other tables**: row 145 ("console"/"CLI"/"UI"/
342
+ "Cloud"/"mobile" used bare) has its "CLI" part in DETECT-ONLY above and
343
+ its "Cloud" part in TOO-RISKY above; the "UI"/"mobile" parts of that same
344
+ row aren't reflected anywhere.
345
+ - **The remaining 18 do not reappear anywhere in this file** — shipped,
346
+ excluded, or otherwise: nonbreaking space before a unit (row 69),
347
+ thousands-separator commas (row 61), avoiding seasons (row 65), SVG over
348
+ PNG (row 113), not forcing line breaks (row 115), inline HTML (row 116),
349
+ comma before "which"/"because" (rows 48-49), a colon instead of a dash to
350
+ introduce a list item (row 44), ambiguous-conjunction swaps like
351
+ since/while/once (row 131), modal-verb guidance (row 132), noun/verb form
352
+ pairs like setup/set up (row 133), spell-checking prose generally (row
353
+ 142), foo/bar/baz placeholders (row 144), present tense / avoiding
354
+ will/would (row 16), the screen-reader punctuation caution (row 22), and
355
+ 10x-style symbol substitutions (row 37). Their absence here means they
356
+ were never carried into the five verifiers' own ~380-entry check —
357
+ **not** that a verifier confirmed them and this preset silently dropped
358
+ them. Anyone who wants a disposition for one of those 18 specific ideas
359
+ should treat it as unverified against the live guide, not as any of this
360
+ file's excluded categories, and it is not shipped.
361
+
362
+ The four rows below are this preset's own, narrower "NOISY" bucket: guide
363
+ content a verifier independently confirmed as real, but judged too broad to
364
+ enforce as a rule. That is a stricter, verifier-anchored sense of "NOISY"
365
+ than the research draft's own pre-verification tally, which is why the
366
+ counts don't and shouldn't match.
367
+
368
+ | Candidate | Why excluded |
369
+ |---|---|
370
+ | `no-gerund-headings` (heading starting with an -ing word, except Billing/Pricing) | Confirmed principle, but the enforcement shape (first word ends in "-ing") would misfire heavily on common tech-noun headings used as topics, not verb-form imperatives (Networking, Logging, Caching, Monitoring, Testing) — Google names only two exceptions, which is itself evidence the underlying judgment needs more context than a heading's first word. |
371
+ | `no-quotes-around-code`, `no-angle-brackets-around-code` | Confirmed, but the draft itself already flagged these as NOISY; not re-litigated. |
372
+ | table sentence-case (`Use sentence case for all the elements in a table`) | Confirmed, but table cells often legitimately contain short labels, proper nouns, or numeric/code values that don't fit sentence-case cleanly (verifier B row 95, marked NOISY). |
373
+ | `no-slashes-general` (`Avoid using slashes, except in code`) | Too broad — slashes appear constantly in dates, paths, fractions, and URLs; the draft itself marked this NOISY/dropped. |
374
+
375
+ ### NOT-ENFORCEABLE (real guide content, requires human judgment)
376
+
377
+ | Candidate | Why excluded |
378
+ |---|---|
379
+ | `optional-prefix` (use "Optional:" prefix in headings) | Requires knowing whether a section is genuinely optional — not something the linter can determine. |
380
+ | `complete-list-intro` (text before a colon must be a complete sentence) | Requires grammatical-completeness judgment beyond regex/AST primitives. |
381
+ | `oxford-comma` (missing comma before the final "and"/"or" in a list) | Confirmed content, but reliably detecting a MISSING Oxford comma without a high false-positive rate on ordinary two-clause sentences needs real list/clause parsing, not regex. |
382
+ | `abbrev-as-verb` (don't use acronyms as verbs, e.g. "ping the server") | Requires knowing a word's grammatical role (verb vs. noun), which the engine's primitives can't determine. |
383
+ | `abbrev-first-use` (spell out an abbreviation on first mention) | Requires positional "is this the first mention" tracking across an arbitrary, unbounded set of abbreviations. |
384
+ | `no-duplicate-link-destinations` (avoid linking one destination from different texts) | The guide states explicit exceptions (linking to a different section, a long page, multiple entry points) that the engine's existing token rule — which fires on same-destination/different-anchor-text — can't evaluate against. (The rule remains available generically via `recheck/markdown` for projects that want it unconditionally; it just isn't part of this style-fidelity preset.) |
385
+ | external link icon (`Don't use an external link icon`) | About a rendered visual icon/CSS class, not markdown text — nothing to match. |
386
+ | `ui-element-ellipsis` (drop the "..." from a UI element name reference) | Requires reliably identifying "this text is quoting a UI element name", which risks both over- and under-firing with a regex. |
387
+ | `no-directional-language` (above/below/right-hand side as spatial UI references) | The guide's objection is to visual/spatial positioning language specifically; a text pattern can't distinguish that from the equally common non-directional uses of the same words (see the TOO-RISKY note above). |
388
+ | `button-for-link` ("a link isn't the same as a button") | Requires knowing whether a referenced UI element is actually a button or a link, which text alone doesn't establish. |
389
+ | gendered pronouns used generically (bare he/him/his/she/her) | Requires knowing whether a pronoun refers to a specific named person or is used generically — text alone can't distinguish. |
390
+ | "introduce a table in the text preceding it"; the large "what belongs in code font" table; "don't pre-announce anything... unless approved by legal counsel"; "don't use metaphors"; passive voice ("make clear who's performing the action") | All require holistic judgment about content/structure/legal status that the engine's regex/AST primitives cannot evaluate. |
391
+
392
+ ### Dropped in Fix wave A (confirmed content, but not safely fixable/matchable)
393
+
394
+ An independent review (task 9, fix wave A, 2026-07-29) reproduced four
395
+ demonstrable prose-corruption defects and diagnosed a structural coverage
396
+ gap that let 85 of the preset's 86 swap pairs ship with zero fixture
397
+ coverage. These candidates were CONFIRMED guide content (they were already
398
+ shipping) but are dropped here rather than fixed, because no safe
399
+ detection/fix shape was achievable with the assertions available — see
400
+ `task-9-report.md`'s "Fix wave A" section for the full defect list, every
401
+ fix applied, and the re-run acceptance evidence.
402
+
403
+ | Candidate | Why excluded |
404
+ |---|---|
405
+ | `'in line': 'inline'` (space-separated form, `google/compound-forms`) | Google's own quote ("One word as an adjective, inline, not in line or in-line") only objects to the ADJECTIVAL use, but bare "in line" is at least as commonly the correct idiom "in line with" or the plain verb phrase "wait in line"/"stand in line", neither of which the guide says anything about. Reproduced: `"This change is in line with the platform roadmap."` was being rewritten to `"...is inline with..."`. There's no reliable regex-only way to tell the wrong adjectival use apart from the idiom, so the key is dropped rather than shipped fixable or even detection-only. `'in-line': 'inline'` (the hyphenated form) is KEPT — it doesn't collide with the "in line with"/"wait in line" idioms, which are never written hyphenated. |
406
+ | `'API Console': 'Google Cloud console'` (`google/product-names`) | Verifier B row 125 flagged that it doesn't map cleanly — the guide's own text offers "Google APIs Explorer **or** the Google Cloud console" by context, and which one is meant depends on what the original "API Console" reference meant. Not in verifier C's confirmed-clean list either. |
407
+ | `Microservices: 'microservices'` (`google/acronym-forms`) | Fires on legitimate sentence-initial capitalization ("Microservices deployed on the platform can scale independently...") just as readily as on the actual violation (mid-sentence "the Microservices approach"), and `applyMatchCase` re-capitalizes the replacement to match the (all-caps-adjacent) matched casing, so the "fix" is a permanent no-op — an unsuppressible warning either way. There's no reliable way to detect "sentence-initial" from a `swap` pair alone. The lowercase/hyphenated form `'micro-services': 'microservices'` is KEPT (case-sensitive, so it never matches a legitimately-capitalized sentence start). |
408
+
409
+ ## Deliberation and development history
410
+
411
+ Everything from here on records how the shipped and excluded lists above
412
+ were reached: sourcing/hashing methodology, the fabrications an earlier
413
+ draft introduced, the successive fix waves that corrected or excluded
414
+ specific pairs, and the axes a pair must clear before auto-fix is safe.
415
+ **A reader who only wants provenance can stop reading above this point** —
416
+ everything below is for someone auditing or extending this preset.
417
+
418
+ ### `sources.json` normalization
419
+
420
+ `sources.json` records one hash per source page as drift detection for a
421
+ future re-check: re-fetch a page later, and a changed digest means the
422
+ guide's content changed. The version of this file that shipped before
423
+ this pass hashed the raw HTML response directly. That does not work as
424
+ drift detection: every `developers.google.com/style/*` page embeds a
425
+ per-request `<script type="application/json" analytics>` blob whose JSON
426
+ keys serialize in non-deterministic order, a CSP `nonce` attribute
427
+ regenerated on every request, and an inline feature-flag/experiment
428
+ bootstrap array whose element order also varies per request. None of that
429
+ reflects the guide's actual content, but it's enough entropy that two
430
+ consecutive fetches of the identical page produce different raw-HTML
431
+ digests every time — confirmed empirically: of the 30 pages listed in
432
+ `sources.json`, fetched twice each a few seconds apart, 16 had a different
433
+ raw byte length on the second fetch even though nothing about the guide's
434
+ content changed. Hashing raw HTML gives a 100% false-positive drift rate,
435
+ which is not drift detection at all — it's noise indistinguishable from
436
+ signal.
437
+
438
+ **Fix**: hash the extracted `<article class="devsite-article">...</article>`
439
+ region instead of the full page. That region is exactly the guide's
440
+ rendered content (headings, paragraphs, lists, tables, the `<dl>`
441
+ definition lists the word-list page is built from) and excludes all of
442
+ the non-deterministic chrome described above. Verified reproducible: two
443
+ consecutive fetches of all 30 pages in `sources.json` (not just a sample)
444
+ produced a byte-identical extracted region, and therefore an identical
445
+ sha256, for every single page — including the 16 whose raw HTML length
446
+ differed between fetches. Reproduction recipe: fetch the page's HTML with
447
+ **`curl`** (Fix wave C / Item 4: a plain Node `fetch()` was confirmed to
448
+ return a materially different HTML variant of the same URL — not just the
449
+ per-request noise above, but a different response shape from the same
450
+ client-vs-client comparison — so a future re-check that fetches with a
451
+ different HTTP client could see a mismatch and misread it as guide drift;
452
+ `sources.json`'s `normalization.fetchMethod` now records this too), take
453
+ the first `<article class="devsite-article">...</article>` match (a
454
+ non-greedy, dot-matches-newline regex scan; every page has exactly one),
455
+ and sha256 the UTF-8 bytes of that substring.
456
+
457
+ Each `sources.json` entry also records `bytes` — the byte length of the
458
+ extracted region, not the raw page — so a future re-check that produces a
459
+ matching `bytes` but a different `sha256` (or vice versa) is a signal to
460
+ check whether the *extraction* shape changed (e.g. Google renamed the
461
+ wrapper class) before treating the result as real guide drift.
462
+
463
+ This normalization has a real, narrow blind spot: a change confined
464
+ entirely to the `<article>` wrapper's own attributes, or to guide content
465
+ that Google renders outside that element, would not be detected. No such
466
+ case was observed across any of the 30 pages while producing this file.
467
+
468
+ ### Fix wave A corrections (2026-07-29)
469
+
470
+ Beyond the drops above, several shipped pairs were corrected in place —
471
+ same rule content, safer matching or `fix: false` — rather than dropped,
472
+ because the underlying guide entry is real and worth keeping. Full
473
+ before/after detail, reproduction commands, and gate output live in
474
+ `task-9-report.md`; this is the pointer from provenance to what changed
475
+ and why, so a rule split doesn't read as an unexplained addition.
476
+
477
+ - **`google/no-latinisms` split in two.** The period-terminated keys
478
+ (`i.e.`, `e.g.`, `vs.`) keep `wordBoundary: false` (a trailing `\b`
479
+ right after a period-then-space never matches — both are non-word
480
+ characters) but now carry a LEADING `\b` baked into the regex source via
481
+ `keysAreRegex`, so `vs.` can no longer match inside unrelated words like
482
+ "revs.". `aka` and `vice versa` (no trailing period, so they never
483
+ needed the exemption) moved to a new rule, `google/no-latinisms-plain`,
484
+ with full `wordBoundary: true` anchoring — this is what stops `aka` from
485
+ matching inside "Akamai"/"Osaka".
486
+ - **`google/no-slash-abbrev`** (`c/o`, `w/`) similarly gained a
487
+ leading-only `\b` via `keysAreRegex`, so `w/` no longer matches inside
488
+ "www/static", "show/hide", "new/old", and `c/o` no longer matches inside
489
+ "src/output".
490
+ - **`google/acronym-forms`**: `'OAuth 2'` gained a `(?!\.0)` negative
491
+ lookahead so it can no longer match inside the already-correct "OAuth
492
+ 2.0" (which was compounding into "OAuth 2.0.0.0.0.0.0" under
493
+ `runRulesUntilStable`). `SHA1` moved to its own rule
494
+ (`google/sha1-form`, `fix: false`) with a negative lookbehind excluding
495
+ a hyphen-preceded match, matching the guide's own documented
496
+ hyphenated-compound exception (e.g. "HMAC-SHA1"). `UNICODE` and `IPSEC`
497
+ moved to `google/acronym-caps-detect-only` (`fix: false`): both are
498
+ ALL-CAPS matches whose correct replacement (`Unicode`/`IPsec`), when
499
+ `applyMatchCase` re-upper-cases it to match the all-caps input, round-trips
500
+ back to the ORIGINAL wrong spelling byte-for-byte — a permanent,
501
+ silent no-op fix that `--fix` nonetheless reported as "fixed".
502
+ - **`google/product-names`**: `'Cloud console'` gained a `(?<!Google )`
503
+ negative lookbehind — it's a literal substring of its own replacement
504
+ ("Google Cloud console"), so it was compounding a "Google " prefix every
505
+ pass, the same failure shape as the OAuth 2 bug above (and it fed the
506
+ same bug via `'Developers Console'`'s own correct output). `GCP` moved
507
+ to its own rule (`google/gcp-name`, `fix: false`): `applyMatchCase`
508
+ upper-cases an all-caps match's ENTIRE multi-word replacement, so
509
+ "GCP" -> "Google Cloud" was being written as "GOOGLE CLOUD".
510
+ - **`google/colo-form`**: split out of `google/compound-forms`, `fix:
511
+ false`. `colo` is a noun ("a colocation facility"); `colocate` is a
512
+ verb — the unconditional swap produced "The colocate hosts the racks."
513
+ - **`google/no-please-note`**: `fix: false` added. Deleting the phrase
514
+ leaves a capitalization/fragment mess behind ("Please note that the
515
+ endpoint is deprecated." -> "that the endpoint is deprecated.").
516
+ - **`google/second-person`**: `ignoreCase: true` replaced with an explicit
517
+ `(?:We|we|Our|our|Us|us)` alternation, so it no longer matches the
518
+ all-caps abbreviation "US" that `google/us-abbreviation` fixes toward —
519
+ previously the two rules fought each other on every occurrence of "US".
520
+
521
+ All of the above were found and fixed by the per-PAIR coverage gate added
522
+ in the same pass (`src/config/__tests__/preset-google.test.ts`'s "per-pair
523
+ coverage" test) plus targeted idempotency/self-match static analysis — see
524
+ `task-9-report.md` for the methodology and the complete list.
525
+
526
+ ### Fix wave C corrections (2026-07-29)
527
+
528
+ An independent re-review found this same `applyMatchCase` bug — an
529
+ ALL-CAPS match forcing a multi-word replacement to shout — recurring a
530
+ THIRD time (`AKA` -> `ALSO KNOWN AS`, `VICE VERSA` -> `THE OTHER WAY
531
+ AROUND`, `C/O` -> `CARE OF`, all in rules that shipped with no `fix: false`
532
+ workaround at all), after `GCP` and `UNICODE`/`IPSEC` above had each been
533
+ patched around it per-rule. This wave fixed it at the source instead
534
+ (`src/core/case-preserve.ts`'s `applyMatchCase`): an ALL-CAPS match no
535
+ longer forces a MULTI-WORD replacement to upper-case; single-word
536
+ replacements are unchanged (`WHITELIST` -> `ALLOWLIST` still shouts — that
537
+ remains correct). Two statements above are now stale as a result:
538
+
539
+ - **`GCP` is fixable again.** `google/gcp-name`'s `fix: false` (added
540
+ above specifically because `applyMatchCase` was shouting "GOOGLE CLOUD")
541
+ is REMOVED — the engine fix produces the correctly-cased `"Google
542
+ Cloud"` now, and the result is idempotent. `UNICODE`/`IPSEC`
543
+ (`google/acronym-caps-detect-only`) were re-checked against the same
544
+ engine fix and correctly stay `fix: false`: their replacements
545
+ (`Unicode`, `IPsec`) are each a single word, so the multi-word condition
546
+ never applies and the round-trip no-op is unchanged — a genuinely
547
+ different defect shape (same-word casing round-trip, not a
548
+ multi-word-phrase shout), not something this engine fix was meant to
549
+ reach.
550
+ - **`google/product-names`'s `'Cloud console'` lookbehind widened.** The
551
+ `(?<!Google )` guard above only blocked the exact casing `Google `
552
+ (single space); `(?<![Gg][Oo][Oo][Gg][Ll][Ee]\s+)` now blocks any casing
553
+ of "google" followed by any run of whitespace, closing the same
554
+ self-compounding duplication (`"google Cloud console"` ->
555
+ `"google Google Cloud console"`) the original fix was meant to
556
+ eliminate but didn't fully.
557
+ - **`google/no-slash-abbrev` gained a trailing guard.** The leading-only
558
+ `\b` above fixed matches inside a preceding word (`"src/output"`,
559
+ `"www/static"`) but left the TRAILING side open: `w/` still matched
560
+ inside the common, ordinary abbreviation `w/o` ("without"), and `c/o`
561
+ inside a word immediately following it (`"c/oscillator"`). Both keys now
562
+ carry a trailing `(?![A-Za-z])` negative lookahead blocking a following
563
+ ASCII letter, so `w/o` and `c/oscillator` are left alone entirely (the
564
+ guide's own `slashes` page doesn't list "w/o" as a separate entry),
565
+ while `"w/ headers"` and `"c/o the compliance department"` still match.
566
+
567
+ See `task-9-report.md`'s "Fix wave C" section for the full list of every
568
+ rule whose `--fix` output changed as a result of the engine change (9
569
+ rules / 38 pairs across this preset alone — larger than the three rules
570
+ named above, since the fix is in the shared helper every `swap` rule
571
+ uses), the semantics chosen and why, and the complete acceptance evidence.
572
+
573
+ ### CONFIRMED vs. safe-to-fix — a distinction future audits of this preset must also apply
574
+
575
+ Recorded here in mirror of `presets/microsoft/PROVENANCE.md`'s "Fix wave C /
576
+ Step 5" (task 10), per that task's brief: a verifier `CONFIRMED` verdict
577
+ establishes only that the live guide page discusses a term. It does **not**
578
+ establish that a blind textual (`swap`) substitution of that term is safe
579
+ to auto-apply — that is a separate question, and every verification pass
580
+ in task 10 conflated the two. `recheck/microsoft`'s task-10 fix wave C
581
+ found two pairs (`as well as` -> `and`, `or greater`/`or higher`/`or lower`
582
+ -> `or later`/`or earlier`) that were marked plain `CONFIRMED`, with the
583
+ correct quote attached, and still shipped as corrupting unconditional
584
+ swaps: the quotes themselves showed a caution against treating two terms as
585
+ interchangeable ("don't use X as a synonym for Y") or a context scoped
586
+ narrower than what shipped ("when identifying multiple versions..."), not
587
+ an unconditional "use Y instead of X" instruction.
588
+
589
+ This note is a forward-looking record, not a claim that this file's own
590
+ pairs were re-audited against that standard — that re-audit is out of
591
+ scope for task 10 (which targets `recheck/microsoft`) and has not been
592
+ done here. Whoever next adds a candidate pair to this preset, or authors
593
+ either of the two future presets task 10 anticipates, should apply the
594
+ same per-pair check `recheck/microsoft`'s Step 1 table used before marking
595
+ a `CONFIRMED` pair fixable: is the live quote a direct "Use Y, not X"
596
+ instruction, or does it instead read as a synonym-conflation caution, a
597
+ verb/context-scoped rule, a multi-target rule, or a "don't use X" with no
598
+ replacement stated? Only the first shape is safe to ship as an
599
+ unconditional `swap`; the rest need a position anchor, a narrower scope, or
600
+ detection-only, in that preference order. A `CONFIRMED` verdict is
601
+ necessary evidence that a rule belongs in the preset at all — it is not
602
+ sufficient evidence that the rule may safely auto-fix.
603
+
604
+ **This note's own re-audit did eventually happen** — see "Fix-posture
605
+ change" below, which re-audits every fixable pair in THIS preset against a
606
+ second, orthogonal axis the note above doesn't cover, and replaces the
607
+ "CONFIRMED vs. safe-to-fix" framing with a stricter, mechanical posture.
608
+
609
+ ### Fix-posture change (2026-07-30)
610
+
611
+ > **RETIRED 2026-07-30 — see "Detection-only" below.** This section's
612
+ > criterion (same-word normalization) no longer determines which pairs are
613
+ > fixable in this preset: none are. Kept as historical record only.
614
+
615
+ Base commit `eb4f8b11dac`, branch `aa/recheck-style-guides`. Brief:
616
+ `.superpowers/sdd/preset-fix-posture-brief.md`. Report:
617
+ `.superpowers/sdd/preset-fix-posture-report.md`. Applied to both flagship
618
+ presets in the same pass — see `presets/microsoft/PROVENANCE.md`'s own
619
+ "Fix-posture change" section for the full two-axis rationale and the six
620
+ named corruption strings that motivated it (three fix waves on
621
+ `recheck/microsoft`, each fixing the pairs a probe found, each followed by
622
+ another probe finding more — 2, then 2, then 6). This preset shipped nine
623
+ of the same defect class across its own three fix waves (`GCP` shouting,
624
+ `OAuth 2` self-compounding, `w/o`/`c/oscillator` corruption, ...) — the
625
+ CONFIRMED-vs-safe-to-fix note above addresses the GUIDANCE-SHAPE axis
626
+ (direct instruction vs. caution/scoped/multi-target); it does not address
627
+ a second, orthogonal axis: whether the avoid-term ALSO has a legitimate,
628
+ unrelated sense a blind substitution corrupts regardless of how clearly
629
+ the guide states its rule.
630
+
631
+ #### The posture
632
+
633
+ Auto-fix is retained only where a replacement cannot be wrong: the same
634
+ word, normalized (spelling, hyphenation, casing, or a non-standard written
635
+ form of the identical word). A pair that substitutes a genuinely different
636
+ word or phrase — even a synonym that looks safe on the page — moves to
637
+ detection-only. Concrete examples found THIS wave, previously shipped
638
+ fixable under a plain `CONFIRMED`/reasonable-looking verdict:
639
+
640
+ - **`agnostic` → `platform-independent`** (`google/plain-language-swaps`):
641
+ "agnostic" very commonly means doubting or noncommittal about religious
642
+ or philosophical claims ("he's agnostic about the existence of an
643
+ afterlife") — a blind fix corrupts that sentence into "...platform-
644
+ independent about the existence of an afterlife." The word-list entry
645
+ is real and the replacement is reasonable for the INTENDED sense; it is
646
+ simply also a different word with an unrelated common sense, the same
647
+ shape as `recheck/microsoft`'s `DMZ`.
648
+ - **`GCP` → `Google Cloud`** (`google/gcp-name`): the same shape as `DMZ` →
649
+ `perimeter network` — an acronym expanded into a DIFFERENT phrase than
650
+ its own literal expansion (`GCP` stands for "Google Cloud Platform", not
651
+ "Google Cloud"), not a respelling, and `GCP` has unrelated expansions in
652
+ other domains ("Good Clinical Practice", "Grade Control Point"). Note
653
+ this reverses Fix wave C's own "GCP is fixable again" call above: that
654
+ wave correctly fixed a real ENGINE bug (the case-preservation shout),
655
+ but engine-correctness and word-choice-safety are different questions,
656
+ and only the first was checked at the time.
657
+ - **`IO` → `I/O`** (`google/acronym-forms`, moved to
658
+ `google/acronym-caps-detect-only`): bare, case-sensitive "IO" is a real
659
+ product/library name in common developer use ("Socket.IO" — the period
660
+ before "IO" is a non-word character, so `\bIO\b` matches inside it) —
661
+ "Socket.IO connects clients" would corrupt to "Socket.I/O connects
662
+ clients."
663
+
664
+ By contrast, `google/compound-forms`'s ~80 remaining pairs (`data store` →
665
+ `datastore`, `e-mail` → `email`, ...) are genuine spacing/hyphenation
666
+ normalizations of the identical two words and stay fixable — this is the
667
+ family the fix-posture brief itself predicted would "largely survive."
668
+ Four pairs did NOT survive despite living in that same rule: `data
669
+ cleansing` → `data cleaning` (different word, "cleansing" vs "cleaning"),
670
+ `transcompile` → `transpile` (two competing compiler-jargon terms, not a
671
+ spelling variant), `autoupdate` → `automatically update` (expands "auto"
672
+ into a different word), and `pre-emptive` → `preemptible` (a different
673
+ adjective — "pre-emptive" describes acting in advance; "preemptible"
674
+ describes being subject to preemption — not a hyphenation of one word).
675
+ Moved to a new sibling rule, `google/compound-forms-word-choice`
676
+ (`fix: false`), since `fix` is a whole-rule flag and this bundle mixed
677
+ both classes.
678
+
679
+ #### Result
680
+
681
+ **Fixable `swap`/`consistency` pairs: 164 → 114**, across 41 rules with a
682
+ `swap`/`consistency` assertion (up from 38 — `google/vs-versus`,
683
+ `google/aka-form`, and `google/compound-forms-word-choice` are new,
684
+ splitting bundles that mixed same-word and different-word pairs).
685
+ `vs.` → `versus` and `aka` → `also known as` both stay fixable: unlike
686
+ `i.e.`/`e.g.` (Latin abbreviations translated into an unrelated English
687
+ phrase) and `vice versa` (a distinct Latin phrase with no letter-derived
688
+ relationship to its replacement), `vs.` and `aka` are literal truncations
689
+ of the identical word/phrase they abbreviate (`aka` is literally the
690
+ initials of "Also Known As"). **Correction (wave 2, see below): this call
691
+ on `aka` was wrong.** Expanding an abbreviation INTO a phrase is a
692
+ substitution, the same shape as `e.g.`/`i.e.` two sentences above, not a
693
+ respelling — "the letters spell out the words" does not make it a
694
+ same-word normalization. `aka` moved to `fix: false` in wave 2. `cURL` →
695
+ `curl` (a pure casing correction)
696
+ moved from `google/product-names` (now fully detection-only) to
697
+ `google/brand-capitalization`, which already ships the same class of
698
+ case-only brand-name fix.
699
+
700
+ Severity is unaffected: this preset's existing policy already puts every
701
+ word-choice/phrasing rule at `warn` regardless of fixability (see "Shipped
702
+ rules" above), so newly-detection-only rules needed no severity change.
703
+
704
+ #### Gates
705
+
706
+ `pnpm build`, `pnpm test` (105 files, 1467 passed / 5 skipped — shared
707
+ suite with `recheck/microsoft`), `pnpm parity --corpus monorepo-docs`
708
+ (unchanged: 27425 = 27425, 0 unexplained — this preset has no bearing on
709
+ the markdownlint-parity corpus), `npx nx run recheck:lint
710
+ --max-warnings=0` (clean). Every one of the 114 pairs that remain fixable
711
+ in this preset (240 combined with `recheck/microsoft`'s 126) was verified
712
+ programmatically: real change, idempotent (second `--fix` pass is a
713
+ no-op), and the original violation regex no longer matches the fixed
714
+ text — not sampled. Full command output and the exhaustive fixable-rule
715
+ table are in `.superpowers/sdd/preset-fix-posture-report.md`.
716
+
717
+ ### Fix-posture change, wave 2 — the proper-noun axis (2026-07-30)
718
+
719
+ > **RETIRED 2026-07-30 — see "Detection-only" below.** This section's
720
+ > third axis (proper-noun collision) no longer determines which pairs are
721
+ > fixable in this preset: none are. Kept as historical record only.
722
+
723
+ Base commit `25a62c3d1f1`, branch `aa/recheck-style-guides`. Brief:
724
+ `.superpowers/sdd/preset-posture-fix2-brief.md`. Report:
725
+ `.superpowers/sdd/preset-fix-posture-report.md`'s "Wave 2" section. See
726
+ `presets/microsoft/PROVENANCE.md`'s own "Fix-posture change, wave 2"
727
+ section for the full rationale shared by both presets; this section
728
+ covers this preset's specific findings.
729
+
730
+ #### The third axis
731
+
732
+ A pair keeps `fix: true` only if it is the same word normalized (axis 1,
733
+ guidance-shape), has no unrelated legitimate sense (axis 2, homograph —
734
+ wave 1), **and, new this wave, cannot occur as part of a real
735
+ organization, product, brand, or place name.** Four demonstrated
736
+ corruptions share this cause: `markdown` → `Markdown` (a retail markdown
737
+ sentence gets capitalized into the markup-language name), `FinTech` →
738
+ `fintech` (breaks "FinTech Group AG", a real company), `U.S.` → `US`
739
+ (breaks "U.S. Bank", a real bank), `USA` → `US` (breaks "USA Gymnastics",
740
+ a real governing body — this specific pair lives in
741
+ `microsoft/usa-abbreviation`, not this preset, since `google/us-
742
+ abbreviation` never shipped a bare `USA` key). Acronyms and single
743
+ capitalizable words are the highest-risk shapes: a rule that ignores
744
+ case or normalizes punctuation matches a proper noun's own official
745
+ spelling exactly as readily as ordinary prose.
746
+
747
+ #### Sweep and results
748
+
749
+ Every fixable `swap` pair in this preset (105 pairs after this wave,
750
+ sweeping the full ~114-pair fixable set left by wave 1) was checked
751
+ against question 3.
752
+
753
+ | Rule | Pair(s) flipped | Real proper noun / other reason |
754
+ |---|---|---|
755
+ | `google/aka-form` | `aka` → `also known as` | Not proper-noun — **misclassified in wave 1** (see the correction note above): expanding an abbreviation into a phrase is a substitution, the same shape as `e.g.`/`i.e.`, not a same-word normalization. Also happens to double as a real proper noun ("AKA" is the common abbreviation for the sorority Alpha Kappa Alpha — "She was initiated into AKA her freshman year" is a genuine, independent proper-noun collision on top of the misclassification). |
756
+ | `google/us-abbreviation` | `U.S.A.`/`U.S.` → `US` (both) | "U.S. Bank" (top-10 US bank), "U.S. Steel", "U.S.A. Track and Field" (national governing body) — the named brief case. |
757
+ | `google/acronym-forms` | `FinTech` → `fintech` | "FinTech Group AG" — a real, publicly-traded German company whose name keeps the mixed-case "FinTech" spelling. |
758
+ | `google/acronym-forms` | `I-O` → `I/O` | "I-O DATA DEVICE, INC." — a real, major Japanese PC-peripherals manufacturer ("Japan's undisputed market leader" in that industry) whose brand is written exactly "I-O" (hyphenated, capital letters). `fin-tech`/`adtech`/`ad-tech` (all-lowercase keys, no case change involved) do NOT carry this risk: this rule is case-sensitive, so an all-lowercase key can never match a capitalized brand's own casing in the first place. |
759
+ | `google/brand-capitalization` | `markdown` → `Markdown` | The named brief case — both a retail/finance homograph (axis 2) AND, in the reverse direction, a proper-noun risk: capitalizing every lowercase occurrence assumes it always means the Markdown language. |
760
+ | `google/brand-capitalization` | `material design` → `Material Design` | Same reverse-direction risk as `markdown`: "the material design of the building incorporates local stone" is a plain, unrelated architectural phrase this pair would wrongly capitalize into Google's design-language name. |
761
+ | `google/brand-capitalization` | `search console` → `Search Console` | Same shape, weaker but still real: a generic lowercase phrase for an admin/tuning panel, not exclusively Google's product name. |
762
+ | `google/compound-forms` | `datasource` → `data source` | Collides with `javax.sql.DataSource`/Spring's `DataSource` — a real, load-bearing Java/Spring class and config-property name ("Configure the DataSource bean in the Spring context"), exactly the kind of technical content this preset's own audience writes about. |
763
+
764
+ **9 pairs flipped**, each moved to a new detection-only sibling rather
765
+ than anchored (`google/acronym-forms-proper-noun`, `google/brand-
766
+ capitalization-proper-noun`, `google/compound-forms-proper-noun`;
767
+ `google/aka-form` and `google/us-abbreviation` flip whole-rule since every
768
+ pair in each was reclassified). Severity is unaffected — this preset's
769
+ existing policy already puts every word-choice/phrasing rule at `warn`
770
+ regardless of fixability, so nothing needed adjusting, matching wave 1's
771
+ own note.
772
+
773
+ **Fixable pairs: 114 → 105.**
774
+
775
+ #### Why `fix: false`, not another anchor
776
+
777
+ Same reasoning as `recheck/microsoft`'s wave 2 section: three separate
778
+ waves have each shown an anchor leaking exactly one near-miss beyond
779
+ wherever it was tested. A casing/abbreviation fix is low-value enough
780
+ that detection alone is a fine outcome, so every pair this wave found
781
+ moved straight to `fix: false` rather than growing another exclusion
782
+ list.
783
+
784
+ #### Gates
785
+
786
+ `pnpm build`, `pnpm test` (105 files, 1490 passed / 5 skipped — 23 new
787
+ tests this wave, shared suite with `recheck/microsoft`), `pnpm parity
788
+ --corpus monorepo-docs --profile default` (unchanged: 27425 = 27425, 0
789
+ unexplained), `npx nx run recheck:lint --max-warnings=0` (clean). Every
790
+ sentence in the brief's acceptance set 1 verified unchanged through
791
+ `--fix` twice and still detected against the correct new rule name; every
792
+ rule (including the 3 new ones here) still fires on
793
+ `google-violations.md`. Full output in
794
+ `.superpowers/sdd/preset-fix-posture-report.md`'s "Wave 2" section.
795
+
796
+ ### Detection-only (2026-07-30)
797
+
798
+ Base commit `006c026a1f0`, branch `aa/recheck-style-guides`. Brief:
799
+ `.superpowers/sdd/preset-detection-only-brief.md`. Report:
800
+ `.superpowers/sdd/preset-detection-only-report.md`.
801
+
802
+ **This section REPLACES the fixability criterion described in "CONFIRMED
803
+ vs. safe-to-fix," "Fix-posture change," and "Fix-posture change, wave 2"
804
+ above — it does not sit alongside them as a fourth, stricter axis.** Those
805
+ three sections are kept below, unedited, as the historical record of the
806
+ criteria that were tried and superseded; do not read any of them as current
807
+ guidance. As of this section, **`recheck/google` ships zero fixable rules.
808
+ Every rule in this file is `fix: false`, unconditionally** — set
809
+ structurally, once, by a loop at the end of `buildGooglePreset()`
810
+ (`src/config/presets/google.ts`), not by auditing pairs against a sharper
811
+ rule. A dedicated test (`preset-google.test.ts`'s "is detection-only"
812
+ describe block) reads the live preset object and fails if any rule is ever
813
+ fixable again — the same derive-from-the-preset shape the per-pair coverage
814
+ gate already uses, so this cannot regress silently the way three prior
815
+ narrowing passes did.
816
+
817
+ #### Why a fourth axis wasn't the answer
818
+
819
+ Two prior fix-posture changes (above) each replaced "does the guide confirm
820
+ this pair" with a sharper structural test — first "is it the same word
821
+ normalized" (axis 1: guidance-shape, then axis 2: homograph), then "does it
822
+ also collide with a real proper noun" (axis 3). Each pass shipped clean
823
+ against its own criterion and each was then probed again. The fifth
824
+ adversarial probe against this preset and `recheck/microsoft` together (the
825
+ project's fifth in total, after three rounds already narrowed what counted
826
+ as "safe") found **18 of 29 probed pairs (62%) still corrupting correct
827
+ prose** — a RISING hit rate, not a falling one, and the failures spanned
828
+ every category previously believed safe by axes 1-3, including two this
829
+ preset's own criteria treated as clean:
830
+
831
+ - **Spelling**, believed the safest category of all: Hemingway's real,
832
+ correctly spelled published title *A Moveable Feast* is corrected to "A
833
+ Movable Feast" by `microsoft/az-grammar-usage`'s `moveable` → `movable`
834
+ pair (a genuine same-word normalization by every axis above — axis 1
835
+ passes, axis 2 finds no unrelated sense, axis 3 finds no proper-noun
836
+ collision on the WORD "moveable" itself, and the collision is instead
837
+ with a specific, individually unforeseeable literary title).
838
+ - **Hyphenation**, this preset's own `read only` → `read-only` pair
839
+ (`google/compound-forms`): "Please read only the introduction" — an
840
+ adverb ("only") modifying a verb ("read") plus its object — becomes
841
+ "Please read-only the introduction," a nonsense adjective use. Same-word
842
+ by every axis (it is the identical two words, just joined), yet wrong,
843
+ because the axes check the WORDS, not the GRAMMATICAL ROLE those words
844
+ are playing in the sentence being fixed.
845
+ - **Meaning inverted outright**: `google/acronym-forms`'s `No SQL` → `NoSQL`
846
+ turns "No SQL is used here" (a true statement that no NoSQL database is
847
+ in use) into "NoSQL is used here" (a false statement that one is) — same
848
+ word-pair, same axis-1/2/3 clearance, opposite meaning.
849
+
850
+ Full round-5 acceptance evidence (every sentence above, and more, run
851
+ through `--fix` twice and confirmed byte-identical) lives in
852
+ `src/config/__tests__/preset-detection-only-acceptance.test.ts`, plus the
853
+ per-preset regression suites in `preset-google-fix-wave-c.test.ts` and
854
+ `preset-microsoft.test.ts` (both rewritten by this change to assert
855
+ "unchanged" where they used to assert a real rewrite).
856
+
857
+ #### The conclusion this decision rests on
858
+
859
+ A rule's *category* — spelling, hyphenation, casing, word-choice — does not
860
+ predict fix-safety at this scale. A style guide states *intent* ("use X to
861
+ mean Y"); a `swap`/`consistency`/`pattern` rule matches *tokens* (literal
862
+ text, regardless of the grammatical role or referent that text has in a
863
+ given sentence). That gap is not closable by inventing a fourth, fifth, or
864
+ sixth axis: axis 1 (guidance-shape) closed the space of pairs where the
865
+ guide's own wording was ambiguous; axis 2 (homograph) closed the space of
866
+ words with an unrelated common sense; axis 3 (proper-noun) closed the space
867
+ of words that double as real names. Each closure found the NEXT gap, not
868
+ zero gap. The project decision is to stop narrowing and remove fixing
869
+ capability from both style-guide presets entirely: users get every finding
870
+ (detection is completely unaffected — every rule still runs `execute()` and
871
+ reports) and apply the judgment a style guide has always required, same as
872
+ before either preset existed and same as Vale (the tool these presets
873
+ replace), which never shipped an auto-fixer and never had this class of
874
+ bug.
875
+
876
+ #### What did not change
877
+
878
+ Detection. Every rule's `execute()` path, message, severity, and scope are
879
+ untouched — only `fix()` is gated off (`core/runner.ts`'s
880
+ `rule.fix !== false` check). The per-pair and per-rule coverage gates
881
+ (`preset-google.test.ts`) still require every rule to fire on its own clean
882
+ fixture, so a rule that neither fixes nor reports is still caught as dead
883
+ weight, same as before this change.
884
+
885
+ ### Known limitations
886
+
887
+ Two verifier-confirmed guide-sanctioned exceptions that the shipped rules
888
+ do not implement. Both are documented here explicitly, not just in the
889
+ Shipped rules table's own one-line notes, because a rule that is stricter
890
+ than its source needs to say so where a reader is actually looking for
891
+ "why did this fire on text the guide allows" — otherwise a user hitting
892
+ either case reasonably concludes the preset misquotes Google.
893
+
894
+ 1. **`google/no-emphasis-as-heading` over-fires on one shape of the
895
+ guide's own "run-in heading" pattern (verifier A row 12).** The guide
896
+ permits bold for "run-in headings" — a bolded lead-in term followed by
897
+ its description, most commonly inside a description-list item (`Google's
898
+ own example: <li><b>Emu</b>: the best kind of bird</li>`) or a single
899
+ paragraph ("**Emu:** the best kind of bird."), and instructs authors to
900
+ "end the run-in heading with a period or a colon." The shipped rule
901
+ (ported from markdownlint's MD036) already tolerates the common cases:
902
+ it only examines top-level paragraphs (so a run-in heading inside an
903
+ actual list item is never even considered), a bold lead-in with more
904
+ text in the SAME paragraph is excluded (the paragraph has more than one
905
+ meaningful child), and a bold-only paragraph ending in the guide's own
906
+ required punctuation is excluded by the rule's pre-existing punctuation
907
+ check. Empirically verified still-flagged: a bold/italic-only paragraph
908
+ with NO ending punctuation, whose description follows in a SEPARATE,
909
+ later paragraph (e.g. `**Emu**\n\nThe best kind of bird.`) — reproduced
910
+ directly against the shipped rule. Not fixed in this pass: the rule is
911
+ a markdownlint port shared with `recheck/markdown` (out of scope for
912
+ this wave's provenance/documentation focus, and doing so risks the same
913
+ loosening-vs-noise tradeoff every other TOO-RISKY exclusion in this file
914
+ weighs).
915
+ 2. **`google/no-url-as-link-text` does not exempt legal/ToS documents
916
+ (verifier B row 71).** The guide's own text: "Exception: In some legal
917
+ documents (such as some Terms of Service documents), it's okay to use
918
+ URLs as link text." The shipped rule is a plain pattern match on link
919
+ text starting with `http(s)://`, scoped to `link`; it has no way to
920
+ know whether the document containing the link is a legal/ToS document,
921
+ so it will flag a bare URL used as link text there too, against the
922
+ guide's own stated exception. Not enforceable to fix with the engine's
923
+ current primitives (same class of gap as `firewalls` → `firewall
924
+ rules`'s "Compute Engine documentation only" scoping and the
925
+ NOT-ENFORCEABLE table's document-subject-matter entries above) — Recheck
926
+ has no signal for what kind of document a file is.
927
+ 3. **`google/gcp-name` is case-sensitive.** The pair (`GCP: 'Google
928
+ Cloud'`) carries no `ignoreCase`, so only the literal all-caps `GCP`
929
+ token is matched or fixed — `gcp` and `Gcp` are neither flagged nor
930
+ fixed by this rule. A reader could reasonably infer broader coverage
931
+ than exists: many OTHER rules in this same file (e.g. `google/
932
+ compound-forms`, `google/use-contractions`) do set `ignoreCase: true`,
933
+ so the absence here is easy to read as an oversight rather than a
934
+ choice. It is a choice, shared with the sibling `google/product-names`
935
+ (also no `ignoreCase`) and `google/brand-capitalization` (explicitly
936
+ documented as "deliberately case-sensitive keys, matching only the
937
+ wrongly-cased literal form" in its own comment) — all three treat
938
+ brand/product-name casing as exact-match by design. Left as-is rather
939
+ than widened here (carried over from a `recheck/microsoft` fix-wave
940
+ audit, flagged as a documentation gap, not a behavior bug): widening to
941
+ `ignoreCase: true` is a scope decision for whoever owns `google.ts`, not
942
+ a documentation fix.
943
+
944
+ ### Author's judgment calls
945
+
946
+ Decisions this preset's author made that go beyond a verifier's literal
947
+ verdict, recorded per the task's instruction to flag (not silently
948
+ resolve) anything not settled by the inputs:
949
+
950
+ 1. **`google/first-line-h1` and `google/single-h1` share one Google
951
+ quote.** Verifier A's row 4 confirms both rule ids against the same
952
+ sentence ("only use a level-1 heading once on a page"); the research
953
+ draft, not a separate guide statement, is what split them into two rule
954
+ ids. Both ship (both are real markdownlint-ported mechanisms Google's
955
+ principle supports), but see point 2.
956
+ 2. **`single-h1` and `first-line-h1` cannot both fire from one document.**
957
+ Empirically verified (not assumed): the underlying token rules faithfully
958
+ port markdownlint's MD025/MD041, which check OPPOSITE preconditions of
959
+ "the document's first heading" — `single-h1` only reports a second h1
960
+ when nothing but comments/frontmatter precede the first one;
961
+ `first-line-h1` only fires when that first real content is NOT a correct
962
+ h1. `google-violations.md` (which starts with a level-2 heading to
963
+ trigger `first-line-h1`) structurally cannot also trigger `single-h1`, so
964
+ a second, tiny fixture (`google-violations-single-h1.md`) isolates it.
965
+ This is a genuine engine/upstream-semantics interaction, not a fixture
966
+ bug or a noisy rule.
967
+ 3. **Downgraded `list-length` from the generic "list mechanics = error"
968
+ class to `warn`.** The verifier marked the underlying quote
969
+ NOT-ENFORCEABLE (descriptive, not imperative); only the mechanism
970
+ (`list-length`'s `min: 2` default) is deterministic, so the softer
971
+ severity reflects the guide's own softer confidence.
972
+ 4. **Shipped only 5 of ~15 confirmed "timeless documentation" words.** All
973
+ ~15 are confirmed content, but 10 of them (`existing`, `future`,
974
+ `latest`, `new`, `newer`, `now`, `old`, `older`, `soon`, `eventually`,
975
+ `in the future`) are ordinary high-frequency English words with
976
+ extensive legitimate everyday use unrelated to documentation staleness.
977
+ Shipping them would make the preset unusably noisy on typical prose —
978
+ the same class of risk the verifiers flagged elsewhere as TOO-RISKY,
979
+ extended here on the same reasoning to entries the verifiers didn't
980
+ individually re-litigate for riskiness (their job was confirming
981
+ content, not judging blind-match safety for every term).
982
+ 5. **`google/no-numbered-headings` ships despite some residual risk.**
983
+ Narrowly scoped to `Step N`/`Part N` markers and a bare leading ordinal,
984
+ to keep the false-positive rate low; broader numeric heading patterns
985
+ (e.g. version numbers in a heading) are not matched.
986
+ 6. **`google/cons-and-pros` ships only the compound phrase.** Bare `pros`/
987
+ `cons` are excluded even though individually confirmed, because standing
988
+ alone they're closer to ambiguous (conference abbreviations, "con
989
+ artist") than the extremely common, unambiguous two-word phrase.
990
+ 7. **The Oxford-comma/no-and-or "except in tables" exception is not
991
+ separately scoped.** `google/no-and-or` runs over the `summary` scope,
992
+ which includes table cells, so it would also (correctly, per the general
993
+ rule, but against the guide's own table exception) flag "and/or" inside
994
+ a table. Judged not worth a bespoke scope array for one rule; a project
995
+ that hits this can override the rule's `scope`.
996
+
997
+ ### Engine/registry changes this preset required
998
+
999
+ - **Schema**: `src/config/schema.ts`'s top-level `patternProperties` only
1000
+ accepted `^recheck/[a-z0-9-_]+$` rule keys. Spec §2 ("Composition
1001
+ safety") requires per-preset namespacing (`google/<rule>`,
1002
+ `microsoft/<rule>`, ...) precisely so two flagship presets can be
1003
+ composed without collisions — the pattern is widened to
1004
+ `^[a-z][a-z0-9-]*/[a-z0-9-_]+$` to allow that (existing `recheck/*` keys
1005
+ are unaffected; they're just the `recheck` namespace now).
1006
+ - **`length` moved from opt-in to preset-shipped.** `google/sentence-length`
1007
+ is the first non-prose preset rule to ship a native scope-rule
1008
+ assertion beyond what `recheck/prose` already ships. Per
1009
+ cross-task-constraints.md §C / task-9-10-resolutions.md §5, this trips
1010
+ the registry<->preset completeness guard in
1011
+ `src/config/__tests__/presets.test.ts`: `length` is removed from the
1012
+ documented-opt-in list (now `DOCUMENTED_OPT_IN_ASSERTIONS`, moved from
1013
+ `prose.ts` to `presets/index.ts` since the policy is monorepo-wide, not
1014
+ prose-specific) and the completeness test's "shipped" side is derived
1015
+ dynamically from ALL presets rather than a single prose-named constant.
1016
+ See that test file's own comments for the mechanics.
1017
+ - **`.npmignore` widened to include `presets/**/*`.** The package has no
1018
+ `files` field in `package.json`; publishing is governed entirely by
1019
+ `.npmignore`, which was a blanket `*` deny with only `dist/**/*` and
1020
+ `package.json` allowed back in. A new top-level `presets/<name>/`
1021
+ directory (this file, `sources.json`) would have shipped nowhere without
1022
+ this change — verified with `npm pack --dry-run` before and after.