paperlint 3.2.0 → 4.1.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 (442) hide show
  1. package/.github/workflows/ci.yml +16 -23
  2. package/CLAUDE.md +33 -13
  3. package/CONTRIBUTING.md +23 -7
  4. package/README.md +11 -8
  5. package/dist/adapters/banal/failure.d.ts +1 -7
  6. package/dist/adapters/banal/failure.d.ts.map +1 -1
  7. package/dist/adapters/banal/failure.js +5 -7
  8. package/dist/adapters/banal/failure.js.map +1 -1
  9. package/dist/adapters/banal/index.d.ts.map +1 -1
  10. package/dist/adapters/banal/index.js +8 -11
  11. package/dist/adapters/banal/index.js.map +1 -1
  12. package/dist/adapters/banal/install.d.ts.map +1 -1
  13. package/dist/adapters/banal/install.js +4 -3
  14. package/dist/adapters/banal/install.js.map +1 -1
  15. package/dist/adapters/banal/invocation.d.ts +10 -5
  16. package/dist/adapters/banal/invocation.d.ts.map +1 -1
  17. package/dist/adapters/banal/invocation.js +13 -1
  18. package/dist/adapters/banal/invocation.js.map +1 -1
  19. package/dist/adapters/banal/locate.d.ts +12 -9
  20. package/dist/adapters/banal/locate.d.ts.map +1 -1
  21. package/dist/adapters/banal/locate.js +19 -12
  22. package/dist/adapters/banal/locate.js.map +1 -1
  23. package/dist/adapters/banal/run.d.ts +10 -1
  24. package/dist/adapters/banal/run.d.ts.map +1 -1
  25. package/dist/adapters/banal/run.js +8 -4
  26. package/dist/adapters/banal/run.js.map +1 -1
  27. package/dist/adapters/banal/settings.d.ts +1 -1
  28. package/dist/adapters/banal/settings.d.ts.map +1 -1
  29. package/dist/adapters/banal/settings.js +2 -1
  30. package/dist/adapters/banal/settings.js.map +1 -1
  31. package/dist/adapters/latex/document-class.d.ts +22 -0
  32. package/dist/adapters/latex/document-class.d.ts.map +1 -0
  33. package/dist/adapters/latex/document-class.js +102 -0
  34. package/dist/adapters/latex/document-class.js.map +1 -0
  35. package/dist/adapters/latex/hidden.d.ts +16 -0
  36. package/dist/adapters/latex/hidden.d.ts.map +1 -0
  37. package/dist/adapters/latex/hidden.js +49 -0
  38. package/dist/adapters/latex/hidden.js.map +1 -0
  39. package/dist/adapters/latex/index.d.ts +29 -0
  40. package/dist/adapters/latex/index.d.ts.map +1 -0
  41. package/dist/adapters/latex/index.js +18 -0
  42. package/dist/adapters/latex/index.js.map +1 -0
  43. package/dist/adapters/latex/nodes.d.ts +54 -0
  44. package/dist/adapters/latex/nodes.d.ts.map +1 -0
  45. package/dist/adapters/latex/nodes.js +93 -0
  46. package/dist/adapters/latex/nodes.js.map +1 -0
  47. package/dist/adapters/latex/outline.d.ts +10 -0
  48. package/dist/adapters/latex/outline.d.ts.map +1 -0
  49. package/dist/adapters/latex/outline.js +39 -0
  50. package/dist/adapters/latex/outline.js.map +1 -0
  51. package/dist/adapters/latex/parse.d.ts +42 -0
  52. package/dist/adapters/latex/parse.d.ts.map +1 -0
  53. package/dist/adapters/latex/parse.js +94 -0
  54. package/dist/adapters/latex/parse.js.map +1 -0
  55. package/dist/adapters/latex/rendered.d.ts +10 -0
  56. package/dist/adapters/latex/rendered.d.ts.map +1 -0
  57. package/dist/adapters/latex/rendered.js +39 -0
  58. package/dist/adapters/latex/rendered.js.map +1 -0
  59. package/dist/adapters/memory/index.d.ts.map +1 -1
  60. package/dist/adapters/memory/index.js +8 -2
  61. package/dist/adapters/memory/index.js.map +1 -1
  62. package/dist/adapters/node/process.io.d.ts +6 -3
  63. package/dist/adapters/node/process.io.d.ts.map +1 -1
  64. package/dist/adapters/node/process.io.js.map +1 -1
  65. package/dist/adapters/node/workspace.io.d.ts.map +1 -1
  66. package/dist/adapters/node/workspace.io.js +4 -2
  67. package/dist/adapters/node/workspace.io.js.map +1 -1
  68. package/dist/adapters/pdfjs/fill.d.ts.map +1 -1
  69. package/dist/adapters/pdfjs/fill.js +12 -12
  70. package/dist/adapters/pdfjs/fill.js.map +1 -1
  71. package/dist/adapters/references/index.d.ts.map +1 -1
  72. package/dist/adapters/references/index.js +4 -2
  73. package/dist/adapters/references/index.js.map +1 -1
  74. package/dist/build-engine.d.ts +1 -1
  75. package/dist/build-engine.d.ts.map +1 -1
  76. package/dist/build-engine.js +8 -1
  77. package/dist/build-engine.js.map +1 -1
  78. package/dist/build.d.ts +2 -3
  79. package/dist/build.d.ts.map +1 -1
  80. package/dist/build.js +9 -10
  81. package/dist/build.js.map +1 -1
  82. package/dist/caller-path.d.ts +3 -0
  83. package/dist/caller-path.d.ts.map +1 -0
  84. package/dist/caller-path.js +13 -0
  85. package/dist/caller-path.js.map +1 -0
  86. package/dist/cli.d.ts +10 -17
  87. package/dist/cli.d.ts.map +1 -1
  88. package/dist/cli.js +91 -17
  89. package/dist/cli.js.map +1 -1
  90. package/dist/doctor.d.ts +12 -3
  91. package/dist/doctor.d.ts.map +1 -1
  92. package/dist/doctor.js +2 -1
  93. package/dist/doctor.js.map +1 -1
  94. package/dist/domain/paths.d.ts +8 -2
  95. package/dist/domain/paths.d.ts.map +1 -1
  96. package/dist/domain/paths.js +12 -1
  97. package/dist/domain/paths.js.map +1 -1
  98. package/dist/domain/ready.d.ts +24 -0
  99. package/dist/domain/ready.d.ts.map +1 -0
  100. package/dist/domain/ready.js +11 -0
  101. package/dist/domain/ready.js.map +1 -0
  102. package/dist/domain/record.d.ts +8 -0
  103. package/dist/domain/record.d.ts.map +1 -0
  104. package/dist/domain/record.js +8 -0
  105. package/dist/domain/record.js.map +1 -0
  106. package/dist/domain/sha256.d.ts.map +1 -1
  107. package/dist/domain/sha256.js +4 -2
  108. package/dist/domain/sha256.js.map +1 -1
  109. package/dist/domain/tex-document.d.ts +84 -0
  110. package/dist/domain/tex-document.d.ts.map +1 -0
  111. package/dist/domain/tex-document.js +43 -0
  112. package/dist/domain/tex-document.js.map +1 -0
  113. package/dist/engine.d.ts +23 -22
  114. package/dist/engine.d.ts.map +1 -1
  115. package/dist/engine.js.map +1 -1
  116. package/dist/eslint-rules/bib-reachable-entry.d.ts +1 -1
  117. package/dist/eslint-rules/bib-reachable-entry.js.map +1 -1
  118. package/dist/eslint-rules/install-path-literals.d.ts +2 -2
  119. package/dist/eslint-rules/install-path-literals.js.map +1 -1
  120. package/dist/eslint-rules/paper-research-question.d.ts +1 -1
  121. package/dist/eslint-rules/paper-research-question.js.map +1 -1
  122. package/dist/eslint-rules/paper-stages.d.ts +2 -2
  123. package/dist/eslint-rules/paper-stages.js.map +1 -1
  124. package/dist/eslint-rules/paper-typography.d.ts +6 -6
  125. package/dist/eslint-rules/paper-typography.js.map +1 -1
  126. package/dist/eslint-rules/pdf-last-page-balance.d.ts +1 -1
  127. package/dist/eslint-rules/pdf-last-page-balance.js.map +1 -1
  128. package/dist/eslint-rules/review-frontmatter.d.ts +2 -2
  129. package/dist/eslint-rules/review-frontmatter.js.map +1 -1
  130. package/dist/eslint-rules/sibling-frontmatter.d.ts +1 -1
  131. package/dist/eslint-rules/sibling-frontmatter.d.ts.map +1 -1
  132. package/dist/eslint-rules/temp-root-realpath.d.ts +1 -1
  133. package/dist/eslint-rules/temp-root-realpath.js.map +1 -1
  134. package/dist/eslint-rules/tex-build.d.ts +2 -2
  135. package/dist/eslint-rules/tex-build.js.map +1 -1
  136. package/dist/facts-file.d.ts.map +1 -1
  137. package/dist/facts-file.js +31 -28
  138. package/dist/facts-file.js.map +1 -1
  139. package/dist/hooks-settings.d.ts.map +1 -1
  140. package/dist/hooks-settings.js +17 -2
  141. package/dist/hooks-settings.js.map +1 -1
  142. package/dist/init.d.ts +3 -4
  143. package/dist/init.d.ts.map +1 -1
  144. package/dist/init.js +18 -8
  145. package/dist/init.js.map +1 -1
  146. package/dist/lib/paper-config.d.ts.map +1 -1
  147. package/dist/lib/paper-config.js +1 -5
  148. package/dist/lib/paper-config.js.map +1 -1
  149. package/dist/new-paper.d.ts +5 -0
  150. package/dist/new-paper.d.ts.map +1 -1
  151. package/dist/new-paper.js +8 -1
  152. package/dist/new-paper.js.map +1 -1
  153. package/dist/package-dirs.d.ts +5 -0
  154. package/dist/package-dirs.d.ts.map +1 -0
  155. package/dist/package-dirs.js +14 -0
  156. package/dist/package-dirs.js.map +1 -0
  157. package/dist/paper-files.d.ts +4 -4
  158. package/dist/paper-files.d.ts.map +1 -1
  159. package/dist/paper-files.js +6 -6
  160. package/dist/paper-files.js.map +1 -1
  161. package/dist/paper-settings.js +3 -3
  162. package/dist/paper-settings.js.map +1 -1
  163. package/dist/pdf-facts.d.ts +51 -1
  164. package/dist/pdf-facts.d.ts.map +1 -1
  165. package/dist/pdf-facts.js +23 -15
  166. package/dist/pdf-facts.js.map +1 -1
  167. package/dist/pdf-geometry.d.ts.map +1 -1
  168. package/dist/pdf-geometry.js +2 -3
  169. package/dist/pdf-geometry.js.map +1 -1
  170. package/dist/ports/latex.d.ts +24 -0
  171. package/dist/ports/latex.d.ts.map +1 -0
  172. package/dist/ports/latex.js +2 -0
  173. package/dist/ports/latex.js.map +1 -0
  174. package/dist/ports/tool-installer.d.ts +2 -14
  175. package/dist/ports/tool-installer.d.ts.map +1 -1
  176. package/dist/presets.d.ts +15 -2
  177. package/dist/presets.d.ts.map +1 -1
  178. package/dist/presets.js +35 -10
  179. package/dist/presets.js.map +1 -1
  180. package/dist/references.d.ts.map +1 -1
  181. package/dist/references.js +4 -4
  182. package/dist/references.js.map +1 -1
  183. package/dist/rules-config.d.ts +26 -1
  184. package/dist/rules-config.d.ts.map +1 -1
  185. package/dist/rules-config.js +9 -9
  186. package/dist/rules-config.js.map +1 -1
  187. package/dist/tex-requirements.d.ts +15 -0
  188. package/dist/tex-requirements.d.ts.map +1 -1
  189. package/dist/tex-requirements.js +28 -10
  190. package/dist/tex-requirements.js.map +1 -1
  191. package/dist/tex-venue-rules.d.ts +83 -0
  192. package/dist/tex-venue-rules.d.ts.map +1 -0
  193. package/dist/tex-venue-rules.js +254 -0
  194. package/dist/tex-venue-rules.js.map +1 -0
  195. package/dist/types.d.ts +5 -3
  196. package/dist/types.d.ts.map +1 -1
  197. package/dist/venue-rules.d.ts +1 -1
  198. package/dist/venue-rules.d.ts.map +1 -1
  199. package/dist/venue-rules.js +20 -7
  200. package/dist/venue-rules.js.map +1 -1
  201. package/docs/configuration.md +5 -4
  202. package/docs/e2e.md +69 -40
  203. package/docs/install.md +1 -1
  204. package/docs/prior-art/README.md +1 -1
  205. package/docs/prior-art/package-location.md +5 -5
  206. package/docs/prior-art/repro/README.md +1 -1
  207. package/docs/prior-art/test-tooling.md +21 -10
  208. package/docs/rules/tex/required-section.md +73 -0
  209. package/docs/rules/tex/template.md +62 -0
  210. package/docs/rules/tex/venue-leftover.md +71 -0
  211. package/docs/rules.md +70 -48
  212. package/docs/testing.md +17 -4
  213. package/docs/texlive-install-decision.md +1 -1
  214. package/docs/toolchain.md +2 -2
  215. package/eslint-rules/bib-reachable-entry.ts +1 -1
  216. package/eslint-rules/install-path-literals.ts +2 -2
  217. package/eslint-rules/paper-research-question.ts +1 -1
  218. package/eslint-rules/paper-stages.ts +2 -2
  219. package/eslint-rules/paper-typography.ts +2 -2
  220. package/eslint-rules/pdf-last-page-balance.ts +1 -1
  221. package/eslint-rules/review-frontmatter.ts +1 -1
  222. package/eslint-rules/temp-root-realpath.ts +1 -1
  223. package/eslint-rules/tex-build.ts +2 -2
  224. package/eslint.config.mjs +162 -15
  225. package/fixtures/build-e2e/acmart/expect.json +26 -0
  226. package/fixtures/build-e2e/aidc/PIPELINE-STATUS.md +3 -0
  227. package/fixtures/build-e2e/aidc/expect.json +14 -0
  228. package/fixtures/build-e2e/aidc/paper.tex +46 -0
  229. package/fixtures/build-e2e/aidc/paperlint.json +1 -0
  230. package/fixtures/build-e2e/broken/expect.json +11 -0
  231. package/fixtures/build-e2e/cite/expect.json +27 -0
  232. package/fixtures/build-e2e/empty/expect.json +12 -0
  233. package/fixtures/build-e2e/fallback/expect.json +53 -0
  234. package/fixtures/build-e2e/guards/expect.json +6 -0
  235. package/fixtures/build-e2e/no-source/expect.json +10 -0
  236. package/fixtures/build-e2e/unbalanced/expect.json +60 -0
  237. package/fixtures/install-path-literals/clean.fixture.mjs +1 -1
  238. package/fixtures/install-path-literals/clean.md +2 -2
  239. package/fixtures/install-path-literals/defect.fixture.mjs +1 -1
  240. package/fixtures/install-path-literals/defect.md +2 -2
  241. package/fixtures/pdf-facts/README.md +1 -1
  242. package/fixtures/real-markdown-paper/baseline.ts +1 -1
  243. package/fixtures/render-paper/build-clean.sh +3 -5
  244. package/hooks/paper-status-gates.sh +9 -19
  245. package/lib/paper-config.harness.mjs +1 -1
  246. package/lib/paper-config.ts +1 -5
  247. package/lib/skill-checks.mjs +24 -381
  248. package/lib/skill-checks.test.mjs +63 -115
  249. package/lib/skill-corpus.mjs +10 -251
  250. package/lib/skill-corpus.test.mjs +2 -13
  251. package/lib/skill-trigger-cases.mjs +1 -1
  252. package/package.json +13 -2
  253. package/{skills/submit-paper/references/venues → presets}/acm-sigconf.jsonc +4 -1
  254. package/{skills/submit-paper/references/venues → presets}/agenticdev.jsonc +4 -0
  255. package/presets/aidc.jsonc +73 -0
  256. package/{skills/submit-paper/references/venues → presets}/aisec.jsonc +4 -0
  257. package/presets/ieee-conference.jsonc +66 -0
  258. package/{skills/submit-paper/references/venues → presets}/realm.jsonc +8 -1
  259. package/{skills/submit-paper/references/venues → presets/tex}/paper-guards.tex +1 -1
  260. package/{skills/submit-paper/references/venues → presets}/venue-profile.schema.json +30 -2
  261. package/scripts/check.harness.ts +64 -10
  262. package/scripts/check.test.ts +60 -13
  263. package/scripts/check.ts +83 -30
  264. package/scripts/coverage-config.test.ts +0 -1
  265. package/scripts/harness-api.frozen.json +0 -1
  266. package/scripts/test.test.ts +1 -1
  267. package/scripts/test.ts +1 -1
  268. package/skills/analyze-sibling-paper/SKILL.md +1 -39
  269. package/skills/analyze-sibling-paper/SKILL.md.spec.ts +0 -38
  270. package/skills/argument-arc/SKILL.md +2 -36
  271. package/skills/argument-arc/SKILL.md.spec.ts +1 -43
  272. package/skills/build-benchmark/SKILL.md +1 -41
  273. package/skills/build-benchmark/SKILL.md.spec.ts +0 -40
  274. package/skills/camera-ready/SKILL.md +1 -34
  275. package/skills/camera-ready/SKILL.md.spec.ts +0 -33
  276. package/skills/cold-read-diff/SKILL.md +1 -35
  277. package/skills/cold-read-diff/SKILL.md.spec.ts +0 -34
  278. package/skills/draft-paper/SKILL.md +2 -41
  279. package/skills/draft-paper/SKILL.md.spec.ts +1 -50
  280. package/skills/extend-paper/SKILL.md +2 -33
  281. package/skills/extend-paper/SKILL.md.spec.ts +1 -42
  282. package/skills/find-venue/SKILL.md +2 -38
  283. package/skills/find-venue/SKILL.md.spec.ts +1 -47
  284. package/skills/grade-paper-writing/SKILL.md +3 -38
  285. package/skills/grade-paper-writing/SKILL.md.spec.ts +2 -47
  286. package/skills/grade-paper-writing/prose-lint.mjs +2 -3
  287. package/skills/grade-paper-writing/prose-lint.test.ts +1 -1
  288. package/skills/harden-paper/SKILL.md +1 -39
  289. package/skills/harden-paper/SKILL.md.spec.ts +0 -38
  290. package/skills/map-prior-work/SKILL.md +1 -35
  291. package/skills/map-prior-work/SKILL.md.spec.ts +0 -34
  292. package/skills/osf-artifact-upload/osf-artifact-upload.harness.ts +3 -4
  293. package/skills/paper-adversarial-review/SKILL.md +2 -35
  294. package/skills/paper-adversarial-review/SKILL.md.spec.ts +1 -43
  295. package/skills/paper-pipeline/PIPELINE-MAP.md +2 -2
  296. package/skills/paper-pipeline/SKILL.md +3 -29
  297. package/skills/paper-pipeline/SKILL.md.spec.ts +2 -28
  298. package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +1 -1
  299. package/skills/paper-pipeline/paper-pipeline.harness.mjs +25 -40
  300. package/skills/paper-pipeline/references/acceptance-gate.md +1 -6
  301. package/skills/paper-pipeline/references/pipeline-status-template.md +8 -35
  302. package/skills/paper-pipeline/scripts/README.md +30 -225
  303. package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +4 -8
  304. package/skills/paper-pipeline/scripts/consumer.d.mts +0 -2
  305. package/skills/paper-pipeline/scripts/consumer.harness.mjs +6 -104
  306. package/skills/paper-pipeline/scripts/consumer.mjs +24 -119
  307. package/skills/paper-pipeline/scripts/generated-code.harness.mjs +2 -3
  308. package/skills/paper-pipeline/scripts/population-map.harness.mjs +3 -3
  309. package/skills/paper-status/SKILL.md +2 -42
  310. package/skills/paper-status/SKILL.md.spec.ts +1 -41
  311. package/skills/pc-panel-review/SKILL.md +2 -46
  312. package/skills/pc-panel-review/SKILL.md.spec.ts +1 -45
  313. package/skills/plan-paper-timeline/SKILL.md +2 -36
  314. package/skills/plan-paper-timeline/SKILL.md.spec.ts +0 -36
  315. package/skills/render-paper/SKILL.md +2 -38
  316. package/skills/render-paper/SKILL.md.spec.ts +1 -37
  317. package/skills/render-paper/check-render.sh +16 -63
  318. package/skills/render-paper/render-paper.harness.mjs +17 -59
  319. package/skills/research-ideate/SKILL.md +2 -39
  320. package/skills/research-ideate/SKILL.md.spec.ts +1 -47
  321. package/skills/study-accepted-papers/SKILL.md +1 -34
  322. package/skills/study-accepted-papers/SKILL.md.spec.ts +0 -33
  323. package/skills/submit-paper/SKILL.md +1 -33
  324. package/skills/submit-paper/SKILL.md.spec.ts +0 -32
  325. package/skills/sweep-design-space/SKILL.md +1 -38
  326. package/skills/sweep-design-space/SKILL.md.spec.ts +0 -37
  327. package/skills/tighten-paper/SKILL.md +2 -39
  328. package/skills/tighten-paper/SKILL.md.spec.ts +1 -47
  329. package/skills/tighten-paper/structure.mjs +4 -11
  330. package/skills/verify-citations/SKILL.md +2 -38
  331. package/skills/verify-citations/SKILL.md.spec.ts +1 -37
  332. package/src/adapters/banal/failure.ts +9 -10
  333. package/src/adapters/banal/index.test.ts +3 -3
  334. package/src/adapters/banal/index.ts +8 -10
  335. package/src/adapters/banal/install.ts +9 -4
  336. package/src/adapters/banal/invocation.test.ts +20 -8
  337. package/src/adapters/banal/invocation.ts +24 -9
  338. package/src/adapters/banal/locate.test.ts +2 -2
  339. package/src/adapters/banal/locate.ts +27 -21
  340. package/src/adapters/banal/output.test.ts +1 -1
  341. package/src/adapters/banal/run.test.ts +3 -3
  342. package/src/adapters/banal/run.ts +16 -10
  343. package/src/adapters/banal/settings.ts +2 -2
  344. package/src/adapters/banal/xml.test.ts +1 -1
  345. package/src/adapters/curl/download.test.ts +4 -4
  346. package/src/adapters/latex/document-class.ts +147 -0
  347. package/src/adapters/latex/hidden.ts +70 -0
  348. package/src/adapters/latex/index.test.ts +361 -0
  349. package/src/adapters/latex/index.ts +55 -0
  350. package/src/adapters/latex/nodes.ts +141 -0
  351. package/src/adapters/latex/outline.ts +64 -0
  352. package/src/adapters/latex/parse.ts +136 -0
  353. package/src/adapters/latex/rendered.ts +59 -0
  354. package/src/adapters/memory/index.ts +4 -3
  355. package/src/adapters/node/files.test.ts +5 -3
  356. package/src/adapters/node/process.io.ts +9 -2
  357. package/src/adapters/node/process.test.ts +4 -4
  358. package/src/adapters/node/workspace.io.ts +9 -4
  359. package/src/adapters/pdfjs/fill.test.ts +1 -1
  360. package/src/adapters/pdfjs/fill.ts +18 -10
  361. package/src/adapters/references/index.ts +7 -2
  362. package/src/build-engine.test.ts +11 -6
  363. package/src/build-engine.ts +10 -5
  364. package/src/build.harness.mjs +5 -7
  365. package/src/build.test.ts +10 -11
  366. package/src/build.ts +12 -13
  367. package/src/caller-path.ts +13 -0
  368. package/src/cli.harness.mjs +1 -3
  369. package/src/cli.test.ts +21 -16
  370. package/src/cli.ts +115 -33
  371. package/src/doctor.test.ts +1 -1
  372. package/src/doctor.ts +14 -5
  373. package/src/domain/paths.test.ts +15 -0
  374. package/src/domain/paths.ts +20 -1
  375. package/src/domain/ready.ts +31 -0
  376. package/src/domain/record.test.ts +20 -0
  377. package/src/domain/record.ts +12 -0
  378. package/src/domain/sha256.ts +5 -3
  379. package/src/domain/tex-document.test.ts +80 -0
  380. package/src/domain/tex-document.ts +110 -0
  381. package/src/engine.harness.mjs +1 -1
  382. package/src/engine.ts +30 -2
  383. package/src/facts-file.test.ts +4 -7
  384. package/src/facts-file.ts +45 -30
  385. package/src/hooks-settings.harness.ts +17 -0
  386. package/src/hooks-settings.ts +28 -4
  387. package/src/init.test.ts +25 -5
  388. package/src/init.ts +26 -12
  389. package/src/latex-loop.harness.ts +1 -1
  390. package/src/new-paper.harness.mjs +7 -23
  391. package/src/new-paper.test.ts +28 -3
  392. package/src/new-paper.ts +15 -3
  393. package/src/package-dirs.ts +17 -0
  394. package/src/paper-files.test.ts +22 -19
  395. package/src/paper-files.ts +16 -27
  396. package/src/paper-settings-commands.test.ts +6 -1
  397. package/src/paper-settings.test.ts +2 -2
  398. package/src/paper-settings.ts +5 -5
  399. package/src/pdf-facts.test.ts +16 -5
  400. package/src/pdf-facts.ts +79 -22
  401. package/src/pdf-geometry.ts +5 -3
  402. package/src/ports/latex.ts +29 -0
  403. package/src/ports/tool-installer.ts +2 -17
  404. package/src/presets.test.ts +65 -4
  405. package/src/presets.ts +65 -28
  406. package/src/references.test.ts +25 -19
  407. package/src/references.ts +4 -5
  408. package/src/rule-cases.test.ts +474 -0
  409. package/src/rule-docs.test.ts +159 -0
  410. package/src/rules-config.ts +40 -11
  411. package/src/tex-requirements.harness.ts +13 -23
  412. package/src/tex-requirements.test.ts +21 -0
  413. package/src/tex-requirements.ts +64 -14
  414. package/src/tex-venue-rules.test.ts +659 -0
  415. package/src/tex-venue-rules.ts +429 -0
  416. package/src/toolchain.harness.mjs +1 -1
  417. package/src/toolchain.test.ts +9 -5
  418. package/src/toolchain.ts +2 -2
  419. package/src/types.ts +5 -3
  420. package/src/venue-rules.test.ts +104 -8
  421. package/src/venue-rules.ts +22 -12
  422. package/templates/paper/paper.tex +2 -1
  423. package/skills/paper-pipeline/scripts/announce.mjs +0 -67
  424. package/skills/paper-pipeline/scripts/announce.test.mjs +0 -102
  425. package/skills/paper-pipeline/scripts/fixtures/clean/PIPELINE-STATUS.md +0 -51
  426. package/skills/paper-pipeline/scripts/fixtures/dirty/PIPELINE-STATUS.md +0 -52
  427. package/skills/paper-pipeline/scripts/fixtures/dirty/paper.md +0 -6
  428. package/skills/paper-pipeline/scripts/fixtures/scorecard.ts +0 -100
  429. package/skills/paper-pipeline/scripts/ledger.cli.test.mjs +0 -302
  430. package/skills/paper-pipeline/scripts/ledger.mjs +0 -623
  431. package/skills/paper-pipeline/scripts/ledger.test.mjs +0 -254
  432. package/skills/paper-pipeline/scripts/pipeline-check.cli.test.mjs +0 -474
  433. package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +0 -328
  434. package/skills/paper-pipeline/scripts/pipeline-check.mjs +0 -750
  435. package/skills/paper-pipeline/scripts/pipeline-edges.mjs +0 -169
  436. package/skills/paper-pipeline/scripts/pipeline-edges.test.mjs +0 -73
  437. package/skills/paper-pipeline/scripts/run-mechanical.mjs +0 -648
  438. package/skills/paper-pipeline/scripts/run-mechanical.test.mjs +0 -468
  439. package/skills/paper-pipeline/scripts/status.mjs +0 -295
  440. package/skills/paper-pipeline/scripts/status.test.mjs +0 -232
  441. /package/{skills/submit-paper/references/venues → presets/tex}/agenticdev.tex +0 -0
  442. /package/{skills/submit-paper/references/venues → presets}/tex-base.jsonc +0 -0
