paperlint 4.2.0 → 6.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.
- package/CLAUDE.md +15 -2
- package/CONTRIBUTING.md +3 -0
- package/README.md +23 -12
- package/action.harness.mjs +54 -36
- package/action.yml +12 -27
- package/bin/paperlint.mjs +5 -1
- package/dist/adapters/latex/includes.d.ts +5 -1
- package/dist/adapters/latex/includes.d.ts.map +1 -1
- package/dist/adapters/latex/includes.js +8 -2
- package/dist/adapters/latex/includes.js.map +1 -1
- package/dist/adapters/latex/index.d.ts +1 -0
- package/dist/adapters/latex/index.d.ts.map +1 -1
- package/dist/adapters/latex/index.js +3 -0
- package/dist/adapters/latex/index.js.map +1 -1
- package/dist/adapters/latex/layout.d.ts +27 -0
- package/dist/adapters/latex/layout.d.ts.map +1 -0
- package/dist/adapters/latex/layout.js +62 -0
- package/dist/adapters/latex/layout.js.map +1 -0
- package/dist/adapters/latex/nodes.d.ts.map +1 -1
- package/dist/adapters/latex/nodes.js +11 -1
- package/dist/adapters/latex/nodes.js.map +1 -1
- package/dist/adapters/latex/parse.d.ts.map +1 -1
- package/dist/adapters/latex/parse.js +5 -1
- package/dist/adapters/latex/parse.js.map +1 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +7 -3
- package/dist/build.js.map +1 -1
- package/dist/cli.d.ts +2 -8
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +92 -42
- package/dist/cli.js.map +1 -1
- package/dist/domain/anonymity.d.ts +68 -0
- package/dist/domain/anonymity.d.ts.map +1 -0
- package/dist/domain/anonymity.js +94 -0
- package/dist/domain/anonymity.js.map +1 -0
- package/dist/domain/body-pages.d.ts +69 -0
- package/dist/domain/body-pages.d.ts.map +1 -0
- package/dist/domain/body-pages.js +141 -0
- package/dist/domain/body-pages.js.map +1 -0
- package/dist/domain/paper-source.js +2 -2
- package/dist/domain/tex-document.d.ts +9 -0
- package/dist/domain/tex-document.d.ts.map +1 -1
- package/dist/eslint-rules/latex-language.d.ts +5 -1
- package/dist/eslint-rules/latex-language.d.ts.map +1 -1
- package/dist/eslint-rules/latex-language.js +6 -3
- package/dist/eslint-rules/latex-language.js.map +1 -1
- package/dist/eslint-rules/paper-context.d.ts +20 -0
- package/dist/eslint-rules/paper-context.d.ts.map +1 -0
- package/dist/eslint-rules/paper-context.js +44 -0
- package/dist/eslint-rules/paper-context.js.map +1 -0
- package/dist/eslint-rules/paper-typography.d.ts +1 -1
- package/dist/eslint-rules/paper-typography.d.ts.map +1 -1
- package/dist/eslint-rules/paper-typography.js +32 -18
- package/dist/eslint-rules/paper-typography.js.map +1 -1
- package/dist/eslint-rules/pdf-last-page-balance.d.ts +1 -1
- package/dist/eslint-rules/pdf-last-page-balance.js +2 -2
- package/dist/eslint-rules/tex-build.d.ts.map +1 -1
- package/dist/eslint-rules/tex-build.js +7 -1
- package/dist/eslint-rules/tex-build.js.map +1 -1
- package/dist/facts-file.d.ts +28 -2
- package/dist/facts-file.d.ts.map +1 -1
- package/dist/facts-file.js +54 -9
- package/dist/facts-file.js.map +1 -1
- package/dist/init.d.ts +4 -19
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +24 -29
- package/dist/init.js.map +1 -1
- package/dist/lib/paper-config.d.ts.map +1 -1
- package/dist/lib/paper-config.js +2 -1
- package/dist/lib/paper-config.js.map +1 -1
- package/dist/package-dirs.d.ts +2 -0
- package/dist/package-dirs.d.ts.map +1 -1
- package/dist/package-dirs.js +8 -0
- package/dist/package-dirs.js.map +1 -1
- package/dist/paper-files.d.ts +2 -2
- package/dist/paper-includes.d.ts +17 -0
- package/dist/paper-includes.d.ts.map +1 -0
- package/dist/paper-includes.js +51 -0
- package/dist/paper-includes.js.map +1 -0
- package/dist/paper-settings.d.ts +11 -0
- package/dist/paper-settings.d.ts.map +1 -1
- package/dist/paper-settings.js +38 -4
- package/dist/paper-settings.js.map +1 -1
- package/dist/pdf-content.d.ts +40 -0
- package/dist/pdf-content.d.ts.map +1 -0
- package/dist/pdf-content.js +97 -0
- package/dist/pdf-content.js.map +1 -0
- package/dist/pdf-facts.d.ts +30 -0
- package/dist/pdf-facts.d.ts.map +1 -1
- package/dist/pdf-facts.js +37 -1
- package/dist/pdf-facts.js.map +1 -1
- package/dist/ports/latex.d.ts +3 -1
- package/dist/ports/latex.d.ts.map +1 -1
- package/dist/presets.d.ts +2 -0
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +4 -0
- package/dist/presets.js.map +1 -1
- package/dist/rules-config.d.ts +1 -0
- package/dist/rules-config.d.ts.map +1 -1
- package/dist/rules-config.js.map +1 -1
- package/dist/structure.js +1 -1
- package/dist/structure.js.map +1 -1
- package/dist/tex-paper.d.ts +17 -3
- package/dist/tex-paper.d.ts.map +1 -1
- package/dist/tex-paper.js +36 -38
- package/dist/tex-paper.js.map +1 -1
- package/dist/tex-requirements.d.ts +4 -0
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +3 -0
- package/dist/tex-requirements.js.map +1 -1
- package/dist/tex-venue-rules.d.ts +7 -1
- package/dist/tex-venue-rules.d.ts.map +1 -1
- package/dist/tex-venue-rules.js +40 -0
- package/dist/tex-venue-rules.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/venue-rules.d.ts +38 -8
- package/dist/venue-rules.d.ts.map +1 -1
- package/dist/venue-rules.js +179 -41
- package/dist/venue-rules.js.map +1 -1
- package/docs/configuration.md +23 -20
- package/docs/e2e.md +32 -11
- package/docs/install.md +8 -8
- package/docs/prior-art/paper-folder-scaffolding.md +4 -7
- package/docs/rules/anonymity/identity.md +111 -0
- package/docs/rules/format/layout-override.md +61 -0
- package/docs/rules/format/page-limit.md +108 -0
- package/docs/rules/pdf/body-size.md +87 -0
- package/docs/rules.md +55 -46
- package/eslint-rules/latex-language.ts +10 -4
- package/eslint-rules/paper-context.test.ts +47 -0
- package/eslint-rules/paper-context.ts +57 -0
- package/eslint-rules/paper-typography.ts +67 -23
- package/eslint-rules/pdf-last-page-balance.harness.mjs +6 -1
- package/eslint-rules/pdf-last-page-balance.ts +2 -2
- package/eslint-rules/tex-build.ts +8 -1
- package/fixtures/build-e2e/aidc/expect.json +1 -1
- package/fixtures/build-e2e/aidc/paperlint.json +1 -1
- package/fixtures/build-e2e/anon-ack/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/anon-ack/expect.json +19 -0
- package/fixtures/build-e2e/anon-ack/paper.tex +31 -0
- package/fixtures/build-e2e/anon-ack/paperlint.json +1 -0
- package/fixtures/build-e2e/anon-author/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/anon-author/expect.json +19 -0
- package/fixtures/build-e2e/anon-author/paper.tex +29 -0
- package/fixtures/build-e2e/anon-author/paperlint.json +1 -0
- package/fixtures/build-e2e/anon-bib/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/anon-bib/expect.json +20 -0
- package/fixtures/build-e2e/anon-bib/paper.tex +35 -0
- package/fixtures/build-e2e/anon-bib/paperlint.json +1 -0
- package/fixtures/build-e2e/anon-href/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/anon-href/expect.json +19 -0
- package/fixtures/build-e2e/anon-href/paper.tex +30 -0
- package/fixtures/build-e2e/anon-href/paperlint.json +1 -0
- package/fixtures/build-e2e/anon-macro/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/anon-macro/expect.json +19 -0
- package/fixtures/build-e2e/anon-macro/paper.tex +30 -0
- package/fixtures/build-e2e/anon-macro/paperlint.json +1 -0
- package/fixtures/build-e2e/anon-pdfauthor/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/anon-pdfauthor/expect.json +19 -0
- package/fixtures/build-e2e/anon-pdfauthor/paper.tex +31 -0
- package/fixtures/build-e2e/anon-pdfauthor/paperlint.json +1 -0
- package/fixtures/build-e2e/unbalanced/expect.json +2 -2
- package/fixtures/real-markdown-paper/baseline.json +1 -2
- package/lib/paper-config.ts +3 -1
- package/package.json +1 -1
- package/presets/acm-sigconf.jsonc +3 -1
- package/presets/aidc.jsonc +16 -17
- package/presets/ieee-conference.jsonc +8 -0
- package/presets/venue-profile.schema.json +9 -0
- package/scripts/check.ts +2 -2
- package/scripts/eslint-report-guard.mjs +5 -2
- package/scripts/harness-api.frozen.json +0 -1
- package/skills/paper-pipeline/references/pipeline-status-template.md +2 -16
- package/skills/render-paper/extract-pdf-facts.harness.ts +3 -3
- package/src/adapters/latex/includes.ts +10 -2
- package/src/adapters/latex/index.test.ts +8 -0
- package/src/adapters/latex/index.ts +3 -0
- package/src/adapters/latex/layout.ts +106 -0
- package/src/adapters/latex/nodes.ts +11 -3
- package/src/adapters/latex/parse.ts +5 -1
- package/src/build.harness.mjs +6 -4
- package/src/build.test.ts +23 -1
- package/src/build.ts +7 -3
- package/src/cli.harness.mjs +22 -54
- package/src/cli.test.ts +15 -18
- package/src/cli.ts +114 -46
- package/src/domain/anonymity.test.ts +163 -0
- package/src/domain/anonymity.ts +129 -0
- package/src/domain/body-pages.test.ts +300 -0
- package/src/domain/body-pages.ts +223 -0
- package/src/domain/paper-source.ts +2 -2
- package/src/domain/tex-document.ts +10 -0
- package/src/facts-file.test.ts +100 -4
- package/src/facts-file.ts +91 -9
- package/src/init.test.ts +33 -13
- package/src/init.ts +25 -48
- package/src/linted-set.test.ts +11 -1
- package/src/new-paper.harness.mjs +1 -5
- package/src/new-paper.test.ts +7 -1
- package/src/package-dirs.ts +9 -0
- package/src/paper-files.ts +2 -2
- package/src/paper-includes.test.ts +161 -0
- package/src/paper-includes.ts +77 -0
- package/src/paper-settings.test.ts +63 -6
- package/src/paper-settings.ts +53 -5
- package/src/pdf-content.test.ts +71 -0
- package/src/pdf-content.ts +125 -0
- package/src/pdf-facts.test.ts +110 -2
- package/src/pdf-facts.ts +106 -8
- package/src/ports/latex.ts +3 -0
- package/src/presets.ts +8 -8
- package/src/rule-cases.test.ts +29 -33
- package/src/rule-docs.test.ts +0 -3
- package/src/rules-config.ts +1 -0
- package/src/structure.harness.ts +1 -3
- package/src/structure.ts +1 -1
- package/src/tex-paper.test.ts +78 -61
- package/src/tex-paper.ts +57 -48
- package/src/tex-requirements.ts +9 -0
- package/src/tex-venue-rules.test.ts +111 -6
- package/src/tex-venue-rules.ts +57 -2
- package/src/types.ts +2 -0
- package/src/venue-rules.test.ts +351 -39
- package/src/venue-rules.ts +247 -47
- package/templates/paper/PIPELINE-STATUS.md +0 -4
- package/dist/action-ref.d.ts +0 -12
- package/dist/action-ref.d.ts.map +0 -1
- package/dist/action-ref.js +0 -16
- package/dist/action-ref.js.map +0 -1
- package/dist/eslint-rules/paper-research-question.d.ts +0 -31
- package/dist/eslint-rules/paper-research-question.d.ts.map +0 -1
- package/dist/eslint-rules/paper-research-question.js +0 -188
- package/dist/eslint-rules/paper-research-question.js.map +0 -1
- package/docs/rules/tex/missing-input.md +0 -58
- package/eslint-rules/paper-research-question.harness.mjs +0 -212
- package/eslint-rules/paper-research-question.ts +0 -199
- package/fixtures/paper-research-question/comment-only/PIPELINE-STATUS.md +0 -9
- package/fixtures/paper-research-question/comment-only/paper.tex +0 -7
- package/fixtures/paper-research-question/declared-not-in-paper/PIPELINE-STATUS.md +0 -10
- package/fixtures/paper-research-question/declared-not-in-paper/paper.tex +0 -6
- package/fixtures/paper-research-question/draft/PIPELINE-STATUS.md +0 -6
- package/fixtures/paper-research-question/draft/paper.tex +0 -2
- package/fixtures/paper-research-question/markdown-no-rq/PIPELINE-STATUS.md +0 -9
- package/fixtures/paper-research-question/markdown-no-rq/paper.md +0 -4
- package/fixtures/paper-research-question/shipped-no-rq/PIPELINE-STATUS.md +0 -12
- package/fixtures/paper-research-question/shipped-no-rq/paper.tex +0 -3
- package/fixtures/paper-research-question/shipped-with-rq/PIPELINE-STATUS.md +0 -10
- package/fixtures/paper-research-question/shipped-with-rq/paper.tex +0 -2
- package/src/action-ref.test.ts +0 -26
- package/src/action-ref.ts +0 -15
package/docs/rules.md
CHANGED
|
@@ -3,36 +3,43 @@
|
|
|
3
3
|
The README lists what each check catches in one line. This page says exactly which file each one
|
|
4
4
|
reads and when it fails. Errors fail `paperlint lint`; warnings print and do not.
|
|
5
5
|
|
|
6
|
-
| Rule
|
|
7
|
-
|
|
|
8
|
-
| `paper/stages`
|
|
9
|
-
| `paper/source`
|
|
10
|
-
| `paper/author-list`
|
|
11
|
-
| `paper/
|
|
12
|
-
| `paper/
|
|
13
|
-
| `paper/
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
16
|
-
| `paper/
|
|
17
|
-
| `paper/refs-
|
|
18
|
-
| `
|
|
19
|
-
| `tex/
|
|
20
|
-
| `tex/
|
|
21
|
-
| [`tex/
|
|
22
|
-
| [`
|
|
23
|
-
| [`
|
|
24
|
-
| [`tex/
|
|
25
|
-
| [`tex/
|
|
26
|
-
| [`tex/register`](rules/tex/register.md)
|
|
27
|
-
| `review/frontmatter`
|
|
28
|
-
| `sibling/frontmatter`
|
|
29
|
-
| `pdf/fresh`
|
|
30
|
-
| `pdf/profile`
|
|
31
|
-
| `pdf/fonts`
|
|
32
|
-
| `pdf/geometry`
|
|
33
|
-
| `
|
|
34
|
-
| `pdf/body-size`
|
|
35
|
-
| `pdf/measured`
|
|
6
|
+
| Rule | Level | Reads | Fails when |
|
|
7
|
+
| ----------------------------------------------------------- | ----- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
8
|
+
| `paper/stages` | error | `PIPELINE-STATUS.md` | a declared stage's PDF is missing, or the `bytes:` it names does not match the file's real size, or a frozen PDF exists that no stage declares |
|
|
9
|
+
| `paper/source` | error | `PIPELINE-STATUS.md` | a declared stage has no frozen `.tex` beside its PDF (a commit hash does not count — squash and gc destroy it) |
|
|
10
|
+
| `paper/author-list` | error | `paper.tex` → `_build/references.json` | an entry's authors are not those of the version it cites — typically the arXiv preprint's list under a published venue. Checked online by `paperlint build` (DBLP), reported on the entry's own line |
|
|
11
|
+
| `paper/section-word` | warn | `paper.tex` and its body files, `paper.md` | `§` or `\S\ref` instead of the word Section. **Fixable:** `--fix` writes `Section~\ref{…}` / `Section 5` |
|
|
12
|
+
| `paper/leading-zero` | warn | `paper.tex` and its body files, `paper.md` | a decimal below 1 without its leading zero (`.05` instead of `0.05`, IEEE / ISO 80000-1 style), counted only where the reader sees it — prose, math and table cells, not macro options, column specs, comments, code or tikz. A number right after `¶` or `§` (`¶¶.42`) is a paragraph designator and is not reported; in a paragraph that cites numbers that way, another bare decimal is reported without a fix, with the zero as a suggestion. **Fixable** |
|
|
13
|
+
| `paper/figure-ref-style` | warn | `paper.tex` and its body files | `Fig.~\ref` and `Figure~\ref` mixed in one document; each minority occurrence is reported. **Fixable:** `--fix` writes the majority form |
|
|
14
|
+
| `bib/reachable-entry` | warn | `paper.tex` (its `filecontents` bibliography) | an entry has no doi, url or arXiv id — a reader has nothing to follow. Reported on the entry; no fix, the link has to be looked up |
|
|
15
|
+
| `paper/cite-exists` | error | `paper.tex` → `_build/references.json` | an entry's doi or arXiv id resolves to nothing, or to a different work. Checked online by `paperlint build`; a work that was merely not found is not a finding |
|
|
16
|
+
| `paper/refs-fresh` | error | `paper.tex` → `_build/references.json` | the bibliography changed since the build checked it (its SHA-256 differs) — run `npx paperlint build` |
|
|
17
|
+
| `paper/refs-checked` | warn | `paper.tex` → `_build/references.json` | the paper has a bibliography and no build has checked it, or the last build could not (no network) — so the two rules above did not run |
|
|
18
|
+
| `tex/future-promise` | warn | `paper.tex` and its body files | a camera-ready build still says "will be released" about something already handed over |
|
|
19
|
+
| `tex/acm-frontmatter-override` | error | `paper.tex` | an `acmart` build overrides ACM's front-matter commands and drops template elements from page 1 |
|
|
20
|
+
| [`tex/template`](rules/tex/template.md) | error | `paper.tex` → `paperlint.json` | the `\documentclass` is not the preset's `template` class, or lacks an option it names — [page](rules/tex/template.md) |
|
|
21
|
+
| [`tex/required-section`](rules/tex/required-section.md) | error | `paper.tex` → `paperlint.json` | a section the preset requires (AIDC: «LLM Usage Statement», last) is missing or not where it must be — [page](rules/tex/required-section.md) |
|
|
22
|
+
| [`format/layout-override`](rules/format/layout-override.md) | error | `paper.tex` → `paperlint.json` | the source changes the page layout the venue's template sets for the whole document — `geometry`, a layout `\setlength`, `\linespread`, `\baselinestretch` — [page](rules/format/layout-override.md) |
|
|
23
|
+
| [`anonymity/identity`](rules/anonymity/identity.md) | error | `paper.tex` → `_build/paper.facts.json` | a double-blind venue (`"blind": true`) and no `identity` declared, or a declared name, handle, email or project in the PDF's text, metadata or link targets — [page](rules/anonymity/identity.md) |
|
|
24
|
+
| [`tex/venue-leftover`](rules/tex/venue-leftover.md) | warn | `paper.tex` → `paperlint.json` | the text names another shipped venue than the one the paper extends («Submission to VenueA 2026» in a paper now set for VenueB) — [page](rules/tex/venue-leftover.md) |
|
|
25
|
+
| [`tex/claim-provenance`](rules/tex/claim-provenance.md) | warn | `paper.tex` | a sentence of the body states a number and does not say whose it is — no citation or link, no _we_/_our_, no `\ref` or "Table 2", no sample ("n = 134", "48 runs") — [page](rules/tex/claim-provenance.md) |
|
|
26
|
+
| [`tex/register`](rules/tex/register.md) | warn | `paper.tex` and the files it includes | the body runs to a post's register: more than 0.8 sentences per 1000 words open with _And_, _So_, _But_, _Nor_, _Or_ or _Yet_ — the limit is an option — [page](rules/tex/register.md) |
|
|
27
|
+
| `review/frontmatter` | error | `reviews/*.md` | the review's frontmatter fails paperlint's JSON Schema — a `findings` record without `id`/`status`, an unknown field, or an **open** finding with no `cause` (`skill-defect` · `missing-skill` · `hook` · `rule`). A review with no `findings` key is not checked for findings |
|
|
28
|
+
| `sibling/frontmatter` | warn | `siblings/*.md` (not `README.md`) | a sibling card's frontmatter has no `read:` saying how much of the competing paper was read (`full` · `abstract` · `none`) |
|
|
29
|
+
| `pdf/fresh` | error | `paper.tex` → `_build/paper.facts.json` | the paper names a venue and the facts cannot be judged: not JSON, a schema other than 3, or they describe a PDF that is gone or differs from the one on disk (its SHA-256) |
|
|
30
|
+
| `pdf/profile` | error | `paper.tex` → `paperlint.json` | `paperlint.json` is not JSON or has an unknown key, its `extends` does not resolve (not found, a cycle, a chain longer than four, a preset that fails the schema, an npm name), it names no `kind` while the preset has kinds, or names a kind the preset does not have |
|
|
31
|
+
| `pdf/fonts` | error | `paper.tex` → `_build/paper.facts.json` | a font the pages draw is Type 3 or not embedded, or no font starts with the family the venue preset names for body text (`fonts_text`) or headings (`fonts_title`) |
|
|
32
|
+
| `pdf/geometry` | error | `paper.tex` → `_build/paper.facts.json` | the page width or height is more than `dimTol` (default 0.05 in) off the preset's, or the column count differs |
|
|
33
|
+
| [`format/page-limit`](rules/format/page-limit.md) | error | `paper.tex` → `_build/paper.facts.json` | body or reference pages exceed the limits of the paper's kind. With `body_ends_at: "references"` (AIDC) the body ends at the references or the appendix, whichever comes first — found by hyperref's anchors, then the heading line — and the page it ends on counts when body text stands above the heading; an end it cannot find, or signals that disagree, are reported — [page](rules/format/page-limit.md) |
|
|
34
|
+
| [`pdf/body-size`](rules/pdf/body-size.md) | error | `paper.tex` → `_build/paper.facts.json` | the body font size is more than `body_pt_tol` off the preset's, or the reference font size is outside the preset's range widened by `body_pt_tol`. banal reads 0.3 pt above the size the glyphs are set in; checked on five built accepted papers (none reported) and three variants one size off (each reported) — [page](rules/pdf/body-size.md) |
|
|
35
|
+
| `pdf/measured` | warn | `paper.tex` → `_build/paper.facts.json` | the paper's `paperlint.json` names no venue preset yet; or it names one and there are no facts (it was not built), or the facts carry no page geometry (banal was not found) — so the checks above did not run |
|
|
36
|
+
|
|
37
|
+
**Body files** are the files `paper.tex` includes from its document body (`\input`, `\include`,
|
|
38
|
+
`\subfile`, nested ones too), found in the paper's directory — not a preamble include, not a file
|
|
39
|
+
found only in paperlint's own inputs, and not any other `.tex` beside the paper (`versions/`). A
|
|
40
|
+
finding in one is reported at that file and line, and `--fix` edits that file. Which reference form
|
|
41
|
+
is the majority and whether the build is a review build are decided over the whole paper. An include
|
|
42
|
+
that names no file is printed as a note: lint did not read it, and the build will stop on it.
|
|
36
43
|
|
|
37
44
|
`tex/required-section` requires AIDC's «LLM Usage Statement» **always**. ACSAC asks for it «if
|
|
38
45
|
LLMs are used», which no rule can know; an author who used none says so in the section
|
|
@@ -89,14 +96,14 @@ for papers, each with the quote it came from, the TeX packages its template need
|
|
|
89
96
|
the venue implies. A preset may itself extend another: the ACM venues extend the `acm-sigconf`
|
|
90
97
|
family, which holds everything the ACM template decides.
|
|
91
98
|
|
|
92
|
-
| preset | extends | template | kinds | page limit checked
|
|
93
|
-
| --------------------------- | --------------------------- | ------------------------------------- | -------------------------------------------------------- |
|
|
94
|
-
| `paperlint:acm-sigconf` | — | ACM `acmart` sigconf | none | no (no kinds)
|
|
95
|
-
| `paperlint:agenticdev` | `paperlint:acm-sigconf` | ACM `acmart` sigconf | `short` (5 + 2 refs), `full` (10 + 2), `demo` (5 + 2) | yes
|
|
96
|
-
| `paperlint:aisec` | `paperlint:acm-sigconf` | ACM `acmart` sigconf | `research`, `benchmark`, `position`, `sok` (10 + 2 each) | yes
|
|
97
|
-
| `paperlint:realm` | — | ACL | `long`, `short` | no — see below
|
|
98
|
-
| `paperlint:ieee-conference` | — | IEEE `IEEEtran` conference | none | no (no kinds)
|
|
99
|
-
| `paperlint:aidc` | `paperlint:ieee-conference` | IEEE `IEEEtran` conference, `compsoc` | `regular` (12), `short` (6) |
|
|
99
|
+
| preset | extends | template | kinds | page limit checked | rules it turns on |
|
|
100
|
+
| --------------------------- | --------------------------- | ------------------------------------- | -------------------------------------------------------- | -------------------------------------- | ----------------------- |
|
|
101
|
+
| `paperlint:acm-sigconf` | — | ACM `acmart` sigconf | none | no (no kinds) | — |
|
|
102
|
+
| `paperlint:agenticdev` | `paperlint:acm-sigconf` | ACM `acmart` sigconf | `short` (5 + 2 refs), `full` (10 + 2), `demo` (5 + 2) | yes | `pdf/last-page-balance` |
|
|
103
|
+
| `paperlint:aisec` | `paperlint:acm-sigconf` | ACM `acmart` sigconf | `research`, `benchmark`, `position`, `sok` (10 + 2 each) | yes | — |
|
|
104
|
+
| `paperlint:realm` | — | ACL | `long`, `short` | no — see below | — |
|
|
105
|
+
| `paperlint:ieee-conference` | — | IEEE `IEEEtran` conference | none | no (no kinds) | — |
|
|
106
|
+
| `paperlint:aidc` | `paperlint:ieee-conference` | IEEE `IEEEtran` conference, `compsoc` | `regular` (12), `short` (6) | yes, before the references — see below | — (`blind`) |
|
|
100
107
|
|
|
101
108
|
That is all that ships today. There is no NeurIPS, USENIX or Springer preset. A paper for an ACM or
|
|
102
109
|
IEEE conference venue nobody has profiled can extend `paperlint:acm-sigconf` or
|
|
@@ -104,9 +111,11 @@ IEEE conference venue nobody has profiled can extend `paperlint:acm-sigconf` or
|
|
|
104
111
|
says the page limit is not. For anything else, write your own preset
|
|
105
112
|
([below](#writing-your-own-venue-preset)). REALM's preset sets no page limit on purpose: banal
|
|
106
113
|
counts the Limitations and Ethics sections as body, ACL does not, and a limit on banal's number
|
|
107
|
-
would fail a correct paper. AIDC's
|
|
108
|
-
body when an appendix or the LLM Usage Statement sits on it
|
|
109
|
-
|
|
114
|
+
would fail a correct paper. AIDC's limit is checked by another count, for the same reason: banal counts
|
|
115
|
+
a page as body when an appendix or the LLM Usage Statement sits on it, and AIDC counts neither, so a
|
|
116
|
+
correct 12-page paper measured 13. Its preset sets `"body_ends_at": "references"`: the body is the
|
|
117
|
+
pages before the one where the references heading first appears. It is also `"blind": true`, which
|
|
118
|
+
turns on [`anonymity/identity`](rules/anonymity/identity.md). AgenticDev's turns on `pdf/last-page-balance`
|
|
110
119
|
because its proceedings are produced by Conference Publishing Consulting, which sends back an
|
|
111
120
|
unbalanced last page; AISec's does not, because nothing in hand says who produces the AISec
|
|
112
121
|
proceedings.
|
|
@@ -199,6 +208,7 @@ The numbers and packages above illustrate the shape; take yours from the venue's
|
|
|
199
208
|
| `name` | how messages name the venue; the file name otherwise |
|
|
200
209
|
| `template` | the `\documentclass` the venue's template uses — a whole line (`\documentclass[conference,compsoc]{IEEEtran}`) or a bare class name; `tex/template` requires the paper's class and every option named here |
|
|
201
210
|
| `aliases` | other names the venue goes by in a paper's text — its acronym, its parent conference: `["AISec", "ACM CCS"]`. Collected along `extends`, with `name`. Read by `tex/venue-leftover` |
|
|
211
|
+
| `blind` | the venue reviews double-blind — set only where its call says so, with the quote. Turns on `anonymity/identity`; a child's value replaces its parent's |
|
|
202
212
|
| `required_sections` | sections the venue requires, `[{ "title": "LLM Usage Statement", "position": "last" }]`: the exact title, and `last` when it must close the paper. Read by `tex/required-section`; a child's list replaces its parent's |
|
|
203
213
|
| `format` | page size, columns, fonts, font sizes, and `kinds` (page limits per kind of paper) |
|
|
204
214
|
| `tex` | TeX Live packages, each with the files that prove it is installed. Required unless `extends` is set |
|
|
@@ -214,10 +224,10 @@ Presets from npm packages are not supported yet.
|
|
|
214
224
|
|
|
215
225
|
## The paper is LaTeX
|
|
216
226
|
|
|
217
|
-
The paper body is `paper.tex`, and it gets
|
|
218
|
-
`
|
|
219
|
-
|
|
220
|
-
the
|
|
227
|
+
The paper body is `paper.tex`, and it gets three rules of its own: `paper/typography`,
|
|
228
|
+
`tex/future-promise` and `tex/acm-frontmatter-override` — plus the `pdf/` venue rules above, which
|
|
229
|
+
run on it but judge the files beside it. The scorecard and the review notes are Markdown files, and
|
|
230
|
+
the other five rules read those.
|
|
221
231
|
|
|
222
232
|
A Markdown body (`paper.md`, or `draft.md`) is still read today and gets only the two `paper/`
|
|
223
233
|
rules, which is why they list it above. Markdown papers are deprecated and being removed
|
|
@@ -241,7 +251,6 @@ stages:
|
|
|
241
251
|
bytes: 305412
|
|
242
252
|
source: versions/2026-07-22-submitted.tex
|
|
243
253
|
sourceBytes: 57210
|
|
244
|
-
researchQuestion: "Does pruning the state space reduce review cost?"
|
|
245
254
|
---
|
|
246
255
|
```
|
|
247
256
|
|
|
@@ -51,9 +51,12 @@
|
|
|
51
51
|
* paper. The projection counts differently: the same place gave 1239 words before macro
|
|
52
52
|
* blanking and 1208 after — a 2.5% discrepancy. The discrepancy is ACCEPTED deliberately;
|
|
53
53
|
* the thresholds were not recalibrated.
|
|
54
|
-
* 4.
|
|
55
|
-
*
|
|
56
|
-
*
|
|
54
|
+
* 4. ONE FILE AT A TIME. `\input` / `\include` are in OPAQUE: the projection of `paper.tex`
|
|
55
|
+
* does not hold what they pull in. `paperlint lint` hands ESLint each file the paper includes
|
|
56
|
+
* from its body as a file of its own (src/paper-includes.ts), and a file with no `document`
|
|
57
|
+
* environment is projected as body throughout — so an included file is read where it is,
|
|
58
|
+
* and its findings point into it. An ESLint run outside `paperlint lint` reads only the
|
|
59
|
+
* files it was given.
|
|
57
60
|
* 5. `\appendix` IS NOT IMPLEMENTED and is INEXPRESSIBLE in a length-preserving projection:
|
|
58
61
|
* `\appendix` is 9 characters, `## Appendix` is 11, the synthesis does not fit. On the
|
|
59
62
|
* first paper with a real `\appendix`, `appendix-ref` / `appendix-ratio` yield a silent
|
|
@@ -209,6 +212,9 @@ export type MdNode =
|
|
|
209
212
|
readonly value: string;
|
|
210
213
|
};
|
|
211
214
|
|
|
215
|
+
/** The root of a projection: a node that has children. */
|
|
216
|
+
export type MdRoot = Extract<MdNode, { readonly type: "root" | "strong" }>;
|
|
217
|
+
|
|
212
218
|
/** An inline-config comment: an `html` node's text and where it sits. */
|
|
213
219
|
interface ConfigNode {
|
|
214
220
|
readonly value: string;
|
|
@@ -305,7 +311,7 @@ export function texToMdast(
|
|
|
305
311
|
{
|
|
306
312
|
parse = (s: string): TexRoot => getParser().parse(s),
|
|
307
313
|
}: { parse?: (s: string) => TexRoot } = {},
|
|
308
|
-
): { root:
|
|
314
|
+
): { root: MdRoot; text: string } {
|
|
309
315
|
const ast = parse(src);
|
|
310
316
|
const chars = src.split("");
|
|
311
317
|
const blank = (from: number, to: number) => {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The paper a linted file belongs to, as `paperlint lint` hands it in ESLint's settings, and the
|
|
3
|
+
* reading of the paper's other files.
|
|
4
|
+
*/
|
|
5
|
+
import { mkdtempSync, realpathSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { describe, expect, it } from "vitest";
|
|
9
|
+
import {
|
|
10
|
+
PAPERLINT_SETTINGS,
|
|
11
|
+
otherTexts,
|
|
12
|
+
paperOf,
|
|
13
|
+
textOnDisk,
|
|
14
|
+
} from "./paper-context.ts";
|
|
15
|
+
|
|
16
|
+
describe("paperOf", () => {
|
|
17
|
+
it("the paper under settings.paperlint.paper", () => {
|
|
18
|
+
const paper = { main: "/p/paper.tex", files: ["/p/paper.tex", "/p/a.tex"] };
|
|
19
|
+
expect(paperOf({ [PAPERLINT_SETTINGS]: { paper } })).toEqual(paper);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("none when lint handed none, or handed something else", () => {
|
|
23
|
+
expect(paperOf({})).toBe(null);
|
|
24
|
+
expect(paperOf({ [PAPERLINT_SETTINGS]: { paper: { main: 1 } } })).toBe(
|
|
25
|
+
null,
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("otherTexts", () => {
|
|
31
|
+
const dir = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-ctx-")));
|
|
32
|
+
writeFileSync(join(dir, "a.tex"), "A");
|
|
33
|
+
writeFileSync(join(dir, "b.tex"), "B");
|
|
34
|
+
const paper = {
|
|
35
|
+
main: join(dir, "a.tex"),
|
|
36
|
+
files: [join(dir, "a.tex"), join(dir, "b.tex"), join(dir, "gone.tex")],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
it("every file of the paper but the linted one; a file that cannot be read holds no text", () => {
|
|
40
|
+
expect(otherTexts(paper, join(dir, "a.tex"))).toEqual(["B", ""]);
|
|
41
|
+
expect(textOnDisk(join(dir, "gone.tex"))).toBe("");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("none without a paper", () => {
|
|
45
|
+
expect(otherTexts(null, join(dir, "a.tex"))).toEqual([]);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE PAPER A LINTED FILE BELONGS TO — handed to the rules by `paperlint lint` in ESLint's `settings`.
|
|
3
|
+
*
|
|
4
|
+
* ESLint lints one file at a time. A paper whose body is in `sections/*.tex` is several files, and
|
|
5
|
+
* two questions are about the paper, not the file: which reference form (`Fig.` or `Figure`) the
|
|
6
|
+
* paper uses most, and whether the build `paper.tex` declares is a review build. The CLI knows the
|
|
7
|
+
* files (it resolves the includes the way the build does, `bodyFiles` in src/tex-paper.ts) and puts
|
|
8
|
+
* them under `settings.paperlint.paper`
|
|
9
|
+
* for `paper.tex` and for every file it includes. A rule reads the other files from disk.
|
|
10
|
+
*
|
|
11
|
+
* No setting — a file linted outside `paperlint lint`, or a markdown paper — means a paper of one
|
|
12
|
+
* file: every rule decides on the file it was handed, as it did before.
|
|
13
|
+
*/
|
|
14
|
+
import { readFileSync } from "node:fs";
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
|
|
17
|
+
/** The key under ESLint's `settings`; the paper is at `settings[PAPERLINT_SETTINGS].paper`. */
|
|
18
|
+
export const PAPERLINT_SETTINGS = "paperlint";
|
|
19
|
+
|
|
20
|
+
const PaperSetting = z.object({
|
|
21
|
+
/** The absolute path of the paper's `paper.tex`. */
|
|
22
|
+
main: z.string(),
|
|
23
|
+
/** Absolute paths: `paper.tex`, then every file of its body, each once. */
|
|
24
|
+
files: z.array(z.string()).readonly(),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
/** The paper a file belongs to: its main file and all of its files. */
|
|
28
|
+
export type PaperOfFile = Readonly<z.infer<typeof PaperSetting>>;
|
|
29
|
+
|
|
30
|
+
const Settings = z.looseObject({ paper: PaperSetting });
|
|
31
|
+
|
|
32
|
+
/** The paper the linted file belongs to, or null when lint handed none. */
|
|
33
|
+
export function paperOf(
|
|
34
|
+
settings: Readonly<Record<string, unknown>>,
|
|
35
|
+
): PaperOfFile | null {
|
|
36
|
+
const r = Settings.safeParse(settings[PAPERLINT_SETTINGS]);
|
|
37
|
+
return r.success ? r.data.paper : null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The text of a file on disk; empty when it cannot be read — a file gone since `paperlint lint`
|
|
42
|
+
* named it holds no text for any rule.
|
|
43
|
+
*/
|
|
44
|
+
export function textOnDisk(path: string): string {
|
|
45
|
+
try {
|
|
46
|
+
return readFileSync(path, "utf8");
|
|
47
|
+
} catch {
|
|
48
|
+
return "";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The texts of the paper's files other than `filename`, read from disk; none without a paper. */
|
|
53
|
+
export const otherTexts = (
|
|
54
|
+
paper: PaperOfFile | null,
|
|
55
|
+
filename: string,
|
|
56
|
+
): readonly string[] =>
|
|
57
|
+
(paper?.files ?? []).filter((f) => f !== filename).map(textOnDisk);
|
|
@@ -63,7 +63,13 @@ import assert from "node:assert/strict";
|
|
|
63
63
|
import { getParser } from "@unified-latex/unified-latex-util-parse";
|
|
64
64
|
import { takeWhile } from "remeda";
|
|
65
65
|
import type { Nodes } from "mdast";
|
|
66
|
-
import
|
|
66
|
+
import {
|
|
67
|
+
texToMdast,
|
|
68
|
+
type TexArg,
|
|
69
|
+
type TexNode,
|
|
70
|
+
type TexRoot,
|
|
71
|
+
} from "./latex-language.ts";
|
|
72
|
+
import { otherTexts, paperOf } from "./paper-context.ts";
|
|
67
73
|
import type {
|
|
68
74
|
RuleContext,
|
|
69
75
|
RuleFixer,
|
|
@@ -102,16 +108,27 @@ export function bibRange(text: string): BibRange | null {
|
|
|
102
108
|
return { start: m.index, end: m.index + whole.length, bodyStart, body };
|
|
103
109
|
}
|
|
104
110
|
|
|
111
|
+
/** A node of a file's projection, as far as its place goes: mdast's, or the `.tex` language's. */
|
|
112
|
+
interface ProjectedNode {
|
|
113
|
+
readonly type: string;
|
|
114
|
+
readonly position?:
|
|
115
|
+
| {
|
|
116
|
+
readonly start: { readonly offset?: number | undefined };
|
|
117
|
+
readonly end: { readonly offset?: number | undefined };
|
|
118
|
+
}
|
|
119
|
+
| undefined;
|
|
120
|
+
}
|
|
121
|
+
|
|
105
122
|
/**
|
|
106
123
|
* The ranges a markup rule must not read: LaTeX comments (the language gives them as `html`
|
|
107
124
|
* nodes, with positions) and the inline bibliography.
|
|
108
125
|
*/
|
|
109
126
|
function skippedRanges(
|
|
110
|
-
|
|
127
|
+
nodes: readonly ProjectedNode[],
|
|
111
128
|
raw: string,
|
|
112
129
|
): [number, number][] {
|
|
113
130
|
const out: [number, number][] = [];
|
|
114
|
-
for (const n of
|
|
131
|
+
for (const n of nodes)
|
|
115
132
|
if (n.type === "html" && n.position) {
|
|
116
133
|
// An offset-less point compares as NaN: inside no range, as `undefined` always did.
|
|
117
134
|
const {
|
|
@@ -197,12 +214,22 @@ export function crefDefinitionRanges(
|
|
|
197
214
|
const inside = (ranges: readonly [number, number][], i: number) =>
|
|
198
215
|
ranges.some(([s, e]) => i >= s && i < e);
|
|
199
216
|
|
|
200
|
-
/** Every match of `re` in `raw` outside the skipped
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
217
|
+
/** Every match of `re` in `raw` outside the ranges skipped in it (`nodes`: its projection's). */
|
|
218
|
+
const markupMatchesIn = (
|
|
219
|
+
raw: string,
|
|
220
|
+
nodes: readonly ProjectedNode[],
|
|
221
|
+
re: RegExp,
|
|
222
|
+
): readonly RegExpExecArray[] => {
|
|
223
|
+
const skip = skippedRanges(nodes, raw);
|
|
204
224
|
return [...raw.matchAll(re)].filter((m) => !inside(skip, m.index));
|
|
205
|
-
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
/** Every match of `re` in the linted file outside the skipped ranges. */
|
|
228
|
+
const markupMatches = (
|
|
229
|
+
sourceCode: TexSourceCode,
|
|
230
|
+
re: RegExp,
|
|
231
|
+
): readonly RegExpExecArray[] =>
|
|
232
|
+
markupMatchesIn(sourceCode.raw, sourceCode.ast?.children ?? [], re);
|
|
206
233
|
|
|
207
234
|
const isTex = (sourceCode: RuleSourceCode): sourceCode is TexSourceCode =>
|
|
208
235
|
typeof sourceCode.raw === "string";
|
|
@@ -510,25 +537,42 @@ function leadingZero(context: RuleContext) {
|
|
|
510
537
|
|
|
511
538
|
const FIG_REF = /\b(Fig\.|Figure)(?=~?\\(?:ref|autoref)\b)/g;
|
|
512
539
|
|
|
540
|
+
/** The reference forms another file of the paper writes, read the way the linted file is. */
|
|
541
|
+
const figureFormsIn = (raw: string): readonly (string | undefined)[] =>
|
|
542
|
+
markupMatchesIn(raw, texToMdast(raw).root.children, FIG_REF).map(
|
|
543
|
+
([, form]) => form,
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* The minority form is rewritten to the majority one; on a tie, to the full word. The majority is
|
|
548
|
+
* the PAPER's: a body split into `sections/*.tex` is one document to its reader, so the other files
|
|
549
|
+
* `paperlint lint` names for this one (`paperOf`) are counted too, and only this file is reported.
|
|
550
|
+
*/
|
|
513
551
|
function figureRefStyle(context: RuleContext) {
|
|
514
552
|
const sc = context.sourceCode;
|
|
515
553
|
if (!isTex(sc)) return;
|
|
516
|
-
const
|
|
517
|
-
const
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
554
|
+
const own = markupMatches(sc, FIG_REF);
|
|
555
|
+
const forms: readonly (string | undefined)[] = [
|
|
556
|
+
...own.map(([, form]) => form),
|
|
557
|
+
...otherTexts(paperOf(context.settings), context.filename).flatMap(
|
|
558
|
+
figureFormsIn,
|
|
559
|
+
),
|
|
560
|
+
];
|
|
561
|
+
const short = forms.filter((f) => f === "Fig.").length;
|
|
562
|
+
const long = forms.filter((f) => f === "Figure").length;
|
|
563
|
+
if (short === 0 || long === 0) return;
|
|
564
|
+
const [minority, word, n] =
|
|
565
|
+
short > long ? ["Figure", "Fig.", short] : ["Fig.", "Figure", long];
|
|
566
|
+
own
|
|
567
|
+
.filter(([, form]) => form === minority)
|
|
568
|
+
.forEach((m) => {
|
|
569
|
+
const range = [m.index, m.index + minority.length] as const;
|
|
570
|
+
at(context, range[0], range[1], {
|
|
571
|
+
messageId: "mixed",
|
|
572
|
+
data: { form: minority, word, n: String(n) },
|
|
573
|
+
fix: (f) => f.replaceTextRange(range, word),
|
|
574
|
+
});
|
|
530
575
|
});
|
|
531
|
-
}
|
|
532
576
|
}
|
|
533
577
|
|
|
534
578
|
const rule = (
|
|
@@ -39,7 +39,12 @@ const TEX =
|
|
|
39
39
|
const PDF = "%PDF-1.5 pretend";
|
|
40
40
|
const sha = (s) => createHash("sha256").update(s).digest("hex");
|
|
41
41
|
const facts = (lastPage, over = {}) => ({
|
|
42
|
-
schema:
|
|
42
|
+
schema: 3,
|
|
43
|
+
pages_text: [],
|
|
44
|
+
metadata: {},
|
|
45
|
+
links: [],
|
|
46
|
+
bib_anchor_page: null,
|
|
47
|
+
appendix_anchor_page: null,
|
|
43
48
|
pdf: "paper.pdf",
|
|
44
49
|
pdf_sha256: sha(PDF),
|
|
45
50
|
last_page: lastPage,
|
|
@@ -91,7 +91,7 @@ const RuleOptions = z
|
|
|
91
91
|
/** The difference, in points, that two columns may end apart. See the harness for why 120. */
|
|
92
92
|
export const DEFAULT_TOLERANCE_PT = 120;
|
|
93
93
|
/** The schema of `paper.facts.json` this rule reads (`src/facts-file.ts`, FACTS_SCHEMA). */
|
|
94
|
-
export const FACTS_SCHEMA =
|
|
94
|
+
export const FACTS_SCHEMA = 3;
|
|
95
95
|
export const FACTS_REL = join("_build", "paper.facts.json");
|
|
96
96
|
|
|
97
97
|
/** The `last_page` field, parsed. */
|
|
@@ -249,7 +249,7 @@ const rule = {
|
|
|
249
249
|
factsBroken:
|
|
250
250
|
"{{why}} in _build/paper.facts.json — rebuild the paper (`paperlint build`) to rewrite it",
|
|
251
251
|
schema:
|
|
252
|
-
"_build/paper.facts.json has schema {{got}}; this rule reads schema
|
|
252
|
+
"_build/paper.facts.json has schema {{got}}; this rule reads schema 3 — rebuild the paper " +
|
|
253
253
|
"(`paperlint build`) to rewrite it",
|
|
254
254
|
pdfMissing:
|
|
255
255
|
"_build/paper.facts.json describes {{pdf}}, which is not on disk (a failed build removes " +
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
*/
|
|
52
52
|
import { readFileSync } from "node:fs";
|
|
53
53
|
import type { RuleContext } from "./rule-context.ts";
|
|
54
|
+
import { paperOf, textOnDisk } from "./paper-context.ts";
|
|
54
55
|
|
|
55
56
|
/**
|
|
56
57
|
* A copy rather than an import, for the same reason the prose lexicons are copies: a rule may
|
|
@@ -169,7 +170,13 @@ const futurePromise = {
|
|
|
169
170
|
} catch {
|
|
170
171
|
return;
|
|
171
172
|
}
|
|
172
|
-
|
|
173
|
+
// The build mode is the PAPER's: an included file declares none, `paper.tex` does.
|
|
174
|
+
const main = paperOf(context.settings)?.main;
|
|
175
|
+
const declares =
|
|
176
|
+
main === undefined || main === context.filename
|
|
177
|
+
? text
|
|
178
|
+
: textOnDisk(main);
|
|
179
|
+
if (REVIEW_MODE_RE.test(declares)) return;
|
|
173
180
|
// Comments are blanked by CUTTING THE TAIL of the line, exactly as the previous
|
|
174
181
|
// implementation did. Columns do not shift: a match is only possible to the LEFT of
|
|
175
182
|
// `%`, and the left part of the line stays in place character for character. An
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$comment": "A real IEEEtran [conference,compsoc] paper for AIDC, built on paperlint's TeX Live: IEEE's Times, and not one finding from any rule — which is what judges
|
|
2
|
+
"$comment": "A real IEEEtran [conference,compsoc] paper for AIDC, built on paperlint's TeX Live: IEEE's Times, and not one finding from any rule — which is what judges format/page-limit, pdf/body-size and the rest against a real PDF of the class.",
|
|
3
3
|
"build": {
|
|
4
4
|
"outcome": "pdf"
|
|
5
5
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{
|
|
1
|
+
{"extends": "paperlint:aidc", "kind": "short", "identity": ["Ada Example", "adaexample", "ada@example.org", "Example University", "exampletool"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "anonymity/identity on a real IEEEtran build: B: the name in an acknowledgments paragraph. Exactly one finding, naming where.",
|
|
3
|
+
"build": {
|
|
4
|
+
"outcome": "pdf"
|
|
5
|
+
},
|
|
6
|
+
"lint": {
|
|
7
|
+
"rules": {
|
|
8
|
+
"anonymity/identity": [
|
|
9
|
+
{
|
|
10
|
+
"severity": 2,
|
|
11
|
+
"says": [
|
|
12
|
+
"«Ada Example»",
|
|
13
|
+
"PDF, page"
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
% B: the name in an acknowledgments paragraph.
|
|
2
|
+
% The declared identity is invented: "Ada Example", handle "adaexample" (paperlint.json).
|
|
3
|
+
\documentclass[conference,compsoc]{IEEEtran}
|
|
4
|
+
\begin{filecontents*}{refs.bib}
|
|
5
|
+
@article{knuth84,
|
|
6
|
+
title = {Literate Programming},
|
|
7
|
+
author = {Knuth, Donald E.},
|
|
8
|
+
journal = {The Computer Journal},
|
|
9
|
+
volume = {27},
|
|
10
|
+
number = {2},
|
|
11
|
+
pages = {97--111},
|
|
12
|
+
year = {1984},
|
|
13
|
+
doi = {10.1093/comjnl/27.2.97}
|
|
14
|
+
}
|
|
15
|
+
\end{filecontents*}
|
|
16
|
+
\begin{document}
|
|
17
|
+
\title{A Fixture for Double-Blind Review}
|
|
18
|
+
\author{\IEEEauthorblockN{Anonymous Authors}}
|
|
19
|
+
\maketitle
|
|
20
|
+
\section{Introduction}
|
|
21
|
+
This paper builds on earlier work~\cite{knuth84}.
|
|
22
|
+
|
|
23
|
+
\section{Conclusion}
|
|
24
|
+
The conclusion restates that the fixture builds.
|
|
25
|
+
\section*{Acknowledgments}
|
|
26
|
+
We thank Ada Example for comments on a draft.
|
|
27
|
+
\bibliographystyle{IEEEtran}
|
|
28
|
+
\bibliography{refs}
|
|
29
|
+
\section*{LLM Usage Statement}
|
|
30
|
+
No LLMs were used in writing this fixture.
|
|
31
|
+
\end{document}
|