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,521 @@
1
+ /**
2
+ * ESLint configuration for this repository's OWN fixtures.
3
+ *
4
+ * It is not the configuration a consumer will use — a consumer points the same block at the
5
+ * path of its own paper (`papers/<name>/paper.tex` or wherever the source actually lives).
6
+ * What is reusable here is the SHAPE: one plugin object carrying both the language and the
7
+ * rules that read LaTeX source, one `language:` line, and an explicit severity per rule.
8
+ *
9
+ * 🔴 The glob matters more than it looks. A rule whose glob matches nothing is not "clean" —
10
+ * it is never invoked, and the run reports exactly the same green as a rule that passed. That
11
+ * is why `scripts/rules-see-files.mjs` exists and why it runs as part of the test suite:
12
+ * every rule declared below must be enabled for at least one file that is actually on disk.
13
+ */
14
+ import { texLanguage } from "./eslint-rules/latex-language.mjs";
15
+ import texBuild from "./eslint-rules/tex-build.mjs";
16
+ import markdown from "@eslint/markdown";
17
+ import reviewRules from "./eslint-rules/review-findings-cause.mjs";
18
+ import localRules from "./eslint-rules/temp-root-realpath.mjs";
19
+ import portRules from "./eslint-rules/install-path-literals.mjs";
20
+ import n from "eslint-plugin-n";
21
+ import tseslint from "typescript-eslint";
22
+ import boundaries from "eslint-plugin-boundaries";
23
+ import { dirname } from "node:path";
24
+ import { fileURLToPath } from "node:url";
25
+
26
+ // The complexity set, shared by the TypeScript block and the ratchet below. Every function
27
+ // measured over these limits on 2026-09-24 was either refactored under them (the #59 build code)
28
+ // or pinned at its current maximum in RATCHET.
29
+ const MAX_LINES = { max: 60, skipComments: true, skipBlankLines: true };
30
+
31
+ // 🔴 A CEILING, NOT A PERMISSION: each number is the file's measured maximum on 2026-09-24, so a
32
+ // function in these files can get simpler and cannot get worse. Lower a number when a refactor
33
+ // lowers the maximum; never raise one. Paying the debt down is issue #72. A file leaves this table
34
+ // when it passes the shared limits.
35
+ const RATCHET = {
36
+ "src/init.ts": { complexity: 59, "max-lines-per-function": 219 },
37
+ "src/cli.ts": { complexity: 43, "max-lines-per-function": 152 },
38
+ "src/doctor.ts": { complexity: 38, "max-lines-per-function": 125 },
39
+ "src/hooks-settings.ts": { complexity: 12, "max-depth": 4 },
40
+ "src/structure.ts": { complexity: 12, "max-depth": 4 },
41
+ "src/new-paper.ts": { complexity: 11 },
42
+ };
43
+
44
+ // ── Hexagonal layers (src/CLAUDE.md, issue #76) ─────────────────────────────────────────
45
+ // TWO INDEPENDENT AXES, and a file has a position on each.
46
+ //
47
+ // A. KNOWLEDGE — whose vocabulary does the file speak, the paper's or one external program's?
48
+ // domain src/domain/ values and decisions in the paper's vocabulary
49
+ // port src/ports/ interfaces shaped by what the inside needs, never by what a tool does
50
+ // app src/*.ts use cases over ports (the flat files; most are ratcheted, #76)
51
+ // adapter src/adapters/<x>/ everything that exists because program/format <x> exists — pure
52
+ // parsers included. One element per folder, used through index.ts.
53
+ // root src/cli.ts reads process.*, builds adapters, wires ports
54
+ // B. PURITY — can anything here fail for a reason not in its arguments? Effects (disk, process,
55
+ // network, env) only in src/adapters/<x>/<y>.io.ts and the root.
56
+ //
57
+ // "Core" is deliberately not a layer name: the plugin's own `origin: "core"` means NODE BUILT-INS.
58
+ //
59
+ // Folders are ELEMENTS, single files are CATEGORIES (`boundaries/files`): that is v7's model — an
60
+ // element pattern is matched as a folder, and a file-shaped one is refused with a warning. So the
61
+ // root and the flat app files are categories, and the policies below select them with `file:`.
62
+
63
+ /** Modules that ARE effects. A file importing one must be an `io` file or the root. */
64
+ export const IO_MODULES = [
65
+ "fs",
66
+ "fs/promises",
67
+ "child_process",
68
+ "os",
69
+ "net",
70
+ "http",
71
+ "https",
72
+ "worker_threads",
73
+ "readline",
74
+ "readline/promises",
75
+ ].flatMap((m) => [m, `node:${m}`]);
76
+
77
+ /** What the inside may import from outside the repository: types, pure path arithmetic, hashing. */
78
+ export const INSIDE_EXTERNALS = [
79
+ "ts-essentials",
80
+ "path",
81
+ "node:path",
82
+ "crypto",
83
+ "node:crypto",
84
+ ];
85
+ /** What the flat app files may import besides: Node's pure helpers, measured on the first v7 run. */
86
+ export const APP_EXTERNALS = [
87
+ ...INSIDE_EXTERNALS,
88
+ "util",
89
+ "node:util",
90
+ "url",
91
+ "node:url",
92
+ "module",
93
+ "node:module",
94
+ ];
95
+
96
+ const ORIGINS = ["external", "core"];
97
+ const modules = (source) => ({ to: { module: { origin: ORIGINS, source } } });
98
+ const elements = (...types) => ({ element: { types: { anyOf: types } } });
99
+ const APP = { file: { categories: "app" } };
100
+ const ROOT = { file: { categories: "root" } };
101
+
102
+ /**
103
+ * Both axes in one plugin: axis A by element and category, axis B by the file category `io`.
104
+ *
105
+ * 🔴 `boundaries/root-path` IS LOAD-BEARING. Without it the plugin matches its patterns against
106
+ * `process.cwd()` — not ESLint's `cwd` — so lint started from any other directory classifies no file
107
+ * and the rule passes silently. Measured 2026-09-25: a core → adapter import produced zero findings
108
+ * until the root was pinned. `root` is a parameter only so `test/eslint-layers.test.ts` can re-root
109
+ * it at its fixture tree.
110
+ *
111
+ * 🔴 `checkAllOrigins: true` IS LOAD-BEARING TOO. The default checks local imports only, so every
112
+ * `module:` policy below — all of axis B and the inside's library ban — would match nothing and
113
+ * report nothing. The fixture `src/adapters/one/disk.ts` is what makes that loud.
114
+ *
115
+ * Policies are evaluated in order and the LAST one that matches decides, so the narrowing ones
116
+ * (encapsulation, then axis B) come after the allows they narrow. A dependency no policy allows gets
117
+ * the plugin's own message, which names both ends ("no policy allowing dependencies from file of
118
+ * category "app" to elements of type "adapter""); custom messages sit only on the disallows.
119
+ */
120
+ export const layerBoundaries = (root) => ({
121
+ files: ["src/**/*.ts"],
122
+ plugins: { boundaries },
123
+ settings: {
124
+ "boundaries/root-path": root,
125
+ "boundaries/elements": [
126
+ { type: "port", pattern: "src/ports", partialMatch: false },
127
+ { type: "domain", pattern: "src/domain", partialMatch: false },
128
+ // One element PER ADAPTER FOLDER, so adapter → other adapter is a cross-element import.
129
+ {
130
+ type: "adapter",
131
+ pattern: "src/adapters/*",
132
+ partialMatch: false,
133
+ capture: ["name"],
134
+ },
135
+ // The package's plain-JS modules. Named so an import of one is classified, not unknown.
136
+ {
137
+ type: "js-module",
138
+ pattern: "(eslint-rules|hooks|lib|skills)",
139
+ partialMatch: false,
140
+ capture: ["kind"],
141
+ },
142
+ ],
143
+ "boundaries/files": [
144
+ // `exclusive`: the root also matches the app pattern below, and is only the root.
145
+ { category: "root", pattern: "src/cli.ts", exclusive: true },
146
+ // Every other file at the top of src/ is app. A new one gets the full app rules.
147
+ { category: "app", pattern: "src/*.ts" },
148
+ { category: "test", pattern: "src/**/*.test.ts" },
149
+ // AXIS B: purity is a category, orthogonal to the element.
150
+ { category: "io", pattern: "src/adapters/*/*.io.ts" },
151
+ ],
152
+ },
153
+ rules: {
154
+ // 🔴 An unclassified file is invisible to the rules below: a new `src/lib/x.ts` could import an
155
+ // adapter from anywhere and nothing would fire. So every linted file must be a declared element
156
+ // or category, and every import must resolve to one.
157
+ "boundaries/no-unknown-files": "error",
158
+ "boundaries/no-unknown-dependencies": "error",
159
+ "boundaries/dependencies": [
160
+ "error",
161
+ {
162
+ default: "disallow",
163
+ checkAllOrigins: true,
164
+ policies: [
165
+ // ── AXIS A: who may know whom (jMolecules: Application → Port, never → Adapter) ──
166
+ { from: elements("domain"), allow: { to: elements("domain") } },
167
+ { from: elements("port"), allow: { to: elements("domain", "port") } },
168
+ { from: APP, allow: { to: [elements("domain", "port"), APP] } },
169
+ // An adapter knows the domain and the ports (jMolecules#306's allowance), never the app,
170
+ // never another adapter.
171
+ {
172
+ from: elements("adapter"),
173
+ allow: { to: elements("domain", "port") },
174
+ },
175
+ {
176
+ from: ROOT,
177
+ allow: { to: [{ element: { type: "*" } }, APP] },
178
+ },
179
+ // The package's `.mjs` half is read by the outside layers and the flat app files, never
180
+ // by the domain or a port.
181
+ {
182
+ from: [elements("adapter"), APP, ROOT],
183
+ allow: { to: elements("js-module") },
184
+ },
185
+ // ── libraries and built-ins (the plugin calls Node built-ins origin "core") ──
186
+ {
187
+ from: elements("domain", "port"),
188
+ allow: modules(INSIDE_EXTERNALS),
189
+ },
190
+ { from: APP, allow: modules(APP_EXTERNALS) },
191
+ {
192
+ from: [elements("adapter"), ROOT],
193
+ allow: { to: { module: { origin: ORIGINS } } },
194
+ },
195
+ // Tests may import anything, fakes included …
196
+ {
197
+ from: { file: { categories: "test" } },
198
+ allow: {
199
+ to: [
200
+ { element: { type: "*" } },
201
+ APP,
202
+ ROOT,
203
+ { module: { origin: ORIGINS } },
204
+ ],
205
+ },
206
+ },
207
+ // … but everyone, tests too, reaches an adapter through its index.ts. (Imports inside one
208
+ // adapter are internal to its element and not checked.)
209
+ {
210
+ disallow: {
211
+ to: {
212
+ element: { type: "adapter", fileInternalPath: "!index.ts" },
213
+ },
214
+ },
215
+ message:
216
+ "{{ dependency.source }} is inside an adapter: import its index.ts (src/CLAUDE.md, layers).",
217
+ },
218
+ // ── AXIS B: effects only in io files, tests and the root ──
219
+ // Written as "nobody, then these three" rather than "everyone but these three": a file
220
+ // with no category has `categories: null`, and a `noneOf` query never matches null — so the
221
+ // negative form silently exempts every plain file. Measured on `adapters/one/disk.ts`.
222
+ {
223
+ disallow: {
224
+ to: { module: { origin: "core", source: IO_MODULES } },
225
+ },
226
+ message:
227
+ "I/O ({{ dependency.source }}) in a file that is not *.io.ts. Effects live in src/adapters/<tool>/<x>.io.ts or src/cli.ts; this file takes a port (src/CLAUDE.md, purity). Legacy sites carry `-- legacy I/O, moves behind a port in #76`.",
228
+ },
229
+ {
230
+ from: { file: { categories: ["io", "test", "root"] } },
231
+ allow: { to: { module: { origin: "core", source: IO_MODULES } } },
232
+ },
233
+ ],
234
+ },
235
+ ],
236
+ },
237
+ });
238
+
239
+ /**
240
+ * The globals the plugin cannot see: `process` and `fetch` are not imports. Same scope as axis B.
241
+ * Files that did I/O before the layers existed carry an `eslint-disable-next-line` naming #76 above
242
+ * each such import or use; `reportUnusedDisableDirectives: "error"` turns a disable that suppresses
243
+ * nothing into a finding, so an exemption leaves the moment its I/O does, and
244
+ * `scripts/layer-legacy-frozen.mjs` freezes how many each file may carry. A NEW module that needs
245
+ * the outside world is a new `*.io.ts` in the adapter of the program it talks to — not a disable.
246
+ */
247
+ export const IO_GLOBALS = {
248
+ files: ["src/**/*.ts"],
249
+ ignores: ["src/cli.ts", "src/adapters/*/*.io.ts", "src/**/*.test.ts"],
250
+ linterOptions: { reportUnusedDisableDirectives: "error" },
251
+ rules: {
252
+ "no-restricted-globals": [
253
+ "error",
254
+ {
255
+ name: "process",
256
+ message:
257
+ "The environment is an input: the root reads it and passes a value in (src/CLAUDE.md, purity).",
258
+ },
259
+ {
260
+ name: "fetch",
261
+ message: "Network access is an adapter's *.io.ts file.",
262
+ },
263
+ ],
264
+ },
265
+ };
266
+
267
+ const ceiling = (rule, n) =>
268
+ rule === "max-lines-per-function"
269
+ ? ["error", { ...MAX_LINES, max: n }]
270
+ : ["error", n];
271
+
272
+ export default [
273
+ // 🔴 TRANSIENT DIRECTORIES ARE NOT THE CORPUS, and leaving them in is a RACE, not sloppiness.
274
+ // `paper-stages.harness.mjs` creates a temp tree under fixtures and removes it when done,
275
+ // while `latex-language.harness.mjs` lints the whole repository to prove its glob matches
276
+ // real files. Run in parallel, ESLint enumerates a path and then reads it, and the file can
277
+ // be gone in between: ENOENT, in a harness that has nothing to do with either.
278
+ //
279
+ // Measured 2026-09-17: the race had been latent and surfaced the moment a 54th harness
280
+ // shifted the scheduling. Nothing about the new harness was wrong — which is the point.
281
+ // `docs/prior-art/repro/` is EVIDENCE, not source: those scripts are kept exactly as they were
282
+ // run, so that a verdict in the design notes can be re-measured rather than argued with. Linting
283
+ // them invites the next reader to tidy an unused import — and then the file on disk is no longer
284
+ // the file that produced the number it backs.
285
+ // `dist/` is tsc's OUTPUT (gitignored): its declaration files are generated from `src/`, so
286
+ // linting them would report every finding twice and blame a file nobody edits.
287
+ {
288
+ ignores: [
289
+ ".tmp-stages-src-*/",
290
+ "fixtures/.tmp-*/",
291
+ "docs/prior-art/repro/",
292
+ "dist/",
293
+ ],
294
+ },
295
+ // 🔴 THE PACKAGE'S TYPESCRIPT, and before 2026-09-24 no block matched it — the same silent
296
+ // ignore the `.mjs` block below was written against, recurring for `.ts` (#49). `src/` is the
297
+ // CLI and the build; the skill specs compile into the SKILL.md files the package ships; the
298
+ // three hand-written `.d.mts` type the `.mjs` modules `src/` imports. All tracked TypeScript.
299
+ //
300
+ // Measured on the first run (41727cd): 44 findings in 9 of 39 files, all in `src/`, plus one
301
+ // `no-useless-escape` in a spec that had dropped a backslash from its compiled SKILL.md. The
302
+ // #59 build files were refactored clean; the older CLI files sit in RATCHET, one block below.
303
+ //
304
+ // NOT TYPE-AWARE, deliberately: no rule here needs type information, and `npm run build`
305
+ // (tsc, strict) already type-checks `src/` as the first gate of `npm run check`.
306
+ //
307
+ // NOT HERE: `port/js-install-path` (43 findings, 41 of them the specs' `.claude/skills/`
308
+ // literals — issue #19's debt, `warn` for `.mjs` for the same reason; 2 in hooks-settings.ts,
309
+ // which writes the consumer's settings and must name the install) and `n/no-missing-import`
310
+ // (tsc already fails on an unresolved import in `src/`).
311
+ {
312
+ files: ["**/*.ts", "**/*.mts"],
313
+ languageOptions: { parser: tseslint.parser },
314
+ plugins: { "@typescript-eslint": tseslint.plugin, local: localRules },
315
+ rules: {
316
+ // The same macOS-only defect as in the `.mjs` block; clean here, so it opens at `error`.
317
+ "local/temp-root-realpath": "error",
318
+ // A dead import is a sign of an incomplete edit. The TypeScript variant, because the core
319
+ // rule does not understand type-only positions.
320
+ "@typescript-eslint/no-unused-vars": "error",
321
+ // `any` switches the checker off for everything it flows into — the gate that makes `src/`
322
+ // TypeScript at all. Five older sites are pinned inline and named in #49.
323
+ "@typescript-eslint/no-explicit-any": "error",
324
+ // The behaviour rules of the `.mjs` block, for the same reasons: each changes what the code
325
+ // DOES, not how it looks, and a regex that escapes the wrong thing searches for the wrong thing.
326
+ "no-empty": "error",
327
+ "no-constant-condition": "error",
328
+ "no-dupe-keys": "error",
329
+ "no-unreachable": "error",
330
+ "no-fallthrough": "error",
331
+ "no-useless-escape": "error",
332
+ "no-control-regex": "error",
333
+ "no-misleading-character-class": "error",
334
+ "no-prototype-builtins": "error",
335
+ // Branches per function. Ten is the rule's long-standing default; the worst function
336
+ // measured was 59, and one that size cannot be read, only re-run.
337
+ complexity: ["error", 10],
338
+ // `x as unknown as T` tells the checker to look away: two spellings of one port were once
339
+ // reconciled that way (#76). A real conversion is a function; a real subset needs no cast.
340
+ "no-restricted-syntax": [
341
+ "error",
342
+ {
343
+ selector:
344
+ "TSAsExpression > TSAsExpression[typeAnnotation.type='TSUnknownKeyword']",
345
+ message:
346
+ "`as unknown as` switches the type checker off. Convert with a function, or fix the type.",
347
+ },
348
+ ],
349
+ // Nesting beyond three blocks is where a step belongs in its own named function.
350
+ "max-depth": ["error", 3],
351
+ // Five positional parameters are a record without field names; pass an object instead.
352
+ "max-params": ["error", 4],
353
+ // A function longer than a screen is read in pieces, and its pieces then want names.
354
+ "max-lines-per-function": ["error", MAX_LINES],
355
+ // Three levels of callbacks is the ceiling before a promise chain or a named step is due.
356
+ "max-nested-callbacks": ["error", 3],
357
+ },
358
+ },
359
+ // Hexagonal layers, both axes (src/CLAUDE.md): knowledge by element, purity by file category.
360
+ IO_GLOBALS,
361
+ layerBoundaries(dirname(fileURLToPath(import.meta.url))),
362
+ // The ratchet: per-file ceilings for the files written before the limits existed. See RATCHET.
363
+ ...Object.entries(RATCHET).map(([file, max]) => ({
364
+ files: [file],
365
+ rules: Object.fromEntries(
366
+ Object.entries(max).map(([rule, n]) => [rule, ceiling(rule, n)]),
367
+ ),
368
+ })),
369
+ /**
370
+ * 🔴 THIS BLOCK COVERS THE PACKAGE ITSELF, and before 2026-09-15 it was not here: the config held only
371
+ * one block for `.tex` (I don't quote the glob inside this comment: the sequence
372
+ * "star-slash" would close the comment itself — which is where I tripped), and a file with no block gets simply IGNORED by ESLint 9.
373
+ * Meaning: 133 own `.mjs` — mutation engine, three hooks, scripts for 24 skills — went unchecked,
374
+ * with green `npm run lint`. A tool that checks others' papers and not itself.
375
+ *
376
+ * Measurement on the first run: 15 files with dead imports (`resolve`, `pathToFileURL`) and
377
+ * orphaned constants — remnants of a move from `mine`, where those names were needed. All
378
+ * cleaned to zero in the same commit, so the rule opens as a GATE on a clean corpus,
379
+ * not as debt to be silenced.
380
+ *
381
+ * ⚠️ RULES ARE ENUMERATED, NOT TAKEN AS A SET, for two reasons. First: `@eslint/js` is not
382
+ * a dependency, and pulling it for a preset costs more than it saves with six devDeps.
383
+ * Second, more important: enumeration makes each inclusion a DECISION, just like in the .tex
384
+ * block below, where each line has a reason.
385
+ *
386
+ * 🔴 WHAT IS DELIBERATELY NOT HERE: `require-atomic-updates`. It finds three issues in
387
+ * `verify-cites.mjs` on a classic memoizer (read `cache[ck]` → `await` →
388
+ * write). Read from the code: citation walk is sequential, and a race in the worst case
389
+ * is a repeat network call with an equivalent result. So on THIS corpus
390
+ * it is a false positive, and for an `error`-level rule a false positive is worse than a miss:
391
+ * they turn it off the same day, and the rest stop being read with it.
392
+ * In `eslint:recommended`, by the way, it is also not there.
393
+ */
394
+ {
395
+ files: ["**/*.mjs"],
396
+ languageOptions: { ecmaVersion: 2024, sourceType: "module" },
397
+ plugins: { local: localRules, port: portRules },
398
+ rules: {
399
+ // Rule 10's mechanical half, code side. `warn` and not `error`, unlike its neighbour
400
+ // above: this one does NOT open on a clean corpus. Two shipped modules print a command
401
+ // for a human that names an install-specific path, and neither is fixable by the
402
+ // answer the skills get — a printed command has to be resolved through the port at
403
+ // runtime. Nothing freezes that number: a ratchet would say "at least not worse" on the
404
+ // same day the rule was written, which is how a removal turns into a decision to keep.
405
+ // The severity only keeps `npx eslint .` from being red on a healthy clone, which is how
406
+ // a rule gets switched off and its binary neighbours ignored with it.
407
+ "port/js-install-path": "warn",
408
+ // 🔴 `error`, AND THIS IS A DECISION, NOT A DEFAULT. The rule is opened by a GATE on a
409
+ // clean corpus: all fifteen venues are allowed in the same commit, so there's no debt that
410
+ // would have to be muted. Stricter than that: the defect it catches is REPRODUCED ONLY ON
411
+ // macOS, and CI here is just one — `ubuntu-latest`. That is, on Linux this is the only
412
+ // guard that could ever turn red, and `warn` would mean that no one would ever see it:
413
+ // `eslint .` exits zero on warnings.
414
+ "local/temp-root-realpath": "error",
415
+ // A dead import is not style, it is a sign of an incomplete edit: it says a file once
416
+ // did something else. Fifteen such surfaced from the move.
417
+ "no-unused-vars": "error",
418
+ // Below are rules that silently change BEHAVIOR, not appearance: empty `catch {}` block (swallowed
419
+ // error — a documented failure mode of this repository), constant condition, duplicate key,
420
+ // unreachable code, fallthrough in `case`.
421
+ "no-empty": "error",
422
+ "no-constant-condition": "error",
423
+ "no-dupe-keys": "error",
424
+ "no-unreachable": "error",
425
+ "no-fallthrough": "error",
426
+ // Regexes: unnecessary escaping slash and control character in class — both findings about
427
+ // a pattern searching for NOT what the author wrote. The subject of this repository is checks,
428
+ // and a check searching for the wrong thing is green by construction.
429
+ "no-useless-escape": "error",
430
+ "no-control-regex": "error",
431
+ "no-misleading-character-class": "error",
432
+ "no-prototype-builtins": "error",
433
+ },
434
+ },
435
+ /**
436
+ * 🔴 AN IMPORT THAT RESOLVES TO NOTHING IS NOT A STYLE FINDING — it is a module that throws on
437
+ * load. Nothing here noticed one for a week (#67): a fixture imported `lib/consumer.mjs`, a file
438
+ * that has never existed in any commit, and no harness ever loads that fixture — the harness
439
+ * only LINTS it, and the port rule reads string literals, not whether they resolve. The same
440
+ * run found a second, older one: `lib/skill-eval-kit.mjs` dynamically imported
441
+ * `vigiles/testing`, a subpath vigiles stopped exporting in v16, on a branch no caller had
442
+ * taken yet.
443
+ *
444
+ * `n/no-missing-import` is the standard rule for this and covers relative AND package
445
+ * specifiers, static and dynamic, against the real resolver. Its own block so the one
446
+ * exception below does not leak into the other rules: the occupancy probe is EVIDENCE, kept
447
+ * exactly as it was run in a scratch environment where `retext-*` was installed — the same
448
+ * reason `docs/prior-art/repro/` is ignored globally above.
449
+ */
450
+ {
451
+ files: ["**/*.mjs"],
452
+ ignores: ["skills/paper-pipeline/references/occupancy-2026-08-06-probe/**"],
453
+ plugins: { n },
454
+ rules: { "n/no-missing-import": "error" },
455
+ },
456
+ /**
457
+ * The package's first markdown rule — unit 1 of step 9 (moved from the consumer).
458
+ * The block targets OWN fixtures: in the consumer the same plugin is applied to its
459
+ * review report directory. `warn` for the same reason as .tex below: fixtures
460
+ * are DELIBERATELY defective, and `error` would mean `npx eslint .` reds on a healthy
461
+ * checkout. The signal lives in `npm test`, not in the warning count.
462
+ */
463
+ {
464
+ files: ["fixtures/review-findings-cause/**/*.md"],
465
+ plugins: { markdown, review: reviewRules },
466
+ language: "markdown/gfm",
467
+ languageOptions: { frontmatter: "yaml" },
468
+ rules: { "review/findings-cause": "warn" },
469
+ },
470
+ /**
471
+ * Rule 10's mechanical half, prose side — and this is where the debt actually is: 76
472
+ * findings across 29 skills on a healthy checkout, every one of them a command that
473
+ * resolves in one delivery channel and is absent in another.
474
+ *
475
+ * The severity is a statement about the CORPUS, exactly as in the .tex block below: the
476
+ * finding itself is binary, but the corpus carries known debt that cannot be paid in the
477
+ * commit that introduces the rule. That debt is an ISSUE with an owner, deliberately not a
478
+ * frozen constant in a test — see the harness for why. `warn` keeps a clean clone from being
479
+ * red until the paths are gone; when they are, this line becomes `error`.
480
+ */
481
+ {
482
+ files: ["skills/**/*.md"],
483
+ plugins: { markdown, port: portRules },
484
+ language: "markdown/gfm",
485
+ languageOptions: { frontmatter: "yaml" },
486
+ rules: { "port/md-install-path": "warn" },
487
+ },
488
+ {
489
+ files: ["**/*.tex"],
490
+ plugins: {
491
+ // One plugin object carries BOTH the language and the rules: `tex/latex` is the
492
+ // language, `tex/*` are the rules about the LaTeX source itself. ESLint permits this,
493
+ // and a second plugin name would buy nothing.
494
+ tex: { languages: { latex: texLanguage }, rules: texBuild },
495
+ },
496
+ language: "tex/latex",
497
+ rules: {
498
+ // `warn`, and that is an analysis rather than caution. The finding is NOT binary: a
499
+ // promise in a shipped build is sometimes honest (something genuinely not released
500
+ // yet), and the verdict "does this contradict the Availability paragraph" is a human
501
+ // one — which is what the message says. There is also a demonstrable class of false
502
+ // positives: "their replication will be published in 2027" is a sentence about SOMEONE
503
+ // ELSE's work. An `error` that fails on a correct input gets switched off the same day,
504
+ // and then the binary checks stop being read too.
505
+ "tex/future-promise": "warn",
506
+ // 🔴 `warn` HERE AND `error` IN A CONSUMER, on purpose. The finding itself is binary —
507
+ // the macro is present or it is not — and it has a named exemption (`nonacm`), so in a
508
+ // repository that lints a REAL paper it belongs at `error`: the cost of a miss is desk
509
+ // rejection with no review, and that asymmetry is the whole argument. This
510
+ // repository lints fixtures broken by construction, where the same severity
511
+ // would only mean `npx eslint .` reds on a healthy checkout. Severity is
512
+ // a statement about the CORPUS being linted, not the rule's certainty.
513
+ "tex/acm-frontmatter-override": "warn",
514
+ },
515
+ // ⚠️ `npx eslint .` therefore reports six warnings on a healthy checkout: the four defect
516
+ // fixtures are DEFECTIVE ON PURPOSE, and that is what makes the fire half of every harness
517
+ // real. Do not silence them by adding an ignore — a rule that lints only clean inputs is
518
+ // one whose firing path nothing exercises, which is rule 4 wearing a different hat. The
519
+ // pass/fail signal lives in `npm test`, not in the warning count of `npm run lint`.
520
+ },
521
+ ];
@@ -0,0 +1,3 @@
1
+ # Fixture
2
+
3
+ Stage: draft
@@ -0,0 +1,11 @@
1
+ % A minimal acmart paper — exactly what ACM venues build.
2
+ % The full affiliation is not decoration: without \country the class ABORTS the build, and
3
+ % without it the fixture would fail for a reason unrelated to the subject of the test.
4
+ \documentclass[sigconf,nonacm]{acmart}
5
+ \begin{document}
6
+ \title{A fixture that must come out in the class's own fonts}
7
+ \author{A. Author}
8
+ \affiliation{\institution{X University}\city{Almaty}\country{Kazakhstan}}
9
+ \maketitle
10
+ The body exists so the run produces a page with text on it.
11
+ \end{document}
@@ -0,0 +1 @@
1
+ { "venue": "agenticdev", "kind": "short" }
@@ -0,0 +1,3 @@
1
+ # Fixture
2
+
3
+ Stage: draft
@@ -0,0 +1,7 @@
1
+ % A paper that does not compile: an undefined control sequence on line 6.
2
+ % `paperlint build` must name pdflatex, quote the error line and its `l.6` context from the log, and
3
+ % DELETE any paper.pdf left from an earlier build, so a red build cannot leave a PDF that looks current.
4
+ \documentclass{article}
5
+ \begin{document}
6
+ Text before, then \undefinedmacro{} after.
7
+ \end{document}
@@ -0,0 +1,3 @@
1
+ # Fixture
2
+
3
+ Stage: draft
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+ # A leftover paper-supplied build script. `paperlint build` must NOT run it — it compiles the paper
3
+ # itself — and must say that this file is ignored. Running it would leave a trace and fail.
4
+ touch "$(dirname "$0")/RAN"
5
+ exit 1
@@ -0,0 +1,10 @@
1
+ % The bibtex path: a \cite needs pdflatex, bibtex, pdflatex before the citation resolves, and a
2
+ % \ref needs one more pass. The PDF must carry "[1]", not "[?]".
3
+ % The build.sh next to this file must NOT run: it would leave RAN behind and exit 1.
4
+ \documentclass{article}
5
+ \begin{document}
6
+ \section{Intro}\label{sec:intro}
7
+ Section~\ref{sec:intro} cites \cite{knuth84}.
8
+ \bibliographystyle{plain}
9
+ \bibliography{refs}
10
+ \end{document}
@@ -0,0 +1,9 @@
1
+ @article{knuth84,
2
+ author = {Donald E. Knuth},
3
+ title = {Literate Programming},
4
+ journal = {The Computer Journal},
5
+ year = {1984},
6
+ volume = {27},
7
+ number = {2},
8
+ pages = {97--111}
9
+ }
@@ -0,0 +1,3 @@
1
+ # Fixture
2
+
3
+ Stage: draft
@@ -0,0 +1,6 @@
1
+ % A document with no pages. pdflatex prints "No pages of output." and exits 0, writing NO PDF.
2
+ % `paperlint build` must report that as a failure — never a green paper.pdf — and must not let a
3
+ % paper.pdf from an earlier build stand in for the one this run did not write.
4
+ \documentclass{article}
5
+ \begin{document}
6
+ \end{document}
@@ -0,0 +1,3 @@
1
+ # Fixture
2
+
3
+ Stage: draft
@@ -0,0 +1,11 @@
1
+ % STANDS IN FOR THE CASE «acmart silently fell back to Computer Modern».
2
+ %
3
+ % The real substitution mechanism cannot be reproduced here: the class checks for
4
+ % `libertine.sty`/`zi4.sty`/`newtxmath.sty` through \IfFileExists, and hiding an installed
5
+ % package from kpathsea by adding a file is impossible — TEXINPUTS can append paths, not remove them.
6
+ % So the fixture produces the same OBSERVABLE result — a PDF typeset in Computer Modern —
7
+ % and the font check must reject it. What is being checked is the check itself.
8
+ \documentclass{article}
9
+ \begin{document}
10
+ The body exists so the run produces a page with text on it.
11
+ \end{document}
@@ -0,0 +1,3 @@
1
+ # Fixture
2
+
3
+ Stage: draft
@@ -0,0 +1,10 @@
1
+ % `\input{paper-guards}` resolves with NO configuration: rpp puts its own venues directory on
2
+ % TEXINPUTS. The guards fire only on the final pass (\finalpass), so a clean paper with a
3
+ % forward \ref must still build.
4
+ \documentclass{article}
5
+ \input{paper-guards}
6
+ \begin{document}
7
+ See Section~\ref{sec:later}.
8
+ \section{Later}\label{sec:later}
9
+ Text.
10
+ \end{document}
@@ -0,0 +1,3 @@
1
+ # Fixture
2
+
3
+ Stage: draft
@@ -0,0 +1,3 @@
1
+ # Fixture
2
+
3
+ Stage: draft