paperlint 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/.github/workflows/ci.yml +16 -23
  2. package/CLAUDE.md +31 -11
  3. package/CONTRIBUTING.md +23 -7
  4. package/README.md +11 -8
  5. package/dist/adapters/banal/invocation.d.ts +1 -1
  6. package/dist/adapters/latex/document-class.d.ts +22 -0
  7. package/dist/adapters/latex/document-class.d.ts.map +1 -0
  8. package/dist/adapters/latex/document-class.js +102 -0
  9. package/dist/adapters/latex/document-class.js.map +1 -0
  10. package/dist/adapters/latex/hidden.d.ts +16 -0
  11. package/dist/adapters/latex/hidden.d.ts.map +1 -0
  12. package/dist/adapters/latex/hidden.js +49 -0
  13. package/dist/adapters/latex/hidden.js.map +1 -0
  14. package/dist/adapters/latex/index.d.ts +29 -0
  15. package/dist/adapters/latex/index.d.ts.map +1 -0
  16. package/dist/adapters/latex/index.js +18 -0
  17. package/dist/adapters/latex/index.js.map +1 -0
  18. package/dist/adapters/latex/nodes.d.ts +54 -0
  19. package/dist/adapters/latex/nodes.d.ts.map +1 -0
  20. package/dist/adapters/latex/nodes.js +93 -0
  21. package/dist/adapters/latex/nodes.js.map +1 -0
  22. package/dist/adapters/latex/outline.d.ts +10 -0
  23. package/dist/adapters/latex/outline.d.ts.map +1 -0
  24. package/dist/adapters/latex/outline.js +39 -0
  25. package/dist/adapters/latex/outline.js.map +1 -0
  26. package/dist/adapters/latex/parse.d.ts +42 -0
  27. package/dist/adapters/latex/parse.d.ts.map +1 -0
  28. package/dist/adapters/latex/parse.js +94 -0
  29. package/dist/adapters/latex/parse.js.map +1 -0
  30. package/dist/adapters/latex/rendered.d.ts +10 -0
  31. package/dist/adapters/latex/rendered.d.ts.map +1 -0
  32. package/dist/adapters/latex/rendered.js +39 -0
  33. package/dist/adapters/latex/rendered.js.map +1 -0
  34. package/dist/adapters/pdfjs/fill.d.ts.map +1 -1
  35. package/dist/adapters/pdfjs/fill.js +12 -12
  36. package/dist/adapters/pdfjs/fill.js.map +1 -1
  37. package/dist/build.js +4 -4
  38. package/dist/build.js.map +1 -1
  39. package/dist/cli.d.ts.map +1 -1
  40. package/dist/cli.js +29 -5
  41. package/dist/cli.js.map +1 -1
  42. package/dist/domain/tex-document.d.ts +84 -0
  43. package/dist/domain/tex-document.d.ts.map +1 -0
  44. package/dist/domain/tex-document.js +43 -0
  45. package/dist/domain/tex-document.js.map +1 -0
  46. package/dist/facts-file.d.ts.map +1 -1
  47. package/dist/facts-file.js +2 -2
  48. package/dist/facts-file.js.map +1 -1
  49. package/dist/new-paper.d.ts +5 -0
  50. package/dist/new-paper.d.ts.map +1 -1
  51. package/dist/new-paper.js +3 -0
  52. package/dist/new-paper.js.map +1 -1
  53. package/dist/package-dirs.d.ts +5 -0
  54. package/dist/package-dirs.d.ts.map +1 -0
  55. package/dist/package-dirs.js +14 -0
  56. package/dist/package-dirs.js.map +1 -0
  57. package/dist/ports/latex.d.ts +24 -0
  58. package/dist/ports/latex.d.ts.map +1 -0
  59. package/dist/ports/latex.js +2 -0
  60. package/dist/ports/latex.js.map +1 -0
  61. package/dist/presets.d.ts +15 -2
  62. package/dist/presets.d.ts.map +1 -1
  63. package/dist/presets.js +19 -6
  64. package/dist/presets.js.map +1 -1
  65. package/dist/tex-requirements.d.ts +11 -0
  66. package/dist/tex-requirements.d.ts.map +1 -1
  67. package/dist/tex-requirements.js +14 -8
  68. package/dist/tex-requirements.js.map +1 -1
  69. package/dist/tex-venue-rules.d.ts +83 -0
  70. package/dist/tex-venue-rules.d.ts.map +1 -0
  71. package/dist/tex-venue-rules.js +254 -0
  72. package/dist/tex-venue-rules.js.map +1 -0
  73. package/dist/venue-rules.d.ts +1 -1
  74. package/dist/venue-rules.d.ts.map +1 -1
  75. package/docs/configuration.md +3 -2
  76. package/docs/e2e.md +69 -40
  77. package/docs/install.md +1 -1
  78. package/docs/prior-art/README.md +1 -1
  79. package/docs/prior-art/package-location.md +5 -5
  80. package/docs/prior-art/repro/README.md +1 -1
  81. package/docs/prior-art/test-tooling.md +21 -10
  82. package/docs/rules/tex/required-section.md +73 -0
  83. package/docs/rules/tex/template.md +62 -0
  84. package/docs/rules/tex/venue-leftover.md +71 -0
  85. package/docs/rules.md +70 -48
  86. package/docs/testing.md +17 -4
  87. package/docs/texlive-install-decision.md +1 -1
  88. package/docs/toolchain.md +2 -2
  89. package/eslint.config.mjs +155 -15
  90. package/fixtures/build-e2e/acmart/expect.json +26 -0
  91. package/fixtures/build-e2e/aidc/PIPELINE-STATUS.md +3 -0
  92. package/fixtures/build-e2e/aidc/expect.json +14 -0
  93. package/fixtures/build-e2e/aidc/paper.tex +46 -0
  94. package/fixtures/build-e2e/aidc/paperlint.json +1 -0
  95. package/fixtures/build-e2e/broken/expect.json +11 -0
  96. package/fixtures/build-e2e/cite/expect.json +27 -0
  97. package/fixtures/build-e2e/empty/expect.json +12 -0
  98. package/fixtures/build-e2e/fallback/expect.json +53 -0
  99. package/fixtures/build-e2e/guards/expect.json +6 -0
  100. package/fixtures/build-e2e/no-source/expect.json +10 -0
  101. package/fixtures/build-e2e/unbalanced/expect.json +60 -0
  102. package/fixtures/pdf-facts/README.md +1 -1
  103. package/fixtures/real-markdown-paper/baseline.ts +1 -1
  104. package/fixtures/render-paper/build-clean.sh +3 -5
  105. package/package.json +13 -2
  106. package/{skills/submit-paper/references/venues → presets}/acm-sigconf.jsonc +4 -1
  107. package/{skills/submit-paper/references/venues → presets}/agenticdev.jsonc +4 -0
  108. package/presets/aidc.jsonc +73 -0
  109. package/{skills/submit-paper/references/venues → presets}/aisec.jsonc +4 -0
  110. package/presets/ieee-conference.jsonc +66 -0
  111. package/{skills/submit-paper/references/venues → presets}/realm.jsonc +8 -1
  112. package/{skills/submit-paper/references/venues → presets/tex}/paper-guards.tex +1 -1
  113. package/{skills/submit-paper/references/venues → presets}/venue-profile.schema.json +30 -2
  114. package/scripts/check.harness.ts +64 -10
  115. package/scripts/check.test.ts +60 -13
  116. package/scripts/check.ts +83 -30
  117. package/scripts/test.test.ts +1 -1
  118. package/scripts/test.ts +1 -1
  119. package/skills/paper-pipeline/PIPELINE-MAP.md +2 -2
  120. package/skills/paper-pipeline/SKILL.md +2 -2
  121. package/skills/paper-pipeline/SKILL.md.spec.ts +1 -1
  122. package/skills/paper-pipeline/scripts/consumer.d.mts +0 -2
  123. package/skills/paper-pipeline/scripts/consumer.mjs +1 -15
  124. package/skills/render-paper/SKILL.md +2 -2
  125. package/skills/render-paper/SKILL.md.spec.ts +1 -1
  126. package/skills/render-paper/check-render.sh +16 -63
  127. package/skills/render-paper/render-paper.harness.mjs +17 -59
  128. package/src/adapters/banal/index.test.ts +1 -1
  129. package/src/adapters/banal/invocation.ts +1 -1
  130. package/src/adapters/banal/output.test.ts +1 -1
  131. package/src/adapters/banal/run.test.ts +1 -1
  132. package/src/adapters/banal/xml.test.ts +1 -1
  133. package/src/adapters/latex/document-class.ts +147 -0
  134. package/src/adapters/latex/hidden.ts +70 -0
  135. package/src/adapters/latex/index.test.ts +361 -0
  136. package/src/adapters/latex/index.ts +55 -0
  137. package/src/adapters/latex/nodes.ts +141 -0
  138. package/src/adapters/latex/outline.ts +64 -0
  139. package/src/adapters/latex/parse.ts +136 -0
  140. package/src/adapters/latex/rendered.ts +59 -0
  141. package/src/adapters/pdfjs/fill.test.ts +1 -1
  142. package/src/adapters/pdfjs/fill.ts +18 -10
  143. package/src/build.harness.mjs +5 -7
  144. package/src/build.ts +4 -4
  145. package/src/cli.harness.mjs +1 -1
  146. package/src/cli.test.ts +2 -8
  147. package/src/cli.ts +33 -8
  148. package/src/domain/tex-document.test.ts +80 -0
  149. package/src/domain/tex-document.ts +110 -0
  150. package/src/engine.harness.mjs +1 -1
  151. package/src/facts-file.test.ts +4 -7
  152. package/src/facts-file.ts +2 -2
  153. package/src/latex-loop.harness.ts +1 -1
  154. package/src/new-paper.test.ts +28 -3
  155. package/src/new-paper.ts +8 -0
  156. package/src/package-dirs.ts +17 -0
  157. package/src/paper-settings-commands.test.ts +6 -1
  158. package/src/ports/latex.ts +29 -0
  159. package/src/presets.test.ts +65 -4
  160. package/src/presets.ts +38 -15
  161. package/src/rule-cases.test.ts +474 -0
  162. package/src/rule-docs.test.ts +159 -0
  163. package/src/tex-requirements.harness.ts +13 -23
  164. package/src/tex-requirements.ts +35 -8
  165. package/src/tex-venue-rules.test.ts +659 -0
  166. package/src/tex-venue-rules.ts +429 -0
  167. package/src/toolchain.harness.mjs +1 -1
  168. package/src/venue-rules.test.ts +96 -4
  169. package/src/venue-rules.ts +1 -1
  170. package/templates/paper/paper.tex +2 -1
  171. /package/{skills/submit-paper/references/venues → presets/tex}/agenticdev.tex +0 -0
  172. /package/{skills/submit-paper/references/venues → presets}/tex-base.jsonc +0 -0
