paperlint 2.0.1 → 3.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 (226) hide show
  1. package/CLAUDE.md +6 -1
  2. package/CONTRIBUTING.md +21 -0
  3. package/README.md +231 -180
  4. package/action.yml +4 -6
  5. package/dist/adapters/references/index.d.ts +13 -0
  6. package/dist/adapters/references/index.d.ts.map +1 -0
  7. package/dist/adapters/references/index.js +55 -0
  8. package/dist/adapters/references/index.js.map +1 -0
  9. package/dist/adapters/references/reach.io.d.ts +3 -0
  10. package/dist/adapters/references/reach.io.d.ts.map +1 -0
  11. package/dist/adapters/references/reach.io.js +14 -0
  12. package/dist/adapters/references/reach.io.js.map +1 -0
  13. package/dist/build-engine.d.ts.map +1 -1
  14. package/dist/build-engine.js +5 -2
  15. package/dist/build-engine.js.map +1 -1
  16. package/dist/build.d.ts +16 -1
  17. package/dist/build.d.ts.map +1 -1
  18. package/dist/build.js +40 -5
  19. package/dist/build.js.map +1 -1
  20. package/dist/cli.d.ts +37 -20
  21. package/dist/cli.d.ts.map +1 -1
  22. package/dist/cli.js +377 -251
  23. package/dist/cli.js.map +1 -1
  24. package/dist/doctor.d.ts.map +1 -1
  25. package/dist/doctor.js +37 -52
  26. package/dist/doctor.js.map +1 -1
  27. package/dist/facts-file.d.ts +33 -9
  28. package/dist/facts-file.d.ts.map +1 -1
  29. package/dist/facts-file.js +117 -17
  30. package/dist/facts-file.js.map +1 -1
  31. package/dist/hooks-settings.d.ts +0 -21
  32. package/dist/hooks-settings.d.ts.map +1 -1
  33. package/dist/hooks-settings.js +17 -135
  34. package/dist/hooks-settings.js.map +1 -1
  35. package/dist/init.d.ts +40 -19
  36. package/dist/init.d.ts.map +1 -1
  37. package/dist/init.js +144 -109
  38. package/dist/init.js.map +1 -1
  39. package/dist/link-skills.d.ts +1 -1
  40. package/dist/link-skills.d.ts.map +1 -1
  41. package/dist/link-skills.js +11 -29
  42. package/dist/link-skills.js.map +1 -1
  43. package/dist/new-paper.d.ts +2 -2
  44. package/dist/new-paper.d.ts.map +1 -1
  45. package/dist/new-paper.js +6 -2
  46. package/dist/new-paper.js.map +1 -1
  47. package/dist/paper-files.d.ts +63 -0
  48. package/dist/paper-files.d.ts.map +1 -0
  49. package/dist/paper-files.js +69 -0
  50. package/dist/paper-files.js.map +1 -0
  51. package/dist/paper-settings.d.ts +40 -0
  52. package/dist/paper-settings.d.ts.map +1 -0
  53. package/dist/paper-settings.js +144 -0
  54. package/dist/paper-settings.js.map +1 -0
  55. package/dist/ports/check-references.d.ts +31 -0
  56. package/dist/ports/check-references.d.ts.map +1 -0
  57. package/dist/ports/check-references.js +7 -0
  58. package/dist/ports/check-references.js.map +1 -0
  59. package/dist/presets.d.ts +84 -0
  60. package/dist/presets.d.ts.map +1 -0
  61. package/dist/presets.js +184 -0
  62. package/dist/presets.js.map +1 -0
  63. package/dist/reference-rules.d.ts +47 -0
  64. package/dist/reference-rules.d.ts.map +1 -0
  65. package/dist/reference-rules.js +156 -0
  66. package/dist/reference-rules.js.map +1 -0
  67. package/dist/references.d.ts +36 -0
  68. package/dist/references.d.ts.map +1 -0
  69. package/dist/references.js +91 -0
  70. package/dist/references.js.map +1 -0
  71. package/dist/rules-config.d.ts +11 -2
  72. package/dist/rules-config.d.ts.map +1 -1
  73. package/dist/rules-config.js +25 -11
  74. package/dist/rules-config.js.map +1 -1
  75. package/dist/structure.d.ts.map +1 -1
  76. package/dist/structure.js +3 -2
  77. package/dist/structure.js.map +1 -1
  78. package/dist/tex-requirements.d.ts +63 -7
  79. package/dist/tex-requirements.d.ts.map +1 -1
  80. package/dist/tex-requirements.js +91 -24
  81. package/dist/tex-requirements.js.map +1 -1
  82. package/dist/types.d.ts +20 -18
  83. package/dist/types.d.ts.map +1 -1
  84. package/dist/venue-rules.d.ts +109 -0
  85. package/dist/venue-rules.d.ts.map +1 -0
  86. package/dist/venue-rules.js +389 -0
  87. package/dist/venue-rules.js.map +1 -0
  88. package/docs/configuration.md +152 -77
  89. package/docs/e2e.md +1 -1
  90. package/docs/install.md +25 -40
  91. package/docs/optional-rules.md +26 -24
  92. package/docs/rules.md +181 -15
  93. package/eslint-rules/bib-reachable-entry.mjs +75 -0
  94. package/eslint-rules/latex-language.mjs +29 -1
  95. package/eslint-rules/paper-stages.harness.mjs +1 -117
  96. package/eslint-rules/paper-stages.mjs +0 -120
  97. package/eslint-rules/paper-stages.mutations.mjs +0 -76
  98. package/eslint-rules/paper-typography.mjs +267 -192
  99. package/eslint-rules/paper-typography.test.mjs +297 -0
  100. package/eslint-rules/papers.harness.mjs +8 -12
  101. package/eslint-rules/papers.mjs +17 -27
  102. package/eslint-rules/pdf-last-page-balance.mjs +23 -5
  103. package/eslint-rules/review-frontmatter.mjs +118 -0
  104. package/eslint-rules/review-frontmatter.schema.json +36 -0
  105. package/eslint-rules/review-frontmatter.test.mjs +118 -0
  106. package/eslint-rules/sibling-frontmatter.mjs +19 -0
  107. package/eslint-rules/sibling-frontmatter.schema.json +14 -0
  108. package/eslint.config.mjs +0 -15
  109. package/fixtures/build-e2e/acmart/paperlint.json +1 -0
  110. package/fixtures/real-markdown-paper/baseline.json +6 -7
  111. package/hooks/hooks.harness.mjs +39 -98
  112. package/hooks/hooks.mutations.mjs +6 -6
  113. package/hooks/paper-edit-guard.hook.d.mts +2 -3
  114. package/hooks/paper-edit-guard.hook.mjs +31 -42
  115. package/hooks/paper-skills-nudge.hook.mjs +21 -19
  116. package/hooks/paper-status-gates.hook.mjs +21 -19
  117. package/hooks/paper-status-gates.sh +8 -13
  118. package/lib/paper-config.d.mts +15 -18
  119. package/lib/paper-config.harness.mjs +18 -35
  120. package/lib/paper-config.mjs +90 -81
  121. package/lib/paper-config.mutations.mjs +2 -2
  122. package/lib/skill-checks.mjs +1 -1
  123. package/lib/skill-trigger-cases.harness.mjs +9 -8
  124. package/lib/skill-trigger-cases.mjs +7 -15
  125. package/package.json +1 -1
  126. package/scripts/eslint-report-guard.mjs +1 -1
  127. package/scripts/harness-api.frozen.json +0 -3
  128. package/scripts/mutation-batteries.frozen.json +4 -13
  129. package/skills/analyze-sibling-paper/SKILL.md +16 -12
  130. package/skills/analyze-sibling-paper/SKILL.md.spec.ts +15 -11
  131. package/skills/cold-read-diff/SKILL.md +20 -1
  132. package/skills/cold-read-diff/SKILL.md.spec.ts +19 -0
  133. package/skills/find-venue/SKILL.md +5 -1
  134. package/skills/find-venue/SKILL.md.spec.ts +4 -0
  135. package/skills/grade-paper-writing/SKILL.md +20 -1
  136. package/skills/grade-paper-writing/SKILL.md.spec.ts +19 -0
  137. package/skills/paper-pipeline/PIPELINE-MAP.md +14 -4
  138. package/skills/paper-pipeline/SKILL.md +4 -3
  139. package/skills/paper-pipeline/SKILL.md.spec.ts +3 -2
  140. package/skills/paper-pipeline/scripts/consumer.d.mts +0 -1
  141. package/skills/paper-pipeline/scripts/consumer.harness.mjs +8 -12
  142. package/skills/paper-pipeline/scripts/consumer.mjs +19 -33
  143. package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +6 -4
  144. package/skills/paper-pipeline/scripts/run-mechanical.mjs +2 -11
  145. package/skills/plan-paper-timeline/SKILL.md +2 -2
  146. package/skills/plan-paper-timeline/SKILL.md.spec.ts +1 -1
  147. package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +1 -1
  148. package/skills/plan-paper-timeline/plan-paper-timeline.effects.mutations.mjs +2 -2
  149. package/skills/render-paper/check-render.sh +2 -3
  150. package/skills/render-paper/extract-pdf-facts.harness.mjs +5 -5
  151. package/skills/render-paper/extract-pdf-facts.mjs +4 -4
  152. package/skills/render-paper/render-paper.harness.mjs +2 -2
  153. package/skills/study-accepted-papers/SKILL.md +2 -1
  154. package/skills/study-accepted-papers/SKILL.md.spec.ts +1 -0
  155. package/skills/submit-paper/SKILL.md +9 -1
  156. package/skills/submit-paper/SKILL.md.spec.ts +8 -0
  157. package/skills/submit-paper/references/venues/acm-sigconf.jsonc +86 -0
  158. package/skills/submit-paper/references/venues/agenticdev.jsonc +34 -102
  159. package/skills/submit-paper/references/venues/aisec.jsonc +21 -82
  160. package/skills/submit-paper/references/venues/realm.jsonc +45 -43
  161. package/skills/submit-paper/references/venues/tex-base.jsonc +1 -1
  162. package/skills/submit-paper/references/venues/venue-profile.schema.json +119 -29
  163. package/skills/verify-citations/SKILL.md +10 -1
  164. package/skills/verify-citations/SKILL.md.spec.ts +9 -0
  165. package/skills/verify-citations/scripts/bib-authors.mjs +48 -26
  166. package/src/adapters/references/index.ts +102 -0
  167. package/src/adapters/references/reach.io.ts +12 -0
  168. package/src/build-engine.ts +10 -2
  169. package/src/build.harness.mjs +31 -22
  170. package/src/build.ts +65 -8
  171. package/src/cli.harness.mjs +151 -294
  172. package/src/cli.mutations.mjs +35 -44
  173. package/src/cli.ts +457 -288
  174. package/src/doctor.harness.mjs +41 -84
  175. package/src/doctor.mutations.mjs +9 -9
  176. package/src/doctor.ts +41 -61
  177. package/src/facts-file.test.ts +47 -7
  178. package/src/facts-file.ts +145 -24
  179. package/src/hooks-settings.harness.mjs +7 -92
  180. package/src/hooks-settings.mutations.mjs +2 -17
  181. package/src/hooks-settings.ts +18 -162
  182. package/src/init.test.ts +206 -0
  183. package/src/init.ts +193 -159
  184. package/src/link-skills.harness.mjs +1 -38
  185. package/src/link-skills.mutations.mjs +9 -5
  186. package/src/link-skills.ts +10 -34
  187. package/src/lint-fix.test.ts +120 -0
  188. package/src/new-paper.harness.mjs +9 -6
  189. package/src/new-paper.test.ts +133 -0
  190. package/src/new-paper.ts +7 -2
  191. package/src/paper-files.test.ts +290 -0
  192. package/src/paper-files.ts +144 -0
  193. package/src/paper-settings-commands.test.ts +274 -0
  194. package/src/paper-settings.test.ts +249 -0
  195. package/src/paper-settings.ts +208 -0
  196. package/src/ports/check-references.ts +28 -0
  197. package/src/presets.test.ts +236 -0
  198. package/src/presets.ts +297 -0
  199. package/src/reference-rules.ts +229 -0
  200. package/src/references.test.ts +209 -0
  201. package/src/references.ts +138 -0
  202. package/src/rules-config.harness.mjs +24 -8
  203. package/src/rules-config.ts +30 -13
  204. package/src/structure.harness.mjs +6 -4
  205. package/src/structure.ts +3 -2
  206. package/src/tex-requirements.harness.mjs +11 -17
  207. package/src/tex-requirements.ts +179 -29
  208. package/src/types.ts +30 -18
  209. package/src/venue-rules.test.ts +554 -0
  210. package/src/venue-rules.ts +571 -0
  211. package/templates/paper/paperlint.json +4 -0
  212. package/eslint-rules/doc-fields.harness.mjs +0 -336
  213. package/eslint-rules/doc-fields.mjs +0 -186
  214. package/eslint-rules/doc-fields.mutations.mjs +0 -96
  215. package/eslint-rules/paper-typography.harness.mjs +0 -291
  216. package/eslint-rules/paper-typography.mutations.mjs +0 -131
  217. package/eslint-rules/review-findings-cause.harness.mjs +0 -228
  218. package/eslint-rules/review-findings-cause.mjs +0 -135
  219. package/eslint-rules/review-findings-cause.mutations.mjs +0 -72
  220. package/fixtures/build-e2e/acmart/venue.json +0 -1
  221. package/fixtures/paper-stages/authors-ran/PIPELINE-STATUS.md +0 -16
  222. package/fixtures/paper-stages/marker-in-prose/PIPELINE-STATUS.md +0 -17
  223. package/fixtures/review-findings-cause/clean.md +0 -17
  224. package/fixtures/review-findings-cause/defect.md +0 -14
  225. package/fixtures/review-findings-cause/old-debt.md +0 -14
  226. package/fixtures/review-findings-cause/quiet-in-fence.md +0 -16
