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,72 @@
1
+ # Dependency updates by bot, not by hand.
2
+ #
3
+ # WHY A BOT AT ALL, not runtime version checks: an update IS AN ACTION,
4
+ # and the bot creates a pull request you can press. A terminal line
5
+ # saying "something newer exists" is not an action; it gets forgotten in a day. The local gate
6
+ # `npm ls vigiles` in the consumer solves a DIFFERENT problem — "installed does not match manifest" —
7
+ # and knows nothing about freshness against the registry: measurement 2026-09-16 — manifest `^27.1.4`
8
+ # with 27.1.6 and 27.2.0 in the registry gives RC=0, the range is satisfied.
9
+ #
10
+ # WHY THIS IS ALLOWED HERE, though the bot is off in a neighboring repository: that one is
11
+ # PRIVATE, and its runs ate the shared Actions quota. This one is
12
+ # public, free-tier minutes are free, so that objection does not apply here. If it becomes private,
13
+ # the rule changes with it.
14
+ #
15
+ # 🔴 WHAT THIS FILE DOES NOT GOVERN, and we have tripped on this: it controls only
16
+ # version updates. SECURITY updates are a separate mechanism and a separate
17
+ # REPOSITORY SETTING (Settings → Advanced Security), their frequency and
18
+ # grouping cannot be changed from here. If single PRs for indirect
19
+ # dependencies start appearing — look for the lever there, not in this file.
20
+ version: 2
21
+ updates:
22
+ - package-ecosystem: "npm"
23
+ directory: "/"
24
+ # MONTHLY. It used to be daily so that new vigiles releases arrived fast, but we bump
25
+ # vigiles by hand in the same pass that merges a vigiles PR, so the bot was only ever
26
+ # catching up with what we had already done. Once a month is the pace someone reads.
27
+ #
28
+ # 🔴 SCHEDULE IS NOT THE ONLY DELAY, and the second one is easy to miss: version
29
+ # updates have a COOLDOWN, defaulting to 3 days — "a new version is not
30
+ # considered for a version update until 3 days after its release". It is NOT
31
+ # removed deliberately: three days is protection against a just-revoked release, and
32
+ # we learn about our own versions before the bot anyway (vigiles is our package, bumped
33
+ # in the same pass where we merge PRs). Net window ~3-4 days.
34
+ #
35
+ # ⚠️ Publishing to the registry does not wake the bot — it runs on schedule only.
36
+ # For immediate check: Insights -> Dependency graph -> Dependabot -> Check for updates.
37
+ schedule:
38
+ interval: "monthly"
39
+ time: "06:00"
40
+ timezone: "Asia/Almaty"
41
+ # 🔴 OUR OWN PACKAGE IS EXEMPT FROM COOLDOWN, answering a specific objection:
42
+ # we can release multiple vigiles versions in a day, and a three-day
43
+ # default wait would mean the bot offers yesterday's.
44
+ # The wait exists as protection against a just-revoked release — for
45
+ # SOMEONE ELSE'S package that is sound; for our own, it is pointless: we will revoke it ourselves.
46
+ cooldown:
47
+ default-days: 3
48
+ exclude: ["vigiles"]
49
+
50
+ # One PR for the whole dev tree instead of a PR per package. Without this, the first
51
+ # week yields four separate CI runs and four reviews for one piece of work.
52
+ groups:
53
+ dev-dependencies:
54
+ patterns: ["*"]
55
+ open-pull-requests-limit: 2
56
+ commit-message:
57
+ prefix: "deps"
58
+ labels: ["dependencies"]
59
+
60
+ # Versions of GitHub Actions themselves go stale silently and are only noticed when
61
+ # the runner drops a warning about old Node.
62
+ - package-ecosystem: "github-actions"
63
+ directory: "/"
64
+ schedule:
65
+ interval: "monthly"
66
+ groups:
67
+ actions:
68
+ patterns: ["*"]
69
+ open-pull-requests-limit: 1
70
+ commit-message:
71
+ prefix: "ci"
72
+ labels: ["dependencies"]
@@ -0,0 +1,297 @@
1
+ # CI for paperlint — ONE job, every gate, bonds set BY MEASUREMENT.
2
+ #
3
+ # ── WHY ONE JOB ─────────────────────────────────────────────────────────────
4
+ # Parallelism does not save billable minutes, it ADDS them: every job pays for its own
5
+ # setup + checkout + install (~20-30 s). Splitting is worth it for signal isolation on a big
6
+ # matrix; here the whole suite is 84 seconds, so a second job would cost more than it isolates.
7
+ #
8
+ # ⛽ THIS REPOSITORY IS PUBLIC, so its minutes are FREE — they do not touch the account's
9
+ # 3000/month quota (verified against the API 2026-09-12: `"private": false`). The header said
10
+ # PRIVATE until then, which was true when written and is the reason the gates were measured
11
+ # rather than guessed. Keep measuring them anyway: the bond exists to stop a HANG, and a hang
12
+ # costs wall-clock and a red check whether or not anyone is billed.
13
+ #
14
+ # ── THE MEASUREMENT THE BONDS COME FROM (local, 2026-09-12) ─────────────────
15
+ # npm test (12 harnesses) 11 s
16
+ # npm run lint (eslint over fixtures) 1 s
17
+ # rules-see-files.mjs 1 s
18
+ # vigiles lint 3 s ← added 2026-09-12 with the first ten skills
19
+ # run-mutations.mjs (12 batteries) 242 s ← 130 mutations, the expensive and load-bearing part
20
+ # ───────────────────────────────────────────
21
+ # total work ~255 s, plus checkout + install
22
+ #
23
+ # 🔴 THE PREVIOUS NUMBERS ARE KEPT NOWHERE BUT THIS SENTENCE, ON PURPOSE: they said ~84 s total
24
+ # with `timeout-minutes: 8`, i.e. a ~2.7× bond. The pipeline's checkers arrived on 2026-09-12 and
25
+ # tripled every count — 4 harnesses became 12, 45 mutations became 130 — which would have left an
26
+ # 8-minute bond at ~1.3× of a ~6-minute run. A bond that tight fails on a slow runner and reads
27
+ # as a broken suite rather than as a bond. Re-measure when the counts move again; do not cite the
28
+ # table above without re-running it.
29
+ #
30
+ # Expect ~6 minutes warm. `timeout-minutes: 20` is ~3.3×, and `npm ci` carries its OWN bond
31
+ # because a job-level bond cannot tell "the registry hung" from "the suite is slow" and so hands
32
+ # a hang the whole job.
33
+ name: ci
34
+
35
+ on:
36
+ push:
37
+ branches: [main]
38
+ # 🔴 `types:` FULLY ENUMERATED, INCLUDING DEFAULTS. As soon as this key is declared, the defaults
39
+ # (`opened`, `synchronize`, `reopened`) DISAPPEAR; forgetting them would disable CI on PR pushes,
40
+ # and the only way to notice would be by the absence of a run, i.e., silence.
41
+ # `ready_for_review` is not a default either, and without it the draft gate below would work only
42
+ # halfway: marking a draft ready is none of the defaults, so the PR would reach the merge button
43
+ # with zero checks. macOS is a cell of the build-e2e matrix below.
44
+ pull_request:
45
+ types: [opened, synchronize, reopened, ready_for_review]
46
+
47
+ # A newer push supersedes an older one on the same ref. Note this does NOT make the cancelled
48
+ # run free — cancellation arrives after the expensive steps have already run and been billed —
49
+ # it only stops the tail. (Until 2026-09-23 the key also split on `labeled` events, because a
50
+ # label click used to cancel the code run; with no `labeled` trigger that split has nothing to do.)
51
+ concurrency:
52
+ group: ci-${{ github.ref }}
53
+ cancel-in-progress: true
54
+
55
+ permissions:
56
+ contents: read
57
+
58
+ jobs:
59
+ gates:
60
+ # 🔴 A DRAFT PULL REQUEST PAYS NOTHING (same gate as vigiles and the knowledge base). Written as
61
+ # `event_name != 'pull_request' || draft == false`, not `draft == false` alone: on a push to main
62
+ # there is no pull_request object, and a bare `.draft` would be missing and kill every check there.
63
+ if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
64
+ runs-on: ubuntu-latest
65
+ timeout-minutes: 20
66
+ steps:
67
+ - uses: actions/checkout@v7
68
+
69
+ - uses: actions/setup-node@v7
70
+ with:
71
+ # The floor: engines.node is >=22.13, so the gates run on 22. build-e2e runs 24.
72
+ node-version: 22
73
+ cache: npm
74
+
75
+ # 🔴 ITS OWN BOND. This is the only step that talks to the network, and a hung registry is
76
+ # indistinguishable from a slow suite at the job level. Measured in the consumer repo:
77
+ # a `npm ci` without its own bond once held a job for five minutes on a slow mirror.
78
+ - name: npm ci
79
+ run: npm ci
80
+ timeout-minutes: 5
81
+
82
+ # Harnesses. `--min=1` is inside the script: it refuses a run where NOTHING matched.
83
+ #
84
+ # 🔴 WHAT IT DOES NOT DO, contrary to what was written here before 17.09: it does NOT protect
85
+ # against a directory in the argument. Measurement: `npx vigiles test . --min=1` → `0 passed, 1 skipped`,
86
+ # **RC=0** — the directory exists, discovery treats it as a file, and it counts toward the
87
+ # minimum. The prior version called `--min=1` "load-bearing" precisely against this case,
88
+ # i.e., claimed a protection that does not exist. Protection against this case comes from `--no-skip` below.
89
+ # 🔴 THE `claude` CLI IS A TEST DEPENDENCY HERE, and its absence was NOT harmless.
90
+ # `plan-paper-timeline.effects.harness.mjs` spawns the real agent binary; without it the
91
+ # harness SKIPS, the skip exits 0 through `vigiles test`, and the mutation driver interprets
92
+ # that as "the mutation survived". Measured 2026-09-15 on run 34966606186: two survivors in CI,
93
+ # zero locally, the difference being this binary and nothing about the code.
94
+ #
95
+ # The driver now REFUSES a battery whose harness skips (lib/mutation-driver.mjs), so the
96
+ # wrong diagnosis is gone either way. This step is the other half: it restores the evidence
97
+ # instead of settling for an honest "no evidence". No API key — the tier drives a scripted
98
+ # stand-in, which is why it costs seconds and $0.
99
+ # 🔴 DELIBERATELY UNPINNED, and since issue #7 that is finally honest. The tier exists to
100
+ # notice upstream moving, so a pin would defeat it — but until the version was OBSERVED
101
+ # rather than probed for, an unpinned install meant the assertions characterized whatever
102
+ # shipped that morning while the docblock named 2.1.227 (measured drift: 2.1.273). The run
103
+ # now prints both numbers, so the log says which version the green belongs to.
104
+ - name: the agent binary the effects tier observes
105
+ run: npm i -g @anthropic-ai/claude-code
106
+ timeout-minutes: 3
107
+
108
+ # 🔴 `--no-skip` HERE, AND PRECISELY AFTER THE INSTALL ABOVE. Measurement 17.09: without it,
109
+ # skip is green, and `skip` status is applied not only to author-declared `skip()`, but also to a file
110
+ # that failed to LOAD. In other words, one flag closes both doors at once.
111
+ #
112
+ # ⚠️ The step order was incorrect and made the install above nearly pointless: `npm test`
113
+ # ran BEFORE it, so the effects tier in this job was always skipped, and the binary
114
+ # reached only the mutation batteries. The comment about "its absence was NOT harmless"
115
+ # described exactly this harm yet did not protect against it itself.
116
+ #
117
+ # ⚠️ And the flag belongs IN THE STEP, not in `package.json`: the macOS cell of build-e2e runs the same `npm test`
118
+ # WITHOUT installing the binary, where skips are legitimate, and a global flag would falsely color it.
119
+ - name: npm test
120
+ run: npm test -- --no-skip
121
+
122
+ # The rules themselves, over this repository's own fixtures. Two of the four fixtures are
123
+ # DEFECTIVE ON PURPOSE, so a healthy checkout reports warnings here and still exits 0 —
124
+ # the pass/fail signal for rule behaviour lives in `npm test`, not in the warning count.
125
+ - name: npm run lint
126
+ run: npm run lint
127
+
128
+ # Prettier defaults, the same config as vigiles; `.prettierignore` names what it must not
129
+ # reflow (compiled SKILL.md, fixture data, raw runs) and why.
130
+ - name: npm run fmt:check
131
+ run: npm run fmt:check
132
+
133
+ # 🔴 THE ONLY GATE THAT LOOKS AT THE SKILLS THEMSELVES — it existed as a package.json
134
+ # script (`lint:skills`, removed 2026-09-24) long before it was WIRED HERE. That cost
135
+ # nothing while the package shipped rules only; the first skill arrived 2026-09-12 and made it the one check
136
+ # covering frontmatter, `allowed-tools` contracts, dangling pointers and name collisions.
137
+ # `npm run lint` above is ESLint over .tex/.md fixtures and never opens a SKILL.md.
138
+ #
139
+ # A PLAIN GATE, NOT `continue-on-error` — and that is a measurement, not a preference. In
140
+ # the consumer repository this command CANNOT exit 0: a repo-wide "Markdown code block refs"
141
+ # pass walks prose that quotes OTHER projects' commands, so it is pinned under
142
+ # `continue-on-error` there and gates nothing. Measured here on a clean tree 2026-09-12:
143
+ # **28 findings, 0 error, exit 0, 3 seconds**. Nothing to hide, so nothing is hidden.
144
+ #
145
+ # ⚠️ Warnings are expected and do not fail the run (description budgets, missing
146
+ # `disallowed-tools:` fences). What fails it is severity `error` — a malformed frontmatter
147
+ # or a pointer to a file that does not exist. Bond is 3 × the measured 3 s, floored at the
148
+ # 1-minute granularity GitHub bills in.
149
+ #
150
+ # README.md is linted too, by name (see the same gate in scripts/check.mjs). Its
151
+ # `vigiles:symbol` marks name the functions behind `paperlint init` and `paperlint new`; if one is
152
+ # renamed or removed, this step fails and names README.md.
153
+ - name: vigiles lint (skills + README.md)
154
+ run: npx vigiles lint . README.md
155
+ timeout-minutes: 2
156
+
157
+ # 🔴 THE GREEN-ZERO GUARD. A rule whose glob matches no file is never invoked, and a run
158
+ # with no findings is byte-identical to a run that examined everything and passed. This
159
+ # asks ESLint for the effective config of every linted file and names any rule that saw
160
+ # none. In the source corpus it once found NINETEEN blind rules on a green run.
161
+ - name: every rule sees a file
162
+ run: node scripts/rules-see-files.mjs
163
+
164
+ # Hand-written mutation batteries are being removed (#52): the frozen list may only
165
+ # shrink — no new battery, no new case in an existing one.
166
+ - name: mutation batteries are frozen
167
+ run: node scripts/mutation-batteries-frozen.mjs
168
+
169
+ # The legacy exemptions from src/'s layer rules (#76) may only shrink: counted from ESLint's
170
+ # own suppressedMessages, so a new `-- legacy …` directive anywhere fails here.
171
+ - name: legacy layer exemptions are frozen
172
+ run: node scripts/layer-legacy-frozen.mjs
173
+
174
+ # The batteries that remain still run until #52 replaces or removes them. The runner
175
+ # DISCOVERS batteries rather than listing them and errors on finding zero — see
176
+ # scripts/run-mutations.mjs for why (the hand list it replaced had gone stale, silently).
177
+ - name: mutation batteries
178
+ run: node scripts/run-mutations.mjs
179
+
180
+ # pnpm is INSTALLED here rather than hoped for. The runner image may or may not carry it,
181
+ # and the npm/pnpm difference is the entire reason the install e2e exists — so leaving it
182
+ # to the image means the half that matters most is covered by luck. `corepack` ships with
183
+ # Node, so this costs a second and no network round-trip to a third-party action.
184
+ - name: enable pnpm
185
+ run: corepack enable pnpm
186
+ timeout-minutes: 2
187
+
188
+ # Called directly with --strict, the same as the build e2e below. --strict: a manager that does not launch is a declared skip on a
189
+ # contributor's clone and a FAILURE here, because in CI it means a broken environment.
190
+ - name: install e2e (--strict)
191
+ run: node test/e2e/install.mjs --strict
192
+ timeout-minutes: 10
193
+
194
+ # ── BUILDING THE PAPER WITH REAL pdflatex, ON LINUX AND macOS ──────────────────────────────────
195
+ #
196
+ # 🔴 ONE JOB, TWO OPERATING SYSTEMS. TeX Live comes from `paperlint toolchain` — the same command a user
197
+ # runs — so the job tests the install path users get, not a CI-only script. It used to be a bash
198
+ # installer here and a separate macOS workflow (platform.yml) there; both are gone. macOS matters
199
+ # twice: install-tl-unx has to work on it, and issue #9 (`/var` is a symlink to `/private/var`)
200
+ # exists only there, so the macOS cell also runs `npm test` and the temp-dir probe.
201
+ #
202
+ # 🔴 WHY A SEPARATE JOB, NOT A STEP IN `gates`. Its only dependency is TeX Live, and it alone costs
203
+ # minutes: ~270 MB, ~3 min cold (measured 2026-09-24 with `paperlint toolchain`). A separate job runs in
204
+ # parallel and caches itself.
205
+ #
206
+ # 🔴 WHY ON EVERY PUSH. It catches silent font substitution — a class that HAS ALREADY shipped a
207
+ # paper typeset in the wrong font. This repository is public, so the macOS minutes are free.
208
+ #
209
+ # `--strict`: absence of TeX locally is a legitimate skip; here it is an environment failure.
210
+ build-e2e:
211
+ if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
212
+ strategy:
213
+ fail-fast: false
214
+ matrix:
215
+ os: [ubuntu-latest, macos-latest]
216
+ runs-on: ${{ matrix.os }}
217
+ timeout-minutes: 25
218
+ steps:
219
+ - uses: actions/checkout@v7
220
+
221
+ - uses: actions/setup-node@v7
222
+ with:
223
+ # The latest LTS; the gates job covers the 22 floor, so no job runs twice.
224
+ node-version: 24
225
+ cache: npm
226
+
227
+ - name: npm ci
228
+ run: npm ci
229
+ timeout-minutes: 8
230
+
231
+ # Issue #9's distinction: prints BOTH spellings of the temp root. If they match, this machine
232
+ # is not the platform the macOS cell exists for, and green below says nothing about it.
233
+ - name: two spellings of one directory — does this machine see them
234
+ if: runner.os == 'macOS'
235
+ run: |
236
+ node -e '
237
+ const { mkdtempSync, realpathSync } = require("node:fs");
238
+ const { tmpdir } = require("node:os");
239
+ const { join } = require("node:path");
240
+ const d = mkdtempSync(join(tmpdir(), "probe-"));
241
+ console.log("as typed :", d);
242
+ console.log("realpath :", realpathSync(d));
243
+ console.log(d === realpathSync(d) ? "MATCH — issue #9 traps do not exist on this machine"
244
+ : "DIVERGE — this is the platform this job exists for");
245
+ '
246
+
247
+ # Issue #9: `npm test` was red ONLY on macOS. Only `npm test`, not the mutation batteries:
248
+ # they would verify on macOS exactly what the gates job verifies on Linux.
249
+ - name: npm test (macOS)
250
+ if: runner.os == 'macOS'
251
+ run: npm test
252
+ timeout-minutes: 10
253
+
254
+ # The cache key is what the install depends on: the runner and the declared packages (every
255
+ # venue profile, the base set included). NOT the TeX Live year, and not the calendar year that
256
+ # used to stand in for it: the calendar says nothing about CTAN's release, and a restored tree
257
+ # from an older year is safe — `paperlint toolchain` verifies every declared file on it, leaves it
258
+ # alone when it is complete, and when a newly declared package makes tlmgr refuse it across
259
+ # releases, installs the new year beside it (src/toolchain.ts, `newRelease`).
260
+ # No `restore-keys`, deliberately: a partial restore would carry the old year's tree into the
261
+ # new entry, and the old tree is never deleted, so the cache would grow by a whole TeX Live.
262
+ - name: where TeX Live is cached
263
+ run: echo "RPP_TEXLIVE_DIR=$HOME/.cache/rpp-texlive" >> "$GITHUB_ENV"
264
+
265
+ - name: TeX Live from cache
266
+ uses: actions/cache@v6
267
+ with:
268
+ path: ~/.cache/rpp-texlive
269
+ key: texlive-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('skills/submit-paper/references/venues/*.jsonc') }}
270
+
271
+ # The same command a user runs. On a cache hit it verifies and says "nothing to do"; on a miss
272
+ # it installs from an empty directory and verifies every declared file with kpsewhich.
273
+ # `node bin/rpp.mjs`, not `npx paperlint`: inside this repository npx would look the name up on npm.
274
+ # It also downloads banal, the Perl script HotCRP runs to measure page geometry (~90 KB, pinned
275
+ # commit, sha256-checked, then run once on a test page). paperlint keeps banal and no longer needs
276
+ # poppler: paperlint now writes the XML banal reads, which poppler's pdftohtml used to write.
277
+ # Both runner images ship perl. The download is too small to be worth an Actions cache.
278
+ - name: paperlint toolchain
279
+ run: node bin/rpp.mjs toolchain
280
+ timeout-minutes: 15
281
+
282
+ # Proves banal works without poppler. This runner has no poppler, and the test also limits
283
+ # PATH to perl. paperlint turns each fixture PDF into XML with pdf.js, banal measures it, and the
284
+ # result must equal the numbers banal gave on the same PDFs through poppler's pdftohtml
285
+ # (recorded once, hardcoded in the test). Runs in seconds, hence the short timeout.
286
+ - name: banal e2e (--strict)
287
+ run: node test/e2e/banal.mjs --strict
288
+ timeout-minutes: 3
289
+
290
+ # Called directly with --strict (see the install e2e step in the `gates` job).
291
+ - name: build e2e (--strict)
292
+ run: node test/e2e/build.mjs --strict
293
+ timeout-minutes: 10
294
+
295
+ - name: toolchain e2e (--strict)
296
+ run: node test/e2e/toolchain.mjs --strict
297
+ timeout-minutes: 10
@@ -0,0 +1,70 @@
1
+ # MERGE THE UPDATE UPON A GREEN RUN.
2
+ #
3
+ # 🔴 WHY NOT `gh pr merge --auto`, even though the owner enabled "Allow auto-merge" and branch protection.
4
+ # Standard auto-merge waits for precisely the checks declared REQUIRED in the branch protection. That
5
+ # is a setting in the interface, invisible from here, and it can drift from `ci.yml` on any job rename —
6
+ # and it WILL drift silently, because the PR will simply merge faster.
7
+ # Measurement 2026-09-16 showed exactly this cost: before protection, `main` had `"protected": false`, and
8
+ # `--auto` would have merged the PR IMMEDIATELY, looking like "merge on green" while doing the opposite.
9
+ #
10
+ # This workflow fires FROM THE COMPLETION EVENT of `ci` and reads its `conclusion`. The same reasoning
11
+ # as all checks in this repository: we ask for the THING (run verdict), not its shadow (a setting
12
+ # that promises the verdict will be considered).
13
+ #
14
+ # ⚠️ Branch protection is not redundant — it closes a DIFFERENT hole: a person or bot merging
15
+ # a PR outside CI manually. Two mechanisms, two concerns.
16
+ #
17
+ # 🔴 WHY WE MERGE ANY BUMP, INCLUDING MAJOR, not just patch/minor.
18
+ # Semver is a PUBLISHER'S CLAIM about their release; `ci` is a MEASUREMENT on our corpus (full harness suite,
19
+ # mutations, gates for "rules see their files" and "rules don't touch git"). A major bump that breaks us
20
+ # reds the run and does not merge; a minor one that breaks us does the same.
21
+ # A version-number filter adds a manual step where measurement already exists, and would miss
22
+ # exactly the class for which version filters are typically used.
23
+ #
24
+ # ⚠️ WHAT THIS DOES NOT GIVE, stated plainly: green `ci` means "our harnesses detected no difference,"
25
+ # not "no difference exists." Uncovered behavior passes silently — as vigiles schema 19 → 27 passed
26
+ # through a hook reading an empty field for eight releases in a row.
27
+ name: dependabot auto-merge
28
+
29
+ on:
30
+ workflow_run:
31
+ workflows: ["ci"]
32
+ types: [completed]
33
+
34
+ # Merge and delete branch — nothing else this workflow needs.
35
+ permissions:
36
+ contents: write
37
+ pull-requests: write
38
+
39
+ jobs:
40
+ merge:
41
+ # Three conditions, each blocking a case: the run was for a PR (not a push to main) ·
42
+ # the run is GREEN · the author is a bot, not a person. Remove any one and meaning changes.
43
+ if: >
44
+ github.event.workflow_run.event == 'pull_request' &&
45
+ github.event.workflow_run.conclusion == 'success' &&
46
+ github.event.workflow_run.actor.login == 'dependabot[bot]'
47
+ runs-on: ubuntu-latest
48
+ timeout-minutes: 5
49
+ steps:
50
+ - name: merge the PR that just turned green
51
+ env:
52
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
53
+ REPO: ${{ github.repository }}
54
+ RUN_ID: ${{ github.event.workflow_run.id }}
55
+ # 🔴 The number comes FROM THE EVENT, not from a search by branch name. A search would find the PR
56
+ # but would not prove the green run belongs to it.
57
+ PR: ${{ github.event.workflow_run.pull_requests[0].number }}
58
+ # 🔴 The commit the green run TESTED. Without it, a push landing between the run and this job
59
+ # would merge an untested head: `gh pr merge` merges whatever the PR points at NOW.
60
+ HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
61
+ run: |
62
+ set -euo pipefail
63
+ # An empty number is NOT "nothing to do": fork PRs come with an empty array, and
64
+ # silent exit would look like a successful merge. Fail with a reason.
65
+ if [ -z "${PR}" ]; then
66
+ echo "::error::ci run did not yield a PR number (fork?) — nothing merged, resolve manually"
67
+ exit 1
68
+ fi
69
+ gh pr merge "${PR}" --repo "${REPO}" --squash --delete-branch --match-head-commit "${HEAD_SHA}"
70
+ echo "merged PR #${PR} on green ci run ${RUN_ID}"
@@ -0,0 +1,59 @@
1
+ # The PR title becomes the squash commit on main, and that commit is what semantic-release reads
2
+ # (release.yml). A title without a conventional prefix means a merged fix releases nothing, with
3
+ # every check green. Copied from zernie/vigiles.
4
+ name: PR Title
5
+
6
+ on:
7
+ pull_request_target:
8
+ types: [opened, edited, synchronize]
9
+
10
+ permissions:
11
+ pull-requests: read
12
+
13
+ jobs:
14
+ validate:
15
+ runs-on: ubuntu-latest
16
+ # Measured 5 s in vigiles.
17
+ timeout-minutes: 5
18
+ steps:
19
+ - uses: amannn/action-semantic-pull-request@v6
20
+ env:
21
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22
+ with:
23
+ types: |
24
+ feat
25
+ fix
26
+ docs
27
+ chore
28
+ refactor
29
+ test
30
+ perf
31
+ ci
32
+ build
33
+ requireScope: false
34
+ subjectPattern: ^.+$
35
+ subjectPatternError: "PR title must not be empty after the type prefix."
36
+
37
+ # For a PR with exactly ONE commit, GitHub's squash merge takes the COMMIT's subject as the
38
+ # squash title, not the PR's. So a PR can pass the check above and still land a commit with
39
+ # no conventional prefix, and semantic-release then cuts no version. Measured in vigiles
40
+ # (#163, 2026-08-19): a `feat!:` PR title, check green, a plain commit on main, no release.
41
+ - name: The squash title, for a single-commit PR
42
+ if: github.event.pull_request.commits == 1
43
+ env:
44
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
45
+ PR: ${{ github.event.pull_request.number }}
46
+ REPO: ${{ github.repository }}
47
+ run: |
48
+ set -euo pipefail
49
+ subject=$(gh api "repos/$REPO/pulls/$PR/commits" --jq '.[0].commit.message' | head -1)
50
+ echo "commit subject: $subject"
51
+ if ! printf '%s' "$subject" | grep -qE '^(feat|fix|docs|chore|refactor|test|perf|ci|build)(\([^)]+\))?!?: .+'; then
52
+ echo "::error::This PR has ONE commit, so GitHub's squash default will use the COMMIT subject"
53
+ echo "::error::as the squash title — not the PR title, which already passed the check above."
54
+ echo "::error::That subject is: $subject"
55
+ echo "::error::Fix either side: amend the commit to carry the same conventional prefix, or"
56
+ echo "::error::set the squash title explicitly when merging. Otherwise semantic-release sees"
57
+ echo "::error::no releasable change and publishes nothing, with every check green."
58
+ exit 1
59
+ fi
@@ -0,0 +1,54 @@
1
+ # Release: every push to main runs semantic-release, which reads the commits since the last tag,
2
+ # and — only when one of them is `feat:`, `fix:` or breaking — bumps the version, publishes the
3
+ # package to npm as `paperlint`, tags `vX.Y.Z` and writes a GitHub release.
4
+ # Copied from zernie/vigiles, whose release setup this mirrors.
5
+ #
6
+ # The squash title of a merged PR IS the commit this reads, so a PR titled without a
7
+ # conventional prefix releases nothing, silently. pr-title.yml guards that.
8
+ name: Release
9
+
10
+ on:
11
+ push:
12
+ branches: [main]
13
+
14
+ permissions:
15
+ contents: write
16
+ issues: write
17
+ pull-requests: write
18
+
19
+ jobs:
20
+ release:
21
+ runs-on: ubuntu-latest
22
+ # Not measured yet; npm ci + tsc + publish. Bounded generously, but bounded: an unbounded
23
+ # job that hangs runs to GitHub's six-hour ceiling instead of failing.
24
+ timeout-minutes: 20
25
+ if: "!contains(github.event.head_commit.message, '[skip ci]')"
26
+ steps:
27
+ - uses: actions/checkout@v7
28
+ with:
29
+ # semantic-release walks the history back to the last release tag.
30
+ fetch-depth: 0
31
+
32
+ - uses: actions/setup-node@v7
33
+ with:
34
+ # semantic-release 25 refuses to start below Node ^22.14.0.
35
+ node-version: "22"
36
+ cache: npm
37
+
38
+ # `npm ci` runs `prepare` (tsc), so dist/ exists before publishing; `npm publish` runs it
39
+ # again. dist/ is not committed.
40
+ - name: npm ci
41
+ run: npm ci
42
+ timeout-minutes: 5
43
+
44
+ - name: Release
45
+ env:
46
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
47
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
48
+ # `--no-install`: run the version pinned in package.json, never fetch one. The preset is
49
+ # pinned with it (conventional-changelog-conventionalcommits 9.3.1): preset 10 renders only
50
+ # through conventional-changelog-writer 9, which @semantic-release/release-notes-generator
51
+ # 14 does not load, and vigiles shipped empty release notes for five weeks that way
52
+ # (zernie/vigiles#282). scripts/release-config.test.ts renders the notes with the installed
53
+ # toolchain.
54
+ run: npx --no-install semantic-release