@@ -0,0 +1,254 @@
1
+ /**
2
+ * THE VENUE-CONFORMANCE RULES OVER THE SOURCE — `paper.tex` judged against what its venue preset
3
+ * requires of the LaTeX itself, before any build:
4
+ *
5
+ * tex/template error the `\documentclass` is the preset's class, with every option it names
6
+ * tex/required-section error each section the preset requires is there, titled exactly, and
7
+ * where the preset says (`last`: after every section of the body)
8
+ * tex/venue-leftover warn the text a reader sees names ANOTHER shipped venue — its `name`
9
+ * or one of its `aliases` — than the one the paper extends
10
+ *
11
+ * ── WHY THEY LIVE HERE AND NOT IN `eslint-rules/` ────────────────────────────────
12
+ * Like the `pdf/*` venue rules (`venue-rules.ts`), they need the paper's resolved preset —
13
+ * `paperPreset`, the one answer to "which venue is this paper judged against" — and read it through
14
+ * the `Files` port. What they read of the source comes through the `LatexReader` port
15
+ * (`src/ports/latex.ts`, implemented over unified-latex in `src/adapters/latex/`): the parse tree of
16
+ * `sourceCode.raw`, never a search of the text.
17
+ *
18
+ * ── WHO SPEAKS WHEN THERE IS NOTHING TO JUDGE ───────────────────────────────────
19
+ * No `paperlint.json`, no `extends`, or a preset that does not resolve: silent. `pdf/measured` and
20
+ * `pdf/profile` already say so, once. A preset that names no template: silent — there is nothing to
21
+ * compare, and a template-family preset is where it is named.
22
+ */
23
+ import { basename, dirname, join } from "node:path";
24
+ import { CONFIG_FILE } from "#lib/paper-config";
25
+ import { collapse, documentClassLine, missingOptions, runText, spanIn, } from "./domain/tex-document.js";
26
+ import { paperPreset, resolvePreset, SHIPPED_PREFIX, shippedPresets, } from "./presets.js";
27
+ // ── the judges: what the source says + a resolved preset → findings. Pure. ─────────
28
+ /** A place as a finding points at it: its span, or null — the top of the file — when unplaced. */
29
+ const spanOf = (p) => (p.kind === "at" ? p.span : null);
30
+ /** A class line that names a class, against the template's: the same class, with every option. */
31
+ function judgeClass(line, want, data) {
32
+ const at = spanOf(line.place);
33
+ if (line.cls !== want.cls)
34
+ return [{ messageId: "wrongClass", data: { ...data, got: line.cls }, at }];
35
+ return missingOptions(line, want).map((option) => ({
36
+ messageId: "missingOption",
37
+ data: { ...data, option },
38
+ at,
39
+ }));
40
+ }
41
+ /** The paper's class line against a template that was read: present, naming a class, that class. */
42
+ function judgeClassLine(line, want, data) {
43
+ switch (line.kind) {
44
+ case "missing":
45
+ return [{ messageId: "noClass", data, at: null }];
46
+ case "empty":
47
+ return [{ messageId: "emptyClass", data, at: spanOf(line.place) }];
48
+ case "class":
49
+ return judgeClass(line, want, data);
50
+ }
51
+ }
52
+ /**
53
+ * The paper's class line against the preset's template: a template the reader cannot read is said
54
+ * once; then the class must be there, name a class, be the template's class, and carry every
55
+ * option the template names.
56
+ */
57
+ export function judgeTemplate(line, preset, latex) {
58
+ const t = preset.template;
59
+ if (t === null)
60
+ return [];
61
+ const want = latex.template(t.text);
62
+ if (want === null) {
63
+ const data = { file: t.file, text: JSON.stringify(t.text) };
64
+ return [{ messageId: "badTemplate", data, at: null }];
65
+ }
66
+ const data = { venue: preset.label, template: documentClassLine(want) };
67
+ return judgeClassLine(line, want, data);
68
+ }
69
+ /** Where a heading starts, or null when the parser gave it no place. */
70
+ const startOf = (p) => p.kind === "at" ? p.span.start : null;
71
+ /** A required section that is absent, reported where the document ends — where it would go. */
72
+ const missingSection = (outline, data) => ({
73
+ messageId: "missing",
74
+ data,
75
+ at: outline.end === null ? null : { start: outline.end, end: outline.end },
76
+ });
77
+ /** The first section of the body that starts after `start` and is not titled `title`. */
78
+ function bodySectionAfter(outline, start, title) {
79
+ const backMatter = outline.backMatter ?? Number.POSITIVE_INFINITY;
80
+ return outline.sections.find((h) => {
81
+ const s = startOf(h.place);
82
+ return s !== null && s > start && s < backMatter && h.title !== title;
83
+ });
84
+ }
85
+ /**
86
+ * One required section against the outline: missing (reported where the document ends, where it
87
+ * would go), or — for `last` — standing before a section of the body. The body is every section
88
+ * before the appendix and the bibliography, so the required one may follow the bibliography. A
89
+ * heading without a place cannot be ordered, so it is never reported as out of order.
90
+ */
91
+ function judgeSection(outline, want, venue) {
92
+ const title = collapse(want.title);
93
+ const last = outline.sections.filter((h) => h.title === title).at(-1);
94
+ const data = { venue, title };
95
+ if (last === undefined)
96
+ return [missingSection(outline, data)];
97
+ const lastStart = startOf(last.place);
98
+ if (want.position !== "last" || lastStart === null)
99
+ return [];
100
+ const after = bodySectionAfter(outline, lastStart, title);
101
+ if (after === undefined)
102
+ return [];
103
+ const notLast = { ...data, after: after.title };
104
+ return [{ messageId: "notLast", data: notLast, at: spanOf(last.place) }];
105
+ }
106
+ /** Every section the preset requires, against the paper's outline. */
107
+ export function judgeRequiredSections(outline, preset) {
108
+ if (preset.requiredSections.length === 0)
109
+ return [];
110
+ const o = outline();
111
+ return preset.requiredSections.flatMap((r) => judgeSection(o, r, preset.label));
112
+ }
113
+ /** `s` as a pattern that matches it literally. */
114
+ const literal = (s) => s.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
115
+ /**
116
+ * Each place the rendered text names another venue — one of its aliases as a whole word, case
117
+ * and all — unless that alias is also this paper's venue's own (a shared parent conference).
118
+ */
119
+ export function judgeLeftover(runs, preset, others) {
120
+ const own = new Set(preset.aliases);
121
+ const names = others.flatMap((o) => o.aliases
122
+ .filter((a) => !own.has(a))
123
+ .map((alias) => ({
124
+ other: o.label,
125
+ re: new RegExp(`(?<![\\p{L}\\p{N}])${literal(alias)}(?![\\p{L}\\p{N}])`, "gu"),
126
+ })));
127
+ return runs.flatMap((run) => names.flatMap(({ other, re }) => [...runText(run).matchAll(re)].map((m) => ({
128
+ messageId: "leftover",
129
+ data: { name: m[0], other, venue: preset.label },
130
+ at: spanIn(run, m.index, m.index + m[0].length),
131
+ }))));
132
+ }
133
+ /** Where a rule's page lives: `docs/rules/tex/<name>.md` on the default branch. */
134
+ export const ruleDocsUrl = (name) => `https://github.com/zernie/paperlint/blob/main/docs/rules/tex/${name}.md`;
135
+ const RULES = {
136
+ template: {
137
+ judge: (r) => judgeTemplate(r.latex.documentClass(r.src), r.preset, r.latex),
138
+ meta: {
139
+ type: "problem",
140
+ docs: {
141
+ description: "the \\documentclass is the venue preset's class, with every option the preset names",
142
+ url: ruleDocsUrl("template"),
143
+ },
144
+ schema: [],
145
+ messages: {
146
+ noClass: "paper.tex has no \\documentclass; {{venue}} requires `{{template}}`",
147
+ emptyClass: "the \\documentclass names no class; {{venue}} requires `{{template}}`",
148
+ badTemplate: '{{file}}: the preset\'s "template" {{text}} is neither a \\documentclass line nor a class name, so the class cannot be checked',
149
+ wrongClass: "the class is `{{got}}`, and {{venue}} requires `{{template}}` — the page size, fonts and layout the venue checks come from the class",
150
+ missingOption: "the class option `{{option}}` is missing: {{venue}} requires `{{template}}`",
151
+ },
152
+ },
153
+ },
154
+ "required-section": {
155
+ judge: (r) => judgeRequiredSections(() => r.latex.outline(r.src), r.preset),
156
+ meta: {
157
+ type: "problem",
158
+ docs: {
159
+ description: "every section the venue preset requires is there, titled exactly, and where the preset says",
160
+ url: ruleDocsUrl("required-section"),
161
+ },
162
+ schema: [],
163
+ messages: {
164
+ missing: "{{venue}} requires a section titled «{{title}}» and there is none — add `\\section*{{{title}}}` (the title exactly; a bold paragraph does not count)",
165
+ notLast: "«{{title}}» must close the paper at {{venue}}, and the section «{{after}}» comes after it — move it after the last section of the body (after the bibliography is fine)",
166
+ },
167
+ },
168
+ },
169
+ "venue-leftover": {
170
+ judge: (r) => judgeLeftover(r.latex.renderedRuns(r.src), r.preset, r.others()),
171
+ meta: {
172
+ type: "suggestion",
173
+ docs: {
174
+ description: "the text names another shipped venue than the one the paper extends — a leftover from an earlier submission",
175
+ url: ruleDocsUrl("venue-leftover"),
176
+ },
177
+ schema: [],
178
+ messages: {
179
+ leftover: "«{{name}}» names {{other}}, and this paper extends {{venue}} — a leftover from an earlier submission? A reviewer reads it before the abstract. Comments and citation keys are not reported; a sentence that names the other venue on purpose can keep it with a disable directive",
180
+ },
181
+ },
182
+ },
183
+ };
184
+ /** The level each rule is on at in paperlint's own config, for every `paper.tex`. */
185
+ export const TEX_VENUE_RULE_LEVELS = {
186
+ "tex/template": "error",
187
+ "tex/required-section": "error",
188
+ "tex/venue-leftover": "warn",
189
+ };
190
+ /**
191
+ * Every shipped venue but the ones on this paper's own chain, each resolved the way a paper
192
+ * extending it would be. A preset that does not resolve is skipped: `pdf/profile` owns that.
193
+ */
194
+ export function otherVenues(preset, fromFile, deps) {
195
+ return shippedPresets(deps.venuesDir).flatMap((name) => {
196
+ const r = resolvePreset(`${SHIPPED_PREFIX}${name}`, fromFile, deps);
197
+ // A preset all of whose files are on this paper's chain is this venue or one it extends.
198
+ return r.ok && !r.value.chain.every((f) => preset.chain.includes(f))
199
+ ? [{ label: r.value.label, aliases: r.value.aliases }]
200
+ : [];
201
+ });
202
+ }
203
+ /**
204
+ * What a judge reads for the paper whose `paper.tex` is `filename`, or null when the paper has no
205
+ * resolved preset (`pdf/measured` and `pdf/profile` already say so).
206
+ */
207
+ function readingOf(filename, src, deps) {
208
+ const dir = dirname(filename);
209
+ const p = paperPreset(dir, deps);
210
+ if (p.kind !== "resolved")
211
+ return null;
212
+ const preset = p.preset;
213
+ const others = () => otherVenues(preset, join(dir, CONFIG_FILE), deps);
214
+ return { src, latex: deps.latex, preset, others };
215
+ }
216
+ /** Each finding reported at its span, or at the top of the file when it has none. */
217
+ function reportAll(context, findings) {
218
+ const at = (i) => context.sourceCode.getLocFromIndex(i);
219
+ findings.forEach((f) => {
220
+ context.report({
221
+ loc: { start: at(f.at?.start ?? 0), end: at(f.at?.end ?? 0) },
222
+ messageId: f.messageId,
223
+ data: f.data,
224
+ });
225
+ });
226
+ }
227
+ function rule(name, deps) {
228
+ const { judge, meta } = RULES[name];
229
+ return {
230
+ meta,
231
+ create(context) {
232
+ // Judged once per paper, on its paper.tex: the paper directory is the file's directory.
233
+ if (basename(context.filename) !== "paper.tex")
234
+ return {};
235
+ return {
236
+ root() {
237
+ const sc = context.sourceCode;
238
+ const reading = readingOf(context.filename, sc.raw ?? sc.text, deps);
239
+ if (reading !== null)
240
+ reportAll(context, judge(reading));
241
+ },
242
+ };
243
+ },
244
+ };
245
+ }
246
+ /** The venue-conformance rules, as rules of the `tex` plugin (beside `tex/future-promise`). */
247
+ export function texVenueRules(deps) {
248
+ return {
249
+ template: rule("template", deps),
250
+ "required-section": rule("required-section", deps),
251
+ "venue-leftover": rule("venue-leftover", deps),
252
+ };
253
+ }
254
+ //# sourceMappingURL=tex-venue-rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tex-venue-rules.js","sourceRoot":"","sources":["../src/tex-venue-rules.ts"],"names":[],"mappings":"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"}
@@ -42,7 +42,7 @@ export type Assessment = {
42
42
  };