@@ -1,468 +0,0 @@
1
- /**
2
- * run-mechanical.mjs — every row against a fake consumer whose checkers are stand-ins with a
3
- * scripted answer, so each way of READING a result (`exit`, `flags`, `json`, `eslint`) and each way
4
- * of NOT getting one (input missing, checker not installed, output that is not what the row reads)
5
- * is driven on purpose. What is compared is what the run tells a reader (stdout) and what it
6
- * records (the ledger rows, minus hashes and timestamps).
7
- *
8
- * The consumer root and the ledger are set BEFORE import: the module resolves both at load time.
9
- */
10
- import assert from "node:assert/strict";
11
- import {
12
- chmodSync,
13
- existsSync,
14
- readFileSync,
15
- rmSync,
16
- writeFileSync,
17
- } from "node:fs";
18
- import { dirname, join } from "node:path";
19
- import { fileURLToPath } from "node:url";
20
- import { afterEach, test, vi } from "vitest";
21
- import { runNode, useTempDir, writeTree } from "../../../test/support.ts";
22
-
23
- const HERE = dirname(fileURLToPath(import.meta.url));
24
- const SCRIPT = join(HERE, "run-mechanical.mjs");
25
- const root = useTempDir("run-mechanical-");
26
- const consumer = join(root, "consumer");
27
- const LEDGER = join(root, "runs.jsonl");
28
-
29
- /** A node stand-in that prints what FAKE_<NAME> says and exits with FAKE_<NAME>_EXIT. */
30
- const nodeFake = (name) =>
31
- `const out = process.env.FAKE_${name} ?? "";\n` +
32
- `process.stdout.write(out);\n` +
33
- `process.exit(Number(process.env.FAKE_${name}_EXIT ?? 0));\n`;
34
- /** The same, in python — the citation checkers and the repro scripts are python. */
35
- const pyFake = (name) =>
36
- `import os, sys\n` +
37
- `sys.stdout.write(os.environ.get("FAKE_${name}", ""))\n` +
38
- `sys.exit(int(os.environ.get("FAKE_${name}_EXIT", "0")))\n`;
39
-
40
- writeTree(consumer, {
41
- "paperlint.json": JSON.stringify({ citeChecks: "cite" }),
42
- "cite/report-submission.py": pyFake("REPORT"),
43
- "cite/uncited_refs.py": pyFake("UNCITED"),
44
- "eslint-rules/paper-structure.mjs":
45
- 'export default { "section-lead": {}, "subsection-size": {} };\n',
46
- "node_modules/.bin/eslint": `#!${process.execPath}\n${nodeFake("ESLINT")}`,
47
- // The stand-in hands over to the REAL prose-lint when asked, so the count the ledger records can
48
- // be checked against what the real checker printed.
49
- ".claude/skills/grade-paper-writing/prose-lint.mjs":
50
- `if (process.env.FAKE_PROSE_REAL) await import(${JSON.stringify(
51
- join(HERE, "..", "..", "grade-paper-writing", "prose-lint.mjs"),
52
- )});\n` + nodeFake("PROSE"),
53
- ".claude/skills/verify-citations/scripts/verify-cites.test.mjs":
54
- nodeFake("CITES"),
55
- // Paper A: a submission — a venue limit, a build, every repro checker present.
56
- "papers/a/paper.md": "# A\n\nA plain paragraph.\n",
57
- "papers/a/.body-limit": "8\n",
58
- "papers/a/build/paper.log": "log\n",
59
- "papers/a/build/paper.aux": "aux\n",
60
- "papers/a/build/acl_latex.pdf": "%PDF\n",
61
- "papers/a/repro/arm_permutation.py": pyFake("ARM"),
62
- "papers/a/repro/delivered_pdf.py": pyFake("DELIVERED"),
63
- "papers/a/repro/textidote_check.py": pyFake("TEXTIDOTE"),
64
- "papers/a/rounds/.keep": "",
65
- // Paper B: built in place, no venue, nothing optional.
66
- "papers/b/paper.md": "# B\n\nAnother paragraph.\n",
67
- // Paper C: one figure whose caption breaks BOTH caption limits — two prose-lint findings.
68
- "papers/c/paper.md": "# C\n\nA paragraph of prose.\n",
69
- "papers/c/figures/f.tex": `\\caption{${"word ".repeat(110)}}`,
70
- "papers/b/paper.log": "log\n",
71
- "textidote.jar": "",
72
- });
73
- chmodSync(join(consumer, "node_modules/.bin/eslint"), 0o755);
74
-
75
- process.env.CLAUDE_PROJECT_DIR = consumer;
76
- process.env.PIPELINE_LEDGER = LEDGER;
77
- process.env.TEXTIDOTE_JAR = join(consumer, "textidote.jar");
78
- const { GATES, main } = await import("./run-mechanical.mjs");
79
-
80
- const ESLINT_FINDINGS = JSON.stringify([
81
- {
82
- filePath: "paper.md",
83
- messages: [
84
- {
85
- ruleId: "paper/section-lead",
86
- severity: 2,
87
- line: 1,
88
- column: 1,
89
- message: "no lead",
90
- },
91
- {
92
- ruleId: "paper/subsection-size",
93
- severity: 1,
94
- line: 3,
95
- column: 1,
96
- message: "long",
97
- },
98
- {
99
- ruleId: "paper/other-row",
100
- severity: 2,
101
- line: 4,
102
- column: 1,
103
- message: "not ours",
104
- },
105
- ],
106
- },
107
- ]);
108
-
109
- /**
110
- * Run `main` on a paper with the given stand-in answers. Returns the exit code, the full stdout,
111
- * the closing summary (the lines after the last blank line — what a reader acts on), and the
112
- * ledger rows written, one compact string each: `skill/check KIND reason|findings [blocking]`.
113
- */
114
- function run(paper, fakes = {}) {
115
- rmSync(LEDGER, { force: true });
116
- const saved = { ...process.env };
117
- Object.assign(process.env, fakes);
118
- const lines = [];
119
- const log = vi.spyOn(console, "log").mockImplementation((...a) => {
120
- lines.push(a.join(" "));
121
- });
122
- const err = vi.spyOn(console, "error").mockImplementation((...a) => {
123
- lines.push(`stderr: ${a.join(" ")}`);
124
- });
125
- let code;
126
- try {
127
- code = main(["node", SCRIPT, paper]);
128
- } finally {
129
- log.mockRestore();
130
- err.mockRestore();
131
- process.env = saved;
132
- }
133
- const rows = existsSync(LEDGER)
134
- ? readFileSync(LEDGER, "utf8")
135
- .trim()
136
- .split("\n")
137
- .map((l) => JSON.parse(l))
138
- .map(
139
- ({ skill, check, kind, reason, findings, blocking }) =>
140
- `${skill}/${check} ${kind} ${kind === "FINDING" ? findings : reason}` +
141
- (blocking ? " blocking" : ""),
142
- )
143
- : [];
144
- const out = lines.join("\n").replaceAll(root, "<root>");
145
- const summary = out.trimEnd().split("\n\n").pop().split("\n");
146
- return { code, out, summary, rows };
147
- }
148
-
149
- afterEach(() => rmSync(LEDGER, { force: true }));
150
-
151
- const NOTE =
152
- " Judgement checks report and never fail the run; read them, and never grep this for green.";
153
-
154
- test("a directory with no paper.md: exit 2, named", () => {
155
- const { code, out, rows } = run(join(root, "nowhere"));
156
- assert.deepEqual(
157
- { code, out, rows },
158
- { code: 2, out: "stderr: no paper.md in <root>/nowhere", rows: [] },
159
- );
160
- });
161
-
162
- test("two rows sharing a key are refused before anything runs", () => {
163
- GATES.push({ ...GATES[0] });
164
- try {
165
- const { code, out, rows } = run(join(consumer, "papers", "a"));
166
- assert.deepEqual(
167
- { code, out, rows },
168
- {
169
- code: 2,
170
- out: 'stderr: two checks share the row key "render-paper/report-submission" — they would take turns being the answer',
171
- rows: [],
172
- },
173
- );
174
- } finally {
175
- GATES.pop();
176
- }
177
- });
178
-
179
- test("paper A, every checker answering with findings: exit 1 on the blocking FACT findings", () => {
180
- const { code, summary, rows } = run(join(consumer, "papers", "a"), {
181
- FAKE_REPORT: "overfull box\nunresolved ref\n",
182
- FAKE_REPORT_EXIT: "1",
183
- FAKE_ESLINT: ESLINT_FINDINGS,
184
- FAKE_PROSE: "✍️ prose-lint — paper.md:\n a long caption\n",
185
- FAKE_PROSE_EXIT: "1",
186
- FAKE_CITES: "1 fabricated entry\n",
187
- FAKE_CITES_EXIT: "1",
188
- FAKE_ARM: JSON.stringify([{ q: 1 }, { q: 2 }]),
189
- FAKE_DELIVERED: "not json",
190
- FAKE_TEXTIDOTE: "[]",
191
- FAKE_UNCITED: JSON.stringify(["smith2020"]),
192
- });
193
- assert.deepEqual(
194
- { code, summary, rows },
195
- {
196
- code: 1,
197
- summary: ["🔴 a FACT check found something — see above", NOTE],
198
- rows: [
199
- "render-paper/report-submission FINDING 2 blocking",
200
- "render-paper/report-submission-citations ABSTAINED input-missing",
201
- // Two of the three messages are this row's rules; the third belongs to another row.
202
- "tighten-paper/structure FINDING 2 blocking",
203
- // No stated count: the stand-in's two lines are counted.
204
- "grade-paper-writing/prose-lint FINDING 2",
205
- "verify-citations/verify-cites FINDING 1 blocking",
206
- "harden-paper/artifact-coverage ABSTAINED no-witness",
207
- "build-benchmark/check-provenance ABSTAINED no-witness",
208
- "build-benchmark/arm-permutation FINDING 2",
209
- "build-benchmark/delivered-pdf ABSTAINED crashed",
210
- "build-benchmark/generated-code ABSTAINED no-witness",
211
- "grade-paper-writing/textidote ABSTAINED no-witness",
212
- "verify-citations/uncited-refs FINDING 1",
213
- "draft-paper/population-map ABSTAINED no-witness",
214
- "tighten-paper/round-diff FINDING 1",
215
- ],
216
- },
217
- );
218
- });
219
-
220
- test("a finding's lines reach the reader, capped at twelve", () => {
221
- const { out } = run(join(consumer, "papers", "a"), {
222
- FAKE_ESLINT: "[]",
223
- FAKE_UNCITED: JSON.stringify(Array.from({ length: 20 }, (_, i) => `e${i}`)),
224
- });
225
- const block = out
226
- .split("\n\n")
227
- .find((b) => b.includes("uncited-refs — 20 finding(s)"));
228
- assert.deepEqual(block.split("\n"), [
229
- "🟠 verify-citations/uncited-refs — 20 finding(s) → reviews/mechanical/verify-citations--uncited-refs.md",
230
- " a bibliography entry no \\cite in the paper points at",
231
- " [",
232
- ...Array.from({ length: 11 }, (_, i) => ` "e${i}",`),
233
- ]);
234
- });
235
-
236
- test("a FACT check whose checker crashed makes the run exit 2, and the summary says so — not green", () => {
237
- const { code, summary, rows } = run(join(consumer, "papers", "a"), {
238
- FAKE_ESLINT: "Oops! Something went wrong",
239
- FAKE_PROSE: "✍️ prose-lint — paper.md:\n",
240
- FAKE_UNCITED: "{}",
241
- });
242
- assert.deepEqual(
243
- { code, summary, rows },
244
- {
245
- code: 2,
246
- summary: [
247
- "🔴 1 FACT check(s) could not run: tighten-paper/structure — what they check is UNKNOWN, not clean",
248
- NOTE,
249
- ],
250
- rows: [
251
- "render-paper/report-submission ABSTAINED no-witness",
252
- "render-paper/report-submission-citations ABSTAINED input-missing",
253
- // eslint printed something that is not JSON.
254
- "tighten-paper/structure ABSTAINED crashed",
255
- // A flags header alone is not a finding.
256
- "grade-paper-writing/prose-lint ABSTAINED no-witness",
257
- "verify-citations/verify-cites ABSTAINED no-witness",
258
- "harden-paper/artifact-coverage ABSTAINED no-witness",
259
- "build-benchmark/check-provenance ABSTAINED no-witness",
260
- // Empty output where a JSON array is documented.
261
- "build-benchmark/arm-permutation ABSTAINED crashed",
262
- "build-benchmark/delivered-pdf ABSTAINED crashed",
263
- "build-benchmark/generated-code ABSTAINED no-witness",
264
- "grade-paper-writing/textidote ABSTAINED crashed",
265
- // A JSON object where an array is documented.
266
- "verify-citations/uncited-refs ABSTAINED crashed",
267
- "draft-paper/population-map ABSTAINED no-witness",
268
- "tighten-paper/round-diff FINDING 1",
269
- ],
270
- },
271
- );
272
- });
273
-
274
- // Three full runs of the script: 1.6 s alone, past vitest's 5 s default under c8 with the whole
275
- // suite in parallel (measured in `npm run check`), so the budget is stated here.
276
- test(
277
- "eslint that crashed on the file, and eslint that ignored it, are not clean runs",
278
- {
279
- timeout: 30_000,
280
- },
281
- () => {
282
- const fatal = JSON.stringify([
283
- {
284
- filePath: "p",
285
- messages: [{ ruleId: null, fatal: true, message: "Parsing error" }],
286
- },
287
- ]);
288
- const ignored = JSON.stringify([
289
- {
290
- filePath: "p",
291
- messages: [
292
- {
293
- ruleId: null,
294
- message: "File ignored because outside of base path.",
295
- },
296
- ],
297
- },
298
- ]);
299
- const structure = (fakes) =>
300
- run(join(consumer, "papers", "a"), fakes).rows.find((x) =>
301
- x.startsWith("tighten-paper/structure "),
302
- );
303
- assert.deepEqual(
304
- [
305
- structure({ FAKE_ESLINT: fatal }),
306
- structure({ FAKE_ESLINT: ignored }),
307
- structure({ FAKE_ESLINT: "[]" }),
308
- ],
309
- [
310
- "tighten-paper/structure ABSTAINED crashed",
311
- "tighten-paper/structure ABSTAINED input-missing",
312
- "tighten-paper/structure ABSTAINED no-witness",
313
- ],
314
- );
315
- },
316
- );
317
-
318
- test("paper B: every row whose input is absent abstains as input-missing; the rest judge", () => {
319
- const { code, summary, rows } = run(join(consumer, "papers", "b"), {
320
- FAKE_ESLINT: "[]",
321
- });
322
- assert.deepEqual(
323
- { code, summary, rows },
324
- {
325
- code: 0,
326
- summary: ["🟢 no FACT check recorded a blocking finding", NOTE],
327
- rows: [
328
- "render-paper/report-submission ABSTAINED input-missing",
329
- "render-paper/report-submission-citations ABSTAINED no-witness",
330
- "tighten-paper/structure ABSTAINED no-witness",
331
- "grade-paper-writing/prose-lint ABSTAINED no-witness",
332
- "verify-citations/verify-cites ABSTAINED no-witness",
333
- "harden-paper/artifact-coverage ABSTAINED no-witness",
334
- "build-benchmark/check-provenance ABSTAINED no-witness",
335
- "build-benchmark/arm-permutation ABSTAINED input-missing",
336
- "build-benchmark/delivered-pdf ABSTAINED input-missing",
337
- "build-benchmark/generated-code ABSTAINED no-witness",
338
- "grade-paper-writing/textidote ABSTAINED input-missing",
339
- "verify-citations/uncited-refs ABSTAINED input-missing",
340
- "draft-paper/population-map ABSTAINED no-witness",
341
- "tighten-paper/round-diff ABSTAINED input-missing",
342
- ],
343
- },
344
- );
345
- });
346
-
347
- test("no checker installed: every row that ran is `crashed`, never a finding, and the run is not green", () => {
348
- const { code, summary, rows } = run(join(consumer, "papers", "b"), {
349
- PATH: join(root, "empty-path"),
350
- });
351
- assert.deepEqual(
352
- { code, summary, rows },
353
- {
354
- code: 2,
355
- summary: [
356
- "🔴 3 FACT check(s) could not run: render-paper/report-submission-citations, tighten-paper/structure, verify-citations/verify-cites — what they check is UNKNOWN, not clean",
357
- NOTE,
358
- ],
359
- rows: [
360
- "render-paper/report-submission ABSTAINED input-missing",
361
- "render-paper/report-submission-citations ABSTAINED crashed",
362
- "tighten-paper/structure ABSTAINED crashed",
363
- "grade-paper-writing/prose-lint ABSTAINED crashed",
364
- "verify-citations/verify-cites ABSTAINED crashed",
365
- "harden-paper/artifact-coverage ABSTAINED crashed",
366
- "build-benchmark/check-provenance ABSTAINED crashed",
367
- "build-benchmark/arm-permutation ABSTAINED input-missing",
368
- "build-benchmark/delivered-pdf ABSTAINED input-missing",
369
- "build-benchmark/generated-code ABSTAINED crashed",
370
- "grade-paper-writing/textidote ABSTAINED input-missing",
371
- "verify-citations/uncited-refs ABSTAINED input-missing",
372
- "draft-paper/population-map ABSTAINED crashed",
373
- "tighten-paper/round-diff ABSTAINED input-missing",
374
- ],
375
- },
376
- );
377
- });
378
-
379
- test("as a process, in a consumer that declares nothing: no FACT check can run, and it says so", () => {
380
- const bare = join(root, "bare");
381
- writeTree(bare, { "papers/p/paper.md": "# P\n\nText.\n" });
382
- writeFileSync(join(bare, "package.json"), "{}\n");
383
- const r = runNode(SCRIPT, [join(bare, "papers", "p")], {
384
- env: {
385
- CLAUDE_PROJECT_DIR: bare,
386
- PIPELINE_LEDGER: join(root, "bare.jsonl"),
387
- },
388
- });
389
- assert.deepEqual(
390
- {
391
- status: r.status,
392
- stderr: r.stderr,
393
- summary: r.stdout.trimEnd().split("\n\n").pop().split("\n"),
394
- },
395
- {
396
- status: 0,
397
- stderr: "",
398
- summary: [
399
- "⬜ no FACT check ran — each one lacked its input, so nothing here was judged",
400
- NOTE,
401
- ],
402
- },
403
- );
404
- });
405
-
406
- test("the prose-lint row records the number of findings prose-lint found, not its line count", () => {
407
- const row = run(join(consumer, "papers", "c"), {
408
- FAKE_PROSE_REAL: "1",
409
- }).rows.find((x) => x.startsWith("grade-paper-writing/prose-lint "));
410
- assert.equal(row, "grade-paper-writing/prose-lint FINDING 2");
411
- });
412
-
413
- test("no directory argument means the current directory", () => {
414
- const { code, out, rows } = run(undefined);
415
- assert.deepEqual(
416
- { code, out, rows },
417
- { code: 2, out: `stderr: no paper.md in ${process.cwd()}`, rows: [] },
418
- );
419
- });
420
-
421
- test("a blocking finding with no output still gets a report a reader can open", () => {
422
- const r = run(join(consumer, "papers", "a"), { FAKE_REPORT_EXIT: "1" });
423
- const report = readFileSync(
424
- join(
425
- consumer,
426
- "papers",
427
- "a",
428
- "reviews",
429
- "mechanical",
430
- "render-paper--report-submission.md",
431
- ),
432
- "utf8",
433
- ).replace(/^created: .*$/m, "created: <date>");
434
- assert.deepEqual(
435
- { row: r.rows[0], report },
436
- {
437
- row: "render-paper/report-submission FINDING 1 blocking",
438
- report: [
439
- "---",
440
- 'title: "render-paper/report-submission — mechanical check output"',
441
- "created: <date>",
442
- "findings: 1",
443
- "---",
444
- "",
445
- "Written by `run-mechanical.mjs`. What this check looks at: body pages, overfull boxes, unresolved refs, dropped characters, anonymity, artifact URLs",
446
- "",
447
- "```",
448
- "(the check produced no output)",
449
- "```",
450
- "",
451
- ].join("\n"),
452
- },
453
- );
454
- });
455
-
456
- test("an empty $TEXTIDOTE_JAR is no declaration: the row looks at /opt/textidote, like the checker", () => {
457
- // The answer depends on this machine (some images ship the jar in /opt), so the expectation is
458
- // derived from the same fact the row reads rather than hard-coded.
459
- const atOpt = existsSync("/opt/textidote/textidote.jar");
460
- const row = run(join(consumer, "papers", "a"), {
461
- TEXTIDOTE_JAR: "",
462
- FAKE_TEXTIDOTE: "[]",
463
- }).rows.find((x) => x.startsWith("grade-paper-writing/textidote "));
464
- assert.equal(
465
- row,
466
- `grade-paper-writing/textidote ABSTAINED ${atOpt ? "no-witness" : "input-missing"}`,
467
- );
468
- });