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,239 @@
1
+ /**
2
+ * READ A FINISHED PDF with pdf.js — the shell around `pdf-geometry.ts` and `adapters/banal/xml.ts`. Page
3
+ * count, the fonts the pages draw text with, the last page's words, and every page's text boxes
4
+ * (the input banal measures page geometry from, written as pdftohtml XML by `adapters/banal/xml.ts`).
5
+ *
6
+ * pdf.js arrives as the npm package `unpdf` (a zero-dependency build of Mozilla's pdf.js), so
7
+ * reading a PDF needs nothing installed on the system. It replaced three poppler programs
8
+ * (`pdfinfo`, `pdffonts`, `pdftotext -bbox`), which every consumer had to install by hand; the
9
+ * equivalence was measured before the switch (issue #61, see `pdf-geometry.ts`).
10
+ *
11
+ * ── THE RESULT IS A UNION, AND A FAILED READ CANNOT LOOK LIKE A CLEAN ONE ─────
12
+ * `readPdf` never throws for a property of the file: an unreadable or encrypted PDF, and a PDF whose
13
+ * pages draw text while pdf.js resolves no font for it, come back as `ok: false` with a reason. The
14
+ * last case is the one that matters: on Node 20 pdf.js returns zero fonts and no error, and an empty
15
+ * font list reads as "no Type 3, nothing unembedded" — a clean verdict over nothing.
16
+ *
17
+ * ── LAZY ────────────────────────────────────────────────────────────────────────
18
+ * `unpdf` is imported on the first read, not when this module loads: `paperlint lint` imports the build
19
+ * and must not pay pdf.js's start-up for a command that never opens a PDF.
20
+ */
21
+ // eslint-disable-next-line boundaries/dependencies -- legacy I/O, moves behind a port in #76
22
+ import { readFileSync } from "node:fs";
23
+ import { DEFAULT_ASCENT, DEFAULT_DESCENT, fontsOf, wordsOf, } from "./pdf-geometry.js";
24
+ // eslint-disable-next-line boundaries/dependencies -- legacy layer, moves behind a port in #76
25
+ import { fillsOf, isUpright } from "./adapters/pdfjs/fill.js";
26
+ /**
27
+ * The options every read uses, exactly as measured in the spike. `fontExtraProperties` is
28
+ * load-bearing: without it pdf.js exports no `type`/`subtype`, and every font's program is unknown.
29
+ * `disableFontFace` keeps pdf.js from trying to install fonts into a page that does not exist here.
30
+ */
31
+ export const PDFJS_OPTIONS = {
32
+ disableFontFace: true,
33
+ fontExtraProperties: true,
34
+ verbosity: 0,
35
+ useSystemFonts: false,
36
+ isEvalSupported: false,
37
+ };
38
+ let pdfjs = null;
39
+ function loadPdfJs() {
40
+ // eslint-disable-next-line boundaries/dependencies -- legacy layer, moves behind a port in #76
41
+ pdfjs ??= import("unpdf").then((m) => m.getResolvedPDFJS());
42
+ return pdfjs;
43
+ }
44
+ const fail = (reason, detail) => ({
45
+ ok: false,
46
+ reason,
47
+ detail,
48
+ });
49
+ /** A font object pdf.js left in `commonObjs`, read into a `RawFont`, or null when it is not one. */
50
+ export function rawFontOf(id, obj) {
51
+ if (typeof obj !== "object" || obj === null)
52
+ return null;
53
+ const o = obj;
54
+ if (!("loadedName" in o) && !("isType3Font" in o))
55
+ return null;
56
+ const str = (v) => typeof v === "string" ? v : undefined;
57
+ return {
58
+ id,
59
+ name: str(o["name"]) ?? "",
60
+ type: str(o["type"]),
61
+ subtype: str(o["subtype"]),
62
+ isType3Font: o["isType3Font"] === true,
63
+ missingFile: o["missingFile"] === true,
64
+ };
65
+ }
66
+ const isText = (it) => "str" in it && it.str.trim() !== "";
67
+ /** The font pdf.js resolved for `id`, if it did. `get` throws on an unresolved id; `has` does not. */
68
+ function resolved(page, id) {
69
+ return page.commonObjs.has(id) ? page.commonObjs.get(id) : null;
70
+ }
71
+ /**
72
+ * The fonts pdf.js has loaded for the document so far, by id. `commonObjs` is shared by every page
73
+ * and holds fonts alongside other objects; `rawFontOf` keeps only the fonts.
74
+ */
75
+ function loadedFonts(page) {
76
+ const out = new Map();
77
+ for (const [id, obj] of page.commonObjs) {
78
+ const f = rawFontOf(id, obj);
79
+ if (f)
80
+ out.set(id, f);
81
+ }
82
+ return out;
83
+ }
84
+ /**
85
+ * One page: its text items, and the fonts it drew with — the fonts its text items name, plus every
86
+ * font first loaded while its operator list ran. The second half is not redundant: pdf.js can
87
+ * render a run in a font that no text item names (measured on an all-Type-3 page: four fonts
88
+ * loaded, two named by items; poppler lists four). A page that reuses an earlier page's fonts loads
89
+ * none, and its items name them.
90
+ */
91
+ async function readPage(page, seen, lib) {
92
+ // The operator list is what makes pdf.js load a page's fonts into `commonObjs`.
93
+ const ops = await page.getOperatorList();
94
+ const items = (await page.getTextContent()).items.filter(isText);
95
+ const loaded = loadedFonts(page);
96
+ const named = new Set(items.map((it) => it.fontName));
97
+ const fonts = [...loaded.values()].filter((f) => named.has(f.id) || !seen.has(f.id));
98
+ return {
99
+ raw: { hasText: items.length > 0, fonts },
100
+ items,
101
+ layout: layoutOf(page, items, ops, lib),
102
+ };
103
+ }
104
+ /**
105
+ * A font's ascent and descent as fractions of its size, or the defaults when pdf.js has none.
106
+ *
107
+ * ⚠️ Type 3 fonts get the defaults: pdf.js exports no ascent for them. The obvious substitute,
108
+ * FontBBox scaled by FontMatrix (what poppler uses), was measured on `fixtures/pdf-facts/t3-all.pdf`
109
+ * and came out FURTHER from poppler than the defaults: 8.5 pt off on the left column against 7.2.
110
+ * Both are far below any balance tolerance, so the simpler rule stays.
111
+ */
112
+ function metricsOf(font) {
113
+ const f = (font ?? {});
114
+ const ascent = f["ascent"];
115
+ if (typeof ascent !== "number" || !(ascent > 0))
116
+ return { ascent: DEFAULT_ASCENT, descent: DEFAULT_DESCENT };
117
+ const d = f["descent"];
118
+ return { ascent, descent: typeof d === "number" ? d : DEFAULT_DESCENT };
119
+ }
120
+ /** The last page's text runs in top-down coordinates, and its size. */
121
+ function pageText(page, items) {
122
+ const vp = page.getViewport({ scale: 1 });
123
+ const runs = items.map((it) => {
124
+ const [a = 0, b = 0, , d = 0, e = 0, f = 0] = it.transform;
125
+ const [x, baseline] = vp.convertToViewportPoint(e, f);
126
+ const size = Math.hypot(a, b) || it.height || Math.abs(d);
127
+ return {
128
+ text: it.str,
129
+ x,
130
+ baseline,
131
+ width: it.width,
132
+ size,
133
+ ...metricsOf(resolved(page, it.fontName)),
134
+ };
135
+ });
136
+ return {
137
+ widthPt: vp.width,
138
+ heightPt: vp.height,
139
+ words: runs.flatMap(wordsOf),
140
+ };
141
+ }
142
+ /** The font name pdf.js resolved for an item, else its internal id. */
143
+ function fontNameOf(font, id) {
144
+ const name = font?.name;
145
+ return typeof name === "string" && name ? name : id;
146
+ }
147
+ /**
148
+ * One text item as a box, in points, top-down. The size is the item's vertical scale, which is what
149
+ * pdftohtml reports as the font size; upright is judged in VIEWER space (the page's /Rotate applied),
150
+ * because that is where pdftohtml draws.
151
+ */
152
+ function boxOf(page, vp, it, lib) {
153
+ const [a = 0, b = 0, c = 0, d = 0, e = 0, f = 0] = it.transform;
154
+ const [x, baseline] = vp.convertToViewportPoint(e, f);
155
+ const size = Math.hypot(c, d) || Math.hypot(a, b) || it.height;
156
+ const font = resolved(page, it.fontName);
157
+ const m = metricsOf(font);
158
+ return {
159
+ top: baseline - m.ascent * size,
160
+ left: x,
161
+ width: it.width,
162
+ height: (m.ascent - m.descent) * size,
163
+ size,
164
+ font: fontNameOf(font, it.fontName),
165
+ text: it.str,
166
+ upright: isUpright(lib.Util.transform(vp.transform, it.transform)),
167
+ };
168
+ }
169
+ /** Every text item of a page as a box with its fill — the input of `pdf2xml`. */
170
+ function layoutOf(page, items, ops, lib) {
171
+ const vp = page.getViewport({ scale: 1 });
172
+ const fills = fillsOf(lib.OPS, ops, items.map((it) => it.str));
173
+ const boxes = items.map((it, k) => ({
174
+ ...boxOf(page, vp, it, lib),
175
+ fill: fills[k] ?? { kind: "unknown" },
176
+ }));
177
+ return { widthPt: vp.width, heightPt: vp.height, boxes };
178
+ }
179
+ /** Every page's fonts, and the last page's words. */
180
+ async function factsOf(doc, lib) {
181
+ const read = [];
182
+ const seen = new Set();
183
+ for (let i = 1; i <= doc.numPages; i++) {
184
+ const page = await doc.getPage(i);
185
+ const r = await readPage(page, seen, lib);
186
+ for (const f of r.raw.fonts)
187
+ seen.add(f.id);
188
+ read.push({ page, ...r });
189
+ }
190
+ const [last] = read.slice(-1);
191
+ if (!last)
192
+ return fail("empty", "the PDF has no pages");
193
+ const fonts = fontsOf(read.map((r) => r.raw));
194
+ if (fonts.kind === "zero-fonts-on-text")
195
+ return fail("zero-fonts-on-text", `page(s) ${fonts.pages.join(", ")} draw text, but pdf.js resolved no font for it ` +
196
+ // eslint-disable-next-line no-restricted-globals -- legacy I/O, moves behind a port in #76
197
+ `(Node ${process.version}; paperlint needs Node >= 22.13, where pdf.js reports fonts)`);
198
+ return {
199
+ ok: true,
200
+ facts: {
201
+ pages: doc.numPages,
202
+ fonts,
203
+ last: pageText(last.page, last.items),
204
+ layout: read.map((r) => r.layout),
205
+ },
206
+ };
207
+ }
208
+ /** pdf.js's own exception names for the two conditions a caller can act on. */
209
+ function failureOf(e) {
210
+ const err = e;
211
+ const detail = `${err.name ?? "Error"}: ${err.message ?? String(e)}`;
212
+ return fail(err.name === "PasswordException" ? "encrypted" : "unreadable", detail);
213
+ }
214
+ /** Read a PDF from disk. Never throws for a property of the file; see the module header. */
215
+ export const readPdf = async (path) => {
216
+ let data;
217
+ try {
218
+ data = new Uint8Array(readFileSync(path));
219
+ }
220
+ catch (e) {
221
+ return fail("unreadable", e.message);
222
+ }
223
+ const lib = await loadPdfJs();
224
+ const task = lib.getDocument({ data, ...PDFJS_OPTIONS });
225
+ try {
226
+ return await factsOf(await task.promise, lib);
227
+ }
228
+ catch (e) {
229
+ return failureOf(e);
230
+ }
231
+ finally {
232
+ await task.destroy();
233
+ }
234
+ };
235
+ /** The one wording of a failed read, as a build step or a CLI prints it. */
236
+ export function describeFailure(r, pdf) {
237
+ return `could not read ${pdf} with pdf.js (${r.reason}): ${r.detail}`;
238
+ }
239
+ //# sourceMappingURL=pdf-facts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf-facts.js","sourceRoot":"","sources":["../src/pdf-facts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * WHAT A PDF'S TEXT AND FONTS SAY, once pdf.js has read them — PURE. No disk, no pdf.js.
3
+ *
4
+ * `pdf-facts.ts` is the shell: it opens the PDF with pdf.js (the `unpdf` package) and hands this
5
+ * module plain data — text runs with their metrics, and the fonts each page resolved. Everything
6
+ * that DECIDES something is here, so every branch is a row in a table test fed with hand-written
7
+ * words, including pages the fixture corpus does not contain.
8
+ *
9
+ * ── WHY pdf.js AND NOT POPPLER (issue #61) ───────────────────────────────────
10
+ * The facts used to come from three poppler programs (`pdfinfo`, `pdffonts`, `pdftotext -bbox`), a
11
+ * system install paperlint could not provide and every consumer had to add by hand. A measurement on 25
12
+ * PDFs (2026-09-24) found pdf.js equal on page count and on the Type 3 count of every PDF, and the
13
+ * last page's column heights within 0.2 pt except on an all-Type-3 page (7.2 pt; pdf.js has no
14
+ * ascent for Type 3). No verdict flipped at a 120 pt tolerance.
15
+ *
16
+ * ── THE LAST PAGE IS A MEASUREMENT, NOT A VERDICT ────────────────────────────
17
+ * `classifyLastPage` returns the heights of the two columns, or says why there is nothing to
18
+ * measure. It carries no threshold: whether a venue requires balance, and how much difference it
19
+ * allows, belongs to the lint rule `pdf/last-page-balance`, which a consumer turns on per venue.
20
+ */
21
+ /** One word on a page, in points, top-down: `y0` is the top edge, `y1` the bottom. */
22
+ export interface Word {
23
+ readonly x0: number;
24
+ readonly y0: number;
25
+ readonly x1: number;
26
+ readonly y1: number;
27
+ readonly text: string;
28
+ }
29
+ /** A page's size and its words. */
30
+ export interface PageText {
31
+ readonly widthPt: number;
32
+ readonly heightPt: number;
33
+ readonly words: readonly Word[];
34
+ }
35
+ /** The heights of the last page's two columns, in points: [left, right]. */
36
+ export type Columns = readonly [left: number, right: number];
37
+ /**
38
+ * What the last page is, as far as balance goes.
39
+ *
40
+ * - `measured` — the heights of both columns.
41
+ * - `stub` — fewer than `STUB_WORDS` words: a tail of a few lines is not a layout.
42
+ * - `review` — numbered lines in the outer margins. The numbers run down the whole page, so every
43
+ * column would measure full height; a review build is not judged at all.
44
+ */
45
+ export type LastPage = {
46
+ readonly kind: "measured";
47
+ readonly columns: Columns;
48
+ } | {
49
+ readonly kind: "stub";
50
+ readonly words: number;
51
+ } | {
52
+ readonly kind: "review";
53
+ readonly lineNumbers: number;
54
+ };
55
+ /** Below this many words the last page is a stub. */
56
+ export declare const STUB_WORDS = 60;
57
+ /** This many short integers in the outer margins make a review build. */
58
+ export declare const REVIEW_LINE_NUMBERS = 5;
59
+ /** The outer margins, as a fraction of the page width, where line numbers sit. */
60
+ export declare const MARGIN_FRACTION = 0.08;
61
+ /**
62
+ * Classify the last page and, when it has a layout, measure its two columns.
63
+ *
64
+ * 🔴 THE PAGE IS SPLIT AT ITS OWN MIDDLE, not between the outermost words: on a page where only the
65
+ * left column is filled, the words' midpoint falls inside that column and cuts it in two ("77 / 731"
66
+ * for six lines at the top left, measured 2026-08-29). A word belongs to the side its left edge is on.
67
+ *
68
+ * Throws on a page with no width — the caller built `PageText` from the PDF's own viewport, so a
69
+ * non-positive width is a defect in the caller, not a property of a paper.
70
+ */
71
+ export declare function classifyLastPage(p: PageText): LastPage;
72
+ /**
73
+ * One text run as pdf.js reports it, already in top-down page coordinates: where its baseline
74
+ * starts, how wide it is, its font size, and the font's ascent and descent as fractions of the size
75
+ * (descent negative, as fonts store it).
76
+ */
77
+ export interface TextRun {
78
+ readonly text: string;
79
+ readonly x: number;
80
+ readonly baseline: number;
81
+ readonly width: number;
82
+ readonly size: number;
83
+ readonly ascent: number;
84
+ readonly descent: number;
85
+ }
86
+ /** A font's vertical metrics as fractions of its size, when pdf.js supplies none. */
87
+ export declare const DEFAULT_ASCENT = 0.8;
88
+ export declare const DEFAULT_DESCENT = -0.2;
89
+ /**
90
+ * Split a run into words. pdf.js gives a run's total width, not each glyph's, so a word's horizontal
91
+ * extent is estimated in proportion to its characters. Only `x0` decides the column a word falls
92
+ * in, and a run never crosses the gutter, so the estimate cannot move a word to the other side.
93
+ */
94
+ export declare function wordsOf(run: TextRun): Word[];
95
+ /** The font programs a PDF can carry, in pdf.js's vocabulary. */
96
+ export type FontProgram = "Type1" | "Type1C" | "MMType1" | "TrueType" | "OpenType" | "CIDType0" | "CIDType0C" | "CIDTrueType" | "Type3";
97
+ /** A program pdf.js named but this module does not know — kept verbatim, never guessed. */
98
+ export type UnknownProgram = `unknown:${string}`;
99
+ /**
100
+ * One font the PDF draws text with.
101
+ *
102
+ * - `embedded` — a usable font program is in the file.
103
+ * - `type3` — a Type 3 font: glyphs drawn as PDF procedures, usually bitmaps. pdf.js gives
104
+ * every Type 3 font the same name, `Type3`, so the name carries nothing.
105
+ * - `not-embedded` — the PDF names the font but carries no usable program for it: either none at
106
+ * all, or one pdf.js could not parse. pdf.js reports both as one flag
107
+ * (`missingFile`), so the two are not told apart here.
108
+ */
109
+ export type FontFact = {
110
+ readonly kind: "embedded";
111
+ readonly name: string;
112
+ readonly program: Exclude<FontProgram, "Type3"> | UnknownProgram;
113
+ } | {
114
+ readonly kind: "type3";
115
+ readonly name: "Type3";
116
+ } | {
117
+ readonly kind: "not-embedded";
118
+ readonly name: string;
119
+ readonly program: Exclude<FontProgram, "Type3"> | UnknownProgram;
120
+ };
121
+ /** A font as pdf.js exports it with `fontExtraProperties`, reduced to what is read here. */
122
+ export interface RawFont {
123
+ /** pdf.js's id for the font within the document (`loadedName`). */
124
+ readonly id: string;
125
+ readonly name: string;
126
+ readonly type: string | undefined;
127
+ readonly subtype: string | undefined;
128
+ readonly isType3Font: boolean;
129
+ readonly missingFile: boolean;
130
+ }
131
+ /** One page: whether it draws any text, and the fonts its text resolved to. */
132
+ export interface RawPage {
133
+ readonly hasText: boolean;
134
+ readonly fonts: readonly RawFont[];
135
+ }
136
+ /**
137
+ * The fonts of a document. `drawn` is non-empty BY TYPE: a document whose pages draw text has at
138
+ * least one font, and "no fonts" is spelled only as `no-text`.
139
+ */
140
+ export type Fonts = {
141
+ readonly kind: "drawn";
142
+ readonly list: readonly [FontFact, ...FontFact[]];
143
+ } | {
144
+ readonly kind: "no-text";
145
+ };
146
+ /**
147
+ * 🔴 TEXT ON A PAGE WITH NO FONT BEHIND IT IS A FAILED READ, NOT AN EMPTY LIST. Measured on Node
148
+ * 20.20.2: pdf.js opens the same PDFs and returns ZERO fonts with no error. An empty list there
149
+ * would read downstream as "no Type 3, nothing unembedded" — a clean verdict over nothing.
150
+ */
151
+ export interface ZeroFontsOnText {
152
+ readonly kind: "zero-fonts-on-text";
153
+ /** 1-based numbers of the pages that draw text with no font resolved. */
154
+ readonly pages: readonly number[];
155
+ }
156
+ export declare function programOf(type: string | undefined, subtype: string | undefined): Exclude<FontProgram, "Type3"> | UnknownProgram;
157
+ export declare function fontFact(f: RawFont): FontFact;
158
+ /**
159
+ * The document's fonts, each once, in the order pages first drew them — or the pages that drew
160
+ * text with no font, which the caller turns into a failed read.
161
+ */
162
+ export declare function fontsOf(pages: readonly RawPage[]): Fonts | ZeroFontsOnText;
163
+ /**
164
+ * poppler's spelling of the font's type column (`pdffonts`), which the facts JSON has always used
165
+ * and consumers match on (`Type 3`).
166
+ */
167
+ export declare const POPPLER_TYPE: Readonly<Record<FontProgram, string>>;
168
+ /** The type column for any fact, the unknown ones verbatim. */
169
+ export declare function popplerType(f: FontFact): string;
170
+ //# sourceMappingURL=pdf-geometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf-geometry.d.ts","sourceRoot":"","sources":["../src/pdf-geometry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,158 @@
1
+ /**
2
+ * WHAT A PDF'S TEXT AND FONTS SAY, once pdf.js has read them — PURE. No disk, no pdf.js.
3
+ *
4
+ * `pdf-facts.ts` is the shell: it opens the PDF with pdf.js (the `unpdf` package) and hands this
5
+ * module plain data — text runs with their metrics, and the fonts each page resolved. Everything
6
+ * that DECIDES something is here, so every branch is a row in a table test fed with hand-written
7
+ * words, including pages the fixture corpus does not contain.
8
+ *
9
+ * ── WHY pdf.js AND NOT POPPLER (issue #61) ───────────────────────────────────
10
+ * The facts used to come from three poppler programs (`pdfinfo`, `pdffonts`, `pdftotext -bbox`), a
11
+ * system install paperlint could not provide and every consumer had to add by hand. A measurement on 25
12
+ * PDFs (2026-09-24) found pdf.js equal on page count and on the Type 3 count of every PDF, and the
13
+ * last page's column heights within 0.2 pt except on an all-Type-3 page (7.2 pt; pdf.js has no
14
+ * ascent for Type 3). No verdict flipped at a 120 pt tolerance.
15
+ *
16
+ * ── THE LAST PAGE IS A MEASUREMENT, NOT A VERDICT ────────────────────────────
17
+ * `classifyLastPage` returns the heights of the two columns, or says why there is nothing to
18
+ * measure. It carries no threshold: whether a venue requires balance, and how much difference it
19
+ * allows, belongs to the lint rule `pdf/last-page-balance`, which a consumer turns on per venue.
20
+ */
21
+ /** Below this many words the last page is a stub. */
22
+ export const STUB_WORDS = 60;
23
+ /** This many short integers in the outer margins make a review build. */
24
+ export const REVIEW_LINE_NUMBERS = 5;
25
+ /** The outer margins, as a fraction of the page width, where line numbers sit. */
26
+ export const MARGIN_FRACTION = 0.08;
27
+ const round1 = (n) => Number(n.toFixed(1));
28
+ /** Top of the highest word to bottom of the lowest one; 0 for no words. */
29
+ function heightOf(words) {
30
+ if (words.length === 0)
31
+ return 0;
32
+ const top = Math.min(...words.map((w) => w.y0));
33
+ const bottom = Math.max(...words.map((w) => w.y1));
34
+ return round1(bottom - top);
35
+ }
36
+ /** A short integer standing in the left or right margin: a line number of a review build. */
37
+ function isLineNumber(w, widthPt) {
38
+ const margin = widthPt * MARGIN_FRACTION;
39
+ return (/^\d{1,4}$/.test(w.text.trim()) &&
40
+ (w.x1 < margin || w.x0 > widthPt - margin));
41
+ }
42
+ /**
43
+ * Classify the last page and, when it has a layout, measure its two columns.
44
+ *
45
+ * 🔴 THE PAGE IS SPLIT AT ITS OWN MIDDLE, not between the outermost words: on a page where only the
46
+ * left column is filled, the words' midpoint falls inside that column and cuts it in two ("77 / 731"
47
+ * for six lines at the top left, measured 2026-08-29). A word belongs to the side its left edge is on.
48
+ *
49
+ * Throws on a page with no width — the caller built `PageText` from the PDF's own viewport, so a
50
+ * non-positive width is a defect in the caller, not a property of a paper.
51
+ */
52
+ export function classifyLastPage(p) {
53
+ if (!(p.widthPt > 0))
54
+ throw new RangeError(`page width must be positive, got ${p.widthPt}`);
55
+ if (p.words.length < STUB_WORDS)
56
+ return { kind: "stub", words: p.words.length };
57
+ const lineNumbers = p.words.filter((w) => isLineNumber(w, p.widthPt)).length;
58
+ if (lineNumbers >= REVIEW_LINE_NUMBERS)
59
+ return { kind: "review", lineNumbers };
60
+ const mid = p.widthPt / 2;
61
+ return {
62
+ kind: "measured",
63
+ columns: [
64
+ heightOf(p.words.filter((w) => w.x0 < mid)),
65
+ heightOf(p.words.filter((w) => w.x0 >= mid)),
66
+ ],
67
+ };
68
+ }
69
+ /** A font's vertical metrics as fractions of its size, when pdf.js supplies none. */
70
+ export const DEFAULT_ASCENT = 0.8;
71
+ export const DEFAULT_DESCENT = -0.2;
72
+ /**
73
+ * Split a run into words. pdf.js gives a run's total width, not each glyph's, so a word's horizontal
74
+ * extent is estimated in proportion to its characters. Only `x0` decides the column a word falls
75
+ * in, and a run never crosses the gutter, so the estimate cannot move a word to the other side.
76
+ */
77
+ export function wordsOf(run) {
78
+ const perChar = run.width / Math.max(1, run.text.length);
79
+ const y0 = run.baseline - run.ascent * run.size;
80
+ const y1 = run.baseline - run.descent * run.size;
81
+ return [...run.text.matchAll(/\S+/g)].map((m) => {
82
+ const x0 = run.x + (m.index ?? 0) * perChar;
83
+ return { x0, y0, x1: x0 + m[0].length * perChar, y1, text: m[0] };
84
+ });
85
+ }
86
+ /**
87
+ * pdf.js's `type`/`subtype` pair → the program, keyed `type/subtype`. Composite fonts carry their
88
+ * descendant's type. A pair not listed is kept verbatim as `unknown:…`, never guessed.
89
+ */
90
+ const PROGRAMS = {
91
+ "Type1/undefined": "Type1",
92
+ "Type1/Type1C": "Type1C",
93
+ "MMType1/undefined": "MMType1",
94
+ "TrueType/undefined": "TrueType",
95
+ "CIDFontType0/undefined": "CIDType0",
96
+ "CIDFontType0/CIDFontType0C": "CIDType0C",
97
+ "CIDFontType2/undefined": "CIDTrueType",
98
+ };
99
+ export function programOf(type, subtype) {
100
+ // pdf.js names an OpenType-wrapped program by the wrapper, on either field, whatever it wraps.
101
+ if (subtype === "OpenType" || type === "OpenType")
102
+ return "OpenType";
103
+ const key = `${String(type)}/${String(subtype)}`;
104
+ return PROGRAMS[key] ?? `unknown:${key}`;
105
+ }
106
+ /** Subset fonts carry a six-letter tag, `ABCDEF+Name`; the name is what follows it. */
107
+ const withoutSubsetTag = (name) => name.replace(/^[A-Z]{6}\+/, "");
108
+ export function fontFact(f) {
109
+ if (f.isType3Font)
110
+ return { kind: "type3", name: "Type3" };
111
+ const name = withoutSubsetTag(f.name);
112
+ const program = programOf(f.type, f.subtype);
113
+ return f.missingFile
114
+ ? { kind: "not-embedded", name, program }
115
+ : { kind: "embedded", name, program };
116
+ }
117
+ /**
118
+ * The document's fonts, each once, in the order pages first drew them — or the pages that drew
119
+ * text with no font, which the caller turns into a failed read.
120
+ */
121
+ export function fontsOf(pages) {
122
+ const bad = pages.flatMap((p, i) => p.hasText && p.fonts.length === 0 ? [i + 1] : []);
123
+ if (bad.length > 0)
124
+ return { kind: "zero-fonts-on-text", pages: bad };
125
+ const seen = new Map();
126
+ for (const p of pages)
127
+ for (const f of p.fonts)
128
+ if (!seen.has(f.id))
129
+ seen.set(f.id, fontFact(f));
130
+ const [first, ...rest] = seen.values();
131
+ return first
132
+ ? { kind: "drawn", list: [first, ...rest] }
133
+ : { kind: "no-text" };
134
+ }
135
+ /**
136
+ * poppler's spelling of the font's type column (`pdffonts`), which the facts JSON has always used
137
+ * and consumers match on (`Type 3`).
138
+ */
139
+ export const POPPLER_TYPE = {
140
+ Type1: "Type 1",
141
+ Type1C: "Type 1C",
142
+ MMType1: "Type 1",
143
+ TrueType: "TrueType",
144
+ OpenType: "OpenType",
145
+ CIDType0: "CID Type 0",
146
+ CIDType0C: "CID Type 0C",
147
+ CIDTrueType: "CID TrueType",
148
+ Type3: "Type 3",
149
+ };
150
+ /** The type column for any fact, the unknown ones verbatim. */
151
+ export function popplerType(f) {
152
+ if (f.kind === "type3")
153
+ return POPPLER_TYPE.Type3;
154
+ return f.program.startsWith("unknown:")
155
+ ? f.program
156
+ : POPPLER_TYPE[f.program];
157
+ }
158
+ //# sourceMappingURL=pdf-geometry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf-geometry.js","sourceRoot":"","sources":["../src/pdf-geometry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /** Fetching a URL — the generic port for the network. */
2
+ import type { Result } from "../domain/result.ts";
3
+ /** Fetch a URL's bytes. */
4
+ export interface Download {
5
+ fetch(url: string, timeoutMs: number): Result<Uint8Array, {
6
+ readonly detail: string;
7
+ }>;
8
+ }
9
+ //# sourceMappingURL=download.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/ports/download.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD,2BAA2B;AAC3B,MAAM,WAAW,QAAQ;IACvB,KAAK,CACH,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,GAChB,MAAM,CAAC,UAAU,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=download.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download.js","sourceRoot":"","sources":["../../src/ports/download.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ /** Files by path — the generic port for the disk. */
2
+ import type { AbsolutePath } from "../domain/paths.ts";
3
+ /** Files by path. A read that fails for any reason but absence throws: that is an adapter bug. */
4
+ export interface Files {
5
+ isFile(p: AbsolutePath): boolean;
6
+ /** The file's bytes, or null when there is no such file. */
7
+ readBytes(p: AbsolutePath): Uint8Array | null;
8
+ /** Write through `<p>.part` and a rename, creating the directory: a reader never sees half a file. */
9
+ writeAtomic(p: AbsolutePath, bytes: Uint8Array): void;
10
+ }
11
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/ports/files.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,kGAAkG;AAClG,MAAM,WAAW,KAAK;IACpB,MAAM,CAAC,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC;IACjC,4DAA4D;IAC5D,SAAS,CAAC,CAAC,EAAE,YAAY,GAAG,UAAU,GAAG,IAAI,CAAC;IAC9C,sGAAsG;IACtG,WAAW,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;CACvD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/ports/files.ts"],"names":[],"mappings":""}