43
43
  export interface VenueRuleDeps {
44
44
  readonly files: Files;
45
- /** The package's venues directory (`packageVenuesDir()`): the shipped presets and their schema. */
45
+ /** The package's presets directory (`presetsDir()`): the shipped presets and their schema. */
46
46
  readonly venuesDir: string;
47
47
  }
48
48
  /** One paper, assessed. Reads through `deps.files` only; never throws on a paper's files. */
@@ -1 +1 @@
1
- {"version":3,"file":"venue-rules.d.ts","sourceRoot":"","sources":["../src/venue-rules.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"venue-rules.d.ts","sourceRoot":"","sources":["../src/venue-rules.ts"],"names":[],"mappings":"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"}
@@ -15,7 +15,8 @@ venues/usenix-sec.jsonc (optional, your own) a VENUE PRESET: format, page l
15
15
  ```
16
16
 
17
17
  The shipped presets (`paperlint:<name>`) live in the package, under
18
- `skills/submit-paper/references/venues/`: `acm-sigconf`, `agenticdev`, `aisec`, `realm`.
18
+ `presets/`: `acm-sigconf`, `agenticdev`, `aidc`, `aisec`,
19
+ `ieee-conference`, `realm`.
19
20
 
20
21
  ## The root `paperlint.json`
21
22
 
@@ -229,7 +230,7 @@ write. It prints its plan first, one line per step, then runs it:
229
230
 
230
231
  ```
231
232
  papers/my-paper
232
- inputs: TEXINPUTS += <paperlint>/skills/submit-paper/references/venues
233
+ inputs: TEXINPUTS += <paperlint>/presets/tex
233
234
  compile: paper.tex (\documentclass[sigconf,screen]{acmart}, venue agenticdev)
234
235
  measure: pdf.js → _build/paper.facts.json (facts for the lint rules; nothing is judged here)
235
236
  ✓ paper.pdf — 4 pdflatex passes, 1 bibtex run; facts: _build/paper.facts.json, last page 621.5 / 264.8 pt
package/docs/e2e.md CHANGED
@@ -1,37 +1,50 @@
1
1
  # End-to-end tests
2
2
 
3
- They are `test/e2e/install.ts` and `test/e2e/build.ts`. Both are
4
- part of `npm run check`, so nobody has to remember to call them. `npm run test:e2e` runs just
5
- these, install first; if the install e2e fails or is skipped, the build e2e does not run.
3
+ They are vitest tests named `*.e2e.ts` under `test/e2e/`, one vitest project per environment they
4
+ need, found by the project's glob — no list of files exists in `package.json`, `scripts/check.ts`
5
+ or CI:
6
+
7
+ | script | project | what it needs | files |
8
+ | -------------------------- | ------------- | -------------------------------------------- | --------------------------- |
9
+ | `npm run test:e2e:install` | `e2e-install` | npm and pnpm, and the network for `npm pack` | `test/e2e/install/*.e2e.ts` |
10
+ | `npm run test:e2e:tex` | `e2e-tex` | TeX Live from `paperlint toolchain`, banal | `test/e2e/tex/*.e2e.ts` |
11
+ | `npm run test:e2e` | both | | |
12
+
13
+ Both areas are part of `npm run check`, so nobody has to remember to call them. CI runs each in the
14
+ job that has its environment: `test:e2e:install` in `gates`, `test:e2e:tex` in `build-e2e`.
6
15
 
7
16
  This page says what they prove, what they deliberately do not, and when a change owes a new one.
8
17
 
9
- ## The line between a harness and an end-to-end run
18
+ ## The line between the other tests and an end-to-end run
10
19
 
11
- Most of this package is tested by harnesses, described in [`CONTRIBUTING.md`](../CONTRIBUTING.md):
12
- a rule is handed a file with a defect and must find it, then a clean file and must stay silent.
13
- That is the right shape for a rule, and it cannot answer a different kind of question.
20
+ The rest of the package is tested by unit and integration tests and, for what an agent sees, by
21
+ harnesses — [`testing.md`](testing.md) says which is which. A rule is tested twice before any e2e:
22
+ unit tests beside it hand its logic a defect and a clean input, and `src/rule-cases.test.ts` runs
23
+ every registered rule through real ESLint on a small paper tree, the way `paperlint lint` does.
24
+ That is still an integration test: no TeX, no install, the working tree as it is.
14
25
 
15
- A harness runs **in this repository's process, against this repository's working tree**. It
16
- imports the module directly. Every path resolves, every dependency is already installed, and the
17
- file it reads is the file in `git`. An end-to-end run exists because all three of those are
26
+ Those tests run **in this repository's process, against this repository's working tree**. They
27
+ import the modules directly. Every path resolves, every dependency is already installed, and the
28
+ file they read is the file in `git`. An end-to-end run exists because all three of those are
18
29
  assumptions that stop holding the moment somebody else installs the package.
19
30
 
20
- | question | answered by |
21
- | ------------------------------------------------------------------------------------ | --------------------------------------------------------- |
22
- | does this rule find this defect, and stay quiet otherwise | harness |
23
- | does the CLI pick the right script, with the right interpreter | harness (`src/build.harness.mjs` substitutes `spawnSync`) |
24
- | does the tarball, installed into a tree that is not this one, contain what it claims | **e2e** |
25
- | do the paths written inside a skill resolve where the package actually lands | **e2e** |
26
- | did a PDF come out, and is it typeset in the fonts the venue requires | **e2e** |
31
+ | question | answered by |
32
+ | ------------------------------------------------------------------------------------ | ------------------------------------------------------------- |
33
+ | does this rule find this defect, and stay quiet otherwise | unit, beside the rule |
34
+ | does the rule run through ESLint, wired, on the file it is for | integration (`src/rule-cases.test.ts`) |
35
+ | does the CLI pick the right script, with the right interpreter | integration (`src/build.harness.mjs` substitutes `spawnSync`) |
36
+ | does the tarball, installed into a tree that is not this one, contain what it claims | **e2e** |
37
+ | do the paths written inside a skill resolve where the package actually lands | **e2e** |
38
+ | did a PDF come out, and is it typeset in the fonts the venue requires | **e2e** |
27
39
 
28
- The third and fourth rows are the ones a harness cannot reach even in principle: the defect only
29
- exists once the code is somewhere else.
40
+ The last three rows are the ones no other test can reach even in principle: the defect only exists
41
+ once the code is somewhere else, or once a real TeX has typeset it.
30
42
 
31
- ## `test/e2e/install.ts` — the package, installed
43
+ ## `test/e2e/install/install.e2e.ts` — the package, installed
32
44
 
33
- `test/e2e/install.ts`. It runs `npm pack`, then installs the resulting tarball into a fresh
34
- temporary tree, **under npm and under pnpm separately**, and drives the installed binary:
45
+ It runs `npm pack`, then installs the resulting tarball into a fresh temporary tree, **under npm
46
+ and under pnpm separately** — one test per manager, each the whole scenario, because its steps
47
+ depend on each other — and drives the installed binary:
35
48
 
36
49
  - the install itself finishes
37
50
  - `paperlint --help` answers with zero
@@ -55,13 +68,21 @@ that works under npm can be dead under pnpm with no error anywhere. The same run
55
68
  binary **directly** rather than through `node <path>`: under npm `.bin` holds a symlink, under
56
69
  pnpm a shell wrapper, and calling `node bin` measures the caller's habit instead of the package.
57
70
 
58
- ## `test/e2e/build.ts` — a real `pdflatex`
71
+ ## `test/e2e/tex/build.e2e.ts` — a real `pdflatex`
59
72
 
60
- `test/e2e/build.ts`. It copies `fixtures/build-e2e/` — eight papers, none with a build script paperlint
61
- would run — into a temporary tree, points a config at it, and runs `paperlint build --all`. paperlint compiles
62
- each paper itself with the real `pdflatex` and `bibtex`; the artifacts are then measured with
73
+ It copies `fixtures/build-e2e/` — nine papers, none with a build script paperlint would run — into
74
+ a temporary tree, points a config at it, and runs `paperlint build --all`. paperlint compiles each
75
+ paper itself with the real `pdflatex` and `bibtex`; the artifacts are then measured with
63
76
  paperlint's own pdf.js reader from `dist/`, and the fonts are cross-checked against the list of
64
- programs pdfTeX writes into `paper.log`:
77
+ programs pdfTeX writes into `paper.log`.
78
+
79
+ **Each fixture declares what it must produce** in `fixtures/build-e2e/<name>/expect.json`, parsed
80
+ with the schema in `test/e2e/tex/build-expect.ts`: whether it builds (`build.outcome`), what its
81
+ lines of the build output say, its fonts, its last page's text, files in its directory, the facts
82
+ the build measured, and — for `paperlint lint` after the build — the exit code and **exactly** the
83
+ findings for each rule it names (`[]` is "none"). One `describe` per fixture directory turns each
84
+ declared field into a test. A folder without `expect.json`, or with one the schema rejects, fails.
85
+ A new venue fixture is a new folder: its paper, its `paperlint.json`, its `expect.json`.
65
86
 
66
87
  | fixture | what it is there to prove |
67
88
  | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
@@ -72,6 +93,8 @@ programs pdfTeX writes into `paper.log`:
72
93
  | `unbalanced` | a two-column acmart paper whose last page comes out 621.5 / 264.8 pt builds **green** — the build does not judge the layout and rewrites nothing — while `_build/paper.facts.json` records both heights, and `paperlint lint` with `pdf/last-page-balance` turned on in `rules` reports the page |
73
94
  | `broken` | a failing build names pdflatex and its exit code, quotes the error line and its `l.NNN` context, and deletes the stale `paper.pdf` planted before the run |
74
95
  | `no-source` | a paper with no `paper.tex` is named separately, and the run as a whole is a failure |
96
+ | `empty` | a document with no pages: pdflatex exits 0 and writes no PDF, and that is a failure, not a green `paper.pdf` |
97
+ | `aidc` | a real `[conference,compsoc]{IEEEtran}` paper builds on paperlint's TeX Live in NimbusRomNo9L, `tex/template`, `tex/required-section` and the `pdf/*` rules find nothing, and `tex/venue-leftover` warns once, on its planted line |
75
98
 
76
99
  The `fallback` row is the point of the whole file. `acmart.cls` checks for `libertine.sty`,
77
100
  `zi4.sty` and `newtxmath.sty`, and failing to find any of them sets `\@ACM@newfontsfalse` and
@@ -79,28 +102,30 @@ typesets the paper in Computer Modern. The build is green, the PDF looks fine, t
79
102
  and therefore so does the pagination. A submitted paper went out that way. **An exit code cannot
80
103
  see it, so the content of the artifact is what gets measured.**
81
104
 
82
- Before the fixtures, the run proves the refusal with no TeX at all: PATH holds `node` alone, the
105
+ Beside the fixtures, one test proves the refusal with no TeX at all: PATH holds `node` alone, the
83
106
  cache directory is empty and `CI` is set, and `paperlint build` must exit 1 with one line naming
84
- `npx paperlint toolchain` and the venue's packages, print no plan and create no PDF and no cache. It then
85
- asks `paperlint build --dry-run` which TeX Live the real run will use; under `--strict` (CI) that must be
86
- paperlint's own cache, because the runner has no other.
107
+ `npx paperlint toolchain` and the venue's packages, print no plan and create no PDF and no cache.
108
+ The rest asks `paperlint build --dry-run` which TeX Live the real run will use; under CI that must
109
+ be paperlint's own cache, because the runner has no other.
87
110
 
88
- ## `test/e2e/toolchain.ts` — real TeX Live, and only it
111
+ ## `test/e2e/tex/toolchain.e2e.ts` — real TeX Live, and only it
89
112
 
90
113
  `paperlint toolchain` into `$PAPERLINT_TEXLIVE_DIR` against real CTAN; a second run must say "nothing to do"
91
114
  within seconds; `--check` must exit 0; then the `acmart` fixture is built with PATH holding `node`
92
115
  only, so no other TeX Live and no PDF tool can stand in, and the PDF must carry Libertine and Biolinum
93
- and no Computer Modern face. Without `PAPERLINT_TEXLIVE_DIR` it is a declared skip: installing ~270 MB
116
+ and no Computer Modern face. Without `PAPERLINT_TEXLIVE_DIR` its tests are skipped: installing ~270 MB
94
117
  into a home directory as a side effect of `npm run check` is the unasked install rule 11 forbids.
95
118
  `src/toolchain.harness.mjs` covers the installer's logic (mirror fallback, archive check, time
96
119
  limit, verification, idempotence) against a fake mirror on disk, without the network.
97
120
 
98
- ## Skips are declared, never silent
121
+ ## Skips are reported, never passed
99
122
 
100
123
  A clone without TeX Live genuinely cannot run the build e2e; a machine without pnpm cannot run
101
- half of the install e2e. Each says so and exits **77** — the skip code vigiles' own runner uses —
102
- not zero, and `npm run check` reports it as `SKIPPED — not run, not passed`. In CI the same absence
103
- means a broken environment, so `--strict` turns the skip into a failure.
124
+ half of the install e2e. Their tests are then `skipIf`'d, and vitest reports them as skipped.
125
+ `npm run check` reads that count from vitest's JSON report and prints the gate as
126
+ `SKIPPED — not run, not passed`. In CI the same absence means a broken environment: with `CI` set
127
+ (or `PAPERLINT_E2E_STRICT=1`) each such file registers one more test that fails and names what is
128
+ missing (`test/e2e/need.ts`).
104
129
 
105
130
  A skipped step and a passed one look identical in a CI interface. That is the class this whole
106
131
  package is written against, so it is not allowed to happen inside it either.
@@ -115,9 +140,13 @@ Not every change does. The trigger is countable — ask whether the change touch
115
140
  - anything read through `package.json` fields — `bin`, `files`, `exports`
116
141
  - a new external program the pipeline shells out to
117
142
  - a new artifact the pipeline produces whose **content** can be wrong while the exit code is zero
143
+ - a new template-family preset: the TeX packages it declares, and the page size and fonts measured
144
+ from a real build of its class → one fixture folder in `fixtures/build-e2e/`, with its
145
+ `expect.json`
118
146
 
119
- If none of those apply, a harness beside the changed module is the right test, and an e2e run
120
- would only make the suite slower without making it stricter.
147
+ If none of those apply, a unit or integration test beside the changed module is the right test,
148
+ and an e2e run would only make the suite slower without making it stricter. A new rule is not a
149
+ trigger by itself: its cases in `src/rule-cases.test.ts` already run it through real ESLint.
121
150
 
122
151
  ## What these two do NOT cover
123
152
 
@@ -142,7 +171,7 @@ everything.
142
171
 
143
172
  ## The corpus
144
173
 
145
- `stageCorpus()` in `test/e2e/install.ts` writes a small paper by hand — a declared stage, its
174
+ `stageCorpus()` in `test/e2e/install/install.e2e.ts` writes a small paper by hand — a declared stage, its
146
175
  PDF, its byte counts, the cross-check between them — and copies `fixtures/build-e2e/acmart` beside
147
176
  it so the LaTeX rules see LaTeX rather than a placeholder.
148
177
 
package/docs/install.md CHANGED
@@ -121,7 +121,7 @@ a CI workflow and a heavy toolchain), asks two questions and guesses the rest.
121
121
 