@@ -0,0 +1,86 @@
1
+ // ACM `acmart` sigconf — the TEMPLATE FAMILY: everything the ACM template decides, whatever the
2
+ // venue. A venue preset extends it ("extends": "paperlint:acm-sigconf") and adds what its call for
3
+ // papers sets: the page limit of each kind of paper, and the rules its producer implies.
4
+ //
5
+ // The numbers were measured on acmart sigconf builds (banal + pdf.js, 2026-08-26) and quoted from
6
+ // the Conference Publishing author instructions for ACM (2026-08-25), which state ACM's own
7
+ // requirements. A paper may extend this family directly for an ACM venue nobody has profiled:
8
+ // page size, columns and fonts are checked; with no kinds, the page limit is not.
9
+ {
10
+ "template": "acmart",
11
+
12
+ // TeX Live packages the acmart template needs ON TOP OF tex-base.jsonc, each with the files that
13
+ // prove it is present (checked with kpsewhich after every install). Read by `paperlint toolchain` and
14
+ // `paperlint build`; the shape is venue-profile.schema.json.
15
+ // libertine · inconsolata (zi4) · newtx — acmart.cls checks ALL THREE and, if ANY is missing,
16
+ // silently falls back to Computer Modern: the PDF compiles, looks normal, is set in the wrong
17
+ // font, and the other metric gives another pagination. A submitted paper went out that way.
18
+ // kastrup (binhex.tex) — pulled in by newtx. Missing it is a HARD stop, so adding the fonts
19
+ // without it breaks a build that worked.
20
+ // fancyhdr — required by acmart itself; present on most images by accident, absent on a
21
+ // minimal one ("File `fancyhdr.sty' not found", measured on TinyTeX, issue #37).
22
+ // the rest — pulled in by acmart ITSELF, not by the paper, so reading \usepackage never finds
23
+ // them. Found 2026-09-01 by the loop build → missing file → tlmgr search --file → install.
24
+ "tex": {
25
+ "packages": {
26
+ "acmart": ["acmart.cls"],
27
+ "amscls": ["amsart.cls"],
28
+ "libertine": ["libertine.sty"],
29
+ "inconsolata": ["zi4.sty"],
30
+ "newtx": ["newtxmath.sty"],
31
+ "kastrup": ["binhex.tex"],
32
+ "fancyhdr": ["fancyhdr.sty"],
33
+ "totpages": ["totpages.sty"],
34
+ "environ": ["environ.sty"],
35
+ "preprint": ["balance.sty"],
36
+ // acmart's `pbalance` option (the vendor's first fix for an unbalanced last page, which the
37
+ // optional rule `pdf/last-page-balance` recommends) loads pbalance, which needs filehook and
38
+ // zref. Measured 2026-09-25 on an acmart sigconf camera-ready: without them the option stops
39
+ // the build; with them the last page went from 639.6 / 224.1 to 424.4 / 439.2 pt, same page count.
40
+ "pbalance": ["pbalance.sty"],
41
+ "filehook": ["filehook.sty"],
42
+ "zref": ["zref-abspage.sty"],
43
+ "xstring": ["xstring.sty"],
44
+ "everyshi": ["everyshi.sty"],
45
+ "hyperxmp": ["hyperxmp.sty"],
46
+ "ncctools": ["manyfoot.sty"],
47
+ "cmap": ["cmap.sty"],
48
+ "float": ["float.sty"],
49
+ "comment": ["comment.sty"],
50
+ "upquote": ["upquote.sty"],
51
+ "doclicense": ["doclicense.sty"],
52
+ "textcase": ["textcase.sty"],
53
+ },
54
+ },
55
+
56
+ "format": {
57
+ // 8.5 x 11 in
58
+ "page_size": "letter",
59
+ "page_w_in": 8.5,
60
+ "page_h_in": 11.0,
61
+
62
+ // «text width must not exceed 18 cm»
63
+ "textblock_w_in": 7.087,
64
+ // «text height must not exceed 23.5 cm»
65
+ "textblock_h_in": 9.252,
66
+ // Last-page balance is NOT a format field: it is the optional rule pdf/last-page-balance, which
67
+ // a VENUE preset turns on in its `rules` when its producer asks (see agenticdev.jsonc).
68
+
69
+ "columns": 2,
70
+
71
+ // «font size is 9 pt for all body text»
72
+ "body_pt": 9,
73
+ // banal measures the MODE of the rendered text, not the declared size (measured 08-26: 9.30 at
74
+ // acmart's declared 9). Hence the tolerance — and hence why body size stays a nudge, not a gate.
75
+ "body_pt_tol": 0.5,
76
+
77
+ // «font size of the references can be from 7 pt to 9 pt»
78
+ "ref_pt_min": 7,
79
+ "ref_pt_max": 9,
80
+
81
+ // «text must be in serif font (Libertine)»
82
+ "fonts_text": "LinLibertine",
83
+ // «The title must be in sans-serif font (Biolinum)»
84
+ "fonts_title": "LinBiolinum",
85
+ },
86
+ }
@@ -1,108 +1,40 @@
1
- // Venue format profile — DATA read by the `pdf/profile` rule in eslint-rules/pdf-facts.mjs.
2
- // Prose about the venue is in agenticdev.md nearby.
3
- // Source for the numbers: the Conference Publishing author instructions 2026-08-25 + ACM's
4
- // requirements.
5
- //
6
- // 🔴 Why JSONC and not YAML (decided 08-26). I had this in YAML, justifying it with "JSON has no
7
- // comments, and every number here carries a quote from the instructions." That objection turned
8
- // out to be false: @eslint/json ships three languages — json, jsonc, json5 — and jsonc is exactly
9
- // JSON with comments. So the quotes stay in place, the file is read by a real parser
10
- // (@humanwhocodes/momoa, already a transitive dependency), AND it's linted by the same engine as
11
- // everything else. One extra dependency (js-yaml on this path) goes away.
1
+ // AgenticDev (an ASE workshop) — a VENUE preset over the ACM sigconf family. Prose about the venue
2
+ // is in agenticdev.md nearby. Everything the ACM template decides (page size, columns, fonts, font
3
+ // sizes, TeX packages) comes from acm-sigconf.jsonc; this file adds what AgenticDev's call for
4
+ // papers sets.
5
+ // Source for the numbers: the Conference Publishing author instructions 2026-08-25.
12
6
  {
13
- "template": "acmart",
14
-
15
- // TeX Live packages the acmart template needs ON TOP OF tex-base.jsonc, each with the files that
16
- // prove it is present (checked with kpsewhich after every install). Read by `paperlint toolchain` and
17
- // `paperlint build`; the shape is venue-profile.schema.json.
18
- // libertine · inconsolata (zi4) · newtx — acmart.cls checks ALL THREE and, if ANY is missing,
19
- // silently falls back to Computer Modern: the PDF compiles, looks normal, is set in the wrong
20
- // font, and the other metric gives another pagination. A submitted paper went out that way.
21
- // kastrup (binhex.tex) — pulled in by newtx. Missing it is a HARD stop, so adding the fonts
22
- // without it breaks a build that worked.
23
- // fancyhdr — required by acmart itself; present on most images by accident, absent on a
24
- // minimal one ("File `fancyhdr.sty' not found", measured on TinyTeX, issue #37).
25
- // the rest — pulled in by acmart ITSELF, not by the paper, so reading \usepackage never finds
26
- // them. Found 2026-09-01 by the loop build → missing file → tlmgr search --file → install.
27
- "tex": {
28
- "packages": {
29
- "acmart": ["acmart.cls"],
30
- "amscls": ["amsart.cls"],
31
- "libertine": ["libertine.sty"],
32
- "inconsolata": ["zi4.sty"],
33
- "newtx": ["newtxmath.sty"],
34
- "kastrup": ["binhex.tex"],
35
- "fancyhdr": ["fancyhdr.sty"],
36
- "totpages": ["totpages.sty"],
37
- "environ": ["environ.sty"],
38
- "preprint": ["balance.sty"],
39
- // acmart's `pbalance` option (the vendor's first fix for an unbalanced last page, which the
40
- // optional rule `pdf/last-page-balance` recommends) loads pbalance, which needs filehook and
41
- // zref. Measured 2026-09-25 on an acmart sigconf camera-ready: without them the option stops
42
- // the build; with them the last page went from 639.6 / 224.1 to 424.4 / 439.2 pt, same page count.
43
- "pbalance": ["pbalance.sty"],
44
- "filehook": ["filehook.sty"],
45
- "zref": ["zref-abspage.sty"],
46
- "xstring": ["xstring.sty"],
47
- "everyshi": ["everyshi.sty"],
48
- "hyperxmp": ["hyperxmp.sty"],
49
- "ncctools": ["manyfoot.sty"],
50
- "cmap": ["cmap.sty"],
51
- "float": ["float.sty"],
52
- "comment": ["comment.sty"],
53
- "upquote": ["upquote.sty"],
54
- "doclicense": ["doclicense.sty"],
55
- "textcase": ["textcase.sty"],
7
+ "extends": "paperlint:acm-sigconf",
8
+
9
+ "format": {
10
+ // The limits are a property of the paper's TYPE at this venue, not of the venue itself.
11
+ // A given paper's type is stated in its <paper-dir>/paperlint.json (`kind`).
12
+ "kinds": {
13
+ "short": {
14
+ "body_pages_max": 5,
15
+ // «up to 2 extra pages of references»
16
+ "ref_pages_max": 2,
17
+ },
18
+ "full": {
19
+ // «Full ≤10pp (mature)»
20
+ "body_pages_max": 10,
21
+ "ref_pages_max": 2,
22
+ },
23
+ "demo": {
24
+ "body_pages_max": 5,
25
+ "ref_pages_max": 2,
26
+ },
56
27
  },
57
28
  },
58
29
 
59
- // 8.5 x 11 in
60
- "page_size": "letter",
61
- "page_w_in": 8.5,
62
- "page_h_in": 11.0,
63
-
64
- // «text width must not exceed 18 cm»
65
- "textblock_w_in": 7.087,
66
- // «text height must not exceed 23.5 cm»
67
- "textblock_h_in": 9.252,
68
- // Last-page balance is NOT a profile field. It is the optional lint rule pdf/last-page-balance,
69
- // which a consumer turns on for the papers whose publisher asks (Conference Publishing bounced
70
- // an accepted paper for it on 08-29: "Please correct the last page balancing") — see
71
- // docs/optional-rules.md.
72
-
73
- "columns": 2,
74
-
75
- // «font size is 9 pt for all body text»
76
- "body_pt": 9,
77
- // banal measures the MODE of the rendered text, not the declared size (measured 08-26: 9.30 at
78
- // acmart's declared 9). Hence the tolerance — and hence why body size stays a nudge, not a gate.
79
- "body_pt_tol": 0.5,
80
-
81
- // «font size of the references can be from 7 pt to 9 pt»
82
- "ref_pt_min": 7,
83
- "ref_pt_max": 9,
84
-
85
- // «text must be in serif font (Libertine)»
86
- "fonts_text": "LinLibertine",
87
- // «The title must be in sans-serif font (Biolinum)»
88
- "fonts_title": "LinBiolinum",
89
-
90
- // The limits are a property of the paper's TYPE at this venue, not of the venue itself.
91
- // A given paper's type is stated in its <paper-dir>/venue.json.
92
- "kinds": {
93
- "short": {
94
- "body_pages_max": 5,
95
- // «up to 2 extra pages of references»
96
- "ref_pages_max": 2,
97
- },
98
- "full": {
99
- // «Full ≤10pp (mature)»
100
- "body_pages_max": 10,
101
- "ref_pages_max": 2,
102
- },
103
- "demo": {
104
- "body_pages_max": 5,
105
- "ref_pages_max": 2,
106
- },
30
+ // The proceedings are produced by Conference Publishing Consulting — agenticdev.md takes its
31
+ // numbers from their author instructions — which require "balanced columns on the last page, if
32
+ // the page is not filled" and sent an accepted camera-ready back for it on 2026-08-29, the day
33
+ // after AgenticDev's camera-ready deadline ("Please correct the last page balancing"). So every
34
+ // AgenticDev paper gets the optional rule; a paper can still turn it off in its own
35
+ // paperlint.json. 120 pt is the rule's default, measured: the balanced build ended 2.7 pt apart,
36
+ // the returned one 321.4 pt apart (docs/optional-rules.md).
37
+ "rules": {
38
+ "pdf/last-page-balance": ["error", { "tolerancePt": 120 }],
107
39
  },
108
40
  }
@@ -1,4 +1,5 @@
1
- // AISec @ ACM CCS format profile. Data for the `pdf/*` rules over `_build/paper.facts.json`.
1
+ // AISec @ ACM CCS — a VENUE preset over the ACM sigconf family. Data for the `pdf/*` rules over
2
+ // `_build/paper.facts.json`.
2
3
  // Prose about the venue and tracks is in `aisec.md` nearby.
3
4
  //
4
5
  // PAGE LIMITS — verbatim from https://aisec.cc/ (verified 2026-08-26, not from memory):
@@ -14,88 +15,26 @@
14
15
  // a 13th page. That's deliberate: the case is rare, and automation that knows "sometimes it's
15
16
  // fine" is worse than a plain rejection a human clears by eye.
16
17
  //
17
- // GEOMETRY AND FONTS are inherited from `acmart sigconf` — the same class as agenticdev, and
18
- // measured the same way (banal + pdffonts, 08-26). AISec points to the "main ACM CCS'26 formatting
19
- // instructions" and does not publish its own numbers.
18
+ // GEOMETRY, FONTS AND TeX PACKAGES come from acm-sigconf.jsonc: AISec points to the "main ACM
19
+ // CCS'26 formatting instructions" and does not publish its own numbers.
20
+ //
21
+ // NO `rules` HERE, on purpose: nothing in aisec.md says who produces the AISec proceedings or
22
+ // whether they require a balanced last page (Sheridan does for CCS itself — docs/optional-rules.md —
23
+ // but that is the main conference). Turn pdf/last-page-balance on in the paper's paperlint.json if
24
+ // the camera-ready instructions ask for it.
20
25
  {
21
- "template": "acmart",
22
-
23
- // TeX Live packages the acmart template needs ON TOP OF tex-base.jsonc, each with the files that
24
- // prove it is present (checked with kpsewhich after every install). Read by `paperlint toolchain` and
25
- // `paperlint build`; the shape is venue-profile.schema.json.
26
- // libertine · inconsolata (zi4) · newtx — acmart.cls checks ALL THREE and, if ANY is missing,
27
- // silently falls back to Computer Modern: the PDF compiles, looks normal, is set in the wrong
28
- // font, and the other metric gives another pagination. A submitted paper went out that way.
29
- // kastrup (binhex.tex) — pulled in by newtx. Missing it is a HARD stop, so adding the fonts
30
- // without it breaks a build that worked.
31
- // fancyhdr — required by acmart itself; present on most images by accident, absent on a
32
- // minimal one ("File `fancyhdr.sty' not found", measured on TinyTeX, issue #37).
33
- // the rest — pulled in by acmart ITSELF, not by the paper, so reading \usepackage never finds
34
- // them. Found 2026-09-01 by the loop build → missing file → tlmgr search --file → install.
35
- "tex": {
36
- "packages": {
37
- "acmart": ["acmart.cls"],
38
- "amscls": ["amsart.cls"],
39
- "libertine": ["libertine.sty"],
40
- "inconsolata": ["zi4.sty"],
41
- "newtx": ["newtxmath.sty"],
42
- "kastrup": ["binhex.tex"],
43
- "fancyhdr": ["fancyhdr.sty"],
44
- "totpages": ["totpages.sty"],
45
- "environ": ["environ.sty"],
46
- "preprint": ["balance.sty"],
47
- // acmart's `pbalance` option (the vendor's first fix for an unbalanced last page, which the
48
- // optional rule `pdf/last-page-balance` recommends) loads pbalance, which needs filehook and
49
- // zref. Measured 2026-09-25 on an acmart sigconf camera-ready: without them the option stops
50
- // the build; with them the last page went from 639.6 / 224.1 to 424.4 / 439.2 pt, same page count.
51
- "pbalance": ["pbalance.sty"],
52
- "filehook": ["filehook.sty"],
53
- "zref": ["zref-abspage.sty"],
54
- "xstring": ["xstring.sty"],
55
- "everyshi": ["everyshi.sty"],
56
- "hyperxmp": ["hyperxmp.sty"],
57
- "ncctools": ["manyfoot.sty"],
58
- "cmap": ["cmap.sty"],
59
- "float": ["float.sty"],
60
- "comment": ["comment.sty"],
61
- "upquote": ["upquote.sty"],
62
- "doclicense": ["doclicense.sty"],
63
- "textcase": ["textcase.sty"],
26
+ "extends": "paperlint:acm-sigconf",
27
+
28
+ "format": {
29
+ // Four tracks (original research · position/open-problem · SoK · benchmark) — the limits are
30
+ // the SAME for all of them, the venue doesn't split them. The keys are kept separate so a
31
+ // paper's `paperlint.json` can name its own track: not for the numbers, but so it's visible which
32
+ // track it was submitted under.
33
+ "kinds": {
34
+ "research": { "body_pages_max": 10, "ref_pages_max": 2 },
35
+ "benchmark": { "body_pages_max": 10, "ref_pages_max": 2 },
36
+ "position": { "body_pages_max": 10, "ref_pages_max": 2 },
37
+ "sok": { "body_pages_max": 10, "ref_pages_max": 2 },
64
38
  },
65
39
  },
66
-
67
- "page_size": "letter",
68
- "page_w_in": 8.5,
69
- "page_h_in": 11.0,
70
-
71
- "textblock_w_in": 7.087,
72
- "textblock_h_in": 9.252,
73
- // Last-page balance is NOT a profile field. It is the optional lint rule pdf/last-page-balance,
74
- // which a consumer turns on for the papers whose publisher asks (Conference Publishing bounced
75
- // an accepted paper for it on 08-29: "Please correct the last page balancing") — see
76
- // docs/optional-rules.md.
77
-
78
- "columns": 2,
79
-
80
- "body_pt": 9,
81
- // banal measures the rendered text's mode, not the declared size (measured: 9.30 at a
82
- // declared 9).
83
- "body_pt_tol": 0.5,
84
-
85
- "ref_pt_min": 7,
86
- "ref_pt_max": 9,
87
-
88
- "fonts_text": "LinLibertine",
89
- "fonts_title": "LinBiolinum",
90
-
91
- // Four tracks (original research · position/open-problem · SoK · benchmark) — the limits are
92
- // the SAME for all of them, the venue doesn't split them. The keys are kept separate so a
93
- // paper's `venue.json` can name its own track: not for the numbers, but so it's visible which
94
- // track it was submitted under.
95
- "kinds": {
96
- "research": { "body_pages_max": 10, "ref_pages_max": 2 },
97
- "benchmark": { "body_pages_max": 10, "ref_pages_max": 2 },
98
- "position": { "body_pages_max": 10, "ref_pages_max": 2 },
99
- "sok": { "body_pages_max": 10, "ref_pages_max": 2 },
100
- },
101
40
  }
@@ -28,54 +28,56 @@
28
28
  },
29
29
  },
30
30
 
31
- "page_size": "a4",
32
- "page_w_in": 8.27,
33
- "page_h_in": 11.69,
31
+ "format": {
32
+ "page_size": "a4",
33
+ "page_w_in": 8.27,
34
+ "page_h_in": 11.69,
34
35
 
35
- "columns": 2,
36
+ "columns": 2,
36
37
 
37
- // ACL's stated body size is 11pt; banal measures the render's mode and gives 11.3.
38
- "body_pt": 11,
39
- "body_pt_tol": 0.5,
38
+ // ACL's stated body size is 11pt; banal measures the render's mode and gives 11.3.
39
+ "body_pt": 11,
40
+ "body_pt_tol": 0.5,
40
41
 
41
- // ⚠️ The range is derived from a SINGLE measurement (10.3), not from ACL's spec — I could not
42
- // find ACL's own numbers for the reference size. So it's set wide: for an `error`-level rule, a
43
- // false positive is worse than a miss. Narrow it once a primary source turns up.
44
- "ref_pt_min": 9,
45
- "ref_pt_max": 11,
42
+ // ⚠️ The range is derived from a SINGLE measurement (10.3), not from ACL's spec — I could not
43
+ // find ACL's own numbers for the reference size. So it's set wide: for an `error`-level rule, a
44
+ // false positive is worse than a miss. Narrow it once a primary source turns up.
45
+ "ref_pt_min": 9,
46
+ "ref_pt_max": 11,
46
47
 
47
- // The Times clone from the ACL template. Headings are NOT gated: the build also contains
48
- // NimbusSanL-Bold, but a `pdffonts` measurement can't establish exactly what the heading is set
49
- // in — it only says a typeface IS present in the file, not where it's applied.
50
- "fonts_text": "NimbusRomNo9L",
48
+ // The Times clone from the ACL template. Headings are NOT gated: the build also contains
49
+ // NimbusSanL-Bold, but a `pdffonts` measurement can't establish exactly what the heading is set
50
+ // in — it only says a typeface IS present in the file, not where it's applied.
51
+ "fonts_text": "NimbusRomNo9L",
51
52
 
52
- // 🔴 PAGE LIMITS ARE NOT GATED HERE, AND THAT IS A MEASUREMENT, NOT LAZINESS.
53
- //
54
- // banal classifies pages by content and counts Limitations and Ethics as part of the BODY.
55
- // ACL excludes them from the limit. Measured 08-26 on our own paper: banal said "body 13," the
56
- // build's own counter said "body pages: 9," and both are right — they're just counting
57
- // different things (the body ends at Conclusion on p.9; pp.10-13 are Limitations/Ethics; the
58
- // bibliography starts at 14).
59
- //
60
- // A gate on banal's number would give 13 against a limit of 8 and would redden on a paper that
61
- // is within the limit. For an `error`-level rule that is the worst outcome. The limit stays
62
- // owned by the counter inside `build-submission.sh`: it reads the markdown source and therefore
63
- // KNOWS where the body ends, which the PDF does not.
64
- //
65
- // The numbers below are DATA for that counter and for humans, not an input to the rule. Source:
66
- // `realm.md`, verified by directly fetching the 2026-08-02/03 CFP.
67
- "page_limits_not_gated": {
68
- "_": "8 at submission, 9 at camera-ready (+1 page). Both numbers are CURRENTLY hardcoded in build-submission.sh — 8 in the header, 9 in the run — which reads like a mismatch even though these are different STAGES. The script should pull them from here instead.",
69
- "long_submission": 8,
70
- "long_camera_ready": 9,
71
- "short_submission": 4,
72
- "short_camera_ready": 5,
73
- "refs_and_appendices": "unlimited",
74
- "limitations_and_ethics": "not counted (ACL rule)",
75
- },
53
+ // 🔴 PAGE LIMITS ARE NOT GATED HERE, AND THAT IS A MEASUREMENT, NOT LAZINESS.
54
+ //
55
+ // banal classifies pages by content and counts Limitations and Ethics as part of the BODY.
56
+ // ACL excludes them from the limit. Measured 08-26 on our own paper: banal said "body 13," the
57
+ // build's own counter said "body pages: 9," and both are right — they're just counting
58
+ // different things (the body ends at Conclusion on p.9; pp.10-13 are Limitations/Ethics; the
59
+ // bibliography starts at 14).
60
+ //
61
+ // A gate on banal's number would give 13 against a limit of 8 and would redden on a paper that
62
+ // is within the limit. For an `error`-level rule that is the worst outcome. The limit stays
63
+ // owned by the counter inside `build-submission.sh`: it reads the markdown source and therefore
64
+ // KNOWS where the body ends, which the PDF does not.
65
+ //
66
+ // The numbers below are DATA for that counter and for humans, not an input to the rule. Source:
67
+ // `realm.md`, verified by directly fetching the 2026-08-02/03 CFP.
68
+ "page_limits_not_gated": {
69
+ "_": "8 at submission, 9 at camera-ready (+1 page). Both numbers are CURRENTLY hardcoded in build-submission.sh — 8 in the header, 9 in the run — which reads like a mismatch even though these are different STAGES. The script should pull them from here instead.",
70
+ "long_submission": 8,
71
+ "long_camera_ready": 9,
72
+ "short_submission": 4,
73
+ "short_camera_ready": 5,
74
+ "refs_and_appendices": "unlimited",
75
+ "limitations_and_ethics": "not counted (ACL rule)",
76
+ },
76
77
 
77
- "kinds": {
78
- "long": {},
79
- "short": {},
78
+ "kinds": {
79
+ "long": {},
80
+ "short": {},
81
+ },
80
82
  },
81
83
  }
@@ -1,5 +1,5 @@
1
1
  // The TeX Live packages EVERY paper gets — and ALL a paper gets when its venue is unknown (no
2
- // venue.json, or a venue.json naming a venue with no profile here). A venue's own `tex.packages`
2
+ // paperlint.json, or a paperlint.json naming a venue with no profile here). A venue's own `tex.packages`
3
3
  // is added on top of this set, never instead of it.
4
4
  //
5
5
  // Read by `paperlint toolchain` (it installs this set plus every venue's) and by `paperlint build` (it checks
@@ -1,43 +1,121 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "https://github.com/zernie/paperlint/venue-profile.schema.json",
4
- "title": "Venue profile",
5
- "description": "One venue's format profile (venues/<venue>.jsonc) and the TeX base set (venues/tex-base.jsonc). The format numbers are read by the consumer's pdf/profile rule; `tex` is read by `paperlint toolchain` and `paperlint build`.",
4
+ "title": "Venue preset",
5
+ "description": "One venue preset or template family (venues/<name>.jsonc), and the TeX base set (venues/tex-base.jsonc). A paper's paperlint.json names one with \"extends\"; a preset may extend another. `format` and `rules` are read by paperlint's pdf/* venue rules and its lint config (src/venue-rules.ts, src/presets.ts); `tex` by `paperlint toolchain` and `paperlint build`.",
6
6
  "type": "object",
7
- "required": ["tex"],
8
7
  "additionalProperties": false,
9
8
  "properties": {
10
- "tex": { "$ref": "#/definitions/tex" },
9
+ "extends": {
10
+ "description": "The preset this one builds on: `paperlint:<name>` (shipped) or a path starting with ./ or ../, relative to this file.",
11
+ "type": "string",
12
+ "minLength": 1
13
+ },
14
+ "name": {
15
+ "description": "A display name for messages; the file name otherwise.",
16
+ "type": "string",
17
+ "minLength": 1
18
+ },
19
+ "tex": {
20
+ "$ref": "#/definitions/tex"
21
+ },
11
22
  "template": {
12
23
  "description": "The \\documentclass the venue's template uses.",
13
24
  "type": "string",
14
25
  "minLength": 1
15
26
  },
16
- "page_size": { "type": "string", "enum": ["letter", "a4"] },
17
- "page_w_in": { "type": "number", "exclusiveMinimum": 0 },
18
- "page_h_in": { "type": "number", "exclusiveMinimum": 0 },
19
- "textblock_w_in": { "type": "number", "exclusiveMinimum": 0 },
20
- "textblock_h_in": { "type": "number", "exclusiveMinimum": 0 },
21
- "columns": { "type": "integer", "minimum": 1 },
22
- "body_pt": { "type": "number", "exclusiveMinimum": 0 },
23
- "body_pt_tol": { "type": "number", "minimum": 0 },
24
- "ref_pt_min": { "type": "number", "exclusiveMinimum": 0 },
25
- "ref_pt_max": { "type": "number", "exclusiveMinimum": 0 },
26
- "fonts_text": { "type": "string", "minLength": 1 },
27
- "fonts_title": { "type": "string", "minLength": 1 },
28
- "page_limits_not_gated": { "type": "object" },
29
- "kinds": {
27
+ "format": {
28
+ "description": "What the venue's call for papers sets. Every key is optional; a child preset's key replaces its parent's, and a child's kind replaces that kind.",
30
29
  "type": "object",
31
- "additionalProperties": {
32
- "type": "object",
33
- "additionalProperties": false,
34
- "properties": {
35
- "body_pages_max": { "type": "integer", "minimum": 1 },
36
- "ref_pages_max": { "type": "integer", "minimum": 0 }
30
+ "additionalProperties": false,
31
+ "properties": {
32
+ "page_size": {
33
+ "type": "string",
34
+ "enum": ["letter", "a4"]
35
+ },
36
+ "page_w_in": {
37
+ "type": "number",
38
+ "exclusiveMinimum": 0
39
+ },
40
+ "page_h_in": {
41
+ "type": "number",
42
+ "exclusiveMinimum": 0
43
+ },
44
+ "textblock_w_in": {
45
+ "type": "number",
46
+ "exclusiveMinimum": 0
47
+ },
48
+ "textblock_h_in": {
49
+ "type": "number",
50
+ "exclusiveMinimum": 0
51
+ },
52
+ "columns": {
53
+ "type": "integer",
54
+ "minimum": 1
55
+ },
56
+ "body_pt": {
57
+ "type": "number",
58
+ "exclusiveMinimum": 0
59
+ },
60
+ "body_pt_tol": {
61
+ "type": "number",
62
+ "minimum": 0
63
+ },
64
+ "ref_pt_min": {
65
+ "type": "number",
66
+ "exclusiveMinimum": 0
67
+ },
68
+ "ref_pt_max": {
69
+ "type": "number",
70
+ "exclusiveMinimum": 0
71
+ },
72
+ "fonts_text": {
73
+ "type": "string",
74
+ "minLength": 1
75
+ },
76
+ "fonts_title": {
77
+ "type": "string",
78
+ "minLength": 1
79
+ },
80
+ "page_limits_not_gated": {
81
+ "type": "object"
82
+ },
83
+ "kinds": {
84
+ "type": "object",
85
+ "additionalProperties": {
86
+ "type": "object",
87
+ "additionalProperties": false,
88
+ "properties": {
89
+ "body_pages_max": {
90
+ "type": "integer",
91
+ "minimum": 1
92
+ },
93
+ "ref_pages_max": {
94
+ "type": "integer",
95
+ "minimum": 0
96
+ }
97
+ }
98
+ }
37
99
  }
38
100
  }
101
+ },
102
+ "rules": {
103
+ "description": "Rules the venue implies, rule id \u2192 ESLint entry (a severity, or [severity, options]). Applied to every paper that extends this preset, before the paper's own rules.",
104
+ "type": "object"
39
105
  }
40
106
  },
107
+ "allOf": [
108
+ {
109
+ "if": {
110
+ "not": {
111
+ "required": ["extends"]
112
+ }
113
+ },
114
+ "then": {
115
+ "required": ["tex"]
116
+ }
117
+ }
118
+ ],
41
119
  "definitions": {
42
120
  "ctanName": {
43
121
  "description": "A package name as tlmgr knows it (CTAN / TeX Live), not a distribution package name.",
@@ -48,7 +126,11 @@
48
126
  "type": "array",
49
127
  "minItems": 1,
50
128
  "uniqueItems": true,
51
- "items": { "type": "string", "minLength": 1, "pattern": "^[^/\\s]+$" }
129
+ "items": {
130
+ "type": "string",
131
+ "minLength": 1,
132
+ "pattern": "^[^/\\s]+$"
133
+ }
52
134
  },
53
135
  "tex": {
54
136
  "description": "The TeX Live packages this template needs. Each package maps to the files that PROVE it is present: `kpsewhich` must find every one after an install, or the install failed and says which package.",
@@ -59,14 +141,22 @@
59
141
  "packages": {
60
142
  "type": "object",
61
143
  "minProperties": 1,
62
- "propertyNames": { "$ref": "#/definitions/ctanName" },
63
- "additionalProperties": { "$ref": "#/definitions/proofs" }
144
+ "propertyNames": {
145
+ "$ref": "#/definitions/ctanName"
146
+ },
147
+ "additionalProperties": {
148
+ "$ref": "#/definitions/proofs"
149
+ }
64
150
  },
65
151
  "tools": {
66
152
  "description": "Packages that ship a PROGRAM rather than a file LaTeX loads. Proved by an executable of that name in TeX Live's bin directory, not by kpsewhich.",
67
153
  "type": "object",
68
- "propertyNames": { "$ref": "#/definitions/ctanName" },
69
- "additionalProperties": { "$ref": "#/definitions/proofs" }
154
+ "propertyNames": {
155
+ "$ref": "#/definitions/ctanName"
156
+ },
157
+ "additionalProperties": {
158
+ "$ref": "#/definitions/proofs"
159
+ }
70
160
  }
71
161
  }
72
162
  }