paperlint 7.0.0 → 7.2.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/.github/workflows/ci.yml +3 -0
- package/.github/workflows/deadlines.yml +100 -0
- package/README.md +24 -11
- package/dist/adapters/hotcrp/deadlines.d.ts +8 -0
- package/dist/adapters/hotcrp/deadlines.d.ts.map +1 -0
- package/dist/adapters/hotcrp/deadlines.io.d.ts +6 -0
- package/dist/adapters/hotcrp/deadlines.io.d.ts.map +1 -0
- package/dist/adapters/hotcrp/deadlines.io.js +26 -0
- package/dist/adapters/hotcrp/deadlines.io.js.map +1 -0
- package/dist/adapters/hotcrp/deadlines.js +134 -0
- package/dist/adapters/hotcrp/deadlines.js.map +1 -0
- package/dist/adapters/hotcrp/index.d.ts +2 -0
- package/dist/adapters/hotcrp/index.d.ts.map +1 -1
- package/dist/adapters/hotcrp/index.js +2 -0
- package/dist/adapters/hotcrp/index.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +42 -7
- package/dist/cli.js.map +1 -1
- package/dist/cycle-rules.d.ts +24 -0
- package/dist/cycle-rules.d.ts.map +1 -0
- package/dist/cycle-rules.js +212 -0
- package/dist/cycle-rules.js.map +1 -0
- package/dist/domain/body-pages.d.ts +29 -0
- package/dist/domain/body-pages.d.ts.map +1 -1
- package/dist/domain/body-pages.js +25 -0
- package/dist/domain/body-pages.js.map +1 -1
- package/dist/domain/cycle.d.ts +148 -0
- package/dist/domain/cycle.d.ts.map +1 -0
- package/dist/domain/cycle.js +281 -0
- package/dist/domain/cycle.js.map +1 -0
- package/dist/domain/deadline.d.ts +128 -0
- package/dist/domain/deadline.d.ts.map +1 -0
- package/dist/domain/deadline.js +281 -0
- package/dist/domain/deadline.js.map +1 -0
- package/dist/eslint-rules/cycle-stage.d.ts +39 -0
- package/dist/eslint-rules/cycle-stage.d.ts.map +1 -0
- package/dist/eslint-rules/cycle-stage.js +119 -0
- package/dist/eslint-rules/cycle-stage.js.map +1 -0
- package/dist/lib/paper-config.d.ts.map +1 -1
- package/dist/lib/paper-config.js +2 -0
- package/dist/lib/paper-config.js.map +1 -1
- package/dist/paper-includes.d.ts +3 -0
- package/dist/paper-includes.d.ts.map +1 -1
- package/dist/paper-includes.js +17 -2
- package/dist/paper-includes.js.map +1 -1
- package/dist/paper-settings.d.ts +7 -0
- package/dist/paper-settings.d.ts.map +1 -1
- package/dist/paper-settings.js +61 -8
- package/dist/paper-settings.js.map +1 -1
- package/dist/ports/venue-deadlines.d.ts +14 -0
- package/dist/ports/venue-deadlines.d.ts.map +1 -0
- package/dist/ports/venue-deadlines.js +2 -0
- package/dist/ports/venue-deadlines.js.map +1 -0
- package/dist/presets.d.ts +22 -3
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +26 -17
- package/dist/presets.js.map +1 -1
- package/dist/submission.d.ts +16 -0
- package/dist/submission.d.ts.map +1 -1
- package/dist/submission.js +59 -16
- package/dist/submission.js.map +1 -1
- package/dist/tex-requirements.d.ts +12 -0
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +26 -3
- package/dist/tex-requirements.js.map +1 -1
- package/dist/tex-venue-rules.d.ts +24 -2
- package/dist/tex-venue-rules.d.ts.map +1 -1
- package/dist/tex-venue-rules.js +98 -10
- package/dist/tex-venue-rules.js.map +1 -1
- package/dist/venue-rules.d.ts +12 -1
- package/dist/venue-rules.d.ts.map +1 -1
- package/dist/venue-rules.js +81 -17
- package/dist/venue-rules.js.map +1 -1
- package/docs/configuration.md +140 -12
- package/docs/e2e.md +14 -12
- package/docs/rules/cycle/evidence.md +87 -0
- package/docs/rules/cycle/port-done.md +75 -0
- package/docs/rules/cycle/record.md +152 -0
- package/docs/rules/cycle/stage.md +85 -0
- package/docs/rules/format/page-limit.md +18 -1
- package/docs/rules/tex/template.md +25 -5
- package/docs/rules/tex/venue-leftover.md +13 -0
- package/docs/rules.md +68 -55
- package/docs/submission.md +10 -1
- package/eslint-rules/cycle-stage.test.mjs +131 -0
- package/eslint-rules/cycle-stage.ts +141 -0
- package/eslint.config.mjs +41 -1
- package/fixtures/build-e2e/msr/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/msr/expect.json +18 -0
- package/fixtures/build-e2e/msr/paper.tex +49 -0
- package/fixtures/build-e2e/msr/paperlint.json +1 -0
- package/fixtures/build-e2e/msr-appendix-after/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/msr-appendix-after/expect.json +20 -0
- package/fixtures/build-e2e/msr-appendix-after/paper.tex +48 -0
- package/fixtures/build-e2e/msr-appendix-after/paperlint.json +1 -0
- package/lib/paper-config.ts +3 -0
- package/package.json +1 -1
- package/presets/aidc.jsonc +24 -3
- package/presets/aidc.md +6 -3
- package/presets/ieee-conference.md +1 -1
- package/presets/msr.jsonc +95 -0
- package/presets/msr.md +98 -0
- package/presets/venue-profile.schema.json +64 -0
- package/scripts/check.ts +4 -0
- package/scripts/refresh-deadlines.test.ts +431 -0
- package/scripts/refresh-deadlines.ts +387 -0
- package/skills/camera-ready/SKILL.md +8 -1
- package/skills/camera-ready/SKILL.md.spec.ts +7 -0
- package/skills/submission-portal/SKILL.md +2 -2
- package/skills/submission-portal/SKILL.md.spec.ts +1 -1
- package/skills/submit-paper/SKILL.md +9 -1
- package/skills/submit-paper/SKILL.md.spec.ts +8 -0
- package/src/adapters/hotcrp/cassette/README.md +22 -0
- package/src/adapters/hotcrp/cassette/deadlines-aidc26.html +17 -0
- package/src/adapters/hotcrp/cassette/deadlines-fse2027.html +24 -0
- package/src/adapters/hotcrp/cassette/deadlines-icse2027.html +25 -0
- package/src/adapters/hotcrp/cassette/deadlines-msr2027.html +25 -0
- package/src/adapters/hotcrp/cassette/deadlines-nosuch.html +18 -0
- package/src/adapters/hotcrp/cassette/deadlines-sosp26.html +26 -0
- package/src/adapters/hotcrp/deadlines.io.test.ts +86 -0
- package/src/adapters/hotcrp/deadlines.io.ts +27 -0
- package/src/adapters/hotcrp/deadlines.test.ts +170 -0
- package/src/adapters/hotcrp/deadlines.ts +161 -0
- package/src/adapters/hotcrp/index.ts +2 -0
- package/src/cli.test.ts +9 -0
- package/src/cli.ts +50 -6
- package/src/cycle-rules.test.ts +278 -0
- package/src/cycle-rules.ts +273 -0
- package/src/domain/body-pages.test.ts +144 -0
- package/src/domain/body-pages.ts +61 -1
- package/src/domain/cycle.test.ts +456 -0
- package/src/domain/cycle.ts +432 -0
- package/src/domain/deadline.test.ts +386 -0
- package/src/domain/deadline.ts +472 -0
- package/src/new-paper.test.ts +28 -0
- package/src/paper-includes.test.ts +26 -0
- package/src/paper-includes.ts +20 -2
- package/src/paper-settings-commands.test.ts +1 -1
- package/src/paper-settings.test.ts +167 -0
- package/src/paper-settings.ts +85 -11
- package/src/ports/venue-deadlines.ts +14 -0
- package/src/preset-card-rule.test.ts +1 -0
- package/src/presets.test.ts +171 -1
- package/src/presets.ts +65 -15
- package/src/rule-cases.test.ts +144 -0
- package/src/submission.test.ts +147 -4
- package/src/submission.ts +100 -18
- package/src/tex-requirements.ts +52 -4
- package/src/tex-venue-rules.test.ts +237 -0
- package/src/tex-venue-rules.ts +130 -15
- package/src/venue-rules.test.ts +255 -2
- package/src/venue-rules.ts +115 -22
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# cycle/port-done
|
|
2
|
+
|
|
3
|
+
**Level:** error · **Reads:** `paper.tex` → the paper's `paperlint.json` (`cycles`) → the venue
|
|
4
|
+
preset's `template` · **Reported on:** `paper.tex`
|
|
5
|
+
|
|
6
|
+
## What it catches
|
|
7
|
+
|
|
8
|
+
The current cycle declares `"phase": "porting"` — the source is still in the previous venue's
|
|
9
|
+
template, so the venue's format rules are silent — and the paper's `\documentclass` is already the
|
|
10
|
+
one the venue's preset requires. The port is done and the declaration is stale.
|
|
11
|
+
|
|
12
|
+
## Why
|
|
13
|
+
|
|
14
|
+
`"phase": "porting"` is a record of open work, not a softer severity: the day a new venue is chosen
|
|
15
|
+
the source is still in the old venue's template, and a page count under the wrong template says
|
|
16
|
+
nothing about the new limit. While the phase is declared, every `pdf/*`, `format/*`, `anonymity/*`
|
|
17
|
+
and `tex/*` venue rule stays silent for that paper. A declaration that silences rules must not be
|
|
18
|
+
able to stay on after its reason is gone: once the class line is the venue's, this rule fires, and
|
|
19
|
+
the only way to be quiet again is to drop the phase — which turns the format rules back on.
|
|
20
|
+
|
|
21
|
+
## Examples
|
|
22
|
+
|
|
23
|
+
Failing — `paperlint.json`:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"cycles": [
|
|
28
|
+
{
|
|
29
|
+
"id": "blind-2027",
|
|
30
|
+
"venue": { "kind": "preset", "extends": "./blindconf.jsonc" },
|
|
31
|
+
"opened": "2026-09-09",
|
|
32
|
+
"phase": "porting"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
where the preset's `template` is `\documentclass[conference,compsoc]{IEEEtran}`, and `paper.tex`
|
|
39
|
+
begins with that very line:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
papers/my-paper/paper.tex
|
|
43
|
+
1:1 error the \documentclass is already `\documentclass[conference,compsoc]{IEEEtran}`, which
|
|
44
|
+
BlindConf requires, and the cycle «blind-2027» still declares "phase": "porting" — the
|
|
45
|
+
port is done: remove the phase, so the venue's format rules judge this paper again
|
|
46
|
+
cycle/port-done
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Passing:
|
|
50
|
+
|
|
51
|
+
- the same paper with the phase removed (or `"phase": "prepared"`);
|
|
52
|
+
- a cycle still porting whose `paper.tex` still has the old venue's class line — the port is open
|
|
53
|
+
work, as declared;
|
|
54
|
+
- a preset that names no `template`: there is nothing to compare, as for `tex/template`;
|
|
55
|
+
- a paper whose `paperlint.json` has no `cycles`.
|
|
56
|
+
|
|
57
|
+
## Options / preset fields
|
|
58
|
+
|
|
59
|
+
No rule options. It reads the preset's `template`, the field [`tex/template`](../tex/template.md)
|
|
60
|
+
reads, and compares the way that rule does.
|
|
61
|
+
|
|
62
|
+
## What it does not check
|
|
63
|
+
|
|
64
|
+
- Whether the port is complete beyond the class line — the page limit, the fonts, the sections. Those
|
|
65
|
+
are the format rules' to judge, and they judge again the moment the phase is dropped.
|
|
66
|
+
- A cycle declared `porting` whose class line is still the old one: that is the state the phase
|
|
67
|
+
describes.
|
|
68
|
+
- A `porting` cycle that has already sent a PDF — [`cycle/stage`](stage.md) reports that on the
|
|
69
|
+
scorecard.
|
|
70
|
+
|
|
71
|
+
## How to fix
|
|
72
|
+
|
|
73
|
+
Remove `"phase": "porting"` from the current cycle in `paperlint.json`, then run `paperlint lint`
|
|
74
|
+
and `paperlint build`: the venue's format rules judge the paper again, and what they report is the
|
|
75
|
+
rest of the port.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# cycle/record
|
|
2
|
+
|
|
3
|
+
**Level:** error · **Reads:** the paper's `paperlint.json` (`cycles`) and the venue presets it names ·
|
|
4
|
+
**Reported on:** the first line of the paper's `PIPELINE-STATUS.md`
|
|
5
|
+
|
|
6
|
+
## What it catches
|
|
7
|
+
|
|
8
|
+
The paper's `cycles` — its attempts at venues, one entry each, oldest first — do not describe one
|
|
9
|
+
consistent history:
|
|
10
|
+
|
|
11
|
+
- **one source's deadlines are out of order.** The order a venue's deadlines keep is
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
registration ≤ submission ≤ resubmission < notification < camera-ready
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
and it is checked within each source: the portal's readings among themselves, the call's among
|
|
18
|
+
themselves, the overrides among themselves. A call's abstract day after its own paper day is a
|
|
19
|
+
date typed wrong or an AoE day miscounted.
|
|
20
|
+
|
|
21
|
+
- **a portal reading the venue's preset already carries.** A cycle whose venue is a preset derives
|
|
22
|
+
the portal's readings from it (`presets/msr.jsonc` carries MSR's); the same reading typed into the
|
|
23
|
+
paper is a second copy, and it goes stale the day paperlint updates the preset.
|
|
24
|
+
|
|
25
|
+
The structural problems of the record — a bare date where an instant is needed, two open cycles, an
|
|
26
|
+
open cycle that is not the last, two cycles with one id, one deadline in two entries, two readings
|
|
27
|
+
from one source, an override without its reason or evidence, an unknown key anywhere in the record
|
|
28
|
+
— are refused earlier, by `paperlint lint` itself before any rule runs, with one line naming the
|
|
29
|
+
file, the place and the problem, and exit code 2, the same way an unknown key in `paperlint.json` is
|
|
30
|
+
refused. This rule judges what the record parses with.
|
|
31
|
+
|
|
32
|
+
## Why
|
|
33
|
+
|
|
34
|
+
A deadline is the one fact in the record whose error costs the attempt. The record keeps what each
|
|
35
|
+
page said — the portal, which refuses the upload, and the call, which only announces — with where and
|
|
36
|
+
when it was read, and the **earlier** reading binds unless a human override says otherwise. That the
|
|
37
|
+
portal and the call disagree is expected and is not a finding: at MSR 2027 the portal's "submissions
|
|
38
|
+
must be completed by" falls at 04:00 UTC on the call's _abstract_ day, four days before the call's
|
|
39
|
+
_paper_ day, and keeping both is how the earlier one is seen. What is a finding is a source that
|
|
40
|
+
contradicts itself, and a fact recorded in two places.
|
|
41
|
+
|
|
42
|
+
## Examples
|
|
43
|
+
|
|
44
|
+
Failing — `paperlint.json`, the call's own dates inverted:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"cycles": [
|
|
49
|
+
{
|
|
50
|
+
"id": "conf-2027",
|
|
51
|
+
"venue": { "kind": "preset", "extends": "paperlint:acm-sigconf" },
|
|
52
|
+
"opened": "2026-09-09",
|
|
53
|
+
"deadlines": [
|
|
54
|
+
{
|
|
55
|
+
"what": "registration",
|
|
56
|
+
"observed": [
|
|
57
|
+
{
|
|
58
|
+
"at": "2026-10-25 AoE",
|
|
59
|
+
"source": "call",
|
|
60
|
+
"url": "https://conf.example/cfp",
|
|
61
|
+
"read": "2026-09-09"
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"what": "submission",
|
|
67
|
+
"observed": [
|
|
68
|
+
{
|
|
69
|
+
"at": "2026-10-23 AoE",
|
|
70
|
+
"source": "call",
|
|
71
|
+
"url": "https://conf.example/cfp",
|
|
72
|
+
"read": "2026-09-09"
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
papers/my-paper/PIPELINE-STATUS.md
|
|
84
|
+
1:1 error paperlint.json, cycle «conf-2027»: the call's "registration" (2026-10-26T11:59:59Z) is
|
|
85
|
+
after its "submission" (2026-10-24T11:59:59Z) — a date typed wrong, or an AoE day
|
|
86
|
+
miscounted (write a call's date as "YYYY-MM-DD AoE" and paperlint converts it)
|
|
87
|
+
cycle/record
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Failing — a cycle at `paperlint:msr` that copies the portal's reading the preset carries:
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
1:1 error paperlint.json, cycle «msr-2027»: the "submission" deadline records a portal reading,
|
|
94
|
+
and the preset paperlint:msr carries the portal's reading of it (2026-10-20T04:00:00Z,
|
|
95
|
+
read 2026-10-05) — a second copy drifts when paperlint updates the preset: delete this
|
|
96
|
+
one (the call's reading and an override stay) cycle/record
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Passing:
|
|
100
|
+
|
|
101
|
+
- the first record with the registration on `2026-10-20 AoE`;
|
|
102
|
+
- a cycle at `paperlint:msr` recording the call's abstract day as `registration` (`2026-10-20 AoE`)
|
|
103
|
+
and its paper day as `submission` (`2026-10-23 AoE`): the preset's portal `submission`
|
|
104
|
+
(`2026-10-20T04:00:00Z`) is earlier than both, binds, and is not a contradiction;
|
|
105
|
+
- a cycle with no `deadlines` at all;
|
|
106
|
+
- a paper whose `paperlint.json` has no `cycles` (the flat form, `"extends": "paperlint:…"`).
|
|
107
|
+
|
|
108
|
+
## Options / preset fields
|
|
109
|
+
|
|
110
|
+
No rule options. One deadline entry:
|
|
111
|
+
|
|
112
|
+
```jsonc
|
|
113
|
+
{
|
|
114
|
+
"what": "submission", // registration (the call's abstract) · submission (the full paper) · resubmission · notification · camera-ready
|
|
115
|
+
"observed": [
|
|
116
|
+
{
|
|
117
|
+
"at": "2026-10-23 AoE", // or an instant with its zone: 2026-10-20T04:00:00Z
|
|
118
|
+
"source": "call", // or "portal"
|
|
119
|
+
"url": "https://conf.example/cfp",
|
|
120
|
+
"read": "2026-10-05", // the day the page was read
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
// optional; wins over every reading
|
|
124
|
+
"override": {
|
|
125
|
+
"at": "2026-10-27T04:00:00Z",
|
|
126
|
+
"reason": "the chairs extended the deadline by mail",
|
|
127
|
+
"evidence": "mail/extension.eml",
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The preset field is `deadlines` in a venue preset: a list of readings `{ "what", "at", "source",
|
|
133
|
+
"url", "read" }`, `at` in UTC. Shipped presets carry their portal's readings and are refreshed by a
|
|
134
|
+
scheduled workflow in paperlint's repository; your own preset may carry readings the same way.
|
|
135
|
+
|
|
136
|
+
## What it does not check
|
|
137
|
+
|
|
138
|
+
- Whether a reading is still what the page says. The rule has no network and no clock; a shipped
|
|
139
|
+
preset's readings are re-read daily in paperlint's repository and reach you as a patch release.
|
|
140
|
+
A venue without a preset (`"kind": "named"`) is re-read by you.
|
|
141
|
+
- Whether a deadline has passed, or how long is left.
|
|
142
|
+
- A disagreement between sources — that is what the record holds; the earlier reading binds.
|
|
143
|
+
- Whether an override's evidence exists — that is [`cycle/evidence`](evidence.md).
|
|
144
|
+
- Whether the scorecard's stages name these cycles — that is [`cycle/stage`](stage.md).
|
|
145
|
+
|
|
146
|
+
## How to fix
|
|
147
|
+
|
|
148
|
+
Correct the date. For a deadline read from a call for papers, write it as the call prints it,
|
|
149
|
+
`"YYYY-MM-DD AoE"`, and let paperlint do the arithmetic; for one read from a portal, write the instant
|
|
150
|
+
the portal shows with its zone. Delete a portal reading the venue's preset already carries. A
|
|
151
|
+
deadline the chairs moved by mail is an `override`, with the mail saved in the paper folder as its
|
|
152
|
+
`evidence`.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# cycle/stage
|
|
2
|
+
|
|
3
|
+
**Level:** error · **Reads:** the paper's `PIPELINE-STATUS.md` frontmatter (`stages`) and its
|
|
4
|
+
`paperlint.json` (`cycles`) · **Reported on:** the frontmatter of `PIPELINE-STATUS.md`
|
|
5
|
+
|
|
6
|
+
## What it catches
|
|
7
|
+
|
|
8
|
+
With `cycles` declared in the paper's `paperlint.json`, a `stages` entry in the scorecard's
|
|
9
|
+
frontmatter must say which attempt it belongs to, `cycle: <id>`, and that attempt must exist. Three
|
|
10
|
+
things can be wrong:
|
|
11
|
+
|
|
12
|
+
- a stage names no `cycle`;
|
|
13
|
+
- a stage names a `cycle` no declared cycle has as its `id`;
|
|
14
|
+
- a `submitted` or `camera-ready` stage belongs to a cycle whose `phase` is still `porting` — a PDF
|
|
15
|
+
was sent to the venue while the record says the source is still in the previous venue's template.
|
|
16
|
+
|
|
17
|
+
Without `cycles` (the flat form) the rule is silent and `venue:` stays what it was.
|
|
18
|
+
|
|
19
|
+
## Why
|
|
20
|
+
|
|
21
|
+
A paper's attempts are recorded once, in `paperlint.json`; the scorecard's stages are the evidence
|
|
22
|
+
— which PDF, how many bytes, when. A stage that names its cycle says which attempt the evidence
|
|
23
|
+
belongs to, and the venue's name is then spelled once, in the preset, instead of as a free string
|
|
24
|
+
retyped per stage (`REALM @ EMNLP 2026`, `AISec 2026 @ ACM CCS`, …). A stage whose cycle does not
|
|
25
|
+
exist is evidence of nothing.
|
|
26
|
+
|
|
27
|
+
The `porting` case is the other half of a declaration: `"phase": "porting"` silences the venue's
|
|
28
|
+
format rules because the source is, by declaration, not yet in the venue's template. A PDF sent
|
|
29
|
+
from such a cycle contradicts that declaration — either the port is done and the phase is stale, or
|
|
30
|
+
the stage is wrong.
|
|
31
|
+
|
|
32
|
+
## Examples
|
|
33
|
+
|
|
34
|
+
Failing — `paperlint.json` declares one cycle, `open-2027`, and `PIPELINE-STATUS.md` has
|
|
35
|
+
|
|
36
|
+
```yaml
|
|
37
|
+
---
|
|
38
|
+
stages:
|
|
39
|
+
- stage: submitted
|
|
40
|
+
date: 2026-07-22
|
|
41
|
+
pdf: versions/a.pdf
|
|
42
|
+
bytes: 1
|
|
43
|
+
---
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
papers/my-paper/PIPELINE-STATUS.md
|
|
48
|
+
1:1 error the «submitted» stage (2026-07-22) names no `cycle` — this paper declares `cycles` in
|
|
49
|
+
paperlint.json, so each stage says which attempt it belongs to: `cycle: <id>`, one of
|
|
50
|
+
open-2027 cycle/stage
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Passing:
|
|
54
|
+
|
|
55
|
+
```yaml
|
|
56
|
+
---
|
|
57
|
+
stages:
|
|
58
|
+
- stage: submitted
|
|
59
|
+
cycle: open-2027
|
|
60
|
+
date: 2026-07-22
|
|
61
|
+
pdf: versions/a.pdf
|
|
62
|
+
bytes: 1
|
|
63
|
+
---
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Also passing: a paper without `cycles`, whatever its stages say.
|
|
67
|
+
|
|
68
|
+
## Options / preset fields
|
|
69
|
+
|
|
70
|
+
No rule options.
|
|
71
|
+
|
|
72
|
+
## What it does not check
|
|
73
|
+
|
|
74
|
+
- The PDF, its bytes, or the frozen source — [`paper/stages`](../../rules.md) and `paper/source`
|
|
75
|
+
do.
|
|
76
|
+
- Whether a stage's `date` falls inside its cycle (after `opened`, before the outcome's `date`).
|
|
77
|
+
- Whether a cycle without any `submitted` stage was in fact submitted; that is the portal's record
|
|
78
|
+
(`paperlint submission`), not the scorecard's.
|
|
79
|
+
- The rest of the cycle record — [`cycle/record`](record.md) does.
|
|
80
|
+
|
|
81
|
+
## How to fix
|
|
82
|
+
|
|
83
|
+
Add `cycle: <id>` to each stage, with the id of the cycle the PDF was sent in. For a stage whose
|
|
84
|
+
cycle is still `porting`: if the port is done, remove `"phase": "porting"` from the cycle
|
|
85
|
+
([`cycle/port-done`](port-done.md) says when); if no PDF was sent, remove the stage.
|
|
@@ -17,6 +17,19 @@ How the body is counted depends on the preset:
|
|
|
17
17
|
appendices» excludes an appendix wherever it sits. What follows is not counted. The page the body
|
|
18
18
|
ends on counts as body when body text stands above the heading there: a body that runs half a
|
|
19
19
|
page past the limit is over the limit.
|
|
20
|
+
- **With `"appendix_in_body": true` as well** (MSR: «All submissions must not exceed 10 pages for
|
|
21
|
+
the main text, inclusive of all figures, tables, appendices, etc. Two more pages containing only
|
|
22
|
+
references are permitted.»), the appendix is body wherever it sits, and banal's two counts are
|
|
23
|
+
replaced by the text's. The body is every page up to the references — that page too when body
|
|
24
|
+
text stands above the heading — and, when the appendix starts after the references, every page
|
|
25
|
+
from the appendix's to the last. The pages between hold only references, and they are what
|
|
26
|
+
`ref_pages_max` limits. An appendix before the references ends nothing.
|
|
27
|
+
|
|
28
|
+
Why not banal's count for such a venue, measured on IEEEtran `[10pt,conference]` builds
|
|
29
|
+
(2026-10-05): banal types a page whose first heading reads «Appendix» as bibliography, so ten pages
|
|
30
|
+
of main text whose appendix stands before the references measured body 9, references 3 — a correct
|
|
31
|
+
paper failed on its references; and an appendix after the references was counted as reference
|
|
32
|
+
pages, so eleven pages of main text measured body 10, references 2 — an over-long paper passed.
|
|
20
33
|
|
|
21
34
|
## How it finds where the body ends
|
|
22
35
|
|
|
@@ -83,13 +96,17 @@ When a structural signal and the text disagree, the count is not made and the ru
|
|
|
83
96
|
## Options / preset fields
|
|
84
97
|
|
|
85
98
|
No rule options. It reads, from the preset, `format.kinds.<kind>.body_pages_max`,
|
|
86
|
-
`format.kinds.<kind>.ref_pages_max
|
|
99
|
+
`format.kinds.<kind>.ref_pages_max`, `format.body_ends_at` and `format.appendix_in_body` (which
|
|
100
|
+
the schema accepts only beside `body_ends_at`); from the paper's `paperlint.json`,
|
|
87
101
|
`kind`. A preset with no kinds, or a kind with no limits, has nothing to check.
|
|
88
102
|
|
|
89
103
|
## What it does not check
|
|
90
104
|
|
|
91
105
|
- **An appendix before the references, without hyperref**, is counted as body: nothing structural
|
|
92
106
|
says where it starts. With hyperref it ends the body.
|
|
107
|
+
- **An appendix after the references, without hyperref, under `appendix_in_body`**, is counted
|
|
108
|
+
as reference pages: nothing structural says where it starts. `ref_pages_max` still limits the
|
|
109
|
+
pages after the references, so a long one is reported, as references.
|
|
93
110
|
- **An appendix whose heading does not read «Appendix…»** (the standard classes print `A` and the
|
|
94
111
|
title) is reported as `unclear` when hyperref anchors it.
|
|
95
112
|
- **A bibliography that is not numbered and not linked by hyperref** has no confirmed heading and no
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
## What it catches
|
|
6
6
|
|
|
7
7
|
The paper is not set in the class its venue requires. The rule compares the `\documentclass` line
|
|
8
|
-
of `paper.tex` with the preset's `template`: the class must be the same,
|
|
9
|
-
template names must be there
|
|
8
|
+
of `paper.tex` with the preset's `template`: the class must be the same, every option the
|
|
9
|
+
template names must be there, and no option the preset forbids (`template_forbids`) may be. The
|
|
10
|
+
paper may add other options of its own.
|
|
10
11
|
|
|
11
12
|
A source that keeps one file for several venues and picks the class behind a TeX switch has several
|
|
12
13
|
`\documentclass` lines. The switch is not evaluated: each line is a candidate, the paper passes when
|
|
@@ -49,6 +50,16 @@ The first line reports:
|
|
|
49
50
|
|
|
50
51
|
> the class option `compsoc` is missing: aidc requires `\documentclass[conference,compsoc]{IEEEtran}`
|
|
51
52
|
|
|
53
|
+
Under a preset that forbids options — MSR: «LaTeX users must use \documentclass[10pt,conference]{IEEEtran}
|
|
54
|
+
without including the compsoc or compsocconf options» (https://2027.msrconf.org/track/msr-2027-technical-papers):
|
|
55
|
+
|
|
56
|
+
```latex
|
|
57
|
+
\documentclass[10pt,conference,compsoc]{IEEEtran} % fails: compsoc is forbidden
|
|
58
|
+
\documentclass[10pt,conference]{IEEEtran} % passes
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
> the class option `compsoc` is forbidden: MSR requires `\documentclass[10pt,conference]{IEEEtran}` without `compsoc`, `compsocconf`
|
|
62
|
+
|
|
52
63
|
## Preset field
|
|
53
64
|
|
|
54
65
|
No rule options. It reads `template`, merged along `extends` (a child's replaces its parent's):
|
|
@@ -60,15 +71,24 @@ No rule options. It reads `template`, merged along `extends` (a child's replaces
|
|
|
60
71
|
A whole `\documentclass[…]{…}` line, or a bare class name (`"article"`), which requires the class
|
|
61
72
|
and no option. A value that is neither is reported as a finding that names the preset file.
|
|
62
73
|
|
|
74
|
+
```jsonc
|
|
75
|
+
"template_forbids": ["compsoc", "compsocconf"]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Class options the venue's call forbids, each reported where the class line carries it. Read with the
|
|
79
|
+
`template` of the same file, and only with it: the schema refuses it alone, and a child preset's
|
|
80
|
+
`template` replaces both.
|
|
81
|
+
|
|
63
82
|
## What it does not check
|
|
64
83
|
|
|
65
|
-
-
|
|
66
|
-
|
|
84
|
+
- An option a venue forbids that its preset does not list in `template_forbids` (`review` in a
|
|
85
|
+
camera-ready).
|
|
67
86
|
- The version of the class file (#94).
|
|
68
87
|
- A `\documentclass` inside a comment: the rule reads the parse tree, so it does not count.
|
|
69
88
|
- Which branch of a TeX switch builds: with several class lines, one matching candidate is enough.
|
|
70
89
|
|
|
71
90
|
## How to fix
|
|
72
91
|
|
|
73
|
-
Set the `\documentclass` line to the one the message quotes, keeping your own extra options
|
|
92
|
+
Set the `\documentclass` line to the one the message quotes, keeping your own extra options
|
|
93
|
+
and dropping a forbidden one. A new
|
|
74
94
|
paper made with `paperlint new <name> --venue <preset>` already has it.
|
|
@@ -57,10 +57,23 @@ No rule options. It reads every shipped preset's `name` and `aliases`, collected
|
|
|
57
57
|
A name the paper's own chain also declares (two workshops of one parent conference) is never
|
|
58
58
|
reported.
|
|
59
59
|
|
|
60
|
+
```jsonc
|
|
61
|
+
"mentions": "with-year"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
A venue whose name is also a common acronym counts only with a year beside the name: under
|
|
65
|
+
`paperlint:msr`, «First written for MSR 2027.», «MSR'27» and «MSR~2027» are reported, and
|
|
66
|
+
«Manual Speech Recognition (MSR)», «Microsoft Research (MSR)» or «MSR, in 2027» are not. Any
|
|
67
|
+
whole-word use counted for MSR at first, and an accepted ACSAC paper that defines «MSR» for itself
|
|
68
|
+
drew six warnings. Per preset, not hard-coded: a name that only a venue uses (`AgenticDev`) stays
|
|
69
|
+
reported with or without a year.
|
|
70
|
+
|
|
60
71
|
## What it does not check
|
|
61
72
|
|
|
62
73
|
- A venue no shipped preset describes.
|
|
63
74
|
- A name split by markup (`\textbf{AI}Sec`).
|
|
75
|
+
- A `with-year` venue named without a year («submitted to MSR»): the rule cannot tell it from the
|
|
76
|
+
acronym.
|
|
64
77
|
|
|
65
78
|
## How to fix
|
|
66
79
|
|