122
122
  **npm and pnpm are covered; Yarn Plug'n'Play is not supported.**
123
123
 
124
- `test/e2e/install.ts` packs the tarball, installs it into a clean project with each manager that
124
+ `test/e2e/install/install.e2e.ts` packs the tarball, installs it into a clean project with each manager that
125
125
  launches on the machine, and runs the hook command to see whether it resolves. A grep over
126
126
  `hooks.json` would not do: the string is right under any manager, and whether it resolves depends
127
127
  on the tree the manager laid out. It also checks that every shipped skill is reachable as
@@ -1,7 +1,7 @@
1
1
  # Prior art — how comparable tools are shaped
2
2
 
3
3
  **Why this folder lives in the repository.** These are technical records that code and other docs
4
- cite by path: `test/e2e/install.ts` cites `package-location.md`, `scripts/marketplace-shape.mjs`
4
+ cite by path: `test/e2e/install/install.e2e.ts` cites `package-location.md`, `scripts/marketplace-shape.mjs`
5
5
  and `docs/install.md` point at the probes in `repro/`, and `eslint.config.mjs` excludes `repro/`
6
6
  by name. Moving the folder out would leave those references pointing nowhere. The notes are for
7
7
  contributors and maintainers, not for someone checking a paper, which is why the README does not
@@ -1,6 +1,6 @@
1
1
  # How a package finds its own installed files, and its own bin
2
2
 
3
- **Question this file answers:** `test/e2e/install.ts` inspects the tree it just installed by
3
+ **Question this file answers:** `test/e2e/install/install.e2e.ts` inspects the tree it just installed by
4
4
  spelling the layout out — `node_modules/research-paper-pipeline` (line 172),
5
5
  `node_modules/.bin/rpp` (line 276), and `node_modules/.../plugin/hooks/hooks.json` (line 135).
6
6
  `contentDelivery()` separately tries THREE candidate base directories for a path written as prose
@@ -54,7 +54,7 @@ argument of `import.meta.resolve` is the non-standard part:
54
54
 
55
55
  ### 🔴 `import.meta.resolve` is the wrong tool here, and it fails SILENTLY
56
56
 
57
- `test/e2e/install.ts` runs from the repository and inspects a consumer somewhere in `os.tmpdir()`.
57
+ `test/e2e/install/install.e2e.ts` runs from the repository and inspects a consumer somewhere in `os.tmpdir()`.
58
58
  `import.meta.resolve` always resolves from **the file that calls it** — `cwd` is irrelevant and
59
59
  the `parent` argument is ignored without the flag rather than rejected:
60
60
 
@@ -165,7 +165,7 @@ literal node_modules/ : <WORK>/consumer-pnpm/node_modules/research-paper-pipelin
165
165
  Under pnpm the hardcode addresses the **symlink** and resolution addresses the **store realpath**
166
166
  (Node realpaths by default; `--preserve-symlinks` flips it, and `resolve-package-path` carries
167
167
  `lib/should-preserve-symlinks.js` for exactly that). `realpathSync` of one equals the other, so
168
- every `existsSync` / `readdirSync` / `readFileSync` in `test/e2e/install.ts` gives the same answer
168
+ every `existsSync` / `readdirSync` / `readFileSync` in `test/e2e/install/install.e2e.ts` gives the same answer
169
169
  either way. Two interesting asymmetries, both measured:
170
170
 
171
171
  - `module.findPackageJSON` returns the **symlink** path under pnpm, not the store path — the
@@ -348,7 +348,7 @@ node_modules present? NO
348
348
  today the npm channel is the one that delivers the runnable code — but it is not impossible.
349
349
  3. 🔴 **The blocker the note does NOT name is the immovable one, and no resolution API fixes it.**
350
350
  PnP resolves to a path **inside a zip**. Line 3: the outside process gets `existsSync: false`
351
- for the very path PnP just handed it; only PnP's patched `fs` can open it. `test/e2e/install.ts`
351
+ for the very path PnP just handed it; only PnP's patched `fs` can open it. `test/e2e/install/install.e2e.ts`
352
352
  inspects the installed tree with `existsSync`, `readdirSync` and `readFileSync` **from its own
353
353
  process**. Under PnP those read nothing, whatever API produced the path — and `yarn bin rpp`
354
354
  (line 4) hands back the same unreadable path, so the supported bin lookup does not help either.
@@ -359,7 +359,7 @@ and it should be recorded as such rather than as "PnP has no node_modules".
359
359
 
360
360
  ## 9. The second guess: the base directory for a path written in prose
361
361
 
362
- `contentDelivery()` (in `test/e2e/install.ts`) tries three bases per reference.
362
+ `contentDelivery()` (in `test/e2e/install/install.e2e.ts`) tries three bases per reference.
363
363
  Measured over the real corpus with `claim6-doc-path-candidates.mjs`:
364
364
 
365
365
  ```console
@@ -56,7 +56,7 @@ $ claude plugin validate /tmp/m --strict; echo "RC=$?"
56
56
  ## The CLAIM 4–6 probes — where an installed package is, and how to find it
57
57
 
58
58
  These back [`../package-location.md`](../package-location.md). They measure the two hardcodes in
59
- `test/e2e/install.ts` (`node_modules/<name>` and `node_modules/.bin/rpp`) against the
59
+ `test/e2e/install/install.e2e.ts` (`node_modules/<name>` and `node_modules/.bin/rpp`) against the
60
60
  resolution APIs Node actually offers, under npm, pnpm and Yarn Berry (PnP).
61
61
 
62
62
  Build the consumer trees once — it packs this repository and installs the SAME tarball twice:
@@ -27,16 +27,27 @@ So the genuinely agent-facing harnesses are three: `hooks/hooks.harness.ts` (`ru
27
27
  (`runHarnessTest`, which spawns the real `claude` binary against a scripted model), and the skill
