paperlint 8.0.0 → 9.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 (356) hide show
  1. package/CLAUDE.md +9 -25
  2. package/CONTRIBUTING.md +5 -4
  3. package/README.md +4 -3
  4. package/dist/adapters/bibtex/index.d.ts +3 -0
  5. package/dist/adapters/bibtex/index.d.ts.map +1 -0
  6. package/dist/adapters/bibtex/index.js +103 -0
  7. package/dist/adapters/bibtex/index.js.map +1 -0
  8. package/dist/adapters/latex/filecontents.d.ts +33 -0
  9. package/dist/adapters/latex/filecontents.d.ts.map +1 -0
  10. package/dist/adapters/latex/filecontents.js +65 -0
  11. package/dist/adapters/latex/filecontents.js.map +1 -0
  12. package/dist/adapters/latex/includes.d.ts +3 -3
  13. package/dist/adapters/latex/includes.d.ts.map +1 -1
  14. package/dist/adapters/latex/includes.js +18 -10
  15. package/dist/adapters/latex/includes.js.map +1 -1
  16. package/dist/adapters/latex/index.d.ts.map +1 -1
  17. package/dist/adapters/latex/index.js +2 -0
  18. package/dist/adapters/latex/index.js.map +1 -1
  19. package/dist/adapters/latex/nodes.d.ts +12 -0
  20. package/dist/adapters/latex/nodes.d.ts.map +1 -1
  21. package/dist/adapters/latex/nodes.js +39 -0
  22. package/dist/adapters/latex/nodes.js.map +1 -1
  23. package/dist/adapters/latex/parse.d.ts +7 -1
  24. package/dist/adapters/latex/parse.d.ts.map +1 -1
  25. package/dist/adapters/latex/parse.js +55 -6
  26. package/dist/adapters/latex/parse.js.map +1 -1
  27. package/dist/adapters/memory/index.d.ts +3 -0
  28. package/dist/adapters/memory/index.d.ts.map +1 -1
  29. package/dist/adapters/memory/index.js +9 -0
  30. package/dist/adapters/memory/index.js.map +1 -1
  31. package/dist/adapters/references/index.d.ts.map +1 -1
  32. package/dist/adapters/references/index.js +27 -24
  33. package/dist/adapters/references/index.js.map +1 -1
  34. package/dist/adapters/sources-record/index.d.ts +9 -0
  35. package/dist/adapters/sources-record/index.d.ts.map +1 -0
  36. package/dist/adapters/sources-record/index.js +68 -0
  37. package/dist/adapters/sources-record/index.js.map +1 -0
  38. package/dist/adapters/tex-output/blg.d.ts +9 -0
  39. package/dist/adapters/tex-output/blg.d.ts.map +1 -0
  40. package/dist/adapters/tex-output/blg.js +20 -0
  41. package/dist/adapters/tex-output/blg.js.map +1 -0
  42. package/dist/adapters/tex-output/fls.d.ts +8 -0
  43. package/dist/adapters/tex-output/fls.d.ts.map +1 -0
  44. package/dist/adapters/tex-output/fls.js +16 -0
  45. package/dist/adapters/tex-output/fls.js.map +1 -0
  46. package/dist/adapters/tex-output/index.d.ts +7 -0
  47. package/dist/adapters/tex-output/index.d.ts.map +1 -0
  48. package/dist/adapters/tex-output/index.js +7 -0
  49. package/dist/adapters/tex-output/index.js.map +1 -0
  50. package/dist/bib-rules.d.ts +47 -0
  51. package/dist/bib-rules.d.ts.map +1 -0
  52. package/dist/bib-rules.js +127 -0
  53. package/dist/bib-rules.js.map +1 -0
  54. package/dist/build.d.ts +29 -1
  55. package/dist/build.d.ts.map +1 -1
  56. package/dist/build.js +40 -24
  57. package/dist/build.js.map +1 -1
  58. package/dist/claim-provenance.d.ts +2 -2
  59. package/dist/cli.d.ts +6 -1
  60. package/dist/cli.d.ts.map +1 -1
  61. package/dist/cli.js +30 -26
  62. package/dist/cli.js.map +1 -1
  63. package/dist/domain/paper-source.d.ts +1 -10
  64. package/dist/domain/paper-source.d.ts.map +1 -1
  65. package/dist/domain/paper-source.js +34 -24
  66. package/dist/domain/paper-source.js.map +1 -1
  67. package/dist/domain/paper-sources.d.ts +72 -0
  68. package/dist/domain/paper-sources.d.ts.map +1 -0
  69. package/dist/domain/paper-sources.js +18 -0
  70. package/dist/domain/paper-sources.js.map +1 -0
  71. package/dist/domain/sources-record.d.ts +102 -0
  72. package/dist/domain/sources-record.d.ts.map +1 -0
  73. package/dist/domain/sources-record.js +38 -0
  74. package/dist/domain/sources-record.js.map +1 -0
  75. package/dist/domain/tex-run.d.ts +25 -0
  76. package/dist/domain/tex-run.d.ts.map +1 -0
  77. package/dist/domain/tex-run.js +79 -0
  78. package/dist/domain/tex-run.js.map +1 -0
  79. package/dist/eslint-rules/paper-context.d.ts.map +1 -1
  80. package/dist/eslint-rules/paper-context.js +4 -3
  81. package/dist/eslint-rules/paper-context.js.map +1 -1
  82. package/dist/eslint-rules/paper-typography.d.ts +0 -9
  83. package/dist/eslint-rules/paper-typography.d.ts.map +1 -1
  84. package/dist/eslint-rules/paper-typography.js +12 -5
  85. package/dist/eslint-rules/paper-typography.js.map +1 -1
  86. package/dist/latex-log.d.ts +10 -0
  87. package/dist/latex-log.d.ts.map +1 -1
  88. package/dist/latex-log.js +10 -0
  89. package/dist/latex-log.js.map +1 -1
  90. package/dist/latex-loop.d.ts +2 -0
  91. package/dist/latex-loop.d.ts.map +1 -1
  92. package/dist/latex-loop.js.map +1 -1
  93. package/dist/package-dirs.d.ts.map +1 -1
  94. package/dist/package-dirs.js +3 -2
  95. package/dist/package-dirs.js.map +1 -1
  96. package/dist/paper-includes.d.ts +4 -7
  97. package/dist/paper-includes.d.ts.map +1 -1
  98. package/dist/paper-includes.js +29 -21
  99. package/dist/paper-includes.js.map +1 -1
  100. package/dist/paper-prose.d.ts +2 -2
  101. package/dist/paper-record.d.ts +25 -0
  102. package/dist/paper-record.d.ts.map +1 -0
  103. package/dist/paper-record.js +39 -0
  104. package/dist/paper-record.js.map +1 -0
  105. package/dist/paper-sources.d.ts +66 -0
  106. package/dist/paper-sources.d.ts.map +1 -0
  107. package/dist/paper-sources.js +92 -0
  108. package/dist/paper-sources.js.map +1 -0
  109. package/dist/ports/bib-reader.d.ts +18 -0
  110. package/dist/ports/bib-reader.d.ts.map +1 -0
  111. package/dist/ports/bib-reader.js +2 -0
  112. package/dist/ports/bib-reader.js.map +1 -0
  113. package/dist/ports/check-references.d.ts +9 -4
  114. package/dist/ports/check-references.d.ts.map +1 -1
  115. package/dist/ports/latex.d.ts +15 -0
  116. package/dist/ports/latex.d.ts.map +1 -1
  117. package/dist/ports/sources-codec.d.ts +11 -0
  118. package/dist/ports/sources-codec.d.ts.map +1 -0
  119. package/dist/ports/sources-codec.js +2 -0
  120. package/dist/ports/sources-codec.js.map +1 -0
  121. package/dist/ports/tex-output.d.ts +17 -0
  122. package/dist/ports/tex-output.d.ts.map +1 -0
  123. package/dist/ports/tex-output.js +2 -0
  124. package/dist/ports/tex-output.js.map +1 -0
  125. package/dist/recorded-bibliography.d.ts +89 -0
  126. package/dist/recorded-bibliography.d.ts.map +1 -0
  127. package/dist/recorded-bibliography.js +136 -0
  128. package/dist/recorded-bibliography.js.map +1 -0
  129. package/dist/reference-rules.d.ts +5 -5
  130. package/dist/reference-rules.d.ts.map +1 -1
  131. package/dist/reference-rules.js +150 -77
  132. package/dist/reference-rules.js.map +1 -1
  133. package/dist/references.d.ts +71 -16
  134. package/dist/references.d.ts.map +1 -1
  135. package/dist/references.js +110 -26
  136. package/dist/references.js.map +1 -1
  137. package/dist/register-bands.d.ts +3 -3
  138. package/dist/register-bands.d.ts.map +1 -1
  139. package/dist/register-bands.js.map +1 -1
  140. package/dist/register.d.ts +2 -2
  141. package/dist/sources-record.d.ts +38 -0
  142. package/dist/sources-record.d.ts.map +1 -0
  143. package/dist/sources-record.js +123 -0
  144. package/dist/sources-record.js.map +1 -0
  145. package/dist/sources-rules.d.ts +44 -0
  146. package/dist/sources-rules.d.ts.map +1 -0
  147. package/dist/sources-rules.js +76 -0
  148. package/dist/sources-rules.js.map +1 -0
  149. package/dist/tex-paper.d.ts +10 -15
  150. package/dist/tex-paper.d.ts.map +1 -1
  151. package/dist/tex-paper.js +24 -40
  152. package/dist/tex-paper.js.map +1 -1
  153. package/dist/tex-venue-rules.d.ts +3 -3
  154. package/dist/tex-venue-rules.d.ts.map +1 -1
  155. package/dist/tex-venue-rules.js.map +1 -1
  156. package/docs/configuration.md +12 -4
  157. package/docs/design/paper-sources.md +197 -0
  158. package/docs/package-shape-options.md +1 -1
  159. package/docs/references.md +34 -8
  160. package/docs/rules/bib/reachable-entry.md +64 -0
  161. package/docs/rules/paper/sources-fresh.md +81 -0
  162. package/docs/rules.md +62 -57
  163. package/eslint-rules/paper-context.ts +4 -3
  164. package/eslint-rules/paper-typography.test.mjs +12 -72
  165. package/eslint-rules/paper-typography.ts +13 -6
  166. package/eslint.config.mjs +22 -0
  167. package/fixtures/build-e2e/aidc/paper.tex +1 -1
  168. package/fixtures/build-e2e/anon-ack/paper.tex +1 -1
  169. package/fixtures/build-e2e/anon-author/paper.tex +1 -1
  170. package/fixtures/build-e2e/anon-bib/paper.tex +1 -1
  171. package/fixtures/build-e2e/anon-href/paper.tex +1 -1
  172. package/fixtures/build-e2e/anon-macro/paper.tex +1 -1
  173. package/fixtures/build-e2e/anon-pdfauthor/paper.tex +1 -1
  174. package/fixtures/paper-sources/p1/paper.tex +30 -0
  175. package/fixtures/paper-sources/p1/refs.bib +6 -0
  176. package/fixtures/paper-sources/p1/sections/dead.tex +6 -0
  177. package/fixtures/paper-sources/p1/sections/intro.tex +7 -0
  178. package/fixtures/paper-sources/p1/sections/parked.tex +6 -0
  179. package/fixtures/paper-sources/p1/tex-truth.json +37 -0
  180. package/fixtures/paper-sources/p1/versions/2026-01-01-review.tex +4 -0
  181. package/fixtures/paper-sources/v1-stale/paper.tex +14 -0
  182. package/fixtures/paper-sources/v1-stale/refs.bib +6 -0
  183. package/fixtures/paper-sources/v1-stale/tex-truth.json +32 -0
  184. package/fixtures/paper-sources/v10-ifthenelse/anon.bib +1 -0
  185. package/fixtures/paper-sources/v10-ifthenelse/paper.tex +9 -0
  186. package/fixtures/paper-sources/v10-ifthenelse/real.bib +1 -0
  187. package/fixtures/paper-sources/v10-ifthenelse/tex-truth.json +28 -0
  188. package/fixtures/paper-sources/v11-two-blocks/paper.tex +12 -0
  189. package/fixtures/paper-sources/v11-two-blocks/tex-truth.json +30 -0
  190. package/fixtures/paper-sources/v13-percent-text/paper.tex +6 -0
  191. package/fixtures/paper-sources/v13-percent-text/refs.bib +3 -0
  192. package/fixtures/paper-sources/v13-percent-text/tex-truth.json +30 -0
  193. package/fixtures/paper-sources/v15-after-end/paper.tex +6 -0
  194. package/fixtures/paper-sources/v15-after-end/refs.bib +1 -0
  195. package/fixtures/paper-sources/v15-after-end/tex-truth.json +17 -0
  196. package/fixtures/paper-sources/v16-included-block/bibblock.tex +3 -0
  197. package/fixtures/paper-sources/v16-included-block/paper.tex +7 -0
  198. package/fixtures/paper-sources/v16-included-block/refs.bib +1 -0
  199. package/fixtures/paper-sources/v16-included-block/tex-truth.json +35 -0
  200. package/fixtures/paper-sources/v17-include-order/bibsetup.tex +1 -0
  201. package/fixtures/paper-sources/v17-include-order/first.bib +1 -0
  202. package/fixtures/paper-sources/v17-include-order/later.bib +1 -0
  203. package/fixtures/paper-sources/v17-include-order/paper.tex +7 -0
  204. package/fixtures/paper-sources/v17-include-order/tex-truth.json +40 -0
  205. package/fixtures/paper-sources/v2-overwrite/paper.tex +14 -0
  206. package/fixtures/paper-sources/v2-overwrite/refs.bib +6 -0
  207. package/fixtures/paper-sources/v2-overwrite/tex-truth.json +30 -0
  208. package/fixtures/paper-sources/v22-parked-thebibliography/paper.tex +7 -0
  209. package/fixtures/paper-sources/v22-parked-thebibliography/tex-truth.json +17 -0
  210. package/fixtures/paper-sources/v23-parked-include/old.bib +1 -0
  211. package/fixtures/paper-sources/v23-parked-include/paper.tex +7 -0
  212. package/fixtures/paper-sources/v23-parked-include/parked.tex +1 -0
  213. package/fixtures/paper-sources/v23-parked-include/refs.bib +1 -0
  214. package/fixtures/paper-sources/v23-parked-include/tex-truth.json +28 -0
  215. package/fixtures/paper-sources/v24-quoted-fields/paper.tex +6 -0
  216. package/fixtures/paper-sources/v24-quoted-fields/refs.bib +5 -0
  217. package/fixtures/paper-sources/v24-quoted-fields/tex-truth.json +32 -0
  218. package/fixtures/paper-sources/v25-string-differs/paper.tex +10 -0
  219. package/fixtures/paper-sources/v25-string-differs/refs.bib +2 -0
  220. package/fixtures/paper-sources/v25-string-differs/tex-truth.json +32 -0
  221. package/fixtures/paper-sources/v26-comment-in-names/one.bib +1 -0
  222. package/fixtures/paper-sources/v26-comment-in-names/paper.tex +7 -0
  223. package/fixtures/paper-sources/v26-comment-in-names/tex-truth.json +32 -0
  224. package/fixtures/paper-sources/v26-comment-in-names/two.bib +1 -0
  225. package/fixtures/paper-sources/v27-shared-string/abbreviations.bib +2 -0
  226. package/fixtures/paper-sources/v27-shared-string/paper.tex +6 -0
  227. package/fixtures/paper-sources/v27-shared-string/references.bib +6 -0
  228. package/fixtures/paper-sources/v27-shared-string/tex-truth.json +31 -0
  229. package/fixtures/paper-sources/v28-shared-string-block/abbreviations.bib +2 -0
  230. package/fixtures/paper-sources/v28-shared-string-block/paper.tex +14 -0
  231. package/fixtures/paper-sources/v28-shared-string-block/tex-truth.json +33 -0
  232. package/fixtures/paper-sources/v29-generated-input/paper.tex +9 -0
  233. package/fixtures/paper-sources/v29-generated-input/tex-truth.json +24 -0
  234. package/fixtures/paper-sources/v3-declared/paper.bib +6 -0
  235. package/fixtures/paper-sources/v3-declared/paper.tex +6 -0
  236. package/fixtures/paper-sources/v3-declared/tex-truth.json +28 -0
  237. package/fixtures/paper-sources/v30-name-next-line/paper.tex +14 -0
  238. package/fixtures/paper-sources/v30-name-next-line/tex-truth.json +30 -0
  239. package/fixtures/paper-sources/v4-commented/paper.tex +14 -0
  240. package/fixtures/paper-sources/v4-commented/refs.bib +6 -0
  241. package/fixtures/paper-sources/v4-commented/tex-truth.json +28 -0
  242. package/fixtures/paper-sources/v5-percent-entry/paper.tex +20 -0
  243. package/fixtures/paper-sources/v5-percent-entry/tex-truth.json +37 -0
  244. package/fixtures/paper-sources/v6-unclosed/paper.tex +6 -0
  245. package/fixtures/paper-sources/v6-unclosed/refs.bib +5 -0
  246. package/fixtures/paper-sources/v6-unclosed/tex-truth.json +37 -0
  247. package/fixtures/paper-sources/v8-jobname/paper.tex +9 -0
  248. package/fixtures/paper-sources/v8-jobname/tex-truth.json +30 -0
  249. package/fixtures/paper-sources/v9-two-bibliographies/one.bib +1 -0
  250. package/fixtures/paper-sources/v9-two-bibliographies/paper.tex +7 -0
  251. package/fixtures/paper-sources/v9-two-bibliographies/tex-truth.json +35 -0
  252. package/fixtures/paper-sources/v9-two-bibliographies/two.bib +1 -0
  253. package/fixtures/paper-typography/clean-paper/paper.tex +1 -1
  254. package/fixtures/paper-typography/messy-paper/paper.tex +1 -1
  255. package/package.json +2 -10
  256. package/scripts/check.test.ts +23 -1
  257. package/scripts/check.ts +7 -3
  258. package/scripts/coverage-config.test.ts +19 -10
  259. package/scripts/layer-legacy-frozen.test.ts +41 -2
  260. package/scripts/layer-legacy-frozen.ts +31 -2
  261. package/skills/grade-paper-writing/SKILL.md +4 -3
  262. package/skills/grade-paper-writing/SKILL.md.spec.ts +3 -2
  263. package/skills/grade-paper-writing/prose-lint.mjs +11 -2
  264. package/skills/grade-paper-writing/prose-lint.test.ts +52 -4
  265. package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +83 -33
  266. package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +67 -115
  267. package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +128 -121
  268. package/skills/verify-citations/SKILL.md +7 -4
  269. package/skills/verify-citations/SKILL.md.spec.ts +6 -3
  270. package/skills/verify-citations/scripts/bib-authors.check.test.mjs +17 -19
  271. package/skills/verify-citations/scripts/bib-authors.cli.test.ts +129 -46
  272. package/skills/verify-citations/scripts/bib-authors.d.mts +22 -2
  273. package/skills/verify-citations/scripts/bib-authors.mjs +57 -111
  274. package/skills/verify-citations/scripts/verify-cites.cli.test.ts +16 -0
  275. package/skills/verify-citations/scripts/verify-cites.d.mts +19 -3
  276. package/skills/verify-citations/scripts/verify-cites.mjs +42 -108
  277. package/skills/verify-citations/scripts/verify-cites.test.mjs +23 -36
  278. package/src/adapters/bibtex/index.test.ts +163 -0
  279. package/src/adapters/bibtex/index.ts +155 -0
  280. package/src/adapters/latex/filecontents.ts +114 -0
  281. package/src/adapters/latex/includes.ts +18 -10
  282. package/src/adapters/latex/index.test.ts +73 -0
  283. package/src/adapters/latex/index.ts +2 -0
  284. package/src/adapters/latex/nodes.ts +64 -0
  285. package/src/adapters/latex/parse.test.ts +38 -0
  286. package/src/adapters/latex/parse.ts +67 -6
  287. package/src/adapters/memory/index.ts +17 -0
  288. package/src/adapters/references/index.test.ts +75 -21
  289. package/src/adapters/references/index.ts +37 -32
  290. package/src/adapters/sources-record/index.test.ts +87 -0
  291. package/src/adapters/sources-record/index.ts +82 -0
  292. package/src/adapters/tex-output/blg.ts +27 -0
  293. package/src/adapters/tex-output/fls.ts +24 -0
  294. package/src/adapters/tex-output/index.test.ts +171 -0
  295. package/src/adapters/tex-output/index.ts +12 -0
  296. package/src/bib-rules.test.ts +356 -0
  297. package/src/bib-rules.ts +228 -0
  298. package/src/bibliography-at.test.ts +286 -0
  299. package/src/build.test.ts +210 -0
  300. package/src/build.ts +100 -35
  301. package/src/claim-provenance.test.ts +33 -7
  302. package/src/claim-provenance.ts +2 -2
  303. package/src/cli.harness.mjs +20 -10
  304. package/src/cli.test.ts +14 -10
  305. package/src/cli.ts +37 -28
  306. package/src/domain/paper-source.test.ts +58 -20
  307. package/src/domain/paper-source.ts +38 -42
  308. package/src/domain/paper-sources.ts +94 -0
  309. package/src/domain/sources-record.test.ts +71 -0
  310. package/src/domain/sources-record.ts +136 -0
  311. package/src/domain/tex-run.test.ts +228 -0
  312. package/src/domain/tex-run.ts +139 -0
  313. package/src/heading-case.test.ts +24 -10
  314. package/src/latex-log.ts +20 -0
  315. package/src/latex-loop.harness.ts +1 -0
  316. package/src/latex-loop.ts +2 -0
  317. package/src/linted-set.test.ts +9 -1
  318. package/src/new-paper.harness.mjs +3 -2
  319. package/src/new-paper.test.ts +9 -5
  320. package/src/package-dirs.ts +3 -2
  321. package/src/paper-includes.test.ts +157 -38
  322. package/src/paper-includes.ts +39 -35
  323. package/src/paper-prose.test.ts +34 -5
  324. package/src/paper-prose.ts +2 -2
  325. package/src/paper-record.test.ts +173 -0
  326. package/src/paper-record.ts +66 -0
  327. package/src/paper-settings-commands.test.ts +5 -2
  328. package/src/paper-sources.ts +151 -0
  329. package/src/ports/bib-reader.ts +27 -0
  330. package/src/ports/check-references.ts +7 -3
  331. package/src/ports/latex.ts +19 -0
  332. package/src/ports/sources-codec.ts +11 -0
  333. package/src/ports/tex-output.ts +17 -0
  334. package/src/recorded-bibliography.ts +297 -0
  335. package/src/reference-rules.ts +217 -104
  336. package/src/references.test.ts +520 -177
  337. package/src/references.ts +185 -37
  338. package/src/register-bands.test.ts +5 -1
  339. package/src/register-bands.ts +3 -5
  340. package/src/register.test.ts +6 -1
  341. package/src/register.ts +2 -2
  342. package/src/rule-cases.test.ts +68 -12
  343. package/src/rule-docs.test.ts +0 -1
  344. package/src/sources-record.test.ts +308 -0
  345. package/src/sources-record.ts +231 -0
  346. package/src/sources-rules.test.ts +152 -0
  347. package/src/sources-rules.ts +117 -0
  348. package/src/tex-paper.test.ts +148 -78
  349. package/src/tex-paper.ts +31 -61
  350. package/src/tex-venue-rules.test.ts +36 -8
  351. package/src/tex-venue-rules.ts +3 -5
  352. package/dist/eslint-rules/bib-reachable-entry.d.ts +0 -22
  353. package/dist/eslint-rules/bib-reachable-entry.d.ts.map +0 -1
  354. package/dist/eslint-rules/bib-reachable-entry.js +0 -75
  355. package/dist/eslint-rules/bib-reachable-entry.js.map +0 -1
  356. package/eslint-rules/bib-reachable-entry.ts +0 -76
