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/e2e.md
CHANGED
|
@@ -84,17 +84,18 @@ findings for each rule it names (`[]` is "none"). One `describe` per fixture dir
|
|
|
84
84
|
declared field into a test. A folder without `expect.json`, or with one the schema rejects, fails.
|
|
85
85
|
A new venue fixture is a new folder: its paper, its `paperlint.json`, its `expect.json`.
|
|
86
86
|
|
|
87
|
-
| fixture | what it is there to prove
|
|
88
|
-
| ------------ |
|
|
89
|
-
| `acmart` | a real `\documentclass{acmart}` source builds, and the PDF carries the class's **own** font families — not one family of the silent substitution
|
|
90
|
-
| `fallback` | the same document with the fonts missing still **builds green**, and is **rejected** by the font check. The failure is in the artifact, not in the exit code
|
|
91
|
-
| `cite` | the bibtex path: the PDF shows `[1]`, not `[?]`, and no `??`. Its `build.sh` would leave a trace if run — it must not run, and the run must say it is ignored
|
|
92
|
-
| `guards` | `\input{paper-guards}` resolves with no configuration, because paperlint puts its own venues directory on `TEXINPUTS`
|
|
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
|
|
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
|
|
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
|
|
87
|
+
| fixture | what it is there to prove |
|
|
88
|
+
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
89
|
+
| `acmart` | a real `\documentclass{acmart}` source builds, and the PDF carries the class's **own** font families — not one family of the silent substitution |
|
|
90
|
+
| `fallback` | the same document with the fonts missing still **builds green**, and is **rejected** by the font check. The failure is in the artifact, not in the exit code |
|
|
91
|
+
| `cite` | the bibtex path: the PDF shows `[1]`, not `[?]`, and no `??`. Its `build.sh` would leave a trace if run — it must not run, and the run must say it is ignored |
|
|
92
|
+
| `guards` | `\input{paper-guards}` resolves with no configuration, because paperlint puts its own venues directory on `TEXINPUTS` |
|
|
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 |
|
|
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 |
|
|
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. It declares `identity` and names nobody, so `anonymity/identity` is silent on a real build, and `format/page-limit` finds the IEEEtran «References» heading |
|
|
98
|
+
| `anon-*` | six AIDC papers, each planting the declared (invented) name on ONE path, and `anonymity/identity` reports exactly one finding naming where: `author` the author block · `ack` an acknowledgments paragraph · `bib` a self-citation, printed by `IEEEtran.bst` as «A. Example» · `pdfauthor` `\hypersetup{pdfauthor=…}`, on no page · `href` a link whose target is the author's account while its text is not (the anonymous mirror beside it is not reported) · `macro` the name behind `\newcommand` |
|
|
98
99
|
|
|
99
100
|
The `fallback` row is the point of the whole file. `acmart.cls` checks for `libertine.sty`,
|
|
100
101
|
`zi4.sty` and `newtxmath.sty`, and failing to find any of them sets `\@ACM@newfontsfalse` and
|
|
@@ -102,6 +103,26 @@ typesets the paper in Computer Modern. The build is green, the PDF looks fine, t
|
|
|
102
103
|
and therefore so does the pagination. A submitted paper went out that way. **An exit code cannot
|
|
103
104
|
see it, so the content of the artifact is what gets measured.**
|
|
104
105
|
|
|
106
|
+
**Where the body ends, on real papers** (`test/e2e/tex/page-count.e2e.ts`). `format/page-limit`
|
|
107
|
+
counts a body that ends at the references, and that count is checked on papers nobody here wrote:
|
|
108
|
+
the accepted ACSAC papers in `fixtures/accepted-papers/` that carry a `page-count.json` (ACSAC is
|
|
109
|
+
AIDC's parent venue, same IEEEtran template), built with figures replaced by blank boxes of the
|
|
110
|
+
same size and the packages paperlint's TeX Live lacks vendored beside them. Each `page-count.json`
|
|
111
|
+
holds the references page and the body count established by rendering the PDF and looking at it,
|
|
112
|
+
with the evidence in words. `page-count-variants.json` derives papers from them, each changing ONE
|
|
113
|
+
thing: the references forced to the top of a page, a `\subsection*{References}` or a table headed
|
|
114
|
+
«References» in the body, the appendix moved before or after the bibliography, hyperref removed. The test
|
|
115
|
+
builds all of them in one `build --all` and requires the detector's count to equal the recorded
|
|
116
|
+
one, then lints two of them under AIDC's regular limit of 12: the 12-page body passes, the 13-page
|
|
117
|
+
body is reported. Eleven builds, the lint runs included, took 95 s here.
|
|
118
|
+
|
|
119
|
+
The same build carries `pdf/body-size` (`body-size.json` beside the papers): the four ACSAC papers
|
|
120
|
+
and the ACM `agenticdev-acm26` must draw no finding, and three variants — the body in IEEEtran's
|
|
121
|
+
`9pt`, the bibliography in `\scriptsize`, the bibliography in `\small` — exactly the recorded one.
|
|
122
|
+
The sizes the build measured are compared with the recorded ones first, so a drift in banal shows as
|
|
123
|
+
itself. The rule is linted once over all eight `paper.tex`, since a lint of a real paper takes
|
|
124
|
+
10–25 s, nearly all of it in the prose rules.
|
|
125
|
+
|
|
105
126
|
Beside the fixtures, one test proves the refusal with no TeX at all: PATH holds `node` alone, the
|
|
106
127
|
cache directory is empty and `CI` is set, and `paperlint build` must exit 1 with one line naming
|
|
107
128
|
`npx paperlint toolchain` and the venue's packages, print no plan and create no PDF and no cache.
|
package/docs/install.md
CHANGED
|
@@ -33,7 +33,7 @@ In order:
|
|
|
33
33
|
on the defaults gets no config file at all.
|
|
34
34
|
3. **Links each shipped skill** into `.claude/skills/<name>`, where Claude Code looks for skills.
|
|
35
35
|
4. **Writes the hook commands** into `.claude/settings.json`, beside your own entries.
|
|
36
|
-
5. **Offers a GitHub Actions workflow
|
|
36
|
+
5. **Offers a GitHub Actions workflow** that runs `npm ci`, then the action from `node_modules`.
|
|
37
37
|
6. **Offers a first paper** (`paperlint new`) if the papers directory has none.
|
|
38
38
|
7. **Offers TeX Live** for `paperlint build` (`paperlint toolchain`, ~270 MB, ~3 min, once), with
|
|
39
39
|
the size in the question and NO as the default. Without a terminal it asks nothing and lists the
|
|
@@ -47,13 +47,13 @@ question (Ctrl+D) also takes the default.
|
|
|
47
47
|
|
|
48
48
|
## What `paperlint init` writes
|
|
49
49
|
|
|
50
|
-
| what
|
|
51
|
-
|
|
|
52
|
-
| `{ "papersDir": … }` naming your papers directory
|
|
53
|
-
| the three hook commands, merged in beside your own entries
|
|
54
|
-
| a GitHub Actions workflow
|
|
55
|
-
| a first paper, via `paperlint new`
|
|
56
|
-
| one relative symlink per shipped skill, into the installed package
|
|
50
|
+
| what | where | when |
|
|
51
|
+
| -------------------------------------------------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
52
|
+
| `{ "papersDir": … }` naming your papers directory | `paperlint.json` | only when it is not the default `papers` |
|
|
53
|
+
| the three hook commands, merged in beside your own entries | `.claude/settings.json` | by default. A human at a terminal is asked [Y/n]; an agent, CI or `--yes` gets YES; `--no-hooks` skips. Hand-wired under another spelling: nothing written, so nothing runs twice |
|
|
54
|
+
| a GitHub Actions workflow: `npm ci`, then `./node_modules/paperlint` | `.github/workflows/` | only if you say yes; it asks once, and only when a human is at a terminal (stdin and stdout, no `CI`, no `--yes`) |
|
|
55
|
+
| a first paper, via `paperlint new` | `<papers>/<name>/` | only when the papers directory holds none: asked of a human at a terminal, otherwise only with `--paper <name>` |
|
|
56
|
+
| one relative symlink per shipped skill, into the installed package | `.claude/skills/<name>` | always — except where that name is already taken (a directory, a file, a link elsewhere): that entry is left as it is and named in the report |
|
|
57
57
|
|
|
58
58
|
It installs no software you did not say yes to, and touches nothing else. Commit `.claude/settings.json` so every clone
|
|
59
59
|
gets the hooks; the hook commands run files inside `node_modules`, so a fresh clone needs
|
|
@@ -86,7 +86,6 @@ listed so nobody thinks the two are consistent.
|
|
|
86
86
|
| `paper/stages` (error) | `eslint-rules/paper-stages.mjs:91`, glob `src/cli.ts:126` | front matter `stages[]`: `stage`, `date`, `pdf`, `bytes`; and lists `versions/*.pdf` |
|
|
87
87
|
| `paper/source` (error) | `eslint-rules/paper-stages.mjs:377`, `:415`, `:442` | `stages[].source`, `sourceBytes`, `sourceLost` |
|
|
88
88
|
| `paper/author-list` (warn) | `eslint-rules/paper-stages.mjs:296`, marker `:325` | `stages[]` + searches the scorecard **table cells** for the marker `bib-authors` |
|
|
89
|
-
| `paper/research-question` (warn) | `eslint-rules/paper-research-question.mjs:142` | reads the status file from disk while linting `paper.tex`/`paper.md`: `stages`, `researchQuestion` |
|
|
90
89
|
| `paper/typography` (warn) | `src/cli.ts:118` | **not** the status file — its per-paper allowance lives in the ROOT key `typographyDebt`, keyed by paper path |
|
|
91
90
|
| `pipeline-check.mjs` (skill script) | `skills/paper-pipeline/scripts/pipeline-check.mjs:231-321` | the `Venue:` / `Deadline:` header line, the verdict line, the five section tables |
|
|
92
91
|
| `paper-status-gates` hook | `hooks/paper-status-gates.hook.mjs` | surfaces the verdict and unrun gates after an edit |
|
|
@@ -111,7 +110,6 @@ content of those files is reproduced here.)
|
|
|
111
110
|
| ------------------------------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
112
111
|
| paper short name / title | **config** | human, once — `rpp new` can prefill from the folder name |
|
|
113
112
|
| venue, deadline, blind model (the `Venue:` header line) | **config** | human; changes when the paper is re-targeted |
|
|
114
|
-
| `researchQuestion` | **config** | human, once |
|
|
115
113
|
| format (`.tex` vs `.md`) | **derived** | from which source file exists |
|
|
116
114
|
| venue profile, paper kind, pdf path (a separate `venue.json` in one real paper) | **config** | human; already a per-paper file in practice |
|
|
117
115
|
| typography allowance (`typographyDebt`) | **config** | human, but it sits in the root key keyed by path today |
|
|
@@ -202,7 +200,7 @@ written once at freeze time, then checked forever — and today a human types it
|
|
|
202
200
|
- **Root (`package.json` key): the papers directory only.** Unchanged. It stays a scope, like
|
|
203
201
|
Quarto's project dir or Astro's collection glob. No list of papers.
|
|
204
202
|
- **Per paper: the paper folder's `PIPELINE-STATUS.md` front matter** holds that paper's config
|
|
205
|
-
(
|
|
203
|
+
(the venue/deadline fields that today live in a prose header line) and
|
|
206
204
|
its stage records. Unchanged carrier, so every existing paper keeps working.
|
|
207
205
|
- **Not recommended now: a separate `paper.yml`.** It would split config from the journal cleanly
|
|
208
206
|
(the 1 300-line files are 98% journal), but it moves every rule's input and every consumer's
|
|
@@ -214,9 +212,8 @@ written once at freeze time, then checked forever — and today a human types it
|
|
|
214
212
|
**`rpp new <name> [--format tex|md]`** — creates `<papers>/<name>/` with:
|
|
215
213
|
|
|
216
214
|
- `paper.tex` or `paper.md` (a minimal stub; `--format` defaults to `tex` — ask when interactive);
|
|
217
|
-
- `PIPELINE-STATUS.md` from a **template file** shipped in the package, with the name filled in
|
|
218
|
-
|
|
219
|
-
shipped nothing — `paper-stages.mjs:24`).
|
|
215
|
+
- `PIPELINE-STATUS.md` from a **template file** shipped in the package, with the name filled in and
|
|
216
|
+
**no** `stages` field (a new paper has shipped nothing — `paper-stages.mjs:24`).
|
|
220
217
|
|
|
221
218
|
Rules for it, each taken from a tool above:
|
|
222
219
|
|
|
@@ -251,7 +248,7 @@ the skills tell agents to update them.
|
|
|
251
248
|
| ------------------------------------------ | ------------------------- | ----------------------------------------------------------------------- |
|
|
252
249
|
| the folder and source stub | human | `rpp new` |
|
|
253
250
|
| `PIPELINE-STATUS.md` skeleton | human copies a code fence | `rpp new` |
|
|
254
|
-
|
|
|
251
|
+
| venue, deadline | human | human (unchanged) |
|
|
255
252
|
| `stages` records, bytes, the frozen copies | human types byte counts | still human; `rpp freeze` is the later step that takes it over |
|
|
256
253
|
| `sourceLost` exceptions | human | human (unchanged) |
|
|
257
254
|
| `State:`, `Updated:`, `Requires` cells | human | still human; **derivable** — candidates to drop from the template later |
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# anonymity/identity
|
|
2
|
+
|
|
3
|
+
**Level:** error · **Reads:** `paper.tex` → `paperlint.json`, the preset it extends, and
|
|
4
|
+
`_build/paper.facts.json` (what `paperlint build` read from the PDF)
|
|
5
|
+
|
|
6
|
+
## What it catches
|
|
7
|
+
|
|
8
|
+
For a venue that reviews double-blind — its preset says `"blind": true` — two things:
|
|
9
|
+
|
|
10
|
+
- the paper declares no `identity`, so there is nothing to check the PDF against;
|
|
11
|
+
- a string from `identity` appears in the built PDF: on a page, in its metadata (the Info
|
|
12
|
+
dictionary and the XMP packet: `Author`, `Title`, `Keywords`, …), or in the target of a link.
|
|
13
|
+
|
|
14
|
+
A rule cannot know who wrote a paper, so the authors say it once, in `paperlint.json`. Given that
|
|
15
|
+
list, finding it is mechanical. A missing list is an error, not a pass: a check with no input and a
|
|
16
|
+
check that found nothing would otherwise look the same.
|
|
17
|
+
|
|
18
|
+
The PDF is what reviewers get, so the PDF is what is read — not the source. A name reached through
|
|
19
|
+
a macro (`\newcommand\me{Ada Example}`), a self-citation's author list from the `.bib`, a
|
|
20
|
+
`\hypersetup{pdfauthor=…}` that is in no page's text, a `\href` whose visible text differs from its
|
|
21
|
+
target: each ends up in the PDF, and each is found there.
|
|
22
|
+
|
|
23
|
+
## Why
|
|
24
|
+
|
|
25
|
+
> Submissions must be properly anonymized for double-blind review.
|
|
26
|
+
|
|
27
|
+
— AIDC's call for papers, and most double-blind venues say the same. A paper that names its authors
|
|
28
|
+
is desk-rejected, and the ways a name gets in are mostly invisible when reading the PDF: metadata
|
|
29
|
+
the class copies from `\author`, a link to your own GitHub account, a name in a figure.
|
|
30
|
+
|
|
31
|
+
## How a name is matched
|
|
32
|
+
|
|
33
|
+
PDF text is not the source text; the same name comes out spelled several ways, and each is matched:
|
|
34
|
+
|
|
35
|
+
| in the PDF | matched as |
|
|
36
|
+
| ---------------------------------------- | ---------------------------------------------------- |
|
|
37
|
+
| `ADA EXAMPLE` (small caps), `ада пример` | Unicode case folding, no locale — Cyrillic folds too |
|
|
38
|
+
| `fi` ligature | NFKC on both sides |
|
|
39
|
+
| `Ex` `ample` (two items, kerning) | the page's items are joined without a space |
|
|
40
|
+
| `A d a E x a m p l e` (letter-spaced) | whitespace may stand between any two characters |
|
|
41
|
+
| `Exam-` ⏎ `ple`, `Ada` ⏎ `Example` | so may a hyphen or a line break |
|
|
42
|
+
| `AdaExample`, `Ada-Example` | a token's own spaces and hyphens are optional |
|
|
43
|
+
|
|
44
|
+
Only the ends of a token are anchored: `Ada` does not match inside `Canada`, `MIT` not inside
|
|
45
|
+
`submitted`. Two tokens that find the same text in the same place are one finding.
|
|
46
|
+
|
|
47
|
+
Anonymous hosting needs no exception: an `osf.io/…?view_only=…` or `anonymous.4open.science` link
|
|
48
|
+
names nobody, so nothing in `identity` matches it.
|
|
49
|
+
|
|
50
|
+
## Examples
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"extends": "paperlint:aidc",
|
|
55
|
+
"kind": "regular",
|
|
56
|
+
"identity": [
|
|
57
|
+
"Ada Example",
|
|
58
|
+
"adaexample",
|
|
59
|
+
"ada@example.org",
|
|
60
|
+
"Example University",
|
|
61
|
+
"exampletool"
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Findings:
|
|
67
|
+
|
|
68
|
+
> PDF, page 1: «Ada Example» matches «Ada Example» in `identity`, and aidc reviews double-blind —
|
|
69
|
+
> remove it, or refer to your own work in the third person
|
|
70
|
+
|
|
71
|
+
> PDF metadata: Author: «Ada Example» matches «Ada Example» in `identity`, …
|
|
72
|
+
|
|
73
|
+
> link target on page 4 (https://github.com/adaexample/exampletool): «adaexample» matches …
|
|
74
|
+
|
|
75
|
+
With no `identity`:
|
|
76
|
+
|
|
77
|
+
> aidc reviews double-blind, and this paper declares no `identity`, so nothing in the PDF can be
|
|
78
|
+
> checked against the authors. Declare it in paperlint.json (the root's for every paper, or this
|
|
79
|
+
> paper's): "identity": ["Your Name", "your-handle", "you@example.org", "Your University", "your-project"]
|
|
80
|
+
|
|
81
|
+
## Options / preset fields
|
|
82
|
+
|
|
83
|
+
No rule options.
|
|
84
|
+
|
|
85
|
+
- `identity` in `paperlint.json` — a list of strings, each with a letter or digit. The root file's
|
|
86
|
+
list and the paper's own are **joined**: a paper adds its co-authors to the project's author and
|
|
87
|
+
cannot drop them by accident. Put in everything that identifies you: every spelling of every
|
|
88
|
+
author's name, handles, emails, affiliations, your own tools' and repositories' names.
|
|
89
|
+
- `blind` in the preset — `true` where the venue's call says the review is double-blind. Shipped:
|
|
90
|
+
`paperlint:aidc`. A venue that is not blind: the rule is silent, `identity` or not.
|
|
91
|
+
|
|
92
|
+
## What it does not check
|
|
93
|
+
|
|
94
|
+
- **Text inside raster images.** A screenshot with your name, a logo, a scanned figure: text
|
|
95
|
+
extraction reads text, not pixels, so a name there is not seen. Look at the figures yourself.
|
|
96
|
+
- **What you did not declare.** A co-author missing from `identity`, a nickname, an old handle.
|
|
97
|
+
- **Self-citation in the third person** is allowed by most double-blind venues, and a cited name is
|
|
98
|
+
still a declared name: the rule reports it. Check the venue's policy; if it allows the citation,
|
|
99
|
+
cite in the third person and keep the finding in mind, or drop that token for this paper.
|
|
100
|
+
- **A name split by hyphenation inside a token** that the PDF does not mark (a hyphen is matched,
|
|
101
|
+
a missing space between two lines of a hyphenated compound is).
|
|
102
|
+
- **Words that are also names.** `identity` entries match as whole words anywhere; a common word
|
|
103
|
+
used as a handle will be found in ordinary prose.
|
|
104
|
+
- **The source.** Comments, `\iffalse` blocks and files not in the build are not read — they are
|
|
105
|
+
not in the PDF reviewers get. Supplementary material uploaded separately is not read either.
|
|
106
|
+
|
|
107
|
+
## How to fix
|
|
108
|
+
|
|
109
|
+
Remove the name from where the message says: the author block, the acknowledgments (for review),
|
|
110
|
+
`\hypersetup{pdfauthor=…}` / `pdfusetitle`, a link to your account (use an anonymous mirror), a
|
|
111
|
+
self-citation in the first person. Rebuild (`paperlint build`) and lint again.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# format/layout-override
|
|
2
|
+
|
|
3
|
+
**Level:** error · **Reads:** `paper.tex` and the files it includes, and the preset its
|
|
4
|
+
`paperlint.json` extends
|
|
5
|
+
|
|
6
|
+
## What it catches
|
|
7
|
+
|
|
8
|
+
A command that changes the page layout the venue's template sets for the whole document:
|
|
9
|
+
|
|
10
|
+
| command | changes |
|
|
11
|
+
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
|
|
12
|
+
| `\usepackage{geometry}` (alone or in a list), `\geometry{…}`, `\newgeometry{…}` | the margins |
|
|
13
|
+
| `\setlength` / `\addtolength` on `\textheight`, `\textwidth`, `\topmargin`, `\oddsidemargin`, `\evensidemargin`, `\columnsep`, `\headsep`, `\headheight`, `\footskip`, `\baselineskip` | the text block |
|
|
14
|
+
| `\linespread{…}`, `\renewcommand{\baselinestretch}{…}` | the line spacing |
|
|
15
|
+
|
|
16
|
+
It reads the parse tree, not the text: a command in a comment is not a command, and a `comment`
|
|
17
|
+
environment is skipped. A macro definition is read: `\newcommand{\tall}{\setlength{\textheight}{10in}}`
|
|
18
|
+
sets the length wherever it is used, so it is reported where it is defined.
|
|
19
|
+
|
|
20
|
+
It is silent for a paper whose `paperlint.json` names no preset, or a preset that names no
|
|
21
|
+
`template` — there is no mandated layout to compare against.
|
|
22
|
+
|
|
23
|
+
## Why
|
|
24
|
+
|
|
25
|
+
Venues set the page through their template and reject papers that change it: IEEE and ACM venues
|
|
26
|
+
run format checks, and a shrunken margin or a tightened line spacing is a classic desk reject.
|
|
27
|
+
AIDC: «LaTeX submissions must use the IEEEtran.cls version 1.8b template». The page-limit and
|
|
28
|
+
geometry checks see the result only in the built PDF; this rule points at the line that does it.
|
|
29
|
+
|
|
30
|
+
## Examples
|
|
31
|
+
|
|
32
|
+
```latex
|
|
33
|
+
\usepackage[margin=0.7in]{geometry} % reported
|
|
34
|
+
\setlength{\textheight}{9.6in} % reported
|
|
35
|
+
\linespread{0.95} % reported
|
|
36
|
+
\setlength{\parindent}{0pt} % not reported: not a layout length
|
|
37
|
+
Text.\vspace{-2mm} % not reported: local space, not the template's layout
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
> `\usepackage{geometry}` changes the page layout aidc's template sets
|
|
41
|
+
> (`\documentclass[conference,compsoc]{IEEEtran}`) — a desk-reject reason at venues that check the
|
|
42
|
+
> format. Remove it; if the venue allows it, disable this line with a comment saying so
|
|
43
|
+
|
|
44
|
+
## Options / preset fields
|
|
45
|
+
|
|
46
|
+
No rule options. It reads the preset's `template`.
|
|
47
|
+
|
|
48
|
+
## What it does not check
|
|
49
|
+
|
|
50
|
+
- Local spacing: a negative `\vspace`, `\vspace*` or `\vskip` around a float or a heading. It
|
|
51
|
+
changes one spot, not the layout the template sets, and no venue's call names it.
|
|
52
|
+
- Font size changes (`\small` body text, `\fontsize`), `\baselineskip` changed by `\fontsize`,
|
|
53
|
+
`\enlargethispage`, `\looseness`, or float spacing lengths (`\textfloatsep`, `\abovecaptionskip`).
|
|
54
|
+
- Conditionals are not evaluated: a command inside an `\iffalse … \fi` block in the preamble is
|
|
55
|
+
still reported.
|
|
56
|
+
- Layout lengths set with plain TeX assignment (`\textheight=9in`).
|
|
57
|
+
|
|
58
|
+
## How to fix
|
|
59
|
+
|
|
60
|
+
Remove the command and let the template set the page. If the venue explicitly allows it, keep it
|
|
61
|
+
with `% eslint-disable-next-line format/layout-override -- <the venue's words>`.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# format/page-limit
|
|
2
|
+
|
|
3
|
+
**Level:** error · **Reads:** `paper.tex` → `paperlint.json` (the venue preset and the paper's
|
|
4
|
+
`kind`) and `_build/paper.facts.json` (what `paperlint build` measured in the PDF)
|
|
5
|
+
|
|
6
|
+
## What it catches
|
|
7
|
+
|
|
8
|
+
A PDF longer than its venue allows for the paper's kind: more body pages than `body_pages_max`, or
|
|
9
|
+
more reference pages than `ref_pages_max`.
|
|
10
|
+
|
|
11
|
+
How the body is counted depends on the preset:
|
|
12
|
+
|
|
13
|
+
- **By default**, body and reference pages are banal's count: a page holding any text that is not
|
|
14
|
+
bibliography is a body page.
|
|
15
|
+
- **With `"body_ends_at": "references"`** (AIDC), the body is every page up to where it ends: at
|
|
16
|
+
the references or at the appendix, whichever comes first — «excluding references and
|
|
17
|
+
appendices» excludes an appendix wherever it sits. What follows is not counted. The page the body
|
|
18
|
+
ends on counts as body when body text stands above the heading there: a body that runs half a
|
|
19
|
+
page past the limit is over the limit.
|
|
20
|
+
|
|
21
|
+
## How it finds where the body ends
|
|
22
|
+
|
|
23
|
+
Each end is found by a structural signal first, then its heading line on that page.
|
|
24
|
+
|
|
25
|
+
**The references.**
|
|
26
|
+
|
|
27
|
+
1. **hyperref's first bibliography entry.** hyperref writes a destination named `cite.<key>` for
|
|
28
|
+
every `\bibitem`; the lowest page any of them is on is where the bibliography is. The build
|
|
29
|
+
records it as `bib_anchor_page`.
|
|
30
|
+
2. **The heading.** A line that reads `References` or `Bibliography` — any case, spaces ignored
|
|
31
|
+
(IEEEtran prints `REFERENCES` in small capitals, which the PDF may give as `R EFERENCES`),
|
|
32
|
+
optionally numbered (`7 References`, `VII. REFERENCES`). A heading whose next line opens the
|
|
33
|
+
first entry, `[1]`, is **confirmed**.
|
|
34
|
+
|
|
35
|
+
With the anchor, the references start on its page, at the confirmed heading there, else at the last
|
|
36
|
+
heading line on that page. Without hyperref: at the first confirmed heading, else at the first
|
|
37
|
+
heading line.
|
|
38
|
+
|
|
39
|
+
**The appendix.** hyperref's destination for the appendix's first section (`appendix.A` in the
|
|
40
|
+
standard classes) or the first bookmark titled «Appendix…» (IEEEtran's `\appendices`) gives its
|
|
41
|
+
page, recorded as `appendix_anchor_page`; on that page, the line reading `Appendix A.`,
|
|
42
|
+
`APPENDIX` or `Appendices`. Without hyperref the appendix is not looked for: a sentence can end on
|
|
43
|
+
a line reading «Appendix A.», and nothing would tell it from the heading.
|
|
44
|
+
|
|
45
|
+
On the page the body ends on, the lines above the heading decide: a line with a letter in it is
|
|
46
|
+
body text, unless it is a running header or footer — the same line, digits aside, on at least three
|
|
47
|
+
pages and a third of them. A page number is not body text either.
|
|
48
|
+
|
|
49
|
+
A line reading «References» that is not the bibliography's heading — a `\subsection*{References}`,
|
|
50
|
+
a table column headed «References» — is not taken for it: the anchor names another page, or the
|
|
51
|
+
line is not followed by `[1]`. When a structural signal and the text disagree — a `cite.<key>` page
|
|
52
|
+
with no heading on it, a confirmed heading on another page, an appendix page with no «Appendix»
|
|
53
|
+
line — the count is not made, and the rule says so (`unclear`).
|
|
54
|
+
|
|
55
|
+
## Why
|
|
56
|
+
|
|
57
|
+
AIDC limits the paper to 12 pages «excluding references and appendices», and places the LLM Usage
|
|
58
|
+
Statement after the references, where it «does not count towards the page limit». banal counts any
|
|
59
|
+
page with non-bibliography text as body, so a statement or appendix after the bibliography would
|
|
60
|
+
fail a correct paper. A body that runs onto the references page, even by a few lines, is a
|
|
61
|
+
thirteenth page — the desk-reject case this rule exists to catch.
|
|
62
|
+
|
|
63
|
+
## Examples
|
|
64
|
+
|
|
65
|
+
A 12-page AIDC paper whose conclusion ends at the foot of page 12, «References» at the top of page
|
|
66
|
+
13: body 12 pages, passes.
|
|
67
|
+
|
|
68
|
+
The same paper with the conclusion running four lines onto page 13, «References» below them:
|
|
69
|
+
|
|
70
|
+
> body pages (up to the references on page 13): 13, over the limit 12 for aidc/regular — a desk
|
|
71
|
+
> reject; cut the text
|
|
72
|
+
|
|
73
|
+
An accepted ACSAC paper that places its appendix tables before the bibliography: the appendix starts
|
|
74
|
+
at the top of page 12, the references on page 15. The body is 11 pages.
|
|
75
|
+
|
|
76
|
+
When a structural signal and the text disagree, the count is not made and the rule says so:
|
|
77
|
+
|
|
78
|
+
> could not tell where the body ends, so it was NOT counted against aidc's limit: the first
|
|
79
|
+
> bibliography entry is on page 14 (hyperref's destination), and no line on page 14 reads
|
|
80
|
+
> «References». Check the headings of the bibliography and the appendix, and that nothing before
|
|
81
|
+
> them reads «References» above a [1]
|
|
82
|
+
|
|
83
|
+
## Options / preset fields
|
|
84
|
+
|
|
85
|
+
No rule options. It reads, from the preset, `format.kinds.<kind>.body_pages_max`,
|
|
86
|
+
`format.kinds.<kind>.ref_pages_max` and `format.body_ends_at`; from the paper's `paperlint.json`,
|
|
87
|
+
`kind`. A preset with no kinds, or a kind with no limits, has nothing to check.
|
|
88
|
+
|
|
89
|
+
## What it does not check
|
|
90
|
+
|
|
91
|
+
- **An appendix before the references, without hyperref**, is counted as body: nothing structural
|
|
92
|
+
says where it starts. With hyperref it ends the body.
|
|
93
|
+
- **An appendix whose heading does not read «Appendix…»** (the standard classes print `A` and the
|
|
94
|
+
title) is reported as `unclear` when hyperref anchors it.
|
|
95
|
+
- **A bibliography that is not numbered and not linked by hyperref** has no confirmed heading and no
|
|
96
|
+
anchor; the first line reading «References» is then taken, and a body line reading exactly that
|
|
97
|
+
would be taken for it.
|
|
98
|
+
- **A running header that repeats on fewer than three pages** (a two-page paper) counts as body
|
|
99
|
+
text above a heading at the top of a page.
|
|
100
|
+
- Whether the venue counts pages the same way for other kinds of material (supplementary files,
|
|
101
|
+
artifact appendices).
|
|
102
|
+
|
|
103
|
+
## How to fix
|
|
104
|
+
|
|
105
|
+
Cut the body until it ends within the limit. With `body_ends_at`, check where the body ends on the
|
|
106
|
+
rendered PDF: body text above the heading on that page counts that page. For `unclear`, give the
|
|
107
|
+
bibliography its heading (`\bibliography`, `thebibliography`) and the appendix one reading
|
|
108
|
+
«Appendix», and make sure no line before them reads «References» above a `[1]`.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# pdf/body-size
|
|
2
|
+
|
|
3
|
+
**Level:** error · **Reads:** `paper.tex` → `paperlint.json` (the venue preset) and
|
|
4
|
+
`_build/paper.facts.json` (what `paperlint build` measured in the PDF)
|
|
5
|
+
|
|
6
|
+
## What it catches
|
|
7
|
+
|
|
8
|
+
A PDF set in another size than its venue's template:
|
|
9
|
+
|
|
10
|
+
- the body font size more than `body_pt_tol` off the preset's `body_pt` (`body`);
|
|
11
|
+
- the reference font size outside the preset's `ref_pt_min`–`ref_pt_max`, widened by `body_pt_tol`
|
|
12
|
+
on both sides (`refPt`);
|
|
13
|
+
- a build that measured no body size at all (`bodyMissing`) — rebuild.
|
|
14
|
+
|
|
15
|
+
Both sizes are what banal measures on the rendered pages: the most common size of the body text,
|
|
16
|
+
and of the first bibliography page. It is not the size the source declares, so a
|
|
17
|
+
`\documentclass[9pt]`, a `\fontsize` in the preamble or a bibliography wrapped in `\scriptsize` are
|
|
18
|
+
all seen, whatever macro did it.
|
|
19
|
+
|
|
20
|
+
## Why
|
|
21
|
+
|
|
22
|
+
A call for papers that fixes a template fixes its font size, and shrinking the text is the usual
|
|
23
|
+
way a paper is made to fit the page limit. A reviewer or a production editor who notices returns
|
|
24
|
+
the paper; a page limit checked on a shrunk paper checks nothing.
|
|
25
|
+
|
|
26
|
+
It is an error because it was measured to be exact on real papers, not assumed. On five accepted
|
|
27
|
+
papers, built (`fixtures/accepted-papers/body-size.json`, `test/e2e/tex/page-count.e2e.ts`):
|
|
28
|
+
|
|
29
|
+
| paper | declared | rendered (pdf.js) | measured (banal) | finding |
|
|
30
|
+
| ------------------------------ | ---------------------- | ------------------------------------------ | ---------------- | ------- |
|
|
31
|
+
| four ACSAC papers, IEEEtran | 10 pt, references 8 pt | 10.00 / 8.00 (9.96 / 7.97 without compsoc) | 10.3 / 8.3 | none |
|
|
32
|
+
| AgenticDev '26, acmart sigconf | 9 pt, references 7 pt | 8.97 / 6.97 | 9.3 / 7.3 | none |
|
|
33
|
+
| an ACSAC paper with `9pt` | 9 pt | 9.00 / 8.00 | 9.3 / 8.3 | `body` |
|
|
34
|
+
| references in `\scriptsize` | 7 pt | 10.00 / 7.00 | 10.3 / 7.3 | `refPt` |
|
|
35
|
+
| references in `\small` | 9 pt | 9.96 / 8.97 | 10.3 / 9.3 | `refPt` |
|
|
36
|
+
|
|
37
|
+
banal reads every size 0.3 pt above the size the glyphs are set in, on both templates, and
|
|
38
|
+
`body_pt_tol` of 0.5 absorbs that. Because of that offset the margins are not symmetric: every accepted body, and every IEEE bibliography (a single size, 8 pt), lands 0.2 pt inside the upper edge, a paper one size smaller (9 pt body, 7 pt
|
|
39
|
+
references) 0.2 pt outside the lower edge, and one size larger 0.8 pt outside the upper one. The
|
|
40
|
+
offset was exactly 0.3 on all eight builds; a template on which banal reads differently would need
|
|
41
|
+
its own measurement. A change of half a point (IEEEtran's 9pt `\small` is 8.5 pt) can pass
|
|
42
|
+
unreported.
|
|
43
|
+
|
|
44
|
+
## Examples
|
|
45
|
+
|
|
46
|
+
An IEEEtran paper built with `\documentclass[9pt,conference,compsoc]{IEEEtran}`:
|
|
47
|
+
|
|
48
|
+
> the body font size measures 9.3 pt against 10 ± 0.5 pt for ieee-conference. The measurement is
|
|
49
|
+
> the mode of the rendered text, not the declared size — check \documentclass and its options
|
|
50
|
+
|
|
51
|
+
The same paper with `{\let\footnotesize\scriptsize\bibliography{refs}}`:
|
|
52
|
+
|
|
53
|
+
> the reference font size is 7.3 pt, outside 8 pt for ieee-conference (allowing ±0.5 pt for the
|
|
54
|
+
> measuring drift) — fix the bibliography's font size
|
|
55
|
+
|
|
56
|
+
The paper as the template sets it, `\documentclass[conference,compsoc]{IEEEtran}` and a plain
|
|
57
|
+
`\bibliography{refs}`, passes.
|
|
58
|
+
|
|
59
|
+
## Options / preset fields
|
|
60
|
+
|
|
61
|
+
No rule options. From the preset's `format`: `body_pt` and `body_pt_tol` (both needed for the body
|
|
62
|
+
check), `ref_pt_min` and `ref_pt_max` (both needed for the reference check). A preset without them
|
|
63
|
+
does not check that size. The shipped presets:
|
|
64
|
+
|
|
65
|
+
| preset | `body_pt` ± `body_pt_tol` | references |
|
|
66
|
+
| ----------------------------------------- | ------------------------- | ---------- |
|
|
67
|
+
| `ieee-conference` (and `aidc`) | 10 ± 0.5 | 8 |
|
|
68
|
+
| `acm-sigconf` (and `agenticdev`, `aisec`) | 9 ± 0.5 | 7–9 |
|
|
69
|
+
| `realm` | 11 ± 0.5 | 9–11 |
|
|
70
|
+
|
|
71
|
+
## What it does not check
|
|
72
|
+
|
|
73
|
+
- **The size of anything but the body and the first bibliography page**: captions, footnotes,
|
|
74
|
+
tables, the title and the appendix are not measured. A table set in `\tiny` passes.
|
|
75
|
+
- **A paper that is not built.** Without `_build/paper.facts.json`, or without banal, the rule is
|
|
76
|
+
silent and `pdf/measured` says why.
|
|
77
|
+
- **The `realm` preset** has not been checked on built accepted papers of its venue: its body size
|
|
78
|
+
is ACL's stated 11 pt, and its reference range was set from a single measurement.
|
|
79
|
+
- A paper with no bibliography has no reference size, and only the body is judged.
|
|
80
|
+
|
|
81
|
+
## How to fix
|
|
82
|
+
|
|
83
|
+
Use the template's size: remove the size option from `\documentclass` and any `\fontsize`,
|
|
84
|
+
`\small` or `\footnotesize` around the body; for the references, remove what wraps
|
|
85
|
+
`\bibliography` or changes `\bibfont`. Rebuild (`paperlint build`) and lint again. If the venue
|
|
86
|
+
really allows another size, override the preset's `format` in your own preset, or set the rule to
|
|
87
|
+
`"off"` in `paperlint.json`.
|