28
28
  contract checks. Everything else is ordinary Node testing that happens to carry the suffix.
29
29
 
30
- ## Why the install e2e stays a script
31
-
32
- `test/e2e/install.ts` is one linear scenario per package manager with strictly dependent steps
33
- — install, bin, `init`, `lint`, hook commands, content delivery — and a summary. A runner adds
34
- named subtests and a reporter; the script already prints per-check `✓`/`✗` and a per-manager
35
- verdict. The pack-and-install work stays in our code under any host, so the host buys only the
36
- reporter, at the cost of a second exit-code path.
37
-
38
- It also stays OUT of `npm test`: it needs the network for the consumer's transitive dependencies
39
- and takes minutes. It is its own CI step with its own timeout.
30
+ ## The e2e runs are vitest projects
31
+
32
+ The end-to-end runs are vitest tests (`test/e2e/<area>/*.e2e.ts`), one project per environment
33
+ they need — `e2e-install` (npm and pnpm) and `e2e-tex` (TeX Live) — found by the project's glob, so
34
+ no list of files exists in `package.json`, `scripts/check.ts` or CI (#132). What the runner buys is
35
+ not the reporter alone:
36
+
37
+ - **the fixture table.** `fixtures/build-e2e/` is a table of independent cases, and each fixture's
38
+ `expect.json` becomes its own tests through one `describe.each` over the directory. A new venue
39
+ fixture is a folder, not a block of test code.
40
+ - **a skip the aggregate can read.** A missing tool is `skipIf`, and vitest's JSON report counts it,
41
+ which is what `npm run check` reads to print a gate as skipped rather than passed. Under CI the
42
+ same absence fails (`test/e2e/need.ts`).
43
+
44
+ `install.e2e.ts` stays ONE test per package manager, each the whole scenario — install, bin,
45
+ `init`, `lint`, hook commands, content delivery — because its steps depend on each other: a
46
+ failure in the install makes every later step meaningless. Inside it, each check is a soft
47
+ expectation, so a run reports every mismatch, not the first.
48
+
49
+ The e2e projects stay OUT of `npm test` (project `unit`): they need the network, TeX Live or
50
+ several minutes. Each runs in its own CI job, with its own timeout.
40
51
 
41
52
  ## Tarball-by-path versus a local registry
42
53