@@ -0,0 +1,197 @@
1
+ # Paper sources — TeX answers "which files, which bibliography"
2
+
3
+ As built in PR #174. Everything that asks which files make up a paper, or which bibliography TeX reads,
4
+ reads the record the last build wrote from TeX's own files; nothing in paperlint reads TeX source to
5
+ decide either, and nothing reads a `.bib` but one adapter. TeX's own answer for each planted paper is
6
+ recorded beside it (§8), and the consumers are tested against that, not against prose.
7
+
8
+ ## 1. TeX is the one source of truth
9
+
10
+ Which files make up the paper, and which bibliography it uses, are answered by TeX, from the last
11
+ build — never by reading TeX by hand. There is no static decision: no declaration scan, no
12
+ `filecontents` runs, no include ordering. Lint never runs TeX; it reads what the build recorded.
13
+
14
+ **What the build records — `_build/sources.json`, schema 1.** Written by the compile step, after
15
+ the last pdflatex pass and bibtex, from TeX's own files in the paper directory:
16
+
17
+ | field | from | holds |
18
+ | --------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
19
+ | `inputs` | `paper.fls` (`-recorder`) | every file in the paper directory TeX read, in first-read order, each `role: preamble` or `body` — before or after TeX opens `paper.aux` for writing (`\begin{document}`, measured) |
20
+ | `written` | `paper.fls` `OUTPUT` | the `.bib` and `.tex` files TeX wrote (a `filecontents` block) |
21
+ | `bibdata` | `paper.aux` `\bibdata{…}` | the databases bibtex was told to read, after every switch, macro and `\input` |
22
+ | `bibtex` | `paper.blg`, `paper.bbl` | the database files bibtex opened, the keys it typeset, its exit and errors |
23
+ | `sha256` | the files themselves | every `inputs` file and every database bibtex opened (one TeX wrote too), by path and bytes |
24
+
25
+ A file TeX wrote is an input only when it is a `.tex`: a `filecontents` block that writes `body.tex` the
26
+ body `\input`s puts that text in the PDF, so the file is hashed and spliced like any other (measured:
27
+ pass 1 `OUTPUT body.tex` then `INPUT ./body.tex` after `\begin{document}`). Every other file a pass
28
+ wrote — `.aux`, `.out`, `.toc`, a block's `.bib` — is TeX's bookkeeping or a database, not an input.
29
+ The written `.tex` is not handed to ESLint as a file of its own: its words are the block's, and the
30
+ block's body is no prose to the rules, so the text is judged once, where `readPaper` splices it.
31
+
32
+ Every path is relative to the paper directory and spelled as the directory entries spell it: TeX
33
+ logs the name it opened, which on a file system that ignores case is the source's spelling, so the
34
+ record takes each segment's spelling from the disk and one paper gets one record on every platform.
35
+
36
+ A record of its own, not `references.json`: it is written by every build (the references step is
37
+ optional and may record "not checked"), it is read by rules that have nothing to do with references,
38
+ and it goes stale when the SOURCES change, not when one `.bib` does — one staleness key per file.
39
+ `references.json` keeps its verdicts, one per entry of `sources.json`'s databases, and loses its
40
+ `bibtex` field.
41
+
42
+ **Staleness.** A rule recomputes `sha256` over the files the record lists. No record, or a different
43
+ hash (a file edited, added to an `\input`, deleted), is one finding of a new rule,
44
+ `paper/sources-fresh` — "not built" / "the paper changed since the last build — run
45
+ `npx paperlint build`" — and every rule below is silent until a build records the paper again.
46
+
47
+ **The consumers.**
48
+
49
+ | consumer | reads | no record / stale |
50
+ | ---------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------ |
51
+ | `bib/reachable-entry`, `paper/author-list`, `paper/cite-exists` | the entries of the databases bibtex opened, through the `BibReader` | silent (`paper/sources-fresh` speaks) |
52
+ | `paper/refs-checked` | keys bibtex typeset that the reader did not read (`%`, `@comment`) | silent |
53
+ | `extract-ref-facts`, `bib-authors`, `verify-cites` (dir, `.tex`) | `bibliographyAt` → the record's databases | refused: "run `npx paperlint build` first" |
54
+ | `bibliographyAt` with a `.bib` | that file alone, unchanged | — |
55
+ | `paperlint lint`'s files (`paper-includes.ts`), `paper-context` | the `body` `.tex` files of `inputs` TeX did not write | `paper.tex` only; `paper/sources-fresh` says the rest is unlinted |
56
+ | the prose rules' text (`readPaper`) | `paper.tex` with the files of `inputs` spliced where they stand | `paper.tex` alone; `paper/sources-fresh` says the rest is unlinted |
57
+
58
+ An entry in a `.bib` TeX wrote is reported at the `filecontents` block whose body bibtex takes as
59
+ the same database (`sameDatabase`) — a lookup of where the text stands, not a decision of what TeX
60
+ reads; an entry in any other `.bib` at the top of `paper.tex`, `refs.bib:12:1:` first.
61
+
62
+ **What stays syntactic: where an include stands, and nothing else.** The prose rules read
63
+ `paper.tex` with the files of `inputs` spliced in (`readPaper`): a projection of the text for rules to
64
+ read in context, which decides nothing above. Which files are spliced is the record's answer — an
65
+ include of a file `inputs` does not list (behind `\iffalse`, or past `\end{document}`) contributes
66
+ nothing, as it did to TeX, and no include is looked for on disk. Where the macro stands in the text
67
+ is the one thing read from the text, because `.fls` records no position: files are spliced in the
68
+ order the text names them, which is the order TeX read them, each ended the way TeX ends the last
69
+ line of a file it reads (a newline when the file has none — measured: `a\input{f}b` with `f` holding
70
+ `foo` typesets "afoo b"). With no record, or a stale one, the text is `paper.tex` alone. A file the
71
+ record lists that no include macro in the text stands for (an `\input` inside a macro's body, #177)
72
+ is linted as a file of its own and is absent from the whole-paper text.
73
+
74
+ **Ground truth.** `test/e2e/tex/paper-sources.e2e.ts` runs the build's own record step on each
75
+ planted paper and snapshots its `sources.json` as `tex-truth.json`; the unit tests run the
76
+ consumers on the fixture with that record. TeX is the expectation and the input at once.
77
+
78
+ ## 4. One bibtex reader
79
+
80
+ The port is `BibReader` (`src/ports/bib-reader.ts`: `read(path, text, body, inherited?)` for a block,
81
+ `readFile(path, text, inherited?)` for a `.bib`). bibtex reads a paper's databases in sequence with one
82
+ table of `@string`s, so a `@string` of `abbrev.bib` expands an entry of `refs.bib` under
83
+ `\bibliography{abbrev,refs}` (measured; in the other order bibtex warns it is undefined).
84
+ `recordedBibliography` reads the databases in the order bibtex opened them, each with the `strings` the
85
+ one before it left in force (`inherited`), and reads a block with the `@string`s its database was read
86
+ with; each database is still parsed as its own text, so each entry keeps its place in its own file.
87
+
88
+ The adapter, `src/adapters/bibtex/`, is the only code that knows `@retorquere/bibtex-parser`. It reads each database twice:
89
+
90
+ - **`entries`** — LaTeX read into text (`{\"u}` → `ü`, `\url{x}` → a link), names split into parts,
91
+ `@string`s expanded where they are used. What the checks judge.
92
+ - **`written`** — `raw: true`, every field verbatim, no outer braces dropped: each field as bibtex
93
+ takes it, `@string`s expanded where used, LaTeX as written. What `sameDatabase` compares, with the
94
+ `@preamble` commands, so `{\"o}` and `ö` stay two values, as they are in the `.bbl`.
95
+
96
+ **The fence is lint, not prose.** `eslint.config.mjs` sets `n/no-restricted-import` on
97
+ `@retorquere/bibtex-parser` (and its subpaths) for every file but `src/adapters/bibtex/**`
98
+ (`test/eslint-bib-parser.test.ts` plants imports in the app, the domain, another adapter and a
99
+ skill's script).
100
+
101
+ ## 5. A path a person gave → `bibliographyAt`
102
+
103
+ `extract-ref-facts`, `bib-authors` and `verify-cites` (its `.bib` input) take a path on the command
104
+ line. All three ask `bibliographyAt(path, deps)`: a `.bib` is read alone, as named; a `.tex`, or a
105
+ directory holding `paper.tex`, is the bibliography the last build's bibtex opened for that paper
106
+ (`recordedBibliography`) — for a database TeX wrote, the text of the `filecontents` block (or included
107
+ file) that holds it, so an entry stands where the author edits it. A refusal is a `BibliographyUnread`
108
+ (`missing`, `not-bib-or-tex`, `no-paper`, `not-built`, `no-database`, `outside-paper`), and
109
+ `bibliographyUnreadWhy` is its one sentence; `outside-paper` is a paper whose bibtex also opened a
110
+ database the paper directory does not hold (BIBINPUTS) — refused rather than read in part;
111
+ `not-built` covers a missing, unreadable and stale record alike: run `npx paperlint build` first. Each script keeps its own CLI frame around it.
112
+
113
+ ## 6. The references a build checks
114
+
115
+ The build's references step (`src/references.ts`) checks entries, not keys: two databases may each
116
+ define a key with other metadata.
117
+
118
+ - **What is checked is `readBibliography`**: the databases the build's bibtex opened, in the order it
119
+ opened them, each once, read by the bibtex reader — a view over `recordedBibliography`, the one reader
120
+ of the record's databases. Without a fresh record the step checks nothing and its note names the
121
+ reason; a database bibtex opened that is not in the paper directory (bibtex found it on its search
122
+ path, `BIBINPUTS`) is named, not skipped. One removed after the build — a `.bib` TeX wrote, then a
123
+ clean — makes the record stale: every database bibtex opened is hashed.
124
+ - **One verdict per entry, in order.** The `CheckReferences` port returns verdict `i` for entry `i`;
125
+ the adapter runs verify-cites and bib-authors per entry and pairs them by position.
126
+ - **`_build/references.json`, schema 3**: `bib {sources, sha256}`, `status`, `entries`. `sources` are
127
+ the database names as bibtex names them and `sha256` covers their text. Lint lays the verdicts on the
128
+ entries of the same databases and reports each at its entry (`entryReports`); a record that is not one
129
+ verdict per entry, in order, about the bytes the databases hold now, is not about this bibliography
130
+ (`paper/refs-fresh`).
131
+
132
+ ## 7. The post-build check — where bibtex reads differently
133
+
134
+ bibtex has no comment syntax and recovers from malformed text as no parser does, so the reader does
135
+ not emulate it. Each difference has an owner:
136
+
137
+ | shape | answered by |
138
+ | -------------------------------------------------------------- | ---------------------------------------------------------------------- |
139
+ | malformed: an entry or brace never closed, `%` inside an entry | the build — bibtex exits 2, and `paperlint build` fails with its lines |
140
+ | an entry behind `%`, or inside `@comment{…}` | `paper/refs-checked`, from the keys bibtex typeset |
141
+ | `@`, `)` or `{"}` inside a quoted field | the reader, which reads it as bibtex does |
142
+
143
+ `paper/refs-checked` names every key bibtex typeset (`paper.bbl`) that no entry of the databases it
144
+ opened has (`unseenKeys`; keys compare case-insensitively, as bibtex's do), and every database bibtex
145
+ opened that is not in the paper directory.
146
+
147
+ ## 8. Ground truth — `tex-truth.json`
148
+
149
+ Each planted paper of `fixtures/paper-sources/` carries `tex-truth.json`, written by TeX:
150
+ `test/e2e/tex/paper-sources.e2e.ts` builds it with `pdflatex -recorder` and `bibtex` and snapshots the
151
+ record the build writes. `-u` re-records; a TeX that answers differently fails. The unit tests of the
152
+ consumers lay that file at `_build/sources.json` (`test/recorded-fixture.ts`). The accepted papers of
153
+ `fixtures/accepted-papers/` carry theirs the same way (`test/e2e/tex/accepted-papers-record.e2e.ts`).
154
+ The files are the numbers; this document does not copy them.
155
+
156
+ ## 9. Decisions
157
+
158
+ - **The record, not a reading** — which files and databases a paper has is what TeX did on the last
159
+ build; a static reading of the text agrees only on the cases its author thought of.
160
+ - **No record, no verdict** — a rule that needs the record is silent without a current one, and one
161
+ rule (`paper/sources-fresh`) says so once; a stale answer is worse than none.
162
+ - **Prose rules splice only the files the record lists** — an include behind `\iffalse` contributes
163
+ nothing; without a record the text is `paper.tex` alone and `paper/sources-fresh` says the rest went
164
+ unlinted.
165
+ - **A lookup of where a text stands is not a decision of what TeX reads** — `filecontents` blocks are
166
+ found in the text only to put a finding on the line that holds an entry (`sameDatabase`).
167
+ - **One verdict per entry, not per key** — a key does not identify an entry; a verdict that cannot say
168
+ which entry it is about reports at the wrong one.
169
+ - **"Same database" is what bibtex takes** — raw bytes and the final `@string` map are a proxy that
170
+ misses a `@string` redefined after use; the text between entries reaches no `.bbl`.
171
+ - **One `.bib` reader, fenced by lint** — three readers grew before there was one; the parser's name
172
+ may appear in one folder only.
173
+ - **One path → bibliography function** — three scripts each resolved it by hand, each with its own
174
+ refusals.
175
+ - **Recorded TeX truth, not hand-written expectations** — the consumers' tests compare with what TeX
176
+ did, so a wrong belief about TeX cannot be written into both the code and its test.
177
+ - **The post-build check, not emulation** — no parser recovers as bibtex does; bibtex's own output
178
+ is compared instead.
179
+
180
+ ## 10. Scope: what this PR does not decide
181
+
182
+ - A finding for a `filecontents` block without `[overwrite]` (#176).
183
+ - A listed body file that no include macro in the text stands for (an `\input` inside a macro's body,
184
+ #177): linted as a file of its own, absent from the whole-paper text.
185
+ - `eslint --cache`: `bib/reachable-entry` depends on files other than the linted one, so a cached
186
+ result can be stale after a `.bib` changes. `paperlint lint` does not cache.
187
+ - Reading past `\end{document}` in `headings.ts`, `layout.ts` and `rendered.ts` (#178).
188
+ - A file a `filecontents` block writes under another extension than `.tex` (a `.pgf` the body
189
+ `\input`s) is not an input: its text reaches the PDF, but the record neither hashes nor splices it.
190
+ - A database TeX wrote is read from the file it left on disk, which the record does not hash: edited
191
+ after a build, that file is not detected as stale.
192
+ - Two copies compared where bibtex and the parser differ in what no check reads: a `@preamble` is
193
+ compared as written, so a `@string` it uses that is redefined between the copies is not seen; and
194
+ `keywords` are compared as the parser splits, dedupes and sorts them — it does so for that field
195
+ name unconditionally, with no option to keep it as written.
196
+ - Q3 (one projection of the live text, replacing `paper-typography`'s `skippedRanges` and
197
+ `texToMdast`'s own parse), the hooks, and an enforcement rule for Q1–Q3: later PRs.
@@ -138,7 +138,7 @@ lockfile in that array was not.
138
138
  zero: `pipeline-check.mjs`, `extract-ref-facts.mjs` and `bib-authors.mjs`, all reaching
139
139
  `markdown-it`. Which retires a number this note carried: the closure is **two** third-party
140
140
  packages over **eight** referenced scripts, not one over seven — `extract-ref-facts.mjs` reaches
141
- `@retorquere/bibtex-parser` through a dynamic `await import()` that a static grep does not see.
141
+ `@retorquere/bibtex-parser`, now through `#src/adapters/bibtex/index`.
142
142
 
143
143
  ⏳ **Not measured, recorded as such:** the documented 60-second install timeout; the personal
144
144
  `~/.claude/skills` and `--add-dir` channels (2 of 4 locations verified); and a genuine
@@ -7,7 +7,11 @@ what happens in CI and without a network.
7
7
 
8
8
  ## What is checked, and where
9
9
 
10
- For each entry in the bibliography (the `filecontents` block inside `paper.tex`, else `refs.bib`):
10
+ For each entry of the databases the build's bibtex **opened** — the `.bib` files its `\bibliography` /
11
+ `\addbibresource` named after every switch and macro, and the file a `filecontents` block wrote. The
12
+ build records them in `<paper>/_build/sources.json` from TeX's own files (`paper.blg`), so nothing here
13
+ guesses which `.bib` TeX reads by reading the `.tex`. Their text is read by the one bibtex reader
14
+ (`src/ports/bib-reader.ts`):
11
15
 
12
16
  | question | asked of |
13
17
  | -------------------------------------------------------------------------------------------------------- | ------------------------------------------- |
@@ -23,18 +27,40 @@ A work that is simply not found is `unresolvable`, never a failure: plenty of re
23
27
  indexed. Only a positive disproof fails an entry, for example a DOI that resolves to a different
24
28
  paper, or one the DOI authority says does not exist.
25
29
 
26
- The verdicts land in `<paper>/_build/references.json`. `paperlint lint` reads that file and reports:
30
+ The verdicts land in `<paper>/_build/references.json`: one verdict per entry of those databases, in the
31
+ order bibtex opened them, and a hash of their text. `paperlint lint` reads that file and reports:
27
32
 
28
- | rule | when |
29
- | -------------------- | ----------------------------------------------------------------- |
30
- | `paper/cite-exists` | an entry's identifier provably does not resolve to the work cited |
31
- | `paper/author-list` | an entry's authors are the preprint's under a published venue |
32
- | `paper/refs-fresh` | the bibliography changed since the build checked it — build again |
33
- | `paper/refs-checked` | no build has checked the references, or the last one could not |
33
+ | rule | when |
34
+ | -------------------- | ----------------------------------------------------------------------------------------- |
35
+ | `paper/cite-exists` | an entry's identifier provably does not resolve to the work cited |
36
+ | `paper/author-list` | an entry's authors are the preprint's under a published venue |
37
+ | `paper/refs-fresh` | the databases changed since the build checked them — build again |
38
+ | `paper/refs-checked` | no build has checked the references, or the last one could not, or the reader missed some |
39
+
40
+ An entry of a `.bib` is reported at the first line of `paper.tex`, with the entry's file, line and
41
+ column at the front of the message (`refs.bib:12:1: …`): ESLint does not lint a `.bib`.
34
42
 
35
43
  The step never fails the build. Without a network the PDF is still built and lint warns that the
36
44
  references were not checked.
37
45
 
46
+ **No record of the build, no answer.** Which databases bibtex opened is in `sources.json`, so a paper
47
+ with no record (never built), or one whose files changed since (`paper/sources-fresh`), has none to
48
+ check: the step records nothing, and the four rules are silent while `paper/sources-fresh` says once
49
+ that the paper must be built.
50
+
51
+ **What bibtex typeset, against what paperlint read.** The record also holds the keys bibtex typeset
52
+ (its `paper.bbl`). bibtex has no comment syntax, so an entry behind `%` or inside `@comment{…}` is one
53
+ it typesets and paperlint's reader does not; `paper/refs-checked` names each such key as not checked.
54
+ (bibtex's keys are case-insensitive: `\cite{SMITH}` typesets `\bibitem{SMITH}` from an entry `smith`,
55
+ which is the same entry.) A malformed `.bib` never gets that far: bibtex fails on it, and `paperlint
56
+ build` fails with bibtex's own lines.
57
+
58
+ A database TeX wrote itself (a `filecontents` block) is read from the file it left beside the paper.
59
+ The build hashes it like any other, so when that file is edited or removed after the build the record
60
+ is stale and `paper/sources-fresh` says so: build again to write it. A database bibtex found outside
61
+ the paper directory, on its search path (`BIBINPUTS`), is not read: the step says which and
62
+ `paper/refs-checked` warns.
63
+
38
64
  ## `repro/references-cache.json` — commit it
39
65
 
40
66
  Asking five services about every entry is slow. DBLP asks clients to pause between requests, and a
@@ -0,0 +1,64 @@
1
+ # bib/reachable-entry
2
+
3
+ **Level:** warn · **Reads:** `paper.tex`, and `_build/sources.json` — the databases the last build's
4
+ bibtex opened, read through the bibtex reader
5
+
6
+ ## What it catches
7
+
8
+ A bibliography entry with no `doi`, no `url` and no arXiv id: nothing a reader can follow. The entries
9
+ judged are those of the databases bibtex opened in the last build — TeX's own answer, recorded by
10
+ `paperlint build`, not a reading of the source for `\bibliography`.
11
+
12
+ ## Why
13
+
14
+ Reviewer A on HotCRP #20 (2026-08-23): "not every entry has a DOI/link" — one paper had 0 of 47
15
+ entries reachable. Not "no doi": ICLR, NeurIPS and TMLR issue no DOIs at all (measured 2026-08-24),
16
+ so a url or an arXiv id counts.
17
+
18
+ ## Examples
19
+
20
+ Reported on the entry's own line when the `.bib` bibtex opened is one TeX wrote from a `filecontents`
21
+ block of `paper.tex`; any other entry — a `.bib` file the author keeps, or a block in an included file —
22
+ at the top of `paper.tex`, with the entry's file, line and column first:
23
+
24
+ ```bibtex
25
+ @book{lost2026, title = {A Book}, year = {2026}}
26
+ ```
27
+
28
+ > refs.bib:12:1: `lost2026` has no doi, url or arXiv id — a reader has nothing to follow. If none
29
+ > exists, keep the exception with `% eslint-disable-next-line bib/reachable-entry -- <why>` above
30
+ > the entry
31
+
32
+ Not reported:
33
+
34
+ ```bibtex
35
+ @article{a, doi = {10.1/x}}
36
+ @misc{b, url = {https://example.org}}
37
+ @misc{c, note = {arXiv:2310.05736}}
38
+ ```
39
+
40
+ ## Options / preset fields
41
+
42
+ None.
43
+
44
+ ## What it does not check
45
+
46
+ - Whether the link resolves: that is the references check of `paperlint build`.
47
+ - `@string` and `@preamble`, which are not entries.
48
+ - A paper with no record of a build, or one changed since: the rule is silent, and
49
+ [`paper/sources-fresh`](../paper/sources-fresh.md) says once that it was not built.
50
+ - A database bibtex found outside the paper directory, and an entry bibtex reads that the `.bib` reader
51
+ does not (behind `%`, inside `@comment{…}`): that is the post-build check's.
52
+ - With `eslint --cache`, a change to a `.bib` alone does not re-run the rule: its findings depend on
53
+ files other than `paper.tex`. `paperlint lint` does not cache.
54
+
55
+ ## How to fix
56
+
57
+ Add the entry's `doi`, `url` or arXiv id. An entry that has none — a talk, a personal communication
58
+ — keeps its exception where it is written, on the line above the entry, in the block or in the
59
+ `.bib`:
60
+
61
+ ```bibtex
62
+ % eslint-disable-next-line bib/reachable-entry -- an invited talk, no recording exists
63
+ @misc{smith2024talk, …}
64
+ ```
@@ -0,0 +1,81 @@
1
+ # paper/sources-fresh
2
+
3
+ **Level:** warn · **Reads:** `paper.tex` and `_build/sources.json` · **Reported on:** the first line of
4
+ `paper.tex`
5
+
6
+ ## What it catches
7
+
8
+ The paper has no usable record of the last build, so paperlint does not know which files TeX reads for
9
+ it. One finding, in one of three forms:
10
+
11
+ - **not built** — there is no `_build/sources.json`;
12
+ - **unreadable** — the file is not JSON, is of another schema number, or does not have the shape of
13
+ schema 1;
14
+ - **stale** — a file the build read has other bytes now, was deleted, or appeared where the build saw
15
+ none. The message names each: `sections/intro.tex edited, refs.bib deleted`.
16
+
17
+ ## Why
18
+
19
+ `paperlint build` asks TeX itself which files it read (`pdflatex -recorder`) and which databases bibtex
20
+ opened, and writes the answer, with a SHA-256 of every file, to `_build/sources.json`. Nothing in
21
+ paperlint answers "which files make up this paper" or "which bibliography does it use" by reading the
22
+ TeX source: `\input` behind a macro, a `.bib` written by `filecontents`, a database named by a
23
+ switch are TeX's to decide, and TeX has already decided.
24
+
25
+ The rules that need that answer — the ones over the bibliography, and the prose rules over the files
26
+ `paper.tex` includes — read the record. With no record, or one about an earlier paper, they would
27
+ judge files that are not the paper's, so they stay silent, and this rule speaks once in their place.
28
+ Without it a paper that was never built would lint clean because nothing was looked at.
29
+
30
+ ## Examples
31
+
32
+ Failing — a paper that was never built:
33
+
34
+ ```text
35
+ papers/my-paper/paper.tex
36
+ 1:1 warning the paper has not been built — run `npx paperlint build`, which records the files TeX
37
+ reads. Until it has, the files `paper.tex` includes are not linted, and the rules that
38
+ read the bibliography say nothing
39
+ paper/sources-fresh
40
+ ```
41
+
42
+ Failing — `sections/intro.tex` was edited after the last build:
43
+
44
+ ```text
45
+ papers/my-paper/paper.tex
46
+ 1:1 warning the paper changed since the last build (sections/intro.tex edited) — run `npx paperlint
47
+ build`; until then the files `paper.tex` includes are not linted, and the rules that
48
+ read the bibliography say nothing
49
+ paper/sources-fresh
50
+ ```
51
+
52
+ Passing: a paper whose files are byte for byte what the last build read. Editing a file TeX did not
53
+ read (a figure source the paper does not include, a note beside it) changes nothing.
54
+
55
+ ## Options / preset fields
56
+
57
+ No rule options. The level is `warn` for every paper; set it in the paper's `paperlint.json`:
58
+
59
+ ```json
60
+ { "rules": { "paper/sources-fresh": "error" } }
61
+ ```
62
+
63
+ `error` suits a CI job that builds first; `off` suits one that only lints and would rather not hear it.
64
+
65
+ ## What it does not check
66
+
67
+ - That the build was clean. A record is written after a build whose pdflatex and bibtex runs ended;
68
+ what they reported is `paper/refs-checked`'s and the build's own.
69
+ - Files TeX never read. A file the paper does not include is not in the record, so its edits are not
70
+ changes, and it is not linted.
71
+ - A file edited in an editor and not saved: the rule compares the bytes on disk with the record.
72
+ - `.bib` files TeX wrote itself (`filecontents`): their bytes are the block's, which is in a file the
73
+ record hashes.
74
+
75
+ ## How to fix
76
+
77
+ Build the paper again; the record is rewritten from the files as they are:
78
+
79
+ ```sh
80
+ npx paperlint build papers/my-paper
81
+ ```