paperlint 2.0.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 (762) hide show
  1. package/.github/dependabot.yml +72 -0
  2. package/.github/workflows/ci.yml +297 -0
  3. package/.github/workflows/dependabot-automerge.yml +70 -0
  4. package/.github/workflows/pr-title.yml +59 -0
  5. package/.github/workflows/release.yml +54 -0
  6. package/CLAUDE.md +598 -0
  7. package/CONTRIBUTING.md +159 -0
  8. package/LICENSE +21 -0
  9. package/README.md +240 -0
  10. package/action.harness.mjs +287 -0
  11. package/action.mutations.mjs +162 -0
  12. package/action.yml +138 -0
  13. package/bin/rpp.mjs +43 -0
  14. package/dist/action-ref.d.ts +12 -0
  15. package/dist/action-ref.d.ts.map +1 -0
  16. package/dist/action-ref.js +16 -0
  17. package/dist/action-ref.js.map +1 -0
  18. package/dist/adapters/banal/failure.d.ts +73 -0
  19. package/dist/adapters/banal/failure.d.ts.map +1 -0
  20. package/dist/adapters/banal/failure.js +58 -0
  21. package/dist/adapters/banal/failure.js.map +1 -0
  22. package/dist/adapters/banal/index.d.ts +17 -0
  23. package/dist/adapters/banal/index.d.ts.map +1 -0
  24. package/dist/adapters/banal/index.js +56 -0
  25. package/dist/adapters/banal/index.js.map +1 -0
  26. package/dist/adapters/banal/install.d.ts +26 -0
  27. package/dist/adapters/banal/install.d.ts.map +1 -0
  28. package/dist/adapters/banal/install.js +15 -0
  29. package/dist/adapters/banal/install.js.map +1 -0
  30. package/dist/adapters/banal/invocation.d.ts +48 -0
  31. package/dist/adapters/banal/invocation.d.ts.map +1 -0
  32. package/dist/adapters/banal/invocation.js +43 -0
  33. package/dist/adapters/banal/invocation.js.map +1 -0
  34. package/dist/adapters/banal/locate.d.ts +50 -0
  35. package/dist/adapters/banal/locate.d.ts.map +1 -0
  36. package/dist/adapters/banal/locate.js +34 -0
  37. package/dist/adapters/banal/locate.js.map +1 -0
  38. package/dist/adapters/banal/output.d.ts +27 -0
  39. package/dist/adapters/banal/output.d.ts.map +1 -0
  40. package/dist/adapters/banal/output.js +112 -0
  41. package/dist/adapters/banal/output.js.map +1 -0
  42. package/dist/adapters/banal/pin.d.ts +19 -0
  43. package/dist/adapters/banal/pin.d.ts.map +1 -0
  44. package/dist/adapters/banal/pin.js +15 -0
  45. package/dist/adapters/banal/pin.js.map +1 -0
  46. package/dist/adapters/banal/probe.d.ts +12 -0
  47. package/dist/adapters/banal/probe.d.ts.map +1 -0
  48. package/dist/adapters/banal/probe.js +27 -0
  49. package/dist/adapters/banal/probe.js.map +1 -0
  50. package/dist/adapters/banal/run.d.ts +89 -0
  51. package/dist/adapters/banal/run.d.ts.map +1 -0
  52. package/dist/adapters/banal/run.js +104 -0
  53. package/dist/adapters/banal/run.js.map +1 -0
  54. package/dist/adapters/banal/settings.d.ts +18 -0
  55. package/dist/adapters/banal/settings.d.ts.map +1 -0
  56. package/dist/adapters/banal/settings.js +29 -0
  57. package/dist/adapters/banal/settings.js.map +1 -0
  58. package/dist/adapters/banal/xml.d.ts +48 -0
  59. package/dist/adapters/banal/xml.d.ts.map +1 -0
  60. package/dist/adapters/banal/xml.js +67 -0
  61. package/dist/adapters/banal/xml.js.map +1 -0
  62. package/dist/adapters/curl/download.io.d.ts +14 -0
  63. package/dist/adapters/curl/download.io.d.ts.map +1 -0
  64. package/dist/adapters/curl/download.io.js +69 -0
  65. package/dist/adapters/curl/download.io.js.map +1 -0
  66. package/dist/adapters/curl/index.d.ts +6 -0
  67. package/dist/adapters/curl/index.d.ts.map +1 -0
  68. package/dist/adapters/curl/index.js +6 -0
  69. package/dist/adapters/curl/index.js.map +1 -0
  70. package/dist/adapters/memory/index.d.ts +43 -0
  71. package/dist/adapters/memory/index.d.ts.map +1 -0
  72. package/dist/adapters/memory/index.js +79 -0
  73. package/dist/adapters/memory/index.js.map +1 -0
  74. package/dist/adapters/node/files.io.d.ts +3 -0
  75. package/dist/adapters/node/files.io.d.ts.map +1 -0
  76. package/dist/adapters/node/files.io.js +31 -0
  77. package/dist/adapters/node/files.io.js.map +1 -0
  78. package/dist/adapters/node/host.io.d.ts +3 -0
  79. package/dist/adapters/node/host.io.d.ts.map +1 -0
  80. package/dist/adapters/node/host.io.js +14 -0
  81. package/dist/adapters/node/host.io.js.map +1 -0
  82. package/dist/adapters/node/index.d.ts +25 -0
  83. package/dist/adapters/node/index.d.ts.map +1 -0
  84. package/dist/adapters/node/index.js +14 -0
  85. package/dist/adapters/node/index.js.map +1 -0
  86. package/dist/adapters/node/process.io.d.ts +14 -0
  87. package/dist/adapters/node/process.io.d.ts.map +1 -0
  88. package/dist/adapters/node/process.io.js +41 -0
  89. package/dist/adapters/node/process.io.js.map +1 -0
  90. package/dist/adapters/node/workspace.io.d.ts +4 -0
  91. package/dist/adapters/node/workspace.io.d.ts.map +1 -0
  92. package/dist/adapters/node/workspace.io.js +33 -0
  93. package/dist/adapters/node/workspace.io.js.map +1 -0
  94. package/dist/adapters/pdfjs/fill.d.ts +42 -0
  95. package/dist/adapters/pdfjs/fill.d.ts.map +1 -0
  96. package/dist/adapters/pdfjs/fill.js +91 -0
  97. package/dist/adapters/pdfjs/fill.js.map +1 -0
  98. package/dist/build-engine.d.ts +48 -0
  99. package/dist/build-engine.d.ts.map +1 -0
  100. package/dist/build-engine.js +148 -0
  101. package/dist/build-engine.js.map +1 -0
  102. package/dist/build.d.ts +163 -0
  103. package/dist/build.d.ts.map +1 -0
  104. package/dist/build.js +575 -0
  105. package/dist/build.js.map +1 -0
  106. package/dist/cli.d.ts +151 -0
  107. package/dist/cli.d.ts.map +1 -0
  108. package/dist/cli.js +951 -0
  109. package/dist/cli.js.map +1 -0
  110. package/dist/doctor.d.ts +42 -0
  111. package/dist/doctor.d.ts.map +1 -0
  112. package/dist/doctor.js +280 -0
  113. package/dist/doctor.js.map +1 -0
  114. package/dist/domain/geometry.d.ts +71 -0
  115. package/dist/domain/geometry.d.ts.map +1 -0
  116. package/dist/domain/geometry.js +35 -0
  117. package/dist/domain/geometry.js.map +1 -0
  118. package/dist/domain/host.d.ts +16 -0
  119. package/dist/domain/host.d.ts.map +1 -0
  120. package/dist/domain/host.js +8 -0
  121. package/dist/domain/host.js.map +1 -0
  122. package/dist/domain/page-layout.d.ts +34 -0
  123. package/dist/domain/page-layout.d.ts.map +1 -0
  124. package/dist/domain/page-layout.js +8 -0
  125. package/dist/domain/page-layout.js.map +1 -0
  126. package/dist/domain/paths.d.ts +5 -0
  127. package/dist/domain/paths.d.ts.map +1 -0
  128. package/dist/domain/paths.js +2 -0
  129. package/dist/domain/paths.js.map +1 -0
  130. package/dist/domain/result.d.ts +23 -0
  131. package/dist/domain/result.d.ts.map +1 -0
  132. package/dist/domain/result.js +10 -0
  133. package/dist/domain/result.js.map +1 -0
  134. package/dist/domain/sha256.d.ts +7 -0
  135. package/dist/domain/sha256.d.ts.map +1 -0
  136. package/dist/domain/sha256.js +14 -0
  137. package/dist/domain/sha256.js.map +1 -0
  138. package/dist/domain/text.d.ts +6 -0
  139. package/dist/domain/text.d.ts.map +1 -0
  140. package/dist/domain/text.js +7 -0
  141. package/dist/domain/text.js.map +1 -0
  142. package/dist/engine.d.ts +93 -0
  143. package/dist/engine.d.ts.map +1 -0
  144. package/dist/engine.js +119 -0
  145. package/dist/engine.js.map +1 -0
  146. package/dist/exit-code.d.ts +22 -0
  147. package/dist/exit-code.d.ts.map +1 -0
  148. package/dist/exit-code.js +10 -0
  149. package/dist/exit-code.js.map +1 -0
  150. package/dist/facts-file.d.ts +96 -0
  151. package/dist/facts-file.d.ts.map +1 -0
  152. package/dist/facts-file.js +134 -0
  153. package/dist/facts-file.js.map +1 -0
  154. package/dist/hooks-settings.d.ts +141 -0
  155. package/dist/hooks-settings.d.ts.map +1 -0
  156. package/dist/hooks-settings.js +306 -0
  157. package/dist/hooks-settings.js.map +1 -0
  158. package/dist/init.d.ts +201 -0
  159. package/dist/init.d.ts.map +1 -0
  160. package/dist/init.js +579 -0
  161. package/dist/init.js.map +1 -0
  162. package/dist/latex-log.d.ts +80 -0
  163. package/dist/latex-log.d.ts.map +1 -0
  164. package/dist/latex-log.js +187 -0
  165. package/dist/latex-log.js.map +1 -0
  166. package/dist/latex-loop.d.ts +129 -0
  167. package/dist/latex-loop.d.ts.map +1 -0
  168. package/dist/latex-loop.js +113 -0
  169. package/dist/latex-loop.js.map +1 -0
  170. package/dist/link-skills.d.ts +51 -0
  171. package/dist/link-skills.d.ts.map +1 -0
  172. package/dist/link-skills.js +199 -0
  173. package/dist/link-skills.js.map +1 -0
  174. package/dist/new-paper.d.ts +48 -0
  175. package/dist/new-paper.d.ts.map +1 -0
  176. package/dist/new-paper.js +110 -0
  177. package/dist/new-paper.js.map +1 -0
  178. package/dist/pdf-facts.d.ts +44 -0
  179. package/dist/pdf-facts.d.ts.map +1 -0
  180. package/dist/pdf-facts.js +239 -0
  181. package/dist/pdf-facts.js.map +1 -0
  182. package/dist/pdf-geometry.d.ts +170 -0
  183. package/dist/pdf-geometry.d.ts.map +1 -0
  184. package/dist/pdf-geometry.js +158 -0
  185. package/dist/pdf-geometry.js.map +1 -0
  186. package/dist/ports/download.d.ts +9 -0
  187. package/dist/ports/download.d.ts.map +1 -0
  188. package/dist/ports/download.js +2 -0
  189. package/dist/ports/download.js.map +1 -0
  190. package/dist/ports/files.d.ts +11 -0
  191. package/dist/ports/files.d.ts.map +1 -0
  192. package/dist/ports/files.js +2 -0
  193. package/dist/ports/files.js.map +1 -0
  194. package/dist/ports/measure-geometry.d.ts +8 -0
  195. package/dist/ports/measure-geometry.d.ts.map +1 -0
  196. package/dist/ports/measure-geometry.js +2 -0
  197. package/dist/ports/measure-geometry.js.map +1 -0
  198. package/dist/ports/process.d.ts +45 -0
  199. package/dist/ports/process.d.ts.map +1 -0
  200. package/dist/ports/process.js +2 -0
  201. package/dist/ports/process.js.map +1 -0
  202. package/dist/ports/tool-installer.d.ts +29 -0
  203. package/dist/ports/tool-installer.d.ts.map +1 -0
  204. package/dist/ports/tool-installer.js +2 -0
  205. package/dist/ports/tool-installer.js.map +1 -0
  206. package/dist/ports/workspace.d.ts +18 -0
  207. package/dist/ports/workspace.d.ts.map +1 -0
  208. package/dist/ports/workspace.js +2 -0
  209. package/dist/ports/workspace.js.map +1 -0
  210. package/dist/rules-config.d.ts +34 -0
  211. package/dist/rules-config.d.ts.map +1 -0
  212. package/dist/rules-config.js +132 -0
  213. package/dist/rules-config.js.map +1 -0
  214. package/dist/structure.d.ts +34 -0
  215. package/dist/structure.d.ts.map +1 -0
  216. package/dist/structure.js +149 -0
  217. package/dist/structure.js.map +1 -0
  218. package/dist/tex-requirements.d.ts +43 -0
  219. package/dist/tex-requirements.d.ts.map +1 -0
  220. package/dist/tex-requirements.js +127 -0
  221. package/dist/tex-requirements.js.map +1 -0
  222. package/dist/toolchain.d.ts +159 -0
  223. package/dist/toolchain.d.ts.map +1 -0
  224. package/dist/toolchain.js +542 -0
  225. package/dist/toolchain.js.map +1 -0
  226. package/dist/types.d.ts +110 -0
  227. package/dist/types.d.ts.map +1 -0
  228. package/dist/types.js +2 -0
  229. package/dist/types.js.map +1 -0
  230. package/docs/configuration.md +235 -0
  231. package/docs/e2e.md +152 -0
  232. package/docs/incidents.md +59 -0
  233. package/docs/install.md +170 -0
  234. package/docs/optional-rules.md +107 -0
  235. package/docs/package-shape-options.md +262 -0
  236. package/docs/prior-art/README.md +76 -0
  237. package/docs/prior-art/blocking-vs-advisory.md +83 -0
  238. package/docs/prior-art/content-delivery.md +124 -0
  239. package/docs/prior-art/multi-mode-tools.md +106 -0
  240. package/docs/prior-art/nondeterministic-checks.md +99 -0
  241. package/docs/prior-art/package-location.md +422 -0
  242. package/docs/prior-art/paper-folder-scaffolding.md +538 -0
  243. package/docs/prior-art/readme-structure.md +69 -0
  244. package/docs/prior-art/repro/README.md +92 -0
  245. package/docs/prior-art/repro/claim1-allowedtools.mjs +66 -0
  246. package/docs/prior-art/repro/claim1-at2.mjs +40 -0
  247. package/docs/prior-art/repro/claim1-crosschannel.mjs +54 -0
  248. package/docs/prior-art/repro/claim1-frontmatter.mjs +76 -0
  249. package/docs/prior-art/repro/claim1-hook-payload-reporter.mjs +10 -0
  250. package/docs/prior-art/repro/claim1-plugin-frontmatter.mjs +27 -0
  251. package/docs/prior-art/repro/claim1-plugin-skill.mjs +52 -0
  252. package/docs/prior-art/repro/claim1-project-skill.mjs +81 -0
  253. package/docs/prior-art/repro/claim2-marketplace-flat-asclaimed.json +1 -0
  254. package/docs/prior-art/repro/claim2-marketplace-negative-control.json +1 -0
  255. package/docs/prior-art/repro/claim2-marketplace-nested-exact.json +9 -0
  256. package/docs/prior-art/repro/claim2-marketplace-nested-noversion.json +9 -0
  257. package/docs/prior-art/repro/claim2-marketplace-nested-range.json +1 -0
  258. package/docs/prior-art/repro/claim3-imports.mjs +50 -0
  259. package/docs/prior-art/repro/claim4-find-package-json.mjs +8 -0
  260. package/docs/prior-art/repro/claim4-package-dir.mjs +39 -0
  261. package/docs/prior-art/repro/claim4-parent-arg.mjs +17 -0
  262. package/docs/prior-art/repro/claim4-resolve-apis.mjs +21 -0
  263. package/docs/prior-art/repro/claim4-setup-consumers.mjs +45 -0
  264. package/docs/prior-art/repro/claim4-yarn-pnp.mjs +70 -0
  265. package/docs/prior-art/repro/claim5-bin-launch.mjs +39 -0
  266. package/docs/prior-art/repro/claim5-exports-mutation.mjs +57 -0
  267. package/docs/prior-art/repro/claim5-resolved-location-and-bin.mjs +33 -0
  268. package/docs/prior-art/repro/claim6-candidate-ambiguity.mjs +17 -0
  269. package/docs/prior-art/repro/claim6-doc-path-candidates.mjs +27 -0
  270. package/docs/prior-art/test-tooling.md +131 -0
  271. package/docs/rules.md +58 -0
  272. package/docs/texlive-install-decision.md +230 -0
  273. package/docs/toolchain.md +152 -0
  274. package/eslint-rules/doc-fields.harness.mjs +336 -0
  275. package/eslint-rules/doc-fields.mjs +186 -0
  276. package/eslint-rules/doc-fields.mutations.mjs +96 -0
  277. package/eslint-rules/install-path-literals.harness.mjs +121 -0
  278. package/eslint-rules/install-path-literals.mjs +108 -0
  279. package/eslint-rules/install-path-literals.mutations.mjs +62 -0
  280. package/eslint-rules/latex-language.harness.mjs +599 -0
  281. package/eslint-rules/latex-language.mjs +591 -0
  282. package/eslint-rules/latex-language.mutations.mjs +196 -0
  283. package/eslint-rules/paper-research-question.harness.mjs +146 -0
  284. package/eslint-rules/paper-research-question.mjs +180 -0
  285. package/eslint-rules/paper-research-question.mutations.mjs +127 -0
  286. package/eslint-rules/paper-stages.harness.mjs +356 -0
  287. package/eslint-rules/paper-stages.mjs +455 -0
  288. package/eslint-rules/paper-stages.mutations.mjs +157 -0
  289. package/eslint-rules/paper-typography.harness.mjs +291 -0
  290. package/eslint-rules/paper-typography.mjs +313 -0
  291. package/eslint-rules/paper-typography.mutations.mjs +131 -0
  292. package/eslint-rules/papers.harness.mjs +259 -0
  293. package/eslint-rules/papers.mjs +166 -0
  294. package/eslint-rules/papers.mutations.mjs +186 -0
  295. package/eslint-rules/pdf-last-page-balance.harness.mjs +206 -0
  296. package/eslint-rules/pdf-last-page-balance.mjs +208 -0
  297. package/eslint-rules/review-findings-cause.harness.mjs +228 -0
  298. package/eslint-rules/review-findings-cause.mjs +135 -0
  299. package/eslint-rules/review-findings-cause.mutations.mjs +72 -0
  300. package/eslint-rules/temp-root-realpath.harness.mjs +176 -0
  301. package/eslint-rules/temp-root-realpath.mjs +129 -0
  302. package/eslint-rules/temp-root-realpath.mutations.mjs +99 -0
  303. package/eslint-rules/tex-build.harness.mjs +753 -0
  304. package/eslint-rules/tex-build.mjs +322 -0
  305. package/eslint-rules/tex-build.mutations.mjs +258 -0
  306. package/eslint.config.mjs +521 -0
  307. package/fixtures/build-e2e/acmart/PIPELINE-STATUS.md +3 -0
  308. package/fixtures/build-e2e/acmart/paper.tex +11 -0
  309. package/fixtures/build-e2e/acmart/venue.json +1 -0
  310. package/fixtures/build-e2e/broken/PIPELINE-STATUS.md +3 -0
  311. package/fixtures/build-e2e/broken/paper.tex +7 -0
  312. package/fixtures/build-e2e/cite/PIPELINE-STATUS.md +3 -0
  313. package/fixtures/build-e2e/cite/build.sh +5 -0
  314. package/fixtures/build-e2e/cite/paper.tex +10 -0
  315. package/fixtures/build-e2e/cite/refs.bib +9 -0
  316. package/fixtures/build-e2e/empty/PIPELINE-STATUS.md +3 -0
  317. package/fixtures/build-e2e/empty/paper.tex +6 -0
  318. package/fixtures/build-e2e/fallback/PIPELINE-STATUS.md +3 -0
  319. package/fixtures/build-e2e/fallback/paper.tex +11 -0
  320. package/fixtures/build-e2e/guards/PIPELINE-STATUS.md +3 -0
  321. package/fixtures/build-e2e/guards/paper.tex +10 -0
  322. package/fixtures/build-e2e/no-source/PIPELINE-STATUS.md +3 -0
  323. package/fixtures/build-e2e/unbalanced/PIPELINE-STATUS.md +3 -0
  324. package/fixtures/build-e2e/unbalanced/paper.tex +28 -0
  325. package/fixtures/build-e2e/unbalanced/refs.bib +269 -0
  326. package/fixtures/install-path-literals/clean.fixture.mjs +3 -0
  327. package/fixtures/install-path-literals/clean.md +15 -0
  328. package/fixtures/install-path-literals/defect.fixture.mjs +3 -0
  329. package/fixtures/install-path-literals/defect.md +14 -0
  330. package/fixtures/latex-language/clean.tex +50 -0
  331. package/fixtures/latex-language/defect.tex +52 -0
  332. package/fixtures/paper-research-question/comment-only/PIPELINE-STATUS.md +9 -0
  333. package/fixtures/paper-research-question/comment-only/paper.tex +7 -0
  334. package/fixtures/paper-research-question/declared-not-in-paper/PIPELINE-STATUS.md +10 -0
  335. package/fixtures/paper-research-question/declared-not-in-paper/paper.tex +6 -0
  336. package/fixtures/paper-research-question/draft/PIPELINE-STATUS.md +6 -0
  337. package/fixtures/paper-research-question/draft/paper.tex +2 -0
  338. package/fixtures/paper-research-question/markdown-no-rq/PIPELINE-STATUS.md +9 -0
  339. package/fixtures/paper-research-question/markdown-no-rq/paper.md +4 -0
  340. package/fixtures/paper-research-question/shipped-no-rq/PIPELINE-STATUS.md +12 -0
  341. package/fixtures/paper-research-question/shipped-no-rq/paper.tex +3 -0
  342. package/fixtures/paper-research-question/shipped-with-rq/PIPELINE-STATUS.md +10 -0
  343. package/fixtures/paper-research-question/shipped-with-rq/paper.tex +2 -0
  344. package/fixtures/paper-stages/authors-ran/PIPELINE-STATUS.md +16 -0
  345. package/fixtures/paper-stages/marker-in-prose/PIPELINE-STATUS.md +17 -0
  346. package/fixtures/paper-stages/nofile/PIPELINE-STATUS.md +8 -0
  347. package/fixtures/paper-stages/noheader/PIPELINE-STATUS.md +1 -0
  348. package/fixtures/paper-stages/noheader/versions/2026-07-22-submitted.pdf +0 -0
  349. package/fixtures/paper-stages/nothing/PIPELINE-STATUS.md +3 -0
  350. package/fixtures/paper-stages/ok/PIPELINE-STATUS.md +9 -0
  351. package/fixtures/paper-stages/ok/versions/2026-07-22-submitted.pdf +0 -0
  352. package/fixtures/paper-stages/stale/PIPELINE-STATUS.md +1 -0
  353. package/fixtures/paper-stages/stale/versions/2026-07-22-submitted.STALE-WRONG-FILE.pdf +0 -0
  354. package/fixtures/paper-stages/twice/PIPELINE-STATUS.md +14 -0
  355. package/fixtures/paper-stages/twice/versions/2026-08-06-submitted.pdf +0 -0
  356. package/fixtures/paper-stages/twice/versions/2026-10-24-submitted.pdf +0 -0
  357. package/fixtures/paper-stages/undeclared/PIPELINE-STATUS.md +8 -0
  358. package/fixtures/paper-stages/undeclared/versions/2026-07-22-submitted.pdf +0 -0
  359. package/fixtures/paper-stages/undeclared/versions/2026-08-29-camera-ready.pdf +0 -0
  360. package/fixtures/paper-stages/wrongsize/PIPELINE-STATUS.md +8 -0
  361. package/fixtures/paper-stages/wrongsize/versions/2026-07-22-submitted.pdf +0 -0
  362. package/fixtures/paper-typography/clean-paper/paper.tex +29 -0
  363. package/fixtures/paper-typography/messy-paper/paper.tex +27 -0
  364. package/fixtures/pdf-facts/README.md +22 -0
  365. package/fixtures/pdf-facts/corrupt-font.pdf +0 -0
  366. package/fixtures/pdf-facts/encrypted.pdf +0 -0
  367. package/fixtures/pdf-facts/hidden-text.pdf +0 -0
  368. package/fixtures/pdf-facts/hidden-text.tex +28 -0
  369. package/fixtures/pdf-facts/t3-all.pdf +0 -0
  370. package/fixtures/pdf-facts/t3-all.tex +8 -0
  371. package/fixtures/pdf-facts/t3-mixed.pdf +0 -0
  372. package/fixtures/pdf-facts/t3-mixed.tex +9 -0
  373. package/fixtures/pdf-facts/ttf.pdf +2240 -1
  374. package/fixtures/pdf-facts/ttf.tex +6 -0
  375. package/fixtures/real-markdown-paper/baseline.json +24 -0
  376. package/fixtures/real-markdown-paper/baseline.mjs +48 -0
  377. package/fixtures/render-paper/build-clean.sh +25 -0
  378. package/fixtures/render-paper/build-defect.sh +15 -0
  379. package/fixtures/review-findings-cause/clean.md +17 -0
  380. package/fixtures/review-findings-cause/defect.md +14 -0
  381. package/fixtures/review-findings-cause/old-debt.md +14 -0
  382. package/fixtures/review-findings-cause/quiet-in-fence.md +16 -0
  383. package/fixtures/tex-build/clean.tex +21 -0
  384. package/fixtures/tex-build/defect.tex +24 -0
  385. package/fixtures/tex-build/frontmatter-clean.tex +25 -0
  386. package/fixtures/tex-build/frontmatter-defect.tex +23 -0
  387. package/fixtures/toolchain-mirror/catalog.txt +5 -0
  388. package/fixtures/toolchain-mirror/install-tl +27 -0
  389. package/fixtures/toolchain-mirror/release-texlive.txt +3 -0
  390. package/fixtures/toolchain-mirror/release-year +1 -0
  391. package/fixtures/toolchain-mirror/stub-kpsewhich +8 -0
  392. package/fixtures/toolchain-mirror/stub-pdflatex +3 -0
  393. package/fixtures/toolchain-mirror/stub-tlmgr +44 -0
  394. package/hooks/hooks.harness.mjs +713 -0
  395. package/hooks/hooks.mutations.mjs +337 -0
  396. package/hooks/paper-edit-guard.hook.d.mts +13 -0
  397. package/hooks/paper-edit-guard.hook.mjs +457 -0
  398. package/hooks/paper-skills-nudge.hook.mjs +136 -0
  399. package/hooks/paper-status-gates.hook.mjs +156 -0
  400. package/hooks/paper-status-gates.sh +91 -0
  401. package/lib/agent-cli-version.harness.mjs +165 -0
  402. package/lib/agent-cli-version.mjs +106 -0
  403. package/lib/agent-cli-version.mutations.mjs +109 -0
  404. package/lib/markdown.mjs +386 -0
  405. package/lib/mutation-driver.harness.mjs +227 -0
  406. package/lib/mutation-driver.mjs +397 -0
  407. package/lib/mutation-driver.mutations.mjs +68 -0
  408. package/lib/paper-config.d.mts +34 -0
  409. package/lib/paper-config.harness.mjs +286 -0
  410. package/lib/paper-config.mjs +142 -0
  411. package/lib/paper-config.mutations.mjs +143 -0
  412. package/lib/skill-checks.mjs +701 -0
  413. package/lib/skill-corpus.mjs +403 -0
  414. package/lib/skill-eval-fixture.mjs +63 -0
  415. package/lib/skill-eval-kit.mjs +257 -0
  416. package/lib/skill-trigger-cases.harness.mjs +170 -0
  417. package/lib/skill-trigger-cases.mjs +446 -0
  418. package/lib/skill-trigger-cases.mutations.mjs +65 -0
  419. package/lib/trigger-ledger.mjs +215 -0
  420. package/package.json +97 -0
  421. package/plugin/.claude-plugin/plugin.json +8 -0
  422. package/plugin/hooks/hooks.json +30 -0
  423. package/scripts/check.harness.mjs +177 -0
  424. package/scripts/check.mjs +239 -0
  425. package/scripts/check.mutations.mjs +110 -0
  426. package/scripts/eslint-report-guard.mjs +82 -0
  427. package/scripts/exclusive.mjs +138 -0
  428. package/scripts/harness-api.frozen.json +76 -0
  429. package/scripts/harness-api.test.ts +175 -0
  430. package/scripts/layer-legacy-frozen.d.mts +28 -0
  431. package/scripts/layer-legacy-frozen.mjs +152 -0
  432. package/scripts/layer-legacy-frozen.test.ts +115 -0
  433. package/scripts/layer-legacy.frozen.json +50 -0
  434. package/scripts/mutation-batteries-frozen.harness.mjs +204 -0
  435. package/scripts/mutation-batteries-frozen.mjs +238 -0
  436. package/scripts/mutation-batteries.frozen.json +117 -0
  437. package/scripts/release-config.test.ts +90 -0
  438. package/scripts/rules-are-content-only.harness.mjs +113 -0
  439. package/scripts/rules-are-content-only.mjs +138 -0
  440. package/scripts/rules-are-content-only.mutations.mjs +81 -0
  441. package/scripts/rules-see-files.harness.mjs +115 -0
  442. package/scripts/rules-see-files.mjs +99 -0
  443. package/scripts/rules-see-files.mutations.mjs +131 -0
  444. package/scripts/run-mutations.mjs +100 -0
  445. package/scripts/semantic-release-plugins.d.ts +16 -0
  446. package/skills/README.md +15 -0
  447. package/skills/analyze-sibling-paper/SKILL.md +170 -0
  448. package/skills/analyze-sibling-paper/SKILL.md.spec.ts +186 -0
  449. package/skills/analyze-sibling-paper/analyze-sibling-paper.eval.mjs +19 -0
  450. package/skills/analyze-sibling-paper/analyze-sibling-paper.harness.mjs +23 -0
  451. package/skills/argument-arc/SKILL.md +177 -0
  452. package/skills/argument-arc/SKILL.md.spec.ts +192 -0
  453. package/skills/argument-arc/argument-arc.eval.mjs +19 -0
  454. package/skills/argument-arc/argument-arc.harness.mjs +23 -0
  455. package/skills/build-benchmark/SKILL.md +213 -0
  456. package/skills/build-benchmark/SKILL.md.spec.ts +220 -0
  457. package/skills/build-benchmark/build-benchmark.eval.mjs +19 -0
  458. package/skills/build-benchmark/build-benchmark.harness.mjs +23 -0
  459. package/skills/build-benchmark/references/adversarial-cold-repro.md +68 -0
  460. package/skills/camera-ready/SKILL.md +148 -0
  461. package/skills/camera-ready/SKILL.md.spec.ts +164 -0
  462. package/skills/camera-ready/camera-ready.eval.mjs +19 -0
  463. package/skills/camera-ready/camera-ready.harness.mjs +23 -0
  464. package/skills/cold-read-diff/SKILL.md +160 -0
  465. package/skills/cold-read-diff/SKILL.md.spec.ts +166 -0
  466. package/skills/cold-read-diff/cold-read-diff.eval.mjs +19 -0
  467. package/skills/cold-read-diff/cold-read-diff.harness.mjs +23 -0
  468. package/skills/draft-paper/SKILL.md +152 -0
  469. package/skills/draft-paper/SKILL.md.spec.ts +169 -0
  470. package/skills/draft-paper/draft-paper.eval.mjs +19 -0
  471. package/skills/draft-paper/draft-paper.harness.mjs +23 -0
  472. package/skills/extend-paper/SKILL.md +99 -0
  473. package/skills/extend-paper/SKILL.md.spec.ts +116 -0
  474. package/skills/extend-paper/extend-paper.eval.mjs +19 -0
  475. package/skills/extend-paper/extend-paper.harness.mjs +23 -0
  476. package/skills/find-venue/SKILL.md +128 -0
  477. package/skills/find-venue/SKILL.md.spec.ts +145 -0
  478. package/skills/find-venue/find-venue.eval.mjs +19 -0
  479. package/skills/find-venue/find-venue.harness.mjs +23 -0
  480. package/skills/grade-paper-writing/SKILL.md +436 -0
  481. package/skills/grade-paper-writing/SKILL.md.spec.ts +453 -0
  482. package/skills/grade-paper-writing/fixtures/control_gopen.txt +1 -0
  483. package/skills/grade-paper-writing/fixtures/control_human_paper.txt +1 -0
  484. package/skills/grade-paper-writing/fixtures/rewrite.txt +1 -0
  485. package/skills/grade-paper-writing/fixtures/specimen.txt +1 -0
  486. package/skills/grade-paper-writing/fixtures/structure-checks.md +22 -0
  487. package/skills/grade-paper-writing/grade-paper-writing.eval.mjs +19 -0
  488. package/skills/grade-paper-writing/grade-paper-writing.harness.mjs +23 -0
  489. package/skills/grade-paper-writing/prose-lint.mjs +713 -0
  490. package/skills/harden-paper/SKILL.md +318 -0
  491. package/skills/harden-paper/SKILL.md.spec.ts +336 -0
  492. package/skills/harden-paper/check-numbers.sh +33 -0
  493. package/skills/harden-paper/check-release-claims.sh +35 -0
  494. package/skills/harden-paper/fixtures/uncited-assertions-sample.md +43 -0
  495. package/skills/harden-paper/fixtures/uncited-assertions-sample.tex +77 -0
  496. package/skills/harden-paper/harden-paper.eval.mjs +19 -0
  497. package/skills/harden-paper/harden-paper.harness.mjs +23 -0
  498. package/skills/map-prior-work/SKILL.md +211 -0
  499. package/skills/map-prior-work/SKILL.md.spec.ts +227 -0
  500. package/skills/map-prior-work/map-prior-work.eval.mjs +19 -0
  501. package/skills/map-prior-work/map-prior-work.harness.mjs +23 -0
  502. package/skills/osf-artifact-upload/SKILL.md +52 -0
  503. package/skills/osf-artifact-upload/SKILL.md.spec.ts +59 -0
  504. package/skills/osf-artifact-upload/osf-artifact-upload.eval.mjs +22 -0
  505. package/skills/osf-artifact-upload/osf-artifact-upload.harness.mjs +103 -0
  506. package/skills/paper-adversarial-review/SKILL.md +126 -0
  507. package/skills/paper-adversarial-review/SKILL.md.spec.ts +142 -0
  508. package/skills/paper-adversarial-review/paper-adversarial-review.eval.mjs +19 -0
  509. package/skills/paper-adversarial-review/paper-adversarial-review.harness.mjs +23 -0
  510. package/skills/paper-pipeline/PIPELINE-MAP.md +371 -0
  511. package/skills/paper-pipeline/SKILL.md +499 -0
  512. package/skills/paper-pipeline/SKILL.md.spec.ts +517 -0
  513. package/skills/paper-pipeline/description-language.eval.mjs +347 -0
  514. package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +891 -0
  515. package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +1254 -0
  516. package/skills/paper-pipeline/paper-pipeline.eval.mjs +22 -0
  517. package/skills/paper-pipeline/paper-pipeline.harness.mjs +143 -0
  518. package/skills/paper-pipeline/pipeline-firing.baseline.json +270 -0
  519. package/skills/paper-pipeline/pipeline-firing.eval.mjs +664 -0
  520. package/skills/paper-pipeline/pipeline-language.eval.mjs +672 -0
  521. package/skills/paper-pipeline/references/acceptance-gate.md +329 -0
  522. package/skills/paper-pipeline/references/acl-venue-rules.md +142 -0
  523. package/skills/paper-pipeline/references/anonymization.md +68 -0
  524. package/skills/paper-pipeline/references/artifact-checklist.md +93 -0
  525. package/skills/paper-pipeline/references/body-vs-appendix.md +97 -0
  526. package/skills/paper-pipeline/references/credit-criteria.md +69 -0
  527. package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/README.md +35 -0
  528. package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/run_retext.mjs +24 -0
  529. package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/sentences.txt +11 -0
  530. package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/test_sentences.py +25 -0
  531. package/skills/paper-pipeline/references/occupancy-2026-08-06-prose-checkers.md +538 -0
  532. package/skills/paper-pipeline/references/occupancy-2026-08-06-reproducible-tooling.md +431 -0
  533. package/skills/paper-pipeline/references/occupancy-2026-08-06-staleness-and-orchestration.md +592 -0
  534. package/skills/paper-pipeline/references/pipeline-status-template.md +162 -0
  535. package/skills/paper-pipeline/references/review-ratchet.md +36 -0
  536. package/skills/paper-pipeline/references/sweep-2026-08-09-ideal-pipeline.md +585 -0
  537. package/skills/paper-pipeline/references/writing-craft.md +448 -0
  538. package/skills/paper-pipeline/repro/2026-08-07-description-language-control.log +63 -0
  539. package/skills/paper-pipeline/repro/2026-08-07-fork-check.log +52 -0
  540. package/skills/paper-pipeline/repro/2026-08-07-fork-check2.log +33 -0
  541. package/skills/paper-pipeline/repro/2026-08-07-language-eval-pilot.log +33 -0
  542. package/skills/paper-pipeline/repro/2026-08-07-language-eval-raw.log +166 -0
  543. package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.json +338 -0
  544. package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.log +118 -0
  545. package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-raw.log +245 -0
  546. package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary.json +776 -0
  547. package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-oracle.log +53 -0
  548. package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-raw.log +89 -0
  549. package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.json +450 -0
  550. package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.log +136 -0
  551. package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-raw.log +242 -0
  552. package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-setupdiff.log +59 -0
  553. package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation.json +1032 -0
  554. package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.json +139 -0
  555. package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.log +98 -0
  556. package/skills/paper-pipeline/repro/2026-08-08-parent-replication.mjs +92 -0
  557. package/skills/paper-pipeline/repro/README.md +129 -0
  558. package/skills/paper-pipeline/repro/analyze-language-eval.py +116 -0
  559. package/skills/paper-pipeline/scripts/README.md +344 -0
  560. package/skills/paper-pipeline/scripts/announce.mjs +67 -0
  561. package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +496 -0
  562. package/skills/paper-pipeline/scripts/artifact-coverage.mjs +397 -0
  563. package/skills/paper-pipeline/scripts/artifact-coverage.mutations.mjs +218 -0
  564. package/skills/paper-pipeline/scripts/check-provenance.mjs +184 -0
  565. package/skills/paper-pipeline/scripts/consumer.d.mts +32 -0
  566. package/skills/paper-pipeline/scripts/consumer.harness.mjs +562 -0
  567. package/skills/paper-pipeline/scripts/consumer.mjs +535 -0
  568. package/skills/paper-pipeline/scripts/consumer.mutations.mjs +190 -0
  569. package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +457 -0
  570. package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +656 -0
  571. package/skills/paper-pipeline/scripts/extract-ref-facts.mutations.mjs +54 -0
  572. package/skills/paper-pipeline/scripts/fixtures/clean/PIPELINE-STATUS.md +51 -0
  573. package/skills/paper-pipeline/scripts/fixtures/dirty/PIPELINE-STATUS.md +52 -0
  574. package/skills/paper-pipeline/scripts/fixtures/dirty/paper.md +6 -0
  575. package/skills/paper-pipeline/scripts/fixtures/real-bib/refs.bib +153 -0
  576. package/skills/paper-pipeline/scripts/generated-code.harness.mjs +466 -0
  577. package/skills/paper-pipeline/scripts/generated-code.mjs +338 -0
  578. package/skills/paper-pipeline/scripts/generated-code.mutations.mjs +254 -0
  579. package/skills/paper-pipeline/scripts/ledger.mjs +623 -0
  580. package/skills/paper-pipeline/scripts/ledger.selftest.mjs +286 -0
  581. package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +389 -0
  582. package/skills/paper-pipeline/scripts/pipeline-check.mjs +737 -0
  583. package/skills/paper-pipeline/scripts/pipeline-check.mutations.mjs +54 -0
  584. package/skills/paper-pipeline/scripts/pipeline-edges.mjs +169 -0
  585. package/skills/paper-pipeline/scripts/population-map.harness.mjs +178 -0
  586. package/skills/paper-pipeline/scripts/population-map.mjs +181 -0
  587. package/skills/paper-pipeline/scripts/population-map.mutations.mjs +65 -0
  588. package/skills/paper-pipeline/scripts/population-map.selftest.mjs +122 -0
  589. package/skills/paper-pipeline/scripts/provenance.harness.mjs +240 -0
  590. package/skills/paper-pipeline/scripts/provenance.mutations.mjs +59 -0
  591. package/skills/paper-pipeline/scripts/round-diff.harness.mjs +881 -0
  592. package/skills/paper-pipeline/scripts/round-diff.mjs +576 -0
  593. package/skills/paper-pipeline/scripts/round-diff.mutations.mjs +276 -0
  594. package/skills/paper-pipeline/scripts/run-mechanical.mjs +633 -0
  595. package/skills/paper-pipeline/scripts/status.mjs +295 -0
  596. package/skills/paper-status/SKILL.md +183 -0
  597. package/skills/paper-status/SKILL.md.spec.ts +190 -0
  598. package/skills/paper-status/paper-status.eval.mjs +22 -0
  599. package/skills/paper-status/paper-status.harness.mjs +25 -0
  600. package/skills/pc-panel-review/SKILL.md +263 -0
  601. package/skills/pc-panel-review/SKILL.md.spec.ts +280 -0
  602. package/skills/pc-panel-review/pc-panel-review.eval.mjs +19 -0
  603. package/skills/pc-panel-review/pc-panel-review.harness.mjs +23 -0
  604. package/skills/plan-paper-timeline/SKILL.md +182 -0
  605. package/skills/plan-paper-timeline/SKILL.md.spec.ts +200 -0
  606. package/skills/plan-paper-timeline/fixtures/fake-google-calendar.mjs +239 -0
  607. package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +431 -0
  608. package/skills/plan-paper-timeline/plan-paper-timeline.effects.mutations.mjs +65 -0
  609. package/skills/plan-paper-timeline/plan-paper-timeline.eval.mjs +19 -0
  610. package/skills/plan-paper-timeline/plan-paper-timeline.harness.mjs +23 -0
  611. package/skills/render-paper/SKILL.md +159 -0
  612. package/skills/render-paper/SKILL.md.spec.ts +166 -0
  613. package/skills/render-paper/check-render.sh +419 -0
  614. package/skills/render-paper/checkers-requirements.txt +55 -0
  615. package/skills/render-paper/ensure-checkers.sh +69 -0
  616. package/skills/render-paper/extract-pdf-facts.harness.mjs +166 -0
  617. package/skills/render-paper/extract-pdf-facts.mjs +144 -0
  618. package/skills/render-paper/render-paper.eval.mjs +19 -0
  619. package/skills/render-paper/render-paper.harness.mjs +339 -0
  620. package/skills/research-ideate/SKILL.md +136 -0
  621. package/skills/research-ideate/SKILL.md.spec.ts +152 -0
  622. package/skills/research-ideate/research-ideate.eval.mjs +19 -0
  623. package/skills/research-ideate/research-ideate.harness.mjs +23 -0
  624. package/skills/skill-contract.mutations.mjs +179 -0
  625. package/skills/study-accepted-papers/SKILL.md +206 -0
  626. package/skills/study-accepted-papers/SKILL.md.spec.ts +223 -0
  627. package/skills/study-accepted-papers/study-accepted-papers.eval.mjs +19 -0
  628. package/skills/study-accepted-papers/study-accepted-papers.harness.mjs +23 -0
  629. package/skills/submit-paper/SKILL.md +182 -0
  630. package/skills/submit-paper/SKILL.md.spec.ts +199 -0
  631. package/skills/submit-paper/check-deanon.sh +149 -0
  632. package/skills/submit-paper/references/publishers/acm.md +92 -0
  633. package/skills/submit-paper/references/venues/agenticdev.jsonc +108 -0
  634. package/skills/submit-paper/references/venues/agenticdev.md +139 -0
  635. package/skills/submit-paper/references/venues/agenticdev.tex +19 -0
  636. package/skills/submit-paper/references/venues/aisec.jsonc +101 -0
  637. package/skills/submit-paper/references/venues/aisec.md +105 -0
  638. package/skills/submit-paper/references/venues/paper-guards.tex +41 -0
  639. package/skills/submit-paper/references/venues/realm.jsonc +81 -0
  640. package/skills/submit-paper/references/venues/realm.md +155 -0
  641. package/skills/submit-paper/references/venues/tex-base.jsonc +50 -0
  642. package/skills/submit-paper/references/venues/venue-profile.schema.json +74 -0
  643. package/skills/submit-paper/submit-paper.eval.mjs +19 -0
  644. package/skills/submit-paper/submit-paper.harness.mjs +23 -0
  645. package/skills/sweep-design-space/SKILL.md +269 -0
  646. package/skills/sweep-design-space/SKILL.md.spec.ts +285 -0
  647. package/skills/sweep-design-space/sweep-design-space.eval.mjs +19 -0
  648. package/skills/sweep-design-space/sweep-design-space.harness.mjs +23 -0
  649. package/skills/tighten-paper/SKILL.md +368 -0
  650. package/skills/tighten-paper/SKILL.md.spec.ts +384 -0
  651. package/skills/tighten-paper/structure.mjs +371 -0
  652. package/skills/tighten-paper/tighten-paper.eval.mjs +19 -0
  653. package/skills/tighten-paper/tighten-paper.harness.mjs +23 -0
  654. package/skills/verify-citations/SKILL.md +328 -0
  655. package/skills/verify-citations/SKILL.md.spec.ts +345 -0
  656. package/skills/verify-citations/scripts/bib-authors.mjs +479 -0
  657. package/skills/verify-citations/scripts/bib-authors.test.mjs +175 -0
  658. package/skills/verify-citations/scripts/verify-cites.mjs +1108 -0
  659. package/skills/verify-citations/scripts/verify-cites.test.mjs +735 -0
  660. package/skills/verify-citations/verify-citations.eval.mjs +19 -0
  661. package/skills/verify-citations/verify-citations.harness.mjs +23 -0
  662. package/src/CLAUDE.md +51 -0
  663. package/src/action-ref.test.ts +26 -0
  664. package/src/action-ref.ts +15 -0
  665. package/src/adapters/banal/failure.test.ts +63 -0
  666. package/src/adapters/banal/failure.ts +118 -0
  667. package/src/adapters/banal/index.test.ts +119 -0
  668. package/src/adapters/banal/index.ts +100 -0
  669. package/src/adapters/banal/install.test.ts +20 -0
  670. package/src/adapters/banal/install.ts +41 -0
  671. package/src/adapters/banal/invocation.test.ts +74 -0
  672. package/src/adapters/banal/invocation.ts +95 -0
  673. package/src/adapters/banal/locate.test.ts +52 -0
  674. package/src/adapters/banal/locate.ts +84 -0
  675. package/src/adapters/banal/output.test.ts +140 -0
  676. package/src/adapters/banal/output.ts +141 -0
  677. package/src/adapters/banal/pin.ts +30 -0
  678. package/src/adapters/banal/probe.ts +35 -0
  679. package/src/adapters/banal/run.test.ts +191 -0
  680. package/src/adapters/banal/run.ts +244 -0
  681. package/src/adapters/banal/settings.test.ts +31 -0
  682. package/src/adapters/banal/settings.ts +55 -0
  683. package/src/adapters/banal/xml.test.ts +111 -0
  684. package/src/adapters/banal/xml.ts +112 -0
  685. package/src/adapters/curl/download.io.ts +73 -0
  686. package/src/adapters/curl/download.test.ts +55 -0
  687. package/src/adapters/curl/index.ts +5 -0
  688. package/src/adapters/memory/index.ts +131 -0
  689. package/src/adapters/node/files.io.ts +39 -0
  690. package/src/adapters/node/files.test.ts +28 -0
  691. package/src/adapters/node/host.io.ts +15 -0
  692. package/src/adapters/node/index.ts +36 -0
  693. package/src/adapters/node/process.io.ts +49 -0
  694. package/src/adapters/node/process.test.ts +46 -0
  695. package/src/adapters/node/workspace.io.ts +40 -0
  696. package/src/adapters/node/workspace.test.ts +58 -0
  697. package/src/adapters/pdfjs/fill.test.ts +111 -0
  698. package/src/adapters/pdfjs/fill.ts +141 -0
  699. package/src/build-engine.harness.mjs +314 -0
  700. package/src/build-engine.ts +219 -0
  701. package/src/build.harness.mjs +631 -0
  702. package/src/build.mutations.mjs +195 -0
  703. package/src/build.ts +793 -0
  704. package/src/cli.harness.mjs +2007 -0
  705. package/src/cli.mutations.mjs +448 -0
  706. package/src/cli.ts +1189 -0
  707. package/src/doctor.harness.mjs +396 -0
  708. package/src/doctor.mutations.mjs +175 -0
  709. package/src/doctor.ts +356 -0
  710. package/src/domain/geometry.ts +108 -0
  711. package/src/domain/host.ts +23 -0
  712. package/src/domain/page-layout.ts +32 -0
  713. package/src/domain/paths.ts +5 -0
  714. package/src/domain/result.test.ts +26 -0
  715. package/src/domain/result.ts +29 -0
  716. package/src/domain/sha256.test.ts +12 -0
  717. package/src/domain/sha256.ts +21 -0
  718. package/src/domain/text.ts +11 -0
  719. package/src/engine.harness.mjs +252 -0
  720. package/src/engine.ts +176 -0
  721. package/src/exit-code.test.ts +21 -0
  722. package/src/exit-code.ts +38 -0
  723. package/src/facts-file.test.ts +240 -0
  724. package/src/facts-file.ts +241 -0
  725. package/src/hooks-settings.harness.mjs +386 -0
  726. package/src/hooks-settings.mutations.mjs +116 -0
  727. package/src/hooks-settings.ts +434 -0
  728. package/src/init.ts +900 -0
  729. package/src/latex-log.harness.mjs +226 -0
  730. package/src/latex-log.ts +234 -0
  731. package/src/latex-loop.harness.mjs +449 -0
  732. package/src/latex-loop.ts +211 -0
  733. package/src/link-skills.harness.mjs +273 -0
  734. package/src/link-skills.mutations.mjs +136 -0
  735. package/src/link-skills.ts +258 -0
  736. package/src/new-paper.harness.mjs +216 -0
  737. package/src/new-paper.mutations.mjs +79 -0
  738. package/src/new-paper.ts +158 -0
  739. package/src/pdf-facts.harness.mjs +188 -0
  740. package/src/pdf-facts.ts +327 -0
  741. package/src/pdf-geometry.harness.mjs +254 -0
  742. package/src/pdf-geometry.ts +300 -0
  743. package/src/ports/download.ts +10 -0
  744. package/src/ports/files.ts +11 -0
  745. package/src/ports/measure-geometry.ts +8 -0
  746. package/src/ports/process.ts +46 -0
  747. package/src/ports/tool-installer.ts +33 -0
  748. package/src/ports/workspace.ts +20 -0
  749. package/src/rules-config.harness.mjs +114 -0
  750. package/src/rules-config.ts +178 -0
  751. package/src/structure.harness.mjs +179 -0
  752. package/src/structure.mutations.mjs +83 -0
  753. package/src/structure.ts +166 -0
  754. package/src/tex-requirements.harness.mjs +238 -0
  755. package/src/tex-requirements.ts +181 -0
  756. package/src/toolchain.harness.mjs +651 -0
  757. package/src/toolchain.ts +755 -0
  758. package/src/types.ts +106 -0
  759. package/templates/paper/PIPELINE-STATUS.md +72 -0
  760. package/templates/paper/paper.md +4 -0
  761. package/templates/paper/paper.tex +8 -0
  762. package/tsconfig.json +23 -0
@@ -0,0 +1,538 @@
1
+ ---
2
+ title: "Occupancy research — deterministic prose/readability checkers vs. semantically-opaque-but-simple prose"
3
+ created: 2026-08-06
4
+ tags:
5
+ [
6
+ occupancy-research,
7
+ prose-linting,
8
+ readability,
9
+ paper-pipeline,
10
+ cold-read,
11
+ vale,
12
+ textlint,
13
+ retext,
14
+ proselint,
15
+ coh-metrix,
16
+ coreference,
17
+ ]
18
+ ---
19
+
20
+ # Occupancy research: does any shipped checker catch "short, grammatical, correct — parseable only if you already know the idea"?
21
+
22
+ ## Headline finding (read this first)
23
+
24
+ **No shipped deterministic tool detects the actual defect.** Every mechanical checker in this
25
+ space — Vale, textlint/retext, proselint, write-good, alex, LanguageTool, Hemingway, Grammarly,
26
+ Acrolinx, every readability formula (Flesch-Kincaid, Gunning Fog, SMOG, Coleman-Liau, ARI,
27
+ Dale-Chall, Lexile) — measures **surface form**: word length, syllable count, sentence length,
28
+ passive voice, a fixed word/phrase blocklist. None of them model **whether the reader already
29
+ holds the referent the sentence assumes**. The literature has a name for the thing that would be
30
+ needed — cohesion/coherence modeling, entity-grid coreference tracking (Barzilay & Lapata 2008)
31
+ — and it exists only as academic research code (one toolkit, "Cohere," built for corpus
32
+ evaluation, not as a linter you run on a draft) and inside Coh-Metrix (a research instrument,
33
+ not a red/green pass-fail checker). **Nothing in this space ships as "flag this sentence, a
34
+ reader without your mental model cannot parse it."**
35
+
36
+ I verified this empirically, not just by reading docs — I ran the actual tools (proselint,
37
+ write-good, retext-readability via textstat/npm, all installed and executed in this session)
38
+ against the six sentences below. Results are in the table at the end. The one mechanical thing
39
+ that _does_ fire — sentence-length/clause-count thresholds — only catches sentence 6 (the long,
40
+ syntactically loaded one). Sentences 1–5, the ones a human immediately called "wtf" / "what" /
41
+ "VAGUE AF", passed every tool at every reasonable threshold. The only way one of them lit up at
42
+ all was setting retext-readability's target age to 6 years old — at which point it also flags
43
+ ordinary adult prose indiscriminately, so that's not a usable signal either.
44
+
45
+ This matches the author's own report: the homegrown `prose-lint.mjs` and the LLM-persona grader
46
+ don't catch this because **no comparable tool in the wild catches it either** — this is not a
47
+ gap specific to the homegrown implementation, it's a gap in the entire deterministic-checker
48
+ category. What's needed for these six sentences is a reader (human or LLM) simulating "I don't
49
+ already know what 'the rule' claims about itself" — i.e., exactly the `cold-read-diff` /
50
+ persona-stall pattern already in this pipeline, not a metric.
51
+
52
+ ---
53
+
54
+ ## The test set (verbatim, as given)
55
+
56
+ 1. _"The state to remove is therefore not the rule but its claim about itself"_ — reaction: "wtf"
57
+ 2. _"these files are filled with constructions nobody built"_ — "wtf"
58
+ 3. _"Three answers, all of them after the fact"_ (section heading) — "VAGUE AF"
59
+ 4. _"§4.4 invites the suspicion that the resolver guesses"_ — "what"
60
+ 5. _"The only part that reads English is the one not allowed to decide anything. It proposes; the other two dispose."_ — "needs full rewrite confusing af"
61
+ 6. A sentence carrying six separate factual claims joined by dashes/semicolons (exact original
62
+ text not given to me — I constructed a representative stand-in of the same shape for testing:
63
+ _"The audit found six problems: the config was stale, the hook silently no-ops on error, the
64
+ guard reads a variable the harness never sets, the fallback path was never tested, the log
65
+ rotates before anyone reads it, and the alert fires into a channel nobody watches."_ — this is
66
+ a **reconstruction**, not the original, flagged so it isn't mistaken for a quote later.)
67
+
68
+ ### Empirical readability numbers (textstat 0.7.13, run locally)
69
+
70
+ | # | words | Flesch reading ease | FK grade | Gunning Fog | Coleman-Liau | ARI | Dale-Chall |
71
+ | --- | ------------ | ----------------------- | -------- | ----------- | ------------ | -------- | ---------- |
72
+ | 1 | 14 | 83.9 (easy) | 5.0 | 5.6 | 6.5 | 5.8 | 6.6 |
73
+ | 2 | 8 | 71.8 (fairly easy) | 5.2 | 8.2 | 14.6 | 10.8 | 10.0 |
74
+ | 3 | 8 | 93.0 (very easy) | 2.3 | 3.2 | 4.4 | 3.2 | 6.0 |
75
+ | 4 | 9 | 56.7 (fairly difficult) | 7.6 | 8.0 | 13.1 | 10.3 | 14.6 |
76
+ | 5 | 20 (2 sent.) | 69.8 (fairly easy) | 6.0 | 6.0 | 7.1 | 5.2 | 7.3 |
77
+ | 6 | 46 | 29.6 (difficult) | **20.6** | **20.1** | 10.2 | **24.0** | 12.1 |
78
+
79
+ Every published deterministic tool's threshold for "flag this" lives in the FK-grade-8-to-12 /
80
+ Flesch-under-50 range. On that basis: sentences 1, 2, 3, 5 are **invisible** to every formula in
81
+ this table — grade level 2 to 7, "easy" to "fairly easy." Sentence 4 sits right at a common
82
+ threshold (grade 7.6, Flesch 56.7) — borderline, would fire on strict configs, not on defaults.
83
+ Sentence 6 blows every ceiling (grade 20+) purely because it's long, not because of the six-claims
84
+ structure specifically — a single 46-word sentence about anything would trip the same alarm.
85
+
86
+ ---
87
+
88
+ ## Tool-by-tool
89
+
90
+ ### Vale (errata-ai)
91
+
92
+ **What it is:** The dominant open-source prose linter. Config-driven (YAML rules organized into
93
+ "styles"), CI-native, markup-aware (Markdown/AsciiDoc/reST/HTML). [github.com/errata-ai/vale](https://github.com/errata-ai/vale)
94
+
95
+ **Alive?** Yes, actively maintained — latest release **v3.17.1** (Aug 2024), with a steady
96
+ release cadence through 2024 and 5.7k GitHub stars; no signs of abandonment as of this research.
97
+
98
+ **Rule mechanics:** Rules extend one of several check types — `existence`, `substitution`,
99
+ `occurrence`, `repetition`, `consistency`, `conditional`, `capitalization`, `sequence`, and
100
+ **`readability`**. `existence`/`substitution` are regex-over-tokens; they can absolutely match
101
+ "N semicolons in one sentence" or "3+ commas in one sentence" as a proxy for clause count — but
102
+ this is a **structural/punctuation proxy**, not a semantic one, and I found no shipped Vale style
103
+ (Microsoft, Google, Red Hat, write-good port, Joblint) that actually ships such a rule. Someone
104
+ _could_ author one; nobody has.
105
+
106
+ **Does it ship a readability rule?** Yes — `extends: readability`, config example:
107
+
108
+ ```yaml
109
+ extends: readability
110
+ message: "Content must be readable at 6th grade level (current: %s)"
111
+ level: error
112
+ grade: 6
113
+ metrics:
114
+ - Flesch-Kincaid
115
+ - Coleman-Liau
116
+ ```
117
+
118
+ It supports Flesch-Kincaid, Coleman-Liau, and other classic formulas (implementations live in
119
+ [github.com/errata-ai/readability](https://github.com/errata-ai/readability)), averaging across
120
+ whichever metrics you list. **Critical limitation for this exact problem: the rule is scoped to
121
+ `summary` level — it evaluates a whole document/section's aggregate grade level, not individual
122
+ sentences.** A single opaque-but-short sentence sitting inside an otherwise normal-grade-level
123
+ document will not move the aggregate enough to trip a document-level threshold. It structurally
124
+ cannot do what's being asked here — flag sentence 1 in isolation — even in principle, without a
125
+ custom re-scoped rule nobody ships.
126
+
127
+ **Ecosystem it pulls in:** Microsoft Writing Style Guide and Google Developer Docs Style Guide
128
+ (both Vale-compatible ports, actively maintained), write-good, proselint, alex, Joblint, Red Hat
129
+ style — all as separate installable "styles," same YAML rule engine, same limitation (surface
130
+ regex/word-list/readability-formula, nothing semantic).
131
+ [github.com/vale-cli/Microsoft](https://github.com/vale-cli/Microsoft) ·
132
+ [github.com/vale-cli/Google](https://github.com/vale-cli/Google) ·
133
+ [github.com/vale-cli/Joblint](https://github.com/vale-cli/Joblint) ·
134
+ Red Hat reference: [redhat-documentation.github.io/vale-at-red-hat](https://redhat-documentation.github.io/vale-at-red-hat/reference-guide.html)
135
+ (has "define acronyms/abbreviations on first occurrence" as a rule — see term-first-use section
136
+ below).
137
+
138
+ **Would it flag the 6 test sentences?** No on 1, 2, 3, 5 (short, simple words, well under any
139
+ grade threshold, and readability is document-scoped anyway so it wouldn't even see them
140
+ individually). Borderline-possible on 4 depending on threshold. Would flag 6 on length alone if
141
+ `readability` were scoped per-sentence (it isn't, by default) or if a custom `existence` rule
142
+ counted commas/semicolons.
143
+
144
+ **Adoption cost:** Low-to-medium. Single Go binary, YAML config, drop into CI as a lint step;
145
+ Microsoft/Google style packs are drop-in. Main cost is authoring custom rules for anything beyond
146
+ the shipped styles — and nothing shipped touches semantic opacity.
147
+
148
+ ---
149
+
150
+ ### textlint (JS ecosystem) + retext / unified (remark family)
151
+
152
+ **What they are:** textlint is a pluggable Markdown/text linter (JS); retext is the prose-analysis
153
+ half of the `unified`/`remark` toolchain — same AST pipeline as remark, so it composes naturally
154
+ if the source is Markdown. `retext-textlint` bridges the two.
155
+ [github.com/textlint/textlint](https://github.com/textlint/textlint) ·
156
+ [github.com/retextjs](https://github.com/retextjs)
157
+
158
+ **retext-readability** — actually tested, not just read about.
159
+ [github.com/retextjs/retext-readability](https://github.com/retextjs/retext-readability), v8.0.0.
160
+ Applies 7 formulas (Dale–Chall, Automated Readability, Coleman-Liau, Flesch, Gunning-Fog, SMOG,
161
+ Spache) **per sentence** (unlike Vale, this one _is_ sentence-scoped) and fires when a
162
+ configurable fraction agree ("threshold," default `4/7`) the sentence is hard for a configurable
163
+ target `age` (default 16).
164
+
165
+ **Ran it live, two configs:**
166
+
167
+ - **Default** (age 16, minWords 5, threshold 4/7 — i.e. realistic adult-audience settings): flags
168
+ **only sentence 6** ("5 out of 7 algorithms"). Sentences 1–5 all report "no issues found."
169
+ - **Maximally strict** (age 6, minWords 1, threshold 1/7 — i.e. "flag anything a first-grader
170
+ might struggle with"): flags **all six**, sentences 1–5 at "6 out of 7 algorithms," sentence 6
171
+ at "all 7." This setting is unusable in practice — it would also flag nearly all ordinary adult
172
+ technical or literary prose, because the formulas are keyed to vocabulary/syllable difficulty
173
+ for a 6-year-old, not to "does this presuppose an idea the reader doesn't have." It's not
174
+ detecting the target defect; it's detecting _any_ adult-level word choice.
175
+
176
+ This is the cleanest empirical proof of the headline finding: there is no threshold setting
177
+ between "misses everything real" and "flags everything indiscriminately" that isolates the actual
178
+ defect.
179
+
180
+ **retext-simplify** — flags wordy multi-word phrases against a simpler-alternative dictionary
181
+ (e.g. "in order to" → "to"). Word-substitution list, not semantic.
182
+ [npmjs.com/package/retext-simplify](https://www.npmjs.com/package/retext-simplify)
183
+
184
+ **retext-passive, retext-equality (→ alex)** — passive-voice detection and inclusive-language
185
+ detection respectively; same category as write-good/alex below.
186
+
187
+ **Would they flag the test set?** Same verdict as the empirical run above — retext-readability at
188
+ realistic settings: only #6. retext-simplify: none of the six contain its blocklisted wordy
189
+ phrases. None of the retext family models coreference/discourse.
190
+
191
+ **Adoption cost:** Low if the corpus is Markdown already (natural fit with remark pipeline);
192
+ requires Node tooling. Actively maintained (Titus Wormer / unified ecosystem, frequent releases).
193
+
194
+ ---
195
+
196
+ ### proselint
197
+
198
+ **What it is:** Rule-based Python prose linter, rules drawn from published style guides
199
+ (Strunk & White, Fowler's, Garner's, etc.) plus its own heuristics — clichés, jargon,
200
+ "very"-type weak intensifiers, corporate speak, "there is/are" openers, redundancy.
201
+ [github.com/amperser/proselint](https://github.com/amperser/proselint)
202
+
203
+ **Alive?** v0.16.0 (Nov 2022) is the latest tagged release — over 3 years old at time of writing,
204
+ though the repo shows a 5x perf rewrite and JSON output in that release and issue activity
205
+ continues. Best characterized as **maintained but slow-moving**, not actively shipping new rule
206
+ categories.
207
+
208
+ **Ran it live:** Installed `proselint==0.16.0`, ran `proselint check` against all six sentences.
209
+ **Result: zero warnings, exit code 0, across all six sentences combined.** Not one of proselint's
210
+ ~30 rule categories (weasel words, "obviously"/"of course" condescension, "very"-hedging, sexism,
211
+ redundancy, jargon-from-a-fixed-list, cliché-matching, etc.) matched anything in the test set.
212
+ This is the starkest empirical null result in this report.
213
+
214
+ **Adoption cost:** Trivial — `pip install proselint`, CLI or JSON output, works on plain text.
215
+
216
+ ---
217
+
218
+ ### write-good
219
+
220
+ **What it is:** "Naive linter for English prose" (its own description). Checks: passive voice,
221
+ lexical illusion (doubled words), sentence-initial "so", sentence-initial "there is/are", weasel
222
+ words (from a fixed list — "many," "various," "fairly," etc.), weakening adverbs ("really," "very,"
223
+ "extremely"), "tooWordy" phrase list, clichés, and (opt-in) E-Prime ("to be" verb) detection.
224
+ [github.com/btford/write-good](https://github.com/btford/write-good)
225
+
226
+ **Ran it live** (npm, current version): flagged something on 4 of 6 sentences, all trivial
227
+ style nits, none related to the actual defect:
228
+
229
+ - #1 "therefore" → wordy
230
+ - #2 "are filled" → possible passive voice
231
+ - #3 "all of" → wordy
232
+ - #4 → **nothing**
233
+ - #5 "only" → weakening adverb
234
+ - #6 "silently" → weakening adverb
235
+
236
+ None of these flags bear on why the sentences read as opaque. write-good's checks are
237
+ regex/word-list matches; it has no model of sentence meaning at all.
238
+
239
+ **Adoption cost:** Trivial, npm install, `write-good` CLI or `write-good` npm module for
240
+ programmatic use; also available as a Vale-compatible style pack and a coala-bears linter bear.
241
+
242
+ ---
243
+
244
+ ### alex
245
+
246
+ **What it is:** Catches "insensitive, inconsiderate" language — gendered, ableist, homophobic
247
+ etc. — built on `retext-equality`. Not a readability tool at all; listed here because it's part
248
+ of the same rule-based-linter family and gets reached for reflexively.
249
+ [github.com/get-alex/alex](https://github.com/get-alex/alex)
250
+
251
+ **Would it flag the test set?** No — none of the six sentences contain flagged terminology.
252
+ Out of scope by design.
253
+
254
+ ---
255
+
256
+ ### LanguageTool
257
+
258
+ **What it is:** Grammar/spelling/style checker, Java-based, self-hostable server, has an
259
+ optional stricter **Picky mode** that adds stylistic rules beyond core grammar (redundancy,
260
+ some word-choice nudges, punctuation style).
261
+ [github.com/languagetool-org/languagetool](https://github.com/languagetool-org/languagetool)
262
+
263
+ **Alive?** Very actively maintained — v6.8 released May 2026, repo updated as recently as
264
+ July 2026, moved to a rolling snapshot release model in March 2025 rather than dated version tags.
265
+
266
+ **Would it flag the test set?** Grammar/spelling checks find nothing (all six sentences are
267
+ grammatical). Picky mode's added stylistic rules are still surface pattern-matches (redundant
268
+ phrasing, some clichés, punctuation nits) — same category as write-good/proselint, not semantic.
269
+ No evidence found of a coherence/coreference layer in LanguageTool.
270
+
271
+ **Adoption cost:** Low-medium; self-hostable Docker image, has an HTTP API, editor plugins.
272
+
273
+ ---
274
+
275
+ ### Hemingway Editor
276
+
277
+ **What it is:** Commercial web/desktop editor. Highlights: sentence length via color-coding
278
+ (yellow = hard to read, red = very hard to read), passive voice, unnecessary adverbs, and
279
+ "complex words" with simpler-alternative suggestions. Reports a grade-level score, same family
280
+ of formula as Flesch-Kincaid.
281
+ [hemingwayapp.com](https://hemingwayapp.com/)
282
+
283
+ **What it does NOT do (their own limitation, confirmed via review sources):** No grammar
284
+ checking (misses agreement errors, article mistakes, tense issues) and, same as everything else
285
+ here, no semantic/coherence model — it is sentence-length + word-list + passive-voice detection
286
+ with a friendlier UI.
287
+
288
+ **Would it flag the test set?** Same profile as the formula table above — sentences 1, 2, 3, 5
289
+ would render mostly un-highlighted (short, simple words); sentence 6 would be flagged red for
290
+ length. Not usable for this defect.
291
+
292
+ ---
293
+
294
+ ### Grammarly (Business API), Acrolinx, Expound
295
+
296
+ **Grammarly:** Ships a documented "vague pronoun reference" category under its Clarity
297
+ suggestions in consumer product marketing copy, per third-party writing-center sources that cite
298
+ Grammarly's blog content on the topic — but I could not confirm, from Grammarly's own official
299
+ help-center documentation, that this is a reliably-firing automated check versus aspirational
300
+ product-marketing language, and could not test it directly (no API access in this environment).
301
+ **Mark this [C]/unverified** — it's the single closest-sounding shipped feature to "pronoun with
302
+ no antecedent" in this whole survey, and it deserves a real test with the actual six sentences
303
+ before anyone relies on it. Sentence 5's "It proposes; the other two dispose" is exactly the
304
+ shape of construction a vague-pronoun-reference check is meant to catch (what does "It" refer
305
+ to two sentences up?) — but "the other two" as an unintroduced definite reference is a distinct
306
+ and probably-uncaught failure mode even if the "It" itself resolves grammatically.
307
+
308
+ **Acrolinx:** Enterprise content-governance platform. Its "Clarity"/readability scoring is
309
+ explicitly formula-based (sentence length, syllables, word choice) layered with brand
310
+ terminology/tone rules — confirmed via Acrolinx's own blog content. No cohesion/coreference
311
+ modeling found. Enterprise pricing, heavy adoption cost, wrong shape of tool for a solo author's
312
+ paper pipeline regardless.
313
+
314
+ **Expound:** Searched specifically for a prose-linting tool by this name — found nothing. Either
315
+ it doesn't exist as a shipped tool, is too obscure to surface in search, or the name refers to
316
+ something not in this category. Reporting the null result rather than guessing.
317
+
318
+ ---
319
+
320
+ ### Readability formulas and their published critique
321
+
322
+ Flesch-Kincaid, Gunning Fog, SMOG, Coleman-Liau, ARI, Dale-Chall — all in the same family:
323
+ count words, syllables, sentence length, run them through a linear regression fit to human grade
324
+ levels decades ago (Flesch's original work was WWII-era Navy training manuals; Dale-Chall is
325
+ built around a fixed "familiar word" list from 1948, later revised).
326
+
327
+ **The critique, found directly in this research (not asserted from memory):**
328
+
329
+ - These formulas were "developed for children's school books, not adult technical
330
+ documentation; they ignore between-reader differences and the effects of content, layout, and
331
+ retrieval aids on text usefulness" — from the UXmatters "7 Reasons to Avoid Them" piece
332
+ surfaced in this search.
333
+ - They are "not intended to directly measure ease of comprehension, but rather readability" —
334
+ i.e. the formulas were validated against oral-reading grade-level norms, not against whether a
335
+ reader extracts the correct meaning.
336
+ - Direct, cited comparison found in this research: **"Comparisons indicated that the Coh-Metrix
337
+ formula was significantly more accurate in predicting reading difficulty than the Flesch
338
+ Reading Ease and Flesch-Kincaid Grade Level formulas"** — because Coh-Metrix adds cohesion,
339
+ world-knowledge, and discourse variables the classic formulas ignore entirely.
340
+ - textstat's own numbers above make the critique concrete: sentence 4 (genuinely one of the
341
+ more confusing ones — "§4.4 invites the suspicion that the resolver guesses" personifies a
342
+ document section and uses "invites the suspicion" idiomatically) scores _worse_ by Dale-Chall
343
+ (14.6, "difficult") than sentence 6 does by that same formula (12.1) — the formula is
344
+ responding to unfamiliar-word-list hits ("resolver," "suspicion"), not to the actual
345
+ comprehension problem, which is structural/referential, not lexical.
346
+
347
+ **Lexile:** A commercial/education-market framework (MetaMetrics), same formula family
348
+ (word frequency + sentence length), used mostly for matching children's/YA books to reading
349
+ levels; not positioned or used as a prose-quality linter for technical writing at all.
350
+
351
+ **textstat / py-readability-metrics (Python):** Implementation libraries, not checkers with
352
+ opinions — they compute the same formula family (Flesch, FK grade, Gunning Fog, SMOG,
353
+ Coleman-Liau, ARI, Dale-Chall, plus some less common ones like Linsear Write, Fernandez-Huerta,
354
+ Szigriszt-Pazos). Used to build a custom Vale/CI gate, but the ceiling is the formula family's
355
+ own ceiling — this is what was run directly, live, to produce the numbers table above.
356
+ [github.com/textstat/textstat](https://github.com/textstat/textstat) ·
357
+ [py-readability-metrics.readthedocs.io](https://py-readability-metrics.readthedocs.io/en/latest/)
358
+
359
+ **Coh-Metrix — the closest thing academically, and worth understanding precisely:**
360
+ Built at University of Memphis (Graesser, McNamara et al.), it computes 100+ measures across
361
+ cohesion (referential overlap between sentences, argument overlap, causal/logical connectives,
362
+ LSA-based semantic similarity between adjacent sentences), not just surface word/sentence length.
363
+ It genuinely targets "does this text hang together for a reader" rather than "are the words
364
+ short." **But: it is a research instrument, not a pass/fail linter.** Two free web versions
365
+ exist (Coh-Metrix 3.0 at the University of Memphis's hosted tool, and a "CohMetrixCore Web"
366
+ instance — [iis.memphis.edu/static/cohmetrix](https://iis.memphis.edu/static/cohmetrix/)) but it
367
+ outputs ~100+ numeric indices for a _whole document_, calibrated against corpora of student/
368
+ textbook writing — no accept/reject threshold, no per-sentence flag, no CI integration, and one
369
+ cited limitation notes findings "could be generalized only to a specific genre of texts"
370
+ (academic prose corpora it was validated on). Nobody runs Coh-Metrix as a pre-commit hook. It is
371
+ the right _idea_ — cohesion over word length — with none of the packaging that would make it
372
+ adoptable tomorrow.
373
+
374
+ ---
375
+
376
+ ### Cohesion / coreference / referential clarity — the closest thing to the actual defect
377
+
378
+ This is the section that matters most, and the honest answer is short: **it does not exist as a
379
+ usable shipped tool.** What exists:
380
+
381
+ - **Entity-grid coherence models** (Barzilay & Lapata, 2008) — represent a document as a grid of
382
+ which entities appear in which grammatical role (subject/object/other) across sentences, and
383
+ score coherence by the entity-transition patterns. This is genuinely the right _shape_ of idea
384
+ for "this term was never introduced" / "this pronoun floats" — but it scores _documents_ as
385
+ more-or-less-coherent relative to a shuffled-sentence baseline, it does not point at a specific
386
+ sentence and say "this one presupposes a referent the reader doesn't have."
387
+ - **"Cohere" toolkit** — a research implementation bundling the classic entity-grid model, a
388
+ graph-based coherence metric (Guinaudeau & Strube 2013), and a syntax-augmented model
389
+ (Louis & Nenkova 2012), built explicitly for benchmarking against shuffled-sentence corpora in
390
+ papers, not for linting a draft. [lrec-conf.org paper](http://www.lrec-conf.org/proceedings/lrec2016/pdf/923_Paper.pdf)
391
+ - **spaCy coreference / coreferee** — general-purpose coreference _resolution_ libraries (find
392
+ what "it"/"they" points to across a document). These resolve references that CAN be resolved;
393
+ they do not flag references that CANNOT be resolved from context, and even where they attempt
394
+ novelty-detection, that's a research topic (Winograd-schema-style ambiguity), not a shipped
395
+ "no antecedent found → error" linter rule anywhere I found.
396
+ - **Grammarly's vague-pronoun-reference marketing claim** is the single closest-sounding shipped
397
+ consumer feature — see above, unverified in this research, worth testing directly.
398
+
399
+ Net: coreference resolution as a _library capability_ exists and is mature (spaCy, coreferee).
400
+ Coreference _checking_ — the inverse, "flag when resolution fails or is ambiguous, as a prose
401
+ defect" — exists only in academic evaluation harnesses (Cohere, entity-grid papers), never
402
+ packaged as something you point at a manuscript and get pass/fail on.
403
+
404
+ ---
405
+
406
+ ### Term-first-use checking
407
+
408
+ This is the one category with a genuinely usable, mechanical, **build-failing** answer — but only
409
+ inside LaTeX, and only for acronyms/defined-terms, not general concept-presupposition.
410
+
411
+ - **`glossaries` package:** Tracks first use per glossary entry; entries that reference an
412
+ undefined glossary term produce build warnings (undefined-reference class of error).
413
+ - **`acronym` package** (Oetiker): Explicitly ships an **`error` option** — "lets it throw compile
414
+ errors instead of warnings in case of undefined acronyms" — confirmed from the package's own
415
+ documentation/forum discussion found in this research. This means a LaTeX build _can_ be
416
+ configured to hard-fail if an acronym is used before `\acrodef`ining it. This is real,
417
+ adoptable, and directly on-point for "acronym used before defined" — just not for "concept used
418
+ before its meaning was established," which is the harder, prose-level version of the same
419
+ problem the six test sentences actually exhibit.
420
+ - **ASD-STE100 Simplified Technical English:** A controlled-language standard (aerospace/defense
421
+ documentation lineage, formerly AECMA Simplified English) that constrains vocabulary to an
422
+ approved dictionary and imposes structural writing rules aimed at reducing ambiguity, not just
423
+ word difficulty. **TechScribe ships a customized LanguageTool instance specifically to check
424
+ ASD-STE100 compliance** — a real, existing checker
425
+ ([simplified-english.co.uk](https://www.simplified-english.co.uk/glossary.html)). This is the
426
+ closest _shipped, checkable_ thing to "sentence structure that forces one clear idea per
427
+ sentence" found anywhere in this research — worth a closer look if the goal generalizes beyond
428
+ papers to any technical writing, though it's built for aerospace maintenance manuals, not
429
+ academic argumentation, and its "clarity" is about avoiding ambiguous grammar constructions
430
+ (e.g., banning strings of nouns, restricting verb forms), not about tracking whether a _claim_
431
+ presupposes context.
432
+
433
+ ---
434
+
435
+ ### Academic writing assistants — what journals actually run
436
+
437
+ - **Writefull:** Language-fluency assistant trained on published journal articles (phrasing,
438
+ paraphrasing, abstract/title generation), integrates with Word/Overleaf. Targets non-native-
439
+ English fluency, not semantic clarity for a general reader — a _more_ fluent-sounding sentence
440
+ from Writefull could easily still be one only an insider parses.
441
+ [writefull.com](https://www.writefull.com/)
442
+ - **Penelope.ai / Penelope.ci:** Automated manuscript-completeness checker — ethics statements,
443
+ informed consent, data-availability statements, COI disclosures, word counts, reference
444
+ formatting. Explicitly **does not evaluate intellectual quality** of the writing — confirmed
445
+ directly from search results describing it as "highly effective for formal completeness" but
446
+ not content quality.
447
+ - **SciScore:** Scores _methods sections_ specifically — reagent/resource identifiability, bias
448
+ controls, sample-size/randomization/blinding reporting — a reproducibility-and-rigor checklist
449
+ tool, not a prose-clarity tool at all.
450
+ - **StatReviewer:** Statistical-methodology checker — flags wrong test choice, missing info
451
+ needed to replicate, methods susceptible to bias. Also not a prose-clarity tool.
452
+
453
+ None of these four run on sentence-level readability or semantic opacity at all; they are
454
+ adjacent tools solving a different problem (methodological rigor, manuscript completeness,
455
+ language fluency for non-native speakers) that happens to sit in the same "things journals bolt
456
+ onto submission" category.
457
+
458
+ ---
459
+
460
+ ## (a) Test-sentence × tool table
461
+
462
+ "✗" = tool ran/would run and did not flag it. "✓" = flagged, with the specific reason. "n/a" =
463
+ tool categorically out of scope (not a readability/clarity tool). Blank cells for tools not
464
+ directly tested are inferred from documented mechanics, marked accordingly.
465
+
466
+ | # | proselint (tested) | write-good (tested) | retext-readability @default (tested) | retext-readability @age-6 strict (tested) | textstat/Hemingway-style grade-level | Vale readability (doc-scoped, inferred) | LanguageTool Picky (inferred) | Coh-Metrix (inferred — no threshold exists) | Grammarly vague-pronoun (unverified) |
467
+ | --- | ------------------ | -------------------------------------------- | ---------------------------------------- | ----------------------------------------- | ---------------------------------------- | ----------------------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------- |
468
+ | 1 | ✗ | ✓ "therefore" wordy (irrelevant) | ✗ | ✓ (6/7, age-6 only) | grade 5 — too low to trip | ✗ | ✗ | would score low cohesion if computed, but no pass/fail exists | untested |
469
+ | 2 | ✗ | ✓ "are filled" passive (irrelevant) | ✗ | ✓ (6/7, age-6 only) | grade 5 | ✗ | ✗ | same | untested |
470
+ | 3 | ✗ | ✓ "all of" wordy (irrelevant) | ✗ | ✓ (6/7, age-6 only) | grade 2 | ✗ (also: headings often excluded from scope entirely) | ✗ | same | untested |
471
+ | 4 | ✗ | ✗ (zero flags) | ✗ | ✓ (6/7, age-6 only) | grade 7.6 — borderline on strict configs | possible, borderline | possible, borderline | same | untested |
472
+ | 5 | ✗ | ✓ "only" weakening adverb (irrelevant) | ✗ | ✓ (6/7, age-6 only) | grade 6 | ✗ | ✗ | this is the sentence coreference-checking would target ("It", "the other two") — no tool does it | **plausible target** if the feature is real |
473
+ | 6 | ✗ | ✓ "silently" weakening adverb (coincidental) | **✓ (5/7, default settings — real hit)** | ✓ (7/7) | grade 20.6 | **would flag if scoped per-sentence** | likely flags on length | n/a | n/a |
474
+
475
+ **Reading the table honestly:** the only cell that is a genuine, non-degenerate detection is
476
+ retext-readability on sentence 6 at default settings — and it fired because the sentence is long,
477
+ not because it strings six unrelated claims together. Every other "✓" in the table required
478
+ either an irrelevant trigger (write-good's word-list nits) or an unusably aggressive threshold
479
+ (age-6 retext) that would flag most adult prose. Sentences 1–5 — the ones actually called "wtf"
480
+ — are structurally invisible to this entire tool category at any setting a working author would
481
+ actually run in CI.
482
+
483
+ ## (b) Ranked ADOPT list — deterministic checks worth taking off the shelf tomorrow
484
+
485
+ Ranked by (catches something real) ÷ (adoption cost), **not** by whether it solves the stated
486
+ problem — none of them solve the stated problem, this ranks what's worth having anyway as a cheap
487
+ floor beneath the human/LLM cold-read pass:
488
+
489
+ 1. **proselint**, `pip install proselint`, drop into CI or pre-commit. Zero false positives in
490
+ this test (ran clean on all six), catches real hedge/cliché/weasel-word patterns elsewhere in
491
+ a corpus, costs nothing to add. Ceiling: word-list matching only.
492
+ 2. **retext-readability at default settings (age 16, threshold 4/7)**, if the corpus is Markdown
493
+ (natural fit with an existing remark/unified pipeline). The one tool in this survey that
494
+ actually caught something real (sentence 6) without an unusable threshold. Treat a hit as "this
495
+ sentence is long and worth a manual look," not as proof of a defect and not as proof of its
496
+ absence when it stays quiet.
497
+ 3. **A custom Vale `existence` rule counting semicolons/em-dashes/commas per sentence** (e.g.
498
+ "flag any sentence with ≥3 semicolons or ≥2 em-dash clauses") — not shipped anywhere found in
499
+ this research, but cheap to author (a few lines of YAML regex) and would catch the shape of
500
+ sentence 6 specifically, as opposed to relying on a readability-formula proxy for it.
501
+ 4. **LaTeX `acronym` package with the `error` option**, only if the corpus is LaTeX and has
502
+ acronyms — real, build-failing, on-point for its narrow scope (undefined acronym at first
503
+ use), zero cost if already using LaTeX.
504
+ 5. **write-good**, mainly as a second, differently-tuned word-list pass (catches passive voice
505
+ and weak-adverb patterns proselint's list doesn't cover) — low value on its own given this
506
+ test set (4/6 sentences got an irrelevant flag, the useful information rate is low), but it's
507
+ a 30-second npm install and doesn't cost anything to have running alongside proselint.
508
+
509
+ Deliberately **not** recommending: Vale's readability rule (wrong scope — document-level, not
510
+ sentence-level — for this specific problem, though still fine for its intended purpose of
511
+ catching genuinely bloated paragraphs elsewhere); LanguageTool Picky mode (heavier to self-host
512
+ than the value it added in this test — mostly grammar, marginal style); Coh-Metrix (right idea,
513
+ no pass/fail packaging, would need custom threshold-setting research to turn into a usable gate);
514
+ Acrolinx/Grammarly Business (enterprise pricing/workflow mismatch for a solo paper pipeline, and
515
+ the one feature that sounds relevant — vague pronoun reference — is unverified).
516
+
517
+ ## (c) What remains irreducibly non-deterministic
518
+
519
+ The defect in all five "wtf"-tier sentences is the same shape: each sentence is a valid,
520
+ economical compression of an idea the _writer_ holds fully formed, and the compression relies on
521
+ the reader having already built the same mental model — which term of art "the rule" and "its
522
+ claim about itself" refer to (sentence 1), what "these files" and "constructions nobody built"
523
+ cash out to concretely (sentence 2), what the antecedent of "three answers" even is without the
524
+ paragraph before it (sentence 3), and in sentence 5, what noun phrase "the other two" silently
525
+ picks up from two clauses earlier. This is **not a property of any individual sentence's tokens**
526
+ — it's a property of the relationship between the sentence and a specific reader's prior state,
527
+ which is exactly why every tool surveyed here comes up empty: they all operate on the document (or
528
+ one sentence) in isolation, scoring vocabulary/length/word-choice, with no model of what the
529
+ reader brought into the room. Coh-Metrix gets closest by modeling cross-sentence referential
530
+ overlap, but even that is a statistical proxy for cohesion within the _document itself_, not a
531
+ model of a specific reader's background knowledge — a term can have perfect referential overlap
532
+ with its own prior use three paragraphs up and still be opaque to a reader meeting the paper cold.
533
+ Detecting that gap requires actually simulating a reader who does not have the writer's context —
534
+ which is a language-understanding task, not a countable-surface-feature task, and is why the
535
+ `cold-read-diff` skill's approach (send the diff to a fresh, context-free reader and ask what each
536
+ sentence claims) is targeting the right mechanism even though the homegrown metric-based checks
537
+ are not: the fix for this class of defect is closer to "add another independent reader" than
538
+ "tighten a threshold."