paperlint 6.1.0 → 6.2.1
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/dist/adapters/mp4box/index.d.ts +4 -0
- package/dist/adapters/mp4box/index.d.ts.map +1 -0
- package/dist/adapters/mp4box/index.js +46 -0
- package/dist/adapters/mp4box/index.js.map +1 -0
- package/dist/adapters/node/index.d.ts +1 -0
- package/dist/adapters/node/index.d.ts.map +1 -1
- package/dist/adapters/node/index.js +1 -0
- package/dist/adapters/node/index.js.map +1 -1
- package/dist/adapters/node/list-dir.io.d.ts +3 -0
- package/dist/adapters/node/list-dir.io.d.ts.map +1 -0
- package/dist/adapters/node/list-dir.io.js +15 -0
- package/dist/adapters/node/list-dir.io.js.map +1 -0
- package/dist/adapters/png/index.d.ts +11 -0
- package/dist/adapters/png/index.d.ts.map +1 -0
- package/dist/adapters/png/index.js +21 -0
- package/dist/adapters/png/index.js.map +1 -0
- package/dist/adapters/srt/index.d.ts +4 -0
- package/dist/adapters/srt/index.d.ts.map +1 -0
- package/dist/adapters/srt/index.js +12 -0
- package/dist/adapters/srt/index.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +25 -4
- package/dist/cli.js.map +1 -1
- package/dist/domain/talk.d.ts +121 -0
- package/dist/domain/talk.d.ts.map +1 -0
- package/dist/domain/talk.js +121 -0
- package/dist/domain/talk.js.map +1 -0
- package/dist/lib/paper-config.d.ts +2 -0
- package/dist/lib/paper-config.d.ts.map +1 -1
- package/dist/lib/paper-config.js +3 -0
- package/dist/lib/paper-config.js.map +1 -1
- package/dist/paper-settings.d.ts +3 -0
- package/dist/paper-settings.d.ts.map +1 -1
- package/dist/paper-settings.js +15 -9
- package/dist/paper-settings.js.map +1 -1
- package/dist/ports/talk-media.d.ts +18 -0
- package/dist/ports/talk-media.d.ts.map +1 -0
- package/dist/ports/talk-media.js +2 -0
- package/dist/ports/talk-media.js.map +1 -0
- package/dist/presets.d.ts +3 -0
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +6 -10
- package/dist/presets.js.map +1 -1
- package/dist/talk-rules.d.ts +65 -0
- package/dist/talk-rules.d.ts.map +1 -0
- package/dist/talk-rules.js +451 -0
- package/dist/talk-rules.js.map +1 -0
- package/dist/tex-requirements.d.ts +3 -0
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +2 -0
- package/dist/tex-requirements.js.map +1 -1
- package/docs/configuration.md +2 -1
- package/docs/rules/talk/captions-cover.md +47 -0
- package/docs/rules/talk/duration-floor.md +38 -0
- package/docs/rules/talk/duration.md +43 -0
- package/docs/rules/talk/one-slide-size.md +38 -0
- package/docs/rules/talk/profile.md +64 -0
- package/docs/rules/talk/required-files.md +39 -0
- package/docs/rules/talk/undeclared.md +35 -0
- package/docs/rules/talk/video-format.md +43 -0
- package/docs/rules.md +11 -0
- package/docs/talk.md +94 -0
- package/fixtures/talk/audio-only.mp4 +0 -0
- package/fixtures/talk/one-slide.png +0 -0
- package/fixtures/talk/short-audio.mp4 +0 -0
- package/fixtures/talk/talk.mov +0 -0
- package/fixtures/talk/talk.mp4 +0 -0
- package/fixtures/talk/talk.srt +7 -0
- package/lib/paper-config.harness.mjs +5 -2
- package/lib/paper-config.ts +4 -0
- package/package.json +3 -1
- package/presets/agenticdev.jsonc +30 -0
- package/presets/venue-profile.schema.json +132 -0
- package/skills/submit-paper/references/venues/agenticdev.md +36 -9
- package/src/adapters/mp4box/index.test.ts +63 -0
- package/src/adapters/mp4box/index.ts +50 -0
- package/src/adapters/node/index.ts +1 -0
- package/src/adapters/node/list-dir.io.ts +15 -0
- package/src/adapters/node/list-dir.test.ts +30 -0
- package/src/adapters/png/index.test.ts +43 -0
- package/src/adapters/png/index.ts +29 -0
- package/src/adapters/srt/index.test.ts +24 -0
- package/src/adapters/srt/index.ts +13 -0
- package/src/cli.test.ts +12 -0
- package/src/cli.ts +31 -3
- package/src/domain/talk.test.ts +125 -0
- package/src/domain/talk.ts +274 -0
- package/src/new-paper.test.ts +1 -0
- package/src/paper-settings.test.ts +18 -1
- package/src/paper-settings.ts +21 -11
- package/src/ports/talk-media.ts +19 -0
- package/src/presets.ts +9 -10
- package/src/rule-cases.test.ts +110 -3
- package/src/talk-rules.test.ts +459 -0
- package/src/talk-rules.ts +672 -0
- package/src/tex-requirements.ts +9 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# talk/duration-floor
|
|
2
|
+
|
|
3
|
+
**Level:** warn · **Reads:** `paper.tex` → `paperlint.json` (`talk`, `kind`), the preset's
|
|
4
|
+
`talk.kinds`, and the video's headers
|
|
5
|
+
|
|
6
|
+
## What it catches
|
|
7
|
+
|
|
8
|
+
The video runs shorter than the slot's `talk_s_min` for the paper's kind (`tooShort`).
|
|
9
|
+
|
|
10
|
+
## Why
|
|
11
|
+
|
|
12
|
+
A venue that states a window ("6–7 min talk") states a floor, and a ceiling-only check pushes every
|
|
13
|
+
talk toward shorter. Whether a short talk is a problem is the author's call — some sessions welcome
|
|
14
|
+
extra questions — so this is a warning, and a separate rule: turn it off for a paper, or raise it to
|
|
15
|
+
an error, without touching the ceiling.
|
|
16
|
+
|
|
17
|
+
## Examples
|
|
18
|
+
|
|
19
|
+
Warning — the AgenticDev talk measured 320.84 s against the organizers' 6–7 minutes:
|
|
20
|
+
|
|
21
|
+
> papers/agenticdev-2026/talk/agenticdev-2026-talk.mp4 runs 5:20.8 (320.84 s), under the 6:00.0
|
|
22
|
+
> (360 s) talk agenticdev asks for a short paper — decide whether that is fine, or set this rule to
|
|
23
|
+
> off for the paper
|
|
24
|
+
|
|
25
|
+
Passing: any length from `talk_s_min` up.
|
|
26
|
+
|
|
27
|
+
## Options / preset fields
|
|
28
|
+
|
|
29
|
+
No rule options. Preset: `talk.kinds.<kind>.talk_s_min`; without it the rule is silent.
|
|
30
|
+
|
|
31
|
+
## What it does not check
|
|
32
|
+
|
|
33
|
+
The ceiling (`talk/duration`).
|
|
34
|
+
|
|
35
|
+
## How to fix
|
|
36
|
+
|
|
37
|
+
Lengthen the talk, or decide it is fine and set `"talk/duration-floor": "off"` in the paper's
|
|
38
|
+
`rules`.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# talk/duration
|
|
2
|
+
|
|
3
|
+
**Level:** error · **Reads:** `paper.tex` → `paperlint.json` (`talk`, `kind`), the preset's
|
|
4
|
+
`talk.kinds`, and the video's headers
|
|
5
|
+
|
|
6
|
+
## What it catches
|
|
7
|
+
|
|
8
|
+
- The video runs longer than the slot's `talk_s_max` for the paper's kind (`tooLong`).
|
|
9
|
+
- The video's first audio track is more than `trackToleranceS` (default 1 s) shorter or longer than
|
|
10
|
+
the video itself (`trackMismatch`) — a cut or a re-encode dropped or padded sound.
|
|
11
|
+
|
|
12
|
+
The length is the container's own (the movie header), read with MP4Box.js; the audio track's length
|
|
13
|
+
is its own media duration. On the AgenticDev talk both read 320.84 s and 320.86 s, the same numbers
|
|
14
|
+
ffmpeg prints.
|
|
15
|
+
|
|
16
|
+
## Why
|
|
17
|
+
|
|
18
|
+
A remote talk is played in a fixed slot; a video over it is cut by the session chair, usually at the
|
|
19
|
+
conclusion. Measuring the file — not a word count of the script — is the only number the venue
|
|
20
|
+
limits: a script of 948 words, timed at the script's own assumed 125 words per minute, predicted
|
|
21
|
+
7.6 minutes; the recording ran 5.35.
|
|
22
|
+
|
|
23
|
+
## Examples
|
|
24
|
+
|
|
25
|
+
Failing — an AgenticDev short paper (`talk_s_max` 420):
|
|
26
|
+
|
|
27
|
+
> papers/p/talk/talk.mp4 runs 7:12.0 (432 s), over the 7:00.0 (420 s) talk agenticdev sets for this
|
|
28
|
+
> kind of paper — cut it
|
|
29
|
+
|
|
30
|
+
Passing: a 5:20.8 video with its audio track ending within a second of it.
|
|
31
|
+
|
|
32
|
+
## Options / preset fields
|
|
33
|
+
|
|
34
|
+
`{ "trackToleranceS": 1 }`. Preset: `talk.kinds.<kind>.talk_s_max`.
|
|
35
|
+
|
|
36
|
+
## What it does not check
|
|
37
|
+
|
|
38
|
+
The floor — that is `talk/duration-floor`, separate so its severity can be set on its own. A video
|
|
39
|
+
that does not parse (see `talk/video-format`) or is missing (see `talk/required-files`).
|
|
40
|
+
|
|
41
|
+
## How to fix
|
|
42
|
+
|
|
43
|
+
Cut the talk; re-export with the audio and the picture the same length.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# talk/one-slide-size
|
|
2
|
+
|
|
3
|
+
**Level:** error · **Reads:** `paper.tex` → `paperlint.json` (`talk.one_slide`), the preset's
|
|
4
|
+
`talk.one_slide`, and the one-slide image's PNG header
|
|
5
|
+
|
|
6
|
+
## What it catches
|
|
7
|
+
|
|
8
|
+
- The one-slide image is not the declared size (`size`).
|
|
9
|
+
- The file is not a PNG (`notPng`).
|
|
10
|
+
|
|
11
|
+
The size is the paper's `talk.one_slide` when it states one, else the preset's. When neither states
|
|
12
|
+
a size, the rule is silent: a size nobody asked for is not a requirement. The width and height are
|
|
13
|
+
read from the PNG's `IHDR` header; the pixels are never decoded.
|
|
14
|
+
|
|
15
|
+
## Why
|
|
16
|
+
|
|
17
|
+
A one-slide summary is shown on a fixed canvas — a poster wall, a session screen, a page of the
|
|
18
|
+
program. An image of another size is letterboxed or cropped.
|
|
19
|
+
|
|
20
|
+
## Examples
|
|
21
|
+
|
|
22
|
+
Failing — the paper declares `"one_slide": { "width_px": 1920, "height_px": 1080 }`:
|
|
23
|
+
|
|
24
|
+
> papers/p/talk/one-slide.png is 1280×720 px, and the one-slide must be 1920×1080 px
|
|
25
|
+
|
|
26
|
+
Passing: a 1920×1080 PNG.
|
|
27
|
+
|
|
28
|
+
## Options / preset fields
|
|
29
|
+
|
|
30
|
+
No rule options. Preset: `talk.one_slide`. Paper: `talk.one_slide`, which wins.
|
|
31
|
+
|
|
32
|
+
## What it does not check
|
|
33
|
+
|
|
34
|
+
Its content; formats other than PNG; a missing file (`talk/required-files`).
|
|
35
|
+
|
|
36
|
+
## How to fix
|
|
37
|
+
|
|
38
|
+
Render the image at the declared size, or correct the declared size.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# talk/profile
|
|
2
|
+
|
|
3
|
+
**Level:** error · **Reads:** `paper.tex` → `paperlint.json` (`talk`, `kind`, `extends`) and the
|
|
4
|
+
venue preset's `talk` block
|
|
5
|
+
|
|
6
|
+
## What it catches
|
|
7
|
+
|
|
8
|
+
A talk the other `talk/*` rules cannot judge, one reason at a time:
|
|
9
|
+
|
|
10
|
+
- the paper declares `talk` and names no venue preset (`noPreset`);
|
|
11
|
+
- the preset has no `talk` block (`noTalkBlock`);
|
|
12
|
+
- the paper's `talk.mode` is not one of the preset's `modes` (`mode`);
|
|
13
|
+
- the paper names no `kind` (`kindMissing`), or the preset sets no talk slot for that kind
|
|
14
|
+
(`kindNoSlot`).
|
|
15
|
+
|
|
16
|
+
## Why
|
|
17
|
+
|
|
18
|
+
Every other talk rule needs one of these to say anything. Without a slot the length is not checked;
|
|
19
|
+
without a talk block nothing is. A rule that went quiet for these reasons would look exactly like a
|
|
20
|
+
rule that checked and passed, so this one says which piece is missing.
|
|
21
|
+
|
|
22
|
+
## Examples
|
|
23
|
+
|
|
24
|
+
Failing — AgenticDev allows only remote video:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"extends": "paperlint:agenticdev",
|
|
29
|
+
"kind": "short",
|
|
30
|
+
"talk": { "mode": "in-person" }
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
> `agenticdev` allows the talk modes remote-video; this paper declares `in-person`
|
|
35
|
+
|
|
36
|
+
Failing — the email gave the short-paper slot only:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"extends": "paperlint:agenticdev",
|
|
41
|
+
"kind": "full",
|
|
42
|
+
"talk": { "mode": "remote-video" }
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
> `agenticdev` sets no talk slot for a `full` paper, so the video's length is not checked; its talk
|
|
47
|
+
> kinds: short
|
|
48
|
+
|
|
49
|
+
Passing: `"kind": "short", "talk": { "mode": "remote-video" }`.
|
|
50
|
+
|
|
51
|
+
## Options / preset fields
|
|
52
|
+
|
|
53
|
+
No rule options. Preset: `talk.modes` (a list; empty or absent allows any mode) and `talk.kinds`.
|
|
54
|
+
|
|
55
|
+
## What it does not check
|
|
56
|
+
|
|
57
|
+
A preset or `paperlint.json` that does not resolve or parse at all: `pdf/profile` reports that, and
|
|
58
|
+
every talk rule is silent.
|
|
59
|
+
|
|
60
|
+
## How to fix
|
|
61
|
+
|
|
62
|
+
Declare the mode the venue allows; set `kind`; when the venue's talk numbers are not in the shipped
|
|
63
|
+
preset, put them in your own preset (`"extends": "./my-venue.jsonc"`) with a `talk` block that
|
|
64
|
+
quotes the organizers.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# talk/required-files
|
|
2
|
+
|
|
3
|
+
**Level:** error · **Reads:** `paper.tex` → `paperlint.json` (`talk`), the preset's
|
|
4
|
+
`talk.artifacts` and `talk.video.captions`, and the talk folder
|
|
5
|
+
|
|
6
|
+
## What it catches
|
|
7
|
+
|
|
8
|
+
A file the venue requires for the paper's talk mode that is not in the talk folder — one finding per
|
|
9
|
+
missing file (`missing`). The required set is `talk.artifacts[<mode>].required`, plus `captions`
|
|
10
|
+
when `talk.video.captions` is `"required"` and the mode owes a video.
|
|
11
|
+
|
|
12
|
+
## Why
|
|
13
|
+
|
|
14
|
+
Submitting a talk is usually a form with several uploads, the evening before. A missing one-slide
|
|
15
|
+
image is noticed when the form asks for it; this rule notices it when the paper is linted.
|
|
16
|
+
|
|
17
|
+
## Examples
|
|
18
|
+
|
|
19
|
+
Failing — AgenticDev requires a video and a one-slide for a remote talk, and `talk/one-slide.png` is
|
|
20
|
+
not there:
|
|
21
|
+
|
|
22
|
+
> agenticdev asks for the one-slide of a remote-video talk, and papers/p/talk/one-slide.png is not
|
|
23
|
+
> there — add it, or name the file in `talk.files`
|
|
24
|
+
|
|
25
|
+
Passing: `talk/talk.mp4` and `talk/one-slide.png` both present (or the names given in `talk.files`).
|
|
26
|
+
|
|
27
|
+
## Options / preset fields
|
|
28
|
+
|
|
29
|
+
No rule options. Preset: `talk.artifacts`, `talk.video.captions`. Paper: `talk.dir`, `talk.files`.
|
|
30
|
+
|
|
31
|
+
## What it does not check
|
|
32
|
+
|
|
33
|
+
Whether a present file is any good — the other talk rules read the contents. Optional artifacts.
|
|
34
|
+
A paper that declares no `talk` (see `talk/undeclared`).
|
|
35
|
+
|
|
36
|
+
## How to fix
|
|
37
|
+
|
|
38
|
+
Put the file in the talk folder under its default name ([`docs/talk.md`](../../talk.md)), or name
|
|
39
|
+
it in the paper's `talk.files`.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# talk/undeclared
|
|
2
|
+
|
|
3
|
+
**Level:** warn · **Reads:** `paper.tex` → `paperlint.json` and the paper's `talk/` folder
|
|
4
|
+
|
|
5
|
+
## What it catches
|
|
6
|
+
|
|
7
|
+
An `.mp4` file in the paper's `talk/` folder while the paper declares no `talk` (`undeclared`).
|
|
8
|
+
|
|
9
|
+
## Why
|
|
10
|
+
|
|
11
|
+
Every other talk rule is silent until the paper declares `talk`. A video made and never declared
|
|
12
|
+
would be sent unchecked, and the silence would read as "checked and fine". This is the other
|
|
13
|
+
direction of `talk/required-files`: a file with no declaration, rather than a declaration with no
|
|
14
|
+
file.
|
|
15
|
+
|
|
16
|
+
## Examples
|
|
17
|
+
|
|
18
|
+
Failing: `papers/p/talk/talk.mp4` exists, `papers/p/paperlint.json` has no `talk`:
|
|
19
|
+
|
|
20
|
+
> papers/p/talk/talk.mp4 is a talk video, and this paper declares no `talk`, so its length and
|
|
21
|
+
> format are not checked — declare it in paperlint.json: "talk": { "mode": "remote-video" }
|
|
22
|
+
|
|
23
|
+
Passing: the same file with `"talk": { "mode": "remote-video" }` declared.
|
|
24
|
+
|
|
25
|
+
## Options / preset fields
|
|
26
|
+
|
|
27
|
+
None.
|
|
28
|
+
|
|
29
|
+
## What it does not check
|
|
30
|
+
|
|
31
|
+
Only the default folder `talk/` and only `.mp4` files; a video elsewhere is not looked for.
|
|
32
|
+
|
|
33
|
+
## How to fix
|
|
34
|
+
|
|
35
|
+
Declare the talk in the paper's `paperlint.json`, or move the file out of `talk/`.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# talk/video-format
|
|
2
|
+
|
|
3
|
+
**Level:** error · **Reads:** `paper.tex` → `paperlint.json` (`talk`), the preset's `talk.video`,
|
|
4
|
+
and the video's headers and size
|
|
5
|
+
|
|
6
|
+
## What it catches
|
|
7
|
+
|
|
8
|
+
- The file is not ISO media at all — no `ftyp` box first, or no `moov` box (`unreadable`). Reported
|
|
9
|
+
whatever the preset says, because no other talk rule can read such a file.
|
|
10
|
+
- `container: "mp4"` and the file is QuickTime inside (major brand `qt `), whatever its extension
|
|
11
|
+
(`container`).
|
|
12
|
+
- `min_height_px` and the video track is shorter (`height`), or there is no video track
|
|
13
|
+
(`noVideoTrack`).
|
|
14
|
+
- `max_bytes` and the file is larger (`bytes`).
|
|
15
|
+
|
|
16
|
+
## Why
|
|
17
|
+
|
|
18
|
+
Upload forms reject by format and size, and "HD" is a stated requirement. A `.mov` renamed to
|
|
19
|
+
`.mp4` plays on the author's machine and fails in the form or in the session's player.
|
|
20
|
+
|
|
21
|
+
## Examples
|
|
22
|
+
|
|
23
|
+
Failing — a 1280×720 video against a venue asking for 1080 px:
|
|
24
|
+
|
|
25
|
+
> papers/p/talk/talk.mp4 is 720 px high, under the 1080 px myvenue asks for — export at a higher
|
|
26
|
+
> resolution
|
|
27
|
+
|
|
28
|
+
Passing: the AgenticDev talk, 1920×1080 H.264 in an `isom` MP4, against `min_height_px` 720.
|
|
29
|
+
|
|
30
|
+
## Options / preset fields
|
|
31
|
+
|
|
32
|
+
No rule options. Preset: `talk.video.container`, `talk.video.min_height_px`, `talk.video.max_bytes`.
|
|
33
|
+
A field the preset does not set is not checked. Without a `talk` block the rule is silent.
|
|
34
|
+
|
|
35
|
+
## What it does not check
|
|
36
|
+
|
|
37
|
+
Codec, frame rate, bitrate, audio format, rotation metadata (the height is the track's, before any
|
|
38
|
+
rotation), loudness.
|
|
39
|
+
|
|
40
|
+
## How to fix
|
|
41
|
+
|
|
42
|
+
Export as MP4 (H.264 and AAC are what every player takes) at the venue's resolution; re-encode at a
|
|
43
|
+
lower bitrate to fit the size limit.
|
package/docs/rules.md
CHANGED
|
@@ -36,6 +36,14 @@ reads and when it fails. Errors fail `paperlint lint`; warnings print and do not
|
|
|
36
36
|
| [`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) |
|
|
37
37
|
| [`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) |
|
|
38
38
|
| `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 |
|
|
39
|
+
| [`talk/profile`](rules/talk/profile.md) | error | `paper.tex` → `paperlint.json` | the paper declares `talk` and names no preset, the preset has no `talk` block, the mode is not one the venue allows, or the venue sets no talk slot for the paper's kind — [page](rules/talk/profile.md) |
|
|
40
|
+
| [`talk/required-files`](rules/talk/required-files.md) | error | `paper.tex` → `paperlint.json`, the talk folder | a file the venue requires for the paper's talk mode (video, one-slide, captions…) is not in the talk folder — [page](rules/talk/required-files.md) |
|
|
41
|
+
| [`talk/undeclared`](rules/talk/undeclared.md) | warn | `paper.tex` → the paper's `talk/` folder | an `.mp4` sits in `talk/` and the paper declares no `talk`, so nothing checks it — [page](rules/talk/undeclared.md) |
|
|
42
|
+
| [`talk/duration`](rules/talk/duration.md) | error | `paper.tex` → the talk video | the video runs longer than the slot's `talk_s_max`, or its audio track is more than 1 s off the video's length — [page](rules/talk/duration.md) |
|
|
43
|
+
| [`talk/duration-floor`](rules/talk/duration-floor.md) | warn | `paper.tex` → the talk video | the video runs shorter than the slot's `talk_s_min` — [page](rules/talk/duration-floor.md) |
|
|
44
|
+
| [`talk/video-format`](rules/talk/video-format.md) | error | `paper.tex` → the talk video | the video is not ISO media, is QuickTime where the venue asks for MP4, is under `min_height_px`, or over `max_bytes` — [page](rules/talk/video-format.md) |
|
|
45
|
+
| [`talk/one-slide-size`](rules/talk/one-slide-size.md) | error | `paper.tex` → the one-slide PNG | the one-slide image is not the size the paper or the preset states, or not a PNG — [page](rules/talk/one-slide-size.md) |
|
|
46
|
+
| [`talk/captions-cover`](rules/talk/captions-cover.md) | warn | `paper.tex` → the captions and the video | the captions start late, end early or leave a long gap (uncalibrated thresholds) — [page](rules/talk/captions-cover.md) |
|
|
39
47
|
|
|
40
48
|
**Body files** are the files `paper.tex` includes from its document body (`\input`, `\include`,
|
|
41
49
|
`\subfile`, nested ones too), found in the paper's directory — not a preamble include, not a file
|
|
@@ -48,6 +56,9 @@ that names no file is printed as a note: lint did not read it, and the build wil
|
|
|
48
56
|
LLMs are used», which no rule can know; an author who used none says so in the section
|
|
49
57
|
([page](rules/tex/required-section.md)).
|
|
50
58
|
|
|
59
|
+
The `talk/*` rules judge a talk's finished files and are silent for a paper that declares no
|
|
60
|
+
`talk` (but `talk/undeclared`): [`talk.md`](talk.md).
|
|
61
|
+
|
|
51
62
|
Optional rules — off unless you turn them on in the `rules` setting, because only some venues need
|
|
52
63
|
them — are on their own page: [`optional-rules.md`](optional-rules.md). Today there is one,
|
|
53
64
|
`pdf/last-page-balance`.
|
package/docs/talk.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# The talk
|
|
2
|
+
|
|
3
|
+
A venue that accepts a paper usually asks for a talk too: a video for a remote presenter, a
|
|
4
|
+
one-slide image, sometimes captions. paperlint does not make the talk. It **judges the finished
|
|
5
|
+
files** against what the venue asks, so the same check works whether the video came from Keynote,
|
|
6
|
+
OBS, Zoom, a script, or by hand.
|
|
7
|
+
|
|
8
|
+
## Declaring a talk
|
|
9
|
+
|
|
10
|
+
The paper's own `paperlint.json` says how it is presented:
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"extends": "paperlint:agenticdev",
|
|
15
|
+
"kind": "short",
|
|
16
|
+
"talk": { "mode": "remote-video" }
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
| key | meaning | default |
|
|
21
|
+
| ----------- | -------------------------------------------------------------------------------------------- | --------- |
|
|
22
|
+
| `mode` | `remote-video`, `remote-live` or `in-person` — must be one the venue allows | required |
|
|
23
|
+
| `dir` | the talk folder, relative to the paper | `talk` |
|
|
24
|
+
| `files` | the file name of each artifact in that folder, when it is not the default (no slashes) | see below |
|
|
25
|
+
| `one_slide` | `{ "width_px": 1920, "height_px": 1080 }` — the one-slide's size, when the venue states none | none |
|
|
26
|
+
|
|
27
|
+
`talk` is a paper's setting: the root `paperlint.json` refuses it.
|
|
28
|
+
|
|
29
|
+
**File names.** Each artifact is looked for under one name in the talk folder:
|
|
30
|
+
|
|
31
|
+
| artifact | default name |
|
|
32
|
+
| ------------ | --------------- |
|
|
33
|
+
| `video` | `talk.mp4` |
|
|
34
|
+
| `one-slide` | `one-slide.png` |
|
|
35
|
+
| `captions` | `talk.srt` |
|
|
36
|
+
| `slides-pdf` | `slides.pdf` |
|
|
37
|
+
| `poster` | `poster.pdf` |
|
|
38
|
+
|
|
39
|
+
A video named after the paper is declared once:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
"talk": {
|
|
43
|
+
"mode": "remote-video",
|
|
44
|
+
"files": { "video": "agenticdev-2026-talk.mp4", "captions": "agenticdev-2026-talk.srt" }
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## What the venue says — the preset's `talk` block
|
|
49
|
+
|
|
50
|
+
```jsonc
|
|
51
|
+
"talk": {
|
|
52
|
+
"modes": ["remote-video"],
|
|
53
|
+
"artifacts": { "remote-video": { "required": ["video", "one-slide"], "optional": ["poster"] } },
|
|
54
|
+
"kinds": { "short": { "slot_s": 600, "talk_s_min": 360, "talk_s_max": 420, "qa_s": 210 } },
|
|
55
|
+
"video": { "container": "mp4", "min_height_px": 720, "max_bytes": 500000000, "captions": "optional" },
|
|
56
|
+
"one_slide": { "width_px": 1920, "height_px": 1080 }
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Every key is optional. Durations are seconds. `kinds` uses the names of `format.kinds`, and the
|
|
61
|
+
paper's `kind` picks one. `video.captions: "required"` adds the captions to the files the video's
|
|
62
|
+
mode owes. A child preset's `talk` block replaces its parent's whole. The shipped `agenticdev`
|
|
63
|
+
preset carries the organizers' numbers, each quoted beside it; it states no one-slide size, because
|
|
64
|
+
the venue did not.
|
|
65
|
+
|
|
66
|
+
## What is checked
|
|
67
|
+
|
|
68
|
+
All rules run on the paper's `paper.tex` and are silent for a paper that declares no `talk`, except
|
|
69
|
+
`talk/undeclared`.
|
|
70
|
+
|
|
71
|
+
| rule | level | fails when |
|
|
72
|
+
| ----------------------------------------------------- | ----- | ----------------------------------------------------------------------------------------------- |
|
|
73
|
+
| [`talk/profile`](rules/talk/profile.md) | error | no preset, a preset with no `talk` block, a mode the venue does not allow, no slot for the kind |
|
|
74
|
+
| [`talk/required-files`](rules/talk/required-files.md) | error | a file the venue requires for the mode is not in the talk folder |
|
|
75
|
+
| [`talk/undeclared`](rules/talk/undeclared.md) | warn | an `.mp4` sits in `talk/` and the paper declares no `talk` |
|
|
76
|
+
| [`talk/duration`](rules/talk/duration.md) | error | the video runs longer than `talk_s_max`, or its audio track is more than 1 s off its length |
|
|
77
|
+
| [`talk/duration-floor`](rules/talk/duration-floor.md) | warn | the video runs shorter than `talk_s_min` |
|
|
78
|
+
| [`talk/video-format`](rules/talk/video-format.md) | error | not an MP4 (QuickTime inside, or not ISO media at all), under `min_height_px`, over `max_bytes` |
|
|
79
|
+
| [`talk/one-slide-size`](rules/talk/one-slide-size.md) | error | the one-slide PNG is not the declared size |
|
|
80
|
+
| [`talk/captions-cover`](rules/talk/captions-cover.md) | warn | the captions start late, end early, or leave a long gap (thresholds uncalibrated) |
|
|
81
|
+
|
|
82
|
+
The video is read with [MP4Box.js](https://github.com/gpac/mp4box.js) — the container's own
|
|
83
|
+
headers, in JavaScript, in milliseconds. There is no ffmpeg anywhere in paperlint and no build step
|
|
84
|
+
for the talk.
|
|
85
|
+
|
|
86
|
+
## What is not checked
|
|
87
|
+
|
|
88
|
+
- **What the talk says.** Whether the video matches the paper, its tone, its claims, the QR code on
|
|
89
|
+
a slide — those need a reader, not a count.
|
|
90
|
+
- **How it was made.** A script, slides, a voice recording, cut points: paperlint never sees them,
|
|
91
|
+
and nothing here requires them to exist.
|
|
92
|
+
- **Deadlines and upload channels.** A deadline is a clock condition; it belongs in a calendar.
|
|
93
|
+
- **Whether the file was sent.** Freezing what was sent is not part of the talk rules yet.
|
|
94
|
+
- **Codecs, frame rate, bitrate, loudness.** Only the container, the height and the byte size.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -200,11 +200,14 @@ const loaded = await Promise.all(
|
|
|
200
200
|
// refuses any key not in SETTINGS_KEYS. So the list must hold every key some reader reads — or a
|
|
201
201
|
// consumer's valid key is refused — and nothing no one reads. The readers are FOUND, by the AST:
|
|
202
202
|
// every `settingsOf(root)?.<key>` (and the older `[CONFIG_KEY]?.<key>` shape), plus every field of
|
|
203
|
-
// `PaperlintConfig`, the
|
|
203
|
+
// `PaperlintConfig` and `PaperSettings`, the typed views of the root's and a paper's object.
|
|
204
204
|
{
|
|
205
205
|
const { SETTINGS_KEYS } = await import(join(HERE, "paper-config.ts"));
|
|
206
206
|
const ts = (await import("typescript")).default;
|
|
207
207
|
const readKeys = new Set();
|
|
208
|
+
// The two typed views of the object: the root file's (`PaperlintConfig`, cli.ts) and a paper's
|
|
209
|
+
// (`PaperSettings`, src/paper-settings.ts). A key only a paper may hold (`talk`) is read there.
|
|
210
|
+
const TYPED_VIEWS = ["PaperlintConfig", "PaperSettings"];
|
|
208
211
|
const isConfigKey = (e) =>
|
|
209
212
|
(ts.isIdentifier(e) && e.text === "CONFIG_KEY") ||
|
|
210
213
|
(ts.isStringLiteral(e) && e.text === "paperlint");
|
|
@@ -217,7 +220,7 @@ const loaded = await Promise.all(
|
|
|
217
220
|
const visit = (node) => {
|
|
218
221
|
if (ts.isPropertyAccessExpression(node) && isSettings(node.expression))
|
|
219
222
|
readKeys.add(node.name.text);
|
|
220
|
-
if (ts.isInterfaceDeclaration(node) && node.name.text
|
|
223
|
+
if (ts.isInterfaceDeclaration(node) && TYPED_VIEWS.includes(node.name.text))
|
|
221
224
|
for (const m of node.members)
|
|
222
225
|
if (m.name && ts.isIdentifier(m.name)) readKeys.add(m.name.text);
|
|
223
226
|
ts.forEachChild(node, visit);
|
package/lib/paper-config.ts
CHANGED
|
@@ -140,6 +140,7 @@ export const SETTINGS_KEYS: Readonly<Record<string, string>> = Object.freeze({
|
|
|
140
140
|
pdf: "where the built PDF is, relative to the paper, when it is not paper.pdf — the facts",
|
|
141
141
|
identity:
|
|
142
142
|
"what identifies the authors — names, handles, emails, affiliations, own project names; a blind venue's PDF must not say any of them — anonymity/identity. The root's list and a paper's are joined",
|
|
143
|
+
talk: 'how this paper is presented: { "mode": "remote-video", "dir"?, "files"?, "one_slide"? } — the talk/ rules. A paper\'s own; refused in the root file',
|
|
143
144
|
scripts: "how skill prose names the scripts — consumer.mjs",
|
|
144
145
|
timezone: "the zone deadline anchors are written in — consumer.mjs",
|
|
145
146
|
contactEmail: "the polite-pool address for citation lookups — consumer.mjs",
|
|
@@ -148,6 +149,9 @@ export const SETTINGS_KEYS: Readonly<Record<string, string>> = Object.freeze({
|
|
|
148
149
|
$comment: "a note for humans (JSON Schema's own comment keyword); ignored",
|
|
149
150
|
});
|
|
150
151
|
|
|
152
|
+
/** The keys that only mean something for one paper: refused in the root `paperlint.json`. */
|
|
153
|
+
export const PAPER_ONLY_KEYS: readonly string[] = Object.freeze(["talk"]);
|
|
154
|
+
|
|
151
155
|
/** The keys that only mean something for the whole project: refused in a paper's `paperlint.json`. */
|
|
152
156
|
export const ROOT_ONLY_KEYS: readonly string[] = Object.freeze([
|
|
153
157
|
"papersDir",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paperlint",
|
|
3
|
-
"version": "6.1
|
|
3
|
+
"version": "6.2.1",
|
|
4
4
|
"description": "A linter for scientific papers written in LaTeX: catches mistakes before you submit to a conference or journal.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,7 +27,9 @@
|
|
|
27
27
|
"eslint": "^10.9.1",
|
|
28
28
|
"js-yaml": "^4.1.0",
|
|
29
29
|
"markdown-it": "^14.3.0",
|
|
30
|
+
"mp4box": "^2.4.1",
|
|
30
31
|
"remeda": "^2.50.0",
|
|
32
|
+
"srt-parser-2": "^1.2.3",
|
|
31
33
|
"ts-essentials": "^10.2.1",
|
|
32
34
|
"typescript": "^6.0.3",
|
|
33
35
|
"unpdf": "^1.8.1",
|
package/presets/agenticdev.jsonc
CHANGED
|
@@ -31,6 +31,36 @@
|
|
|
31
31
|
},
|
|
32
32
|
},
|
|
33
33
|
|
|
34
|
+
// The talk (talk/* rules), for a paper whose paperlint.json declares `talk`. The numbers are the
|
|
35
|
+
// organizers', each with its source; one_slide is left out on purpose — the venue never stated a
|
|
36
|
+
// size, so a paper that wants its own checked declares it in its own `talk` key.
|
|
37
|
+
"talk": {
|
|
38
|
+
// AgenticDev adopted ASE's «not in person presentation» policy (co-chairs, 2026-08-27).
|
|
39
|
+
"modes": ["remote-video"],
|
|
40
|
+
"artifacts": {
|
|
41
|
+
// The chairs' email to accepted authors: a video and a one-slide; a poster is optional.
|
|
42
|
+
"remote-video": {
|
|
43
|
+
"required": ["video", "one-slide"],
|
|
44
|
+
"optional": ["poster"],
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
"kinds": {
|
|
48
|
+
// The chairs' email to all authors (2026-09-29): a short paper gets 10 min, about 6–7 for the
|
|
49
|
+
// talk and 3–4 for questions.
|
|
50
|
+
// qa_s is the middle of 3–4 min; it is informational and not judged. The full-paper slot
|
|
51
|
+
// was not in the email, so `full` and `demo` have no entry and talk/profile says so.
|
|
52
|
+
"short": {
|
|
53
|
+
"slot_s": 600,
|
|
54
|
+
"talk_s_min": 360,
|
|
55
|
+
"talk_s_max": 420,
|
|
56
|
+
"qa_s": 210,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
// ASE 2026's not-in-person page: «recommendation: 10 min duration, mp4 file format, HD
|
|
60
|
+
// resolution» — the parent conference's recommendation; the workshop's own slot is above.
|
|
61
|
+
"video": { "container": "mp4", "min_height_px": 720 },
|
|
62
|
+
},
|
|
63
|
+
|
|
34
64
|
// The proceedings are produced by Conference Publishing Consulting — agenticdev.md takes its
|
|
35
65
|
// numbers from their author instructions — which require "balanced columns on the last page, if
|
|
36
66
|
// the page is not filled" and sent an accepted camera-ready back for it on 2026-08-29, the day
|