paperlint 9.0.1 → 10.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/README.md +68 -128
- package/dist/build.d.ts +1 -1
- package/dist/build.js +1 -1
- package/dist/cli.d.ts +5 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +52 -6
- package/dist/cli.js.map +1 -1
- package/dist/domain/layout-tree.d.ts +19 -0
- package/dist/domain/layout-tree.d.ts.map +1 -0
- package/dist/domain/layout-tree.js +32 -0
- package/dist/domain/layout-tree.js.map +1 -0
- package/dist/domain/lookup-cache.d.ts +2 -2
- package/dist/domain/lookup-cache.js +2 -2
- package/dist/domain/paper-layout.d.ts +81 -0
- package/dist/domain/paper-layout.d.ts.map +1 -0
- package/dist/domain/paper-layout.js +259 -0
- package/dist/domain/paper-layout.js.map +1 -0
- package/dist/domain/repro-layout.d.ts +80 -0
- package/dist/domain/repro-layout.d.ts.map +1 -0
- package/dist/domain/repro-layout.js +247 -0
- package/dist/domain/repro-layout.js.map +1 -0
- package/dist/domain/scaffold-layout.d.ts +28 -0
- package/dist/domain/scaffold-layout.d.ts.map +1 -0
- package/dist/domain/scaffold-layout.js +81 -0
- package/dist/domain/scaffold-layout.js.map +1 -0
- package/dist/eslint-rules/prereg-coverage.d.ts +98 -0
- package/dist/eslint-rules/prereg-coverage.d.ts.map +1 -0
- package/dist/eslint-rules/prereg-coverage.js +274 -0
- package/dist/eslint-rules/prereg-coverage.js.map +1 -0
- package/dist/folder-layout-hook.d.ts +21 -0
- package/dist/folder-layout-hook.d.ts.map +1 -0
- package/dist/folder-layout-hook.js +117 -0
- package/dist/folder-layout-hook.js.map +1 -0
- package/dist/folder-layout-rule.d.ts +22 -0
- package/dist/folder-layout-rule.d.ts.map +1 -0
- package/dist/folder-layout-rule.js +107 -0
- package/dist/folder-layout-rule.js.map +1 -0
- package/dist/init.js +1 -1
- package/dist/init.js.map +1 -1
- package/dist/new-paper.d.ts +4 -1
- package/dist/new-paper.d.ts.map +1 -1
- package/dist/new-paper.js +40 -6
- package/dist/new-paper.js.map +1 -1
- package/dist/references.d.ts +1 -1
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +2 -2
- package/dist/references.js.map +1 -1
- package/dist/repro-layout-rule.d.ts +16 -0
- package/dist/repro-layout-rule.d.ts.map +1 -0
- package/dist/repro-layout-rule.js +81 -0
- package/dist/repro-layout-rule.js.map +1 -0
- package/docs/ci.md +60 -0
- package/docs/configuration.md +2 -0
- package/docs/e2e.md +2 -2
- package/docs/install.md +20 -16
- package/docs/layout.md +154 -0
- package/docs/optional-rules.md +5 -4
- package/docs/references.md +5 -2
- package/docs/rules/paper/allowed-files-only.md +192 -0
- package/docs/rules/repro/allowed-files-only.md +128 -0
- package/docs/rules/study/declare-sample.md +146 -0
- package/docs/rules.md +53 -50
- package/docs/skills.md +6 -3
- package/docs/venues.md +94 -0
- package/eslint-rules/prereg-coverage.test.ts +448 -0
- package/eslint-rules/prereg-coverage.ts +419 -0
- package/lib/skill-trigger-cases.harness.mjs +51 -5
- package/lib/skill-trigger-cases.mjs +53 -15
- package/package.json +1 -1
- package/plugin/hooks/hooks.json +9 -0
- package/skills/analyze-sibling-paper/SKILL.md +2 -2
- package/skills/analyze-sibling-paper/SKILL.md.spec.ts +1 -1
- package/skills/build-benchmark/SKILL.md +14 -1
- package/skills/build-benchmark/SKILL.md.spec.ts +13 -0
- package/skills/build-label-page/SKILL.md +118 -0
- package/skills/build-label-page/SKILL.md.spec.ts +129 -0
- package/skills/build-label-page/build-label-page.eval.mjs +15 -0
- package/skills/build-label-page/build-label-page.test.ts +959 -0
- package/skills/build-label-page/references/checklist.md +148 -0
- package/skills/build-label-page/references/example-generator.py +803 -0
- package/skills/find-venue/SKILL.md +5 -3
- package/skills/find-venue/SKILL.md.spec.ts +4 -2
- package/skills/grade-paper-writing/SKILL.md +7 -7
- package/skills/grade-paper-writing/SKILL.md.spec.ts +6 -6
- package/skills/map-prior-work/SKILL.md +2 -2
- package/skills/map-prior-work/SKILL.md.spec.ts +1 -1
- package/skills/paper-pipeline/SKILL.md +8 -6
- package/skills/paper-pipeline/SKILL.md.spec.ts +7 -5
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +19 -9
- package/skills/paper-pipeline/references/acceptance-gate.md +19 -19
- package/skills/paper-pipeline/references/artifact-checklist.md +5 -0
- package/skills/paper-pipeline/references/pipeline-status-template.md +30 -1
- package/skills/paper-pipeline/repro/2026-08-07-fork-check.log +1 -1
- package/skills/paper-pipeline/repro/2026-08-07-fork-check2.log +1 -1
- package/skills/paper-pipeline/repro/2026-08-07-language-eval-pilot.log +1 -1
- package/skills/paper-pipeline/repro/2026-08-07-language-eval-raw.log +1 -1
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.log +48 -48
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-oracle.log +8 -8
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.log +48 -48
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.log +2 -2
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication.mjs +3 -3
- package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-ablation-preflight-after.log +81 -0
- package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-ablation-preflight-before.log +81 -0
- package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-ablation-setupdiff.log +59 -0
- package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-trigger.jsonl +1 -0
- package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-trigger.log +22 -0
- package/skills/paper-pipeline/repro/README.md +4 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +2 -2
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +2 -2
- package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +4 -2
- package/skills/paper-pipeline/scripts/generated-code.harness.mjs +7 -2
- package/skills/paper-pipeline/scripts/generated-code.mjs +8 -4
- package/skills/paper-pipeline/scripts/generated-code.test.ts +15 -1
- package/skills/paper-status/SKILL.md +2 -2
- package/skills/paper-status/SKILL.md.spec.ts +1 -1
- package/skills/study-accepted-papers/SKILL.md +163 -161
- package/skills/study-accepted-papers/SKILL.md.spec.ts +167 -165
- package/skills/study-accepted-papers/references/base-rates.md +69 -0
- package/skills/study-accepted-papers/references/mechanisms.md +181 -0
- package/skills/verify-citations/SKILL.md +2 -2
- package/skills/verify-citations/SKILL.md.spec.ts +1 -1
- package/src/build.ts +1 -1
- package/src/cli.harness.mjs +42 -0
- package/src/cli.test.ts +90 -6
- package/src/cli.ts +78 -5
- package/src/domain/layout-tree.test.ts +32 -0
- package/src/domain/layout-tree.ts +47 -0
- package/src/domain/lookup-cache.ts +2 -2
- package/src/domain/paper-layout.test.ts +264 -0
- package/src/domain/paper-layout.ts +336 -0
- package/src/domain/repro-layout.test.ts +222 -0
- package/src/domain/repro-layout.ts +311 -0
- package/src/domain/scaffold-layout.ts +104 -0
- package/src/folder-layout-hook.test.ts +214 -0
- package/src/folder-layout-hook.ts +141 -0
- package/src/folder-layout-rule.test.ts +235 -0
- package/src/folder-layout-rule.ts +151 -0
- package/src/hooks-settings.harness.ts +9 -8
- package/src/init.ts +1 -1
- package/src/layout-docs.test.ts +52 -0
- package/src/new-paper.harness.mjs +23 -1
- package/src/new-paper.test.ts +299 -6
- package/src/new-paper.ts +51 -5
- package/src/paper-files.test.ts +9 -6
- package/src/references.test.ts +5 -5
- package/src/references.ts +2 -2
- package/src/repro-layout-rule.test.ts +114 -0
- package/src/repro-layout-rule.ts +121 -0
- package/src/rule-cases.test.ts +98 -0
- package/templates/paper/PIPELINE-STATUS.md +41 -7
package/docs/ci.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Run it in CI
|
|
2
|
+
|
|
3
|
+
Two jobs, one choice: how much of the paper CI judges.
|
|
4
|
+
|
|
5
|
+
| job | time | checks |
|
|
6
|
+
| -------------------------- | ----------------------------- | ------------------------------------------ |
|
|
7
|
+
| build, then lint | ~3 min first run, then cached | everything |
|
|
8
|
+
| lint only (`init` adds it) | seconds | the source; not pages, fonts or references |
|
|
9
|
+
|
|
10
|
+
`paperlint lint` exits non-zero on any error; warnings only print, unless you pass
|
|
11
|
+
`--max-warnings <n>`.
|
|
12
|
+
|
|
13
|
+
## Build, then lint
|
|
14
|
+
|
|
15
|
+
Lint judges pages, fonts and references from what build measured, so the full job builds first.
|
|
16
|
+
The cache keeps TeX Live between runs:
|
|
17
|
+
|
|
18
|
+
```yaml
|
|
19
|
+
name: papers
|
|
20
|
+
on: [push, pull_request]
|
|
21
|
+
jobs:
|
|
22
|
+
papers:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
- uses: actions/setup-node@v4
|
|
27
|
+
with:
|
|
28
|
+
node-version: 22
|
|
29
|
+
- run: npm ci
|
|
30
|
+
- uses: actions/cache@v4
|
|
31
|
+
with:
|
|
32
|
+
path: ~/.cache/paperlint/texlive
|
|
33
|
+
key: texlive-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
|
|
34
|
+
- run: npx paperlint toolchain
|
|
35
|
+
- run: npx paperlint build --all
|
|
36
|
+
- run: npx paperlint lint
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Commit `repro/data/references-cache.json` of each paper: with it, a build asks the citation
|
|
40
|
+
services only about new or edited entries, so an unchanged bibliography usually builds without
|
|
41
|
+
the network ([`references.md`](references.md)).
|
|
42
|
+
|
|
43
|
+
## Lint only
|
|
44
|
+
|
|
45
|
+
Each paper then gets one warning that it was not built. Call the action from the installed package,
|
|
46
|
+
after `npm ci`: the action and the CLI it runs are then one revision, pinned by your lockfile.
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@v4
|
|
51
|
+
- uses: actions/setup-node@v4
|
|
52
|
+
with:
|
|
53
|
+
node-version: 22
|
|
54
|
+
- run: npm ci
|
|
55
|
+
- uses: ./node_modules/paperlint
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
With no `paths` it lints the `papersDir` of your `paperlint.json`; pass `paths:` only to lint less.
|
|
59
|
+
|
|
60
|
+
This is the workflow `paperlint init` offers to write ([`install.md`](install.md)).
|
package/docs/configuration.md
CHANGED
|
@@ -13,6 +13,8 @@ papers/
|
|
|
13
13
|
paper.tex
|
|
14
14
|
PIPELINE-STATUS.md
|
|
15
15
|
paperlint.json THIS PAPER: its venue preset, its kind, its own rules — `paperlint new` writes it
|
|
16
|
+
reviews/ research/ figures/ versions/ submission/ repro/
|
|
17
|
+
the paper's layout — `paperlint new` makes them (an empty one holds a `.gitkeep`)
|
|
16
18
|
venues/usenix-sec.jsonc (optional, your own) a VENUE PRESET: format, page limits, TeX packages, rules
|
|
17
19
|
```
|
|
18
20
|
|
package/docs/e2e.md
CHANGED
|
@@ -54,8 +54,8 @@ depend on each other — and drives the installed binary:
|
|
|
54
54
|
publishes, once each — and says they need `npm install` in a fresh clone; a second `init`
|
|
55
55
|
leaves that file byte-identical
|
|
56
56
|
- `paperlint lint` passes the staged corpus
|
|
57
|
-
- `paperlint new demo` scaffolds a paper from the templates that shipped in the tarball
|
|
58
|
-
clean, and `paperlint lint` stays clean with it in the corpus
|
|
57
|
+
- `paperlint new demo` scaffolds a paper from the templates that shipped in the tarball (and the
|
|
58
|
+
layout's folders), its own lint is clean, and `paperlint lint` stays clean with it in the corpus
|
|
59
59
|
- `hooks.json` arrived and parses, and every hook command named in it resolves to a file
|
|
60
60
|
- every skill arrived, and every script path named inside a skill resolves **in the consumer**
|
|
61
61
|
|
package/docs/install.md
CHANGED
|
@@ -50,9 +50,9 @@ question (Ctrl+D) also takes the default.
|
|
|
50
50
|
| what | where | when |
|
|
51
51
|
| -------------------------------------------------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
52
52
|
| `{ "papersDir": … }` naming your papers directory | `paperlint.json` | only when it is not the default `papers` |
|
|
53
|
-
| the
|
|
53
|
+
| the four 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
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
|
|
55
|
+
| a first paper, via `paperlint new`, with its layout's folders | `<papers>/<name>/` | only when the papers directory holds none: asked of a human at a terminal, otherwise only with `--paper <name>` |
|
|
56
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
|
|
@@ -61,9 +61,11 @@ gets the hooks; the hook commands run files inside `node_modules`, so a fresh cl
|
|
|
61
61
|
|
|
62
62
|
## Why it is shaped this way
|
|
63
63
|
|
|
64
|
-
**One file, at the project root.** A hook cannot import code or walk up a tree looking for
|
|
65
|
-
config; it can only read a path it can spell, and `$CLAUDE_PROJECT_DIR/paperlint.json` is one.
|
|
66
|
-
|
|
64
|
+
**One file, at the project root.** A vigiles hook cannot import code or walk up a tree looking for
|
|
65
|
+
a config; it can only read a path it can spell, and `$CLAUDE_PROJECT_DIR/paperlint.json` is one.
|
|
66
|
+
(`paper-folder-layout` is not a vigiles hook: it runs inside the CLI, because it must call the
|
|
67
|
+
function its rule reports with.) The same file is read by the CLI, the three vigiles hooks,
|
|
68
|
+
`eslint-rules/papers.mjs`,
|
|
67
69
|
`lib/skill-trigger-cases.mjs` and `skills/paper-pipeline/scripts/consumer.mjs`. It is optional:
|
|
68
70
|
absent, every reader takes the same defaults.
|
|
69
71
|
|
|
@@ -147,17 +149,19 @@ are probably smaller now; re-measure before quoting them.
|
|
|
147
149
|
| of which `typescript` (via `vigiles`) | 23 MB |
|
|
148
150
|
| of which `vigiles` itself | 6 MB |
|
|
149
151
|
|
|
150
|
-
Neither `paperlint lint` nor any of the
|
|
151
|
-
`vigiles` itself is
|
|
152
|
+
Neither `paperlint lint` nor any of the four hooks loads `@ast-grep` or `typescript` (traced;
|
|
153
|
+
`paper-folder-layout` on 2026-10-12: 223 CommonJS modules, none from either); `vigiles` itself is
|
|
154
|
+
loaded.
|
|
152
155
|
|
|
153
156
|
## Troubleshooting
|
|
154
157
|
|
|
155
|
-
| symptom
|
|
156
|
-
|
|
|
157
|
-
| `init`: skills "nothing linked", or hooks that fail to start
|
|
158
|
-
| `paperlint lint`: `no papers in papers/`
|
|
159
|
-
| hooks fail with `Cannot find module` in a fresh clone
|
|
160
|
-
| `paperlint: not built`
|
|
161
|
-
| a skill does not show up in Claude Code
|
|
162
|
-
| **every** shell command is blocked in Claude Code
|
|
163
|
-
|
|
|
158
|
+
| symptom | cause and fix |
|
|
159
|
+
| ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
160
|
+
| `init`: skills "nothing linked", or hooks that fail to start | the package is not installed in this project (`npx` ran a temporary copy). Run `npm i -D paperlint`, then `npx paperlint init` again |
|
|
161
|
+
| `paperlint lint`: `no papers in papers/` | there is no paper where `papersDir` points. Create one with `npx paperlint new <name>`, or set `"papersDir"` in `paperlint.json` |
|
|
162
|
+
| hooks fail with `Cannot find module` in a fresh clone | the hook commands run files in `node_modules`: run `npm install` |
|
|
163
|
+
| `paperlint: not built` | installed from git with `--ignore-scripts`, or a clone before building: run `npm run build` in the package |
|
|
164
|
+
| a skill does not show up in Claude Code | its name was already taken in `.claude/skills/`. `init` names it and leaves it alone; rename or remove yours and run `npx paperlint init` again |
|
|
165
|
+
| **every** shell command is blocked in Claude Code | `paper-edit-guard` cannot parse the root `paperlint.json` (merge-conflict markers, a typo), and refuses rather than guess. Fix it with a normal file edit — file edits are not blocked |
|
|
166
|
+
| a Write of a new file is denied: "… is at the root of a paper folder" or "… is loose at the top of the paper's repro/ folder" | `paper-folder-layout` refuses to create an entry the paper layout does not allow; the reason says where it goes. Write it there. Existing files are never refused, and the hook does not see shell moves |
|
|
167
|
+
| anything else | `npx paperlint doctor` — it checks the setup and exits non-zero on anything miswired |
|
package/docs/layout.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Where things live, and what checks them when
|
|
2
|
+
|
|
3
|
+
A paper moves through eight stages ([the flow](#the-flow)). Each stage writes into one
|
|
4
|
+
place, and a check guards that place. This page is the map: the places, who writes each one, and the
|
|
5
|
+
moment each check runs. The skills, stage by stage: [`skills.md`](skills.md).
|
|
6
|
+
|
|
7
|
+
## The flow
|
|
8
|
+
|
|
9
|
+
Each box names the stage's skills, what it writes, and what checks it. Stage 3 is drawn from the
|
|
10
|
+
inside: a study's pre-registration and its human labels are made there.
|
|
11
|
+
|
|
12
|
+
```mermaid
|
|
13
|
+
flowchart TB
|
|
14
|
+
N["<b>paperlint new</b><br/>creates the paper folder<br/>checked from here on:<br/>every lint · paper/allowed-files-only<br/>every new file · hook paper-folder-layout"]
|
|
15
|
+
S1["<b>1 · idea</b><br/>research-ideate · map-prior-work<br/>analyze-sibling-paper<br/>writes: siblings/<br/>checked by: sibling/frontmatter"]
|
|
16
|
+
S2["<b>2 · venue</b><br/>find-venue · plan-paper-timeline<br/>study-accepted-papers, mode A or B<br/>writes: paperlint.json, a report in reviews/<br/>checked by: review/frontmatter"]
|
|
17
|
+
V[("<b>#lt;papers-root#gt;/venues/#lt;venue#gt;/</b><br/>beside the papers, not in one:<br/>the venue's accepted papers,<br/>shared by every paper aimed at it<br/>checked by: nothing")]
|
|
18
|
+
subgraph S3["3 · study: repro/#lt;slug#gt;-YYYY-MM-DD/"]
|
|
19
|
+
direction TB
|
|
20
|
+
D["<b>design</b> · build-benchmark<br/>writes: the study's README.md<br/>checked by: repro/allowed-files-only"]
|
|
21
|
+
P["<b>pre-register</b> · build-benchmark<br/>writes: prereg.json, before the draw<br/>checked by: study/declare-sample (experimental, off)"]
|
|
22
|
+
A["<b>author check</b> · you see the drawn units<br/>writes: author_check in prereg.json<br/>checked by: study/declare-sample (experimental, off)"]
|
|
23
|
+
R["<b>run</b> · your scripts<br/>writes: the study folder,<br/>repro/tools/, repro/data/<br/>checked by: repro/allowed-files-only"]
|
|
24
|
+
L["<b>labels</b> · build-label-page<br/>a phone page; the answers go<br/>into the study's data/"]
|
|
25
|
+
Y["<b>analysis</b> · build-benchmark<br/>writes: the numbers, NOTES.md"]
|
|
26
|
+
D --> P --> A --> R --> L --> Y
|
|
27
|
+
end
|
|
28
|
+
S4["<b>4 · draft</b><br/>draft-paper · argument-arc · tighten-paper<br/>writes: paper.tex, sections/, figures/<br/>checked by: the tex/* rules<br/>hook paper-edit-guard: no shell writes"]
|
|
29
|
+
S5["<b>5 · review</b><br/>grade-paper-writing · verify-citations<br/>pc-panel-review · harden-paper<br/>study-accepted-papers, mode C or D<br/>writes: reviews/, the scorecard<br/>in PIPELINE-STATUS.md<br/>checked by: review/frontmatter"]
|
|
30
|
+
S6["<b>6 · submit</b><br/>paperlint build · submit-paper<br/>writes: paper.pdf, _build/,<br/>versions/, submission/<br/>checked by: format/page-limit, pdf/*,<br/>anonymity/identity, paper/stages"]
|
|
31
|
+
S7["<b>7 · camera-ready</b><br/>camera-ready<br/>writes: versions/, talk/<br/>checked by: paper/stages, talk/*"]
|
|
32
|
+
S8["<b>8 · extend</b><br/>extend-paper<br/>writes: a new paper folder"]
|
|
33
|
+
N --> S1 --> S2 --> D
|
|
34
|
+
Y --> S4 --> S5 --> S6 --> S7 --> S8
|
|
35
|
+
S2 -. fills .-> V
|
|
36
|
+
V -. read again .-> S5
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The cylinder is a venue's corpus of accepted papers: it lives beside the papers, not inside one
|
|
40
|
+
([`venues.md`](venues.md)).
|
|
41
|
+
|
|
42
|
+
## The map
|
|
43
|
+
|
|
44
|
+
The papers directory is `papersDir` in the project's `paperlint.json` (default `papers/`). A folder
|
|
45
|
+
in it is a paper when it holds `PIPELINE-STATUS.md`, `paper.tex` or `paperlint.json`; `paperlint new`
|
|
46
|
+
creates one.
|
|
47
|
+
|
|
48
|
+
<!-- layout:begin — drawn from src/domain/paper-layout.ts (PAPERS_LAYOUT_TREE); src/layout-docs.test.ts fails when this block differs -->
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
<papers-root>/
|
|
52
|
+
├── venues/ ← outside every paper folder: one folder per venue
|
|
53
|
+
│ └── <venue>/ ← that venue's accepted papers, shared by every paper aimed at it
|
|
54
|
+
└── <paper>/ ← one folder per paper, named after the work; its root holds only what is below
|
|
55
|
+
├── PIPELINE-STATUS.md ← the paper's one live-state file: stages, plan, scorecard
|
|
56
|
+
├── paper.tex ← the source; `paperlint build` compiles it
|
|
57
|
+
├── paperlint.json ← the paper's settings
|
|
58
|
+
├── README.md ← what the paper is
|
|
59
|
+
├── CLAUDE.md ← the paper's agent rules
|
|
60
|
+
├── CLAIMS.md ← the claims ledger
|
|
61
|
+
├── NOTES.md ← each number in the paper → the data it comes from
|
|
62
|
+
├── SUBMIT-CHECKLIST.md ← the venue's format and submission mechanics
|
|
63
|
+
├── .gitignore ← keeps the build's byproducts out of git
|
|
64
|
+
├── paper.pdf ← the built PDF
|
|
65
|
+
├── *.tex *.bib *.cls *.sty *.bst ← what TeX and bibtex look up beside paper.tex
|
|
66
|
+
├── _build/ ← the facts `paperlint build` writes
|
|
67
|
+
├── versions/ ← each stage's frozen PDF and source
|
|
68
|
+
├── reviews/ ← dated reviews and pass reports
|
|
69
|
+
├── siblings/ ← the cards of competing papers
|
|
70
|
+
├── research/ ← dated research and decision records
|
|
71
|
+
├── repro/ ← the reproduction artifact
|
|
72
|
+
│ ├── README.md ← the index: one line per study
|
|
73
|
+
│ ├── .gitignore ← keeps the scripts' byproducts out of git
|
|
74
|
+
│ ├── tools/ ← scripts used across the paper (numbers, figures, anonymity check, harnesses)
|
|
75
|
+
│ ├── data/ ← shared data (tables, caches)
|
|
76
|
+
│ └── <slug>-YYYY-MM-DD/ ← one folder per study or spike (<slug>-YYYY-MM for one that ran over weeks)
|
|
77
|
+
│ └── README.md ← required: what it asks, how to run it, what it found
|
|
78
|
+
├── figures/ ← graphics and their sources
|
|
79
|
+
├── sections/ ← the body, when it is split
|
|
80
|
+
├── talk/ ← the talk's files
|
|
81
|
+
├── submission/ ← forms, portal records, mail with the venue, the anonymized upload
|
|
82
|
+
└── extra/ ← anything; never judged
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
<!-- layout:end -->
|
|
86
|
+
|
|
87
|
+
Two places with similar names are not in this drawing:
|
|
88
|
+
|
|
89
|
+
- `venues/` at the **project root** holds your own venue presets and their cards
|
|
90
|
+
(`venues/<name>.jsonc`, `venues/<name>.md`), for a venue paperlint does not ship.
|
|
91
|
+
- `<papers-root>/venues/<venue>/` (drawn above) holds a venue's **accepted papers**.
|
|
92
|
+
|
|
93
|
+
Both are explained in [`venues.md`](venues.md).
|
|
94
|
+
|
|
95
|
+
## Each place: who writes it, and what checks it
|
|
96
|
+
|
|
97
|
+
"Stage" is the stage of [the pipeline](#the-flow) that writes the place first.
|
|
98
|
+
"Checked by" is the rule `paperlint lint` runs over it; "nothing" means no rule reads its content.
|
|
99
|
+
|
|
100
|
+
| place | stage | written by | checked by |
|
|
101
|
+
| ------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
102
|
+
| `<papers-root>/venues/<venue>/` | 2 venue | `study-accepted-papers`, its first run for that venue (mode A, usually) | nothing ([why](venues.md#what-checks-it)) |
|
|
103
|
+
| the paper folder's root | before 1 | `paperlint new`: the three files, and an empty `.gitkeep` folder for `reviews/`, `research/`, `figures/`, `versions/`, `submission/`, `repro/tools/`, `repro/data/` (`repro/` gets a stub index) | [`paper/allowed-files-only`](rules/paper/allowed-files-only.md); the hook `paper-folder-layout` refuses a new stray entry |
|
|
104
|
+
| `PIPELINE-STATUS.md` | every stage | `paperlint new`, then `paper-pipeline`, `paper-status`; `study-accepted-papers` (the scorecard) | `paper/stages`, `paper/source`, [`cycle/stage`](rules/cycle/stage.md) ([rules](rules.md)) |
|
|
105
|
+
| `paperlint.json` | before 1, then 2 | `paperlint new`; `find-venue` names the venue | `paperlint lint` refuses an unknown key; [`cycle/record`](rules/cycle/record.md) |
|
|
106
|
+
| `siblings/` | 1 idea | `map-prior-work`, `analyze-sibling-paper` | `sibling/frontmatter` ([records](configuration.md#records-in-frontmatter-checked-by-shipped-json-schemas)) |
|
|
107
|
+
| `reviews/` | 2, then 4–5 | `study-accepted-papers` (its report), `grade-paper-writing`, `cold-read-diff`, the review skills | `review/frontmatter` ([records](configuration.md#records-in-frontmatter-checked-by-shipped-json-schemas)) |
|
|
108
|
+
| `research/` | any | you and the skills: dated research and decision records | nothing |
|
|
109
|
+
| `repro/`, its top | 3 study | `build-benchmark`, your scripts | [`repro/allowed-files-only`](rules/repro/allowed-files-only.md); the hook `paper-folder-layout` refuses a new loose entry |
|
|
110
|
+
| `repro/<slug>-YYYY-MM-DD/` | 3 study | `build-benchmark` (the design, in its `README.md`) | [`repro/allowed-files-only`](rules/repro/allowed-files-only.md): the dated name, and a `README.md` in it |
|
|
111
|
+
| `repro/<slug>-YYYY-MM-DD/prereg.json` | 3 study | `build-benchmark`, before the sample is drawn; you add the author's check | [`study/declare-sample`](rules/study/declare-sample.md) |
|
|
112
|
+
| `repro/<slug>-YYYY-MM-DD/data/` | 3 study | `build-label-page` exports the labels here | nothing |
|
|
113
|
+
| `repro/tools/`, `repro/data/` | 3 study | your scripts; `paperlint build` keeps `repro/data/references-cache.json` | nothing inside them |
|
|
114
|
+
| `NOTES.md` | 3, then 4 | you: each number → the data it comes from; `build-label-page` adds a line per label file | nothing |
|
|
115
|
+
| `paper.tex`, `sections/`, `*.bib` | 4 draft | `draft-paper`, `argument-arc`, `tighten-paper` | the `tex/*`, `paper/*` and `bib/*` rules ([rules](rules.md)); the hook `paper-edit-guard` refuses a shell write |
|
|
116
|
+
| `CLAIMS.md`, `figures/` | 4 draft | you | nothing |
|
|
117
|
+
| `paper.pdf`, `_build/` | 6 submit | `paperlint build` | `pdf/*`, [`format/page-limit`](rules/format/page-limit.md), [`anonymity/identity`](rules/anonymity/identity.md), the reference rules |
|
|
118
|
+
| `versions/` | 6, then 7 | you, at submission and at camera-ready (`submit-paper` says how): the frozen PDF and source of each stage | `paper/stages`, `paper/source` |
|
|
119
|
+
| `submission/`, `SUBMIT-CHECKLIST.md` | 6 submit | you, while `submit-paper` and `submission-portal` do the submitting: forms, portal records, mail with the venue | [`cycle/evidence`](rules/cycle/evidence.md), for the files a closed cycle names |
|
|
120
|
+
| `talk/` | 7 camera-ready | you | the `talk/*` rules ([talk](talk.md)) |
|
|
121
|
+
| `extra/` | any | you | nothing, ever |
|
|
122
|
+
|
|
123
|
+
## What runs when
|
|
124
|
+
|
|
125
|
+
A check runs at one of four moments:
|
|
126
|
+
|
|
127
|
+
- **Before a write** — a Claude Code hook. It sees an agent's Edit or Write, not a shell `mv` or a
|
|
128
|
+
script, so it saves a round trip and guarantees nothing.
|
|
129
|
+
- **On lint** — `paperlint lint`, when you run it. Every rule runs, whatever wrote the files. This is
|
|
130
|
+
the guarantee. No hook runs it for you.
|
|
131
|
+
- **In CI** — the same lint, on every push, if the project has the workflow `paperlint init` offers.
|
|
132
|
+
The lint-only job skips what needs a build (pages, fonts, references).
|
|
133
|
+
- **On demand** — a skill, when you ask for what it does. A skill stops nothing; it writes.
|
|
134
|
+
|
|
135
|
+
| what | kind | when it runs | what it stops |
|
|
136
|
+
| ------------------------------------------------------------------------------------------- | ----- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
137
|
+
| `paper-folder-layout` | hook | before an agent creates a file (Edit, Write) | a new entry the layout does not allow, at a paper's root or the top of its `repro/`; never an existing file |
|
|
138
|
+
| `paper-edit-guard` | hook | before an agent's shell command | a shell command that writes or moves a paper's source, which would skip the hooks that run after an edit |
|
|
139
|
+
| `paper-skills-nudge` | hook | after an agent edits a paper's source | nothing: it shows the agent the pre-submit checklist |
|
|
140
|
+
| `paper-status-gates` | hook | after an agent edits a paper's source | nothing: it prints the paper's readiness and its unrun gates, for a person reading the transcript |
|
|
141
|
+
| [`paper/allowed-files-only`](rules/paper/allowed-files-only.md) | rule | every `paperlint lint`, and CI | a stray file or folder at a paper's root (error) |
|
|
142
|
+
| [`repro/allowed-files-only`](rules/repro/allowed-files-only.md) | rule | every `paperlint lint`, and CI | a loose entry at the top of `repro/`, an undated study folder, a study folder without `README.md` (error) |
|
|
143
|
+
| [`study/declare-sample`](rules/study/declare-sample.md) | rule | every `paperlint lint`, and CI, once a paper turns it on | a study's `prereg.json` without its population, strata, screens, coverage or the author's check (experimental, off by default) |
|
|
144
|
+
| [`paper/folder-venue-leftover`](rules/paper/folder-venue-leftover.md) | rule | every `paperlint lint`, and CI | nothing: it warns when the folder is named after another venue |
|
|
145
|
+
| `review/frontmatter`, `sibling/frontmatter` | rule | every `paperlint lint`, and CI | a review whose findings break the schema (error); a sibling card that does not say how much was read (warning) |
|
|
146
|
+
| `paper/stages`, `paper/source` | rule | every `paperlint lint`, and CI | a stage whose frozen PDF or source is missing or changed (error) |
|
|
147
|
+
| the rules on the source (`tex/*`, `paper/*`, `bib/*`) | rule | every `paperlint lint`, and CI | what each one's [row](rules.md) says |
|
|
148
|
+
| the rules on what `paperlint build` measured (`pdf/*`, `format/page-limit`, the references) | rule | every `paperlint lint` after a build; CI's full job | a PDF over the page limit, in the wrong font or format, or with a bad reference |
|
|
149
|
+
| `study-accepted-papers` | skill | when you ask; stage 2 (modes A, B) and stage 5 (modes C, D) | nothing: it fills the venue's corpus, and writes the strong-accept scorecard in `PIPELINE-STATUS.md` |
|
|
150
|
+
| `build-benchmark` | skill | when you ask; stage 3 | nothing: it writes the study's design and its `prereg.json` |
|
|
151
|
+
| `build-label-page` | skill | when you ask; stage 3 | nothing: it builds the labelling page, and exports the answers into the study's `data/` |
|
|
152
|
+
|
|
153
|
+
The hooks are wired by `paperlint init` ([`install.md`](install.md)). A rule's level and options:
|
|
154
|
+
[`rules.md`](rules.md).
|
package/docs/optional-rules.md
CHANGED
|
@@ -8,10 +8,11 @@ rules apply to that paper. The root `paperlint.json` can turn it on for every pa
|
|
|
8
8
|
with a block ([`configuration.md`](configuration.md#the-rules-key-turning-rules-on-and-off)); a
|
|
9
9
|
paper's own file still has the last word for that paper.
|
|
10
10
|
|
|
11
|
-
| rule
|
|
12
|
-
|
|
|
13
|
-
| `pdf/last-page-balance`
|
|
14
|
-
| [`tex/heading-case`](rules/tex/heading-case.md)
|
|
11
|
+
| rule | what it checks | who needs it |
|
|
12
|
+
| ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
13
|
+
| `pdf/last-page-balance` | the two columns of the last page end at about the same height | two-column papers whose publisher asks for it — see below |
|
|
14
|
+
| [`tex/heading-case`](rules/tex/heading-case.md) | the words of the paper's title and of each heading are in the capitalization the venue asks | papers whose venue names a case style paperlint builds; only the AgenticDev preset turns it on (`chicago-headline` for the title and every heading) — [page](rules/tex/heading-case.md) |
|
|
15
|
+
| [`study/declare-sample`](rules/study/declare-sample.md) | experimental: a study's `prereg.json` declares its population, screens, coverage and the author's check of the drawn units | papers with a sampled study; no preset turns it on — [page](rules/study/declare-sample.md) |
|
|
15
16
|
|
|
16
17
|
## `pdf/last-page-balance`
|
|
17
18
|
|
package/docs/references.md
CHANGED
|
@@ -61,18 +61,21 @@ is stale and `paper/sources-fresh` says so: build again to write it. A database
|
|
|
61
61
|
the paper directory, on its search path (`BIBINPUTS`), is not read: the step says which and
|
|
62
62
|
`paper/refs-checked` warns.
|
|
63
63
|
|
|
64
|
-
## `repro/references-cache.json` — commit it
|
|
64
|
+
## `repro/data/references-cache.json` — commit it
|
|
65
65
|
|
|
66
66
|
Asking five services about every entry is slow. DBLP asks clients to pause between requests, and a
|
|
67
67
|
slow service is retried. Before this cache existed, a 27-reference paper spent 217 seconds on the
|
|
68
68
|
check, on every build, and a 51-reference paper did not finish inside a 12-minute CI job
|
|
69
69
|
([#107](https://github.com/zernie/paperlint/issues/107)).
|
|
70
70
|
|
|
71
|
-
So the build keeps every answer it receives in `<paper>/repro/references-cache.json`, and the next
|
|
71
|
+
So the build keeps every answer it receives in `<paper>/repro/data/references-cache.json`, and the next
|
|
72
72
|
build asks only what that file cannot answer:
|
|
73
73
|
|
|
74
74
|
- An unchanged bibliography whose answers are under 30 days old asks nothing and makes no network request at all, not even the check
|
|
75
75
|
for whether the services can be reached.
|
|
76
|
+
- A cache at the old place, `repro/references-cache.json`, is not read: `repro/allowed-files-only` reports
|
|
77
|
+
it with the command that moves it (`git mv repro/references-cache.json repro/data/`). Until it is
|
|
78
|
+
moved, the build asks every question again.
|
|
76
79
|
- Adding or editing an entry asks only about that entry. Answers are stored per identifier (DOI,
|
|
77
80
|
arXiv id, or title), so changing an entry's DOI or title is what makes it be asked again.
|
|
78
81
|
- A request that failed is not an answer and is not stored, so the next build asks it again.
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# paper/allowed-files-only
|
|
2
|
+
|
|
3
|
+
**Level:** error · **Reads:** the paper folder's root (a directory listing), its `paperlint.json` ·
|
|
4
|
+
**Reported on:** the first line of the paper's `PIPELINE-STATUS.md`, once per stray entry
|
|
5
|
+
|
|
6
|
+
## What it catches
|
|
7
|
+
|
|
8
|
+
A file or folder at the root of a paper folder that is not part of paperlint's layout. The root
|
|
9
|
+
holds a small, known set; every kind of material has one folder; anything else goes into `extra/`,
|
|
10
|
+
which is never judged.
|
|
11
|
+
|
|
12
|
+
It runs on every `paperlint lint` and in CI, from the moment `paperlint new` creates the folder; a
|
|
13
|
+
hook refuses a new stray entry before it is written. Which stage writes each place, and what checks
|
|
14
|
+
it: [the map](../../layout.md).
|
|
15
|
+
|
|
16
|
+
<!-- layout:begin — drawn from src/domain/paper-layout.ts (PAPER_LAYOUT_TREE); src/layout-docs.test.ts fails when this block differs -->
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
<paper>/
|
|
20
|
+
├── PIPELINE-STATUS.md ← the paper's one live-state file: stages, plan, scorecard
|
|
21
|
+
├── paper.tex ← the source; `paperlint build` compiles it
|
|
22
|
+
├── paperlint.json ← the paper's settings
|
|
23
|
+
├── README.md ← what the paper is
|
|
24
|
+
├── CLAUDE.md ← the paper's agent rules
|
|
25
|
+
├── CLAIMS.md ← the claims ledger
|
|
26
|
+
├── NOTES.md ← each number in the paper → the data it comes from
|
|
27
|
+
├── SUBMIT-CHECKLIST.md ← the venue's format and submission mechanics
|
|
28
|
+
├── .gitignore ← keeps the build's byproducts out of git
|
|
29
|
+
├── paper.pdf ← the built PDF
|
|
30
|
+
├── *.tex *.bib *.cls *.sty *.bst ← what TeX and bibtex look up beside paper.tex
|
|
31
|
+
├── _build/ ← the facts `paperlint build` writes
|
|
32
|
+
├── versions/ ← each stage's frozen PDF and source
|
|
33
|
+
├── reviews/ ← dated reviews and pass reports
|
|
34
|
+
├── siblings/ ← the cards of competing papers
|
|
35
|
+
├── research/ ← dated research and decision records
|
|
36
|
+
├── repro/ ← the reproduction artifact
|
|
37
|
+
│ ├── README.md ← the index: one line per study
|
|
38
|
+
│ ├── .gitignore ← keeps the scripts' byproducts out of git
|
|
39
|
+
│ ├── tools/ ← scripts used across the paper (numbers, figures, anonymity check, harnesses)
|
|
40
|
+
│ ├── data/ ← shared data (tables, caches)
|
|
41
|
+
│ └── <slug>-YYYY-MM-DD/ ← one folder per study or spike (<slug>-YYYY-MM for one that ran over weeks)
|
|
42
|
+
│ └── README.md ← required: what it asks, how to run it, what it found
|
|
43
|
+
├── figures/ ← graphics and their sources
|
|
44
|
+
├── sections/ ← the body, when it is split
|
|
45
|
+
├── talk/ ← the talk's files
|
|
46
|
+
├── submission/ ← forms, portal records, mail with the venue, the anonymized upload
|
|
47
|
+
└── extra/ ← anything; never judged
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
<!-- layout:end -->
|
|
51
|
+
|
|
52
|
+
`paperlint new` makes the folders a paper uses from its first day: `versions/`, `reviews/`, `research/`,
|
|
53
|
+
`repro/` (with its index, `tools/` and `data/`), `figures/` and `submission/`, each empty one with a
|
|
54
|
+
`.gitkeep` so the layout is in the first commit. It leaves out `siblings/` (made with the first
|
|
55
|
+
competitor's card), `sections/` (when the body is split), `talk/` (when the paper declares a talk),
|
|
56
|
+
`extra/` (never judged) and `_build/` (the build's). No rule reports a `.gitkeep` inside these folders.
|
|
57
|
+
|
|
58
|
+
`extra/` is never judged by any rule, not only this one: `paperlint lint` does not read a folder named
|
|
59
|
+
`extra/`, wherever it is. An archived snapshot of an older paper kept there, with its own
|
|
60
|
+
`PIPELINE-STATUS.md`, is not taken for a second paper.
|
|
61
|
+
|
|
62
|
+
Never reported: the byproducts a TeX run leaves beside `paper.tex` (`paper.aux`, `.log`, `.out`,
|
|
63
|
+
`.toc`, `.lof`, `.lot`, `.lol`, `.loa`, `.fls`, `.xdv`, `.dvi`, `.synctex.gz`; bibtex's `.bbl`, `.blg`;
|
|
64
|
+
makeindex's `.idx`, `.ind`, `.ilg`; glossaries' `.glo`, `.gls`, `.glg`, `.acn`, `.acr`, `.alg`, `.ist`,
|
|
65
|
+
`.nlo`, `.nls`; biber's `.bcf`, `.run.xml`; beamer's `.nav`, `.snm`, `.vrb`; latexmk's `.fdb_latexmk`;
|
|
66
|
+
the list follows latexmk's clean lists; only under the job name `paper`), the `.aux` that
|
|
67
|
+
`\include{chapter}` writes beside a root `chapter.tex`, and the operating system's `.DS_Store` and
|
|
68
|
+
`Thumbs.db`.
|
|
69
|
+
|
|
70
|
+
A venue's corpus of accepted papers is not in the paper folder at all: it is shared by every paper
|
|
71
|
+
that targets the venue, in `<papers-root>/venues/<venue>/` ([what it holds](../../venues.md)).
|
|
72
|
+
|
|
73
|
+
The paper's settings can move three entries, and what they name at the root is allowed while they
|
|
74
|
+
name it: `pdf` (the built PDF under another path), `talk.dir` (another talk folder), and a preset
|
|
75
|
+
file of the paper's own (`extends`, or the `extends` of any of its `cycles`).
|
|
76
|
+
|
|
77
|
+
## Why
|
|
78
|
+
|
|
79
|
+
A paper folder collects files the way a desk does: a plan, mails, three drafts of the PDF, an old
|
|
80
|
+
attempt's checklist, a corpus. After a few months nobody can say which file is current, where the
|
|
81
|
+
evidence for a decision is, or where a new thing goes, and an agent asked to continue the work reads
|
|
82
|
+
the wrong one. A fixed root is what build tools settled on long ago (Cargo, Maven): a known set, one
|
|
83
|
+
folder per kind of material, and one place for the rest.
|
|
84
|
+
|
|
85
|
+
A structure linter over file names cannot do this from inside ESLint: ESLint is handed only the files
|
|
86
|
+
paperlint lints, so a stray `.pdf`, `.py` or `.md` is never one of them. The rule lists the folder
|
|
87
|
+
itself.
|
|
88
|
+
|
|
89
|
+
## Examples
|
|
90
|
+
|
|
91
|
+
Failing: a plan beside the paper.
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
papers/agent-rule-drift/PIPELINE-STATUS.md
|
|
95
|
+
1:1 error `PLAN.md` is at the root of a paper folder (the folder holding PIPELINE-STATUS.md),
|
|
96
|
+
and that root holds only paperlint's layout. Where it goes: a section of
|
|
97
|
+
PIPELINE-STATUS.md — the paper's one live-state file holds its stages, its plan
|
|
98
|
+
("Plan / now") and its scorecard; move what is still live there and delete the file.
|
|
99
|
+
The layout (extra/ takes anything and is never judged; the build's paper.* byproducts are never reported):
|
|
100
|
+
<paper>/
|
|
101
|
+
├── PIPELINE-STATUS.md ← the paper's one live-state file: stages, plan, scorecard
|
|
102
|
+
…
|
|
103
|
+
└── extra/ ← anything; never judged
|
|
104
|
+
Rule paper/allowed-files-only: https://github.com/zernie/paperlint/blob/main/docs/rules/paper/allowed-files-only.md paper/allowed-files-only
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Each message names the entry and where it goes, and draws the whole layout above, so it reads on its own. A
|
|
108
|
+
name met in real paper folders has one destination:
|
|
109
|
+
|
|
110
|
+
| entry | where it goes |
|
|
111
|
+
| -------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
112
|
+
| `PLAN.md`, `plans/` | a section of `PIPELINE-STATUS.md` ("Plan / now") |
|
|
113
|
+
| `DECISION-LOG.md` | `research/` |
|
|
114
|
+
| `EMAILS.md` | `submission/` |
|
|
115
|
+
| `data/` | `repro/data/` |
|
|
116
|
+
| `artifact/` | `repro/` (scripts in `repro/tools/`, data in `repro/data/`); the anonymized upload copy in `submission/` |
|
|
117
|
+
| `archive/` | `versions/` for frozen PDFs, `submission/` for an earlier attempt's forms and mails, `extra/` for the rest |
|
|
118
|
+
| `venue-corpus/`, `venues/` | `<papers-root>/venues/<venue>/`, outside the paper folder |
|
|
119
|
+
|
|
120
|
+
Any other name gets the choice by what it is: a record or research → `research/`; about submitting
|
|
121
|
+
→ `submission/`; the paper's live state → a section of `PIPELINE-STATUS.md`; reproduction → `repro/`;
|
|
122
|
+
a frozen PDF of a stage → `versions/`; unclear → `extra/`.
|
|
123
|
+
|
|
124
|
+
Passing: the folder `paperlint new` writes (`PIPELINE-STATUS.md`, `paper.tex`, `paperlint.json`),
|
|
125
|
+
and any folder holding only the entries above.
|
|
126
|
+
|
|
127
|
+
## Options / preset fields
|
|
128
|
+
|
|
129
|
+
No rule options. The paper's `pdf`, `talk.dir` and own preset files add what they name (above).
|
|
130
|
+
|
|
131
|
+
## What it does not check
|
|
132
|
+
|
|
133
|
+
- What is inside the allowed folders. `reviews/`, `siblings/` and `repro/` have their own rules
|
|
134
|
+
([`repro/allowed-files-only`](../repro/allowed-files-only.md) for `repro/`); the rest are free.
|
|
135
|
+
- Whether a `.tex` at the root is included by anything: a fragment the body no longer `\input`s
|
|
136
|
+
passes. Old versions of the source belong in `versions/`.
|
|
137
|
+
- A folder without `PIPELINE-STATUS.md`: it is not a paper to paperlint.
|
|
138
|
+
|
|
139
|
+
## The hook: a new stray entry is refused before it is written
|
|
140
|
+
|
|
141
|
+
`paperlint init` also wires the hook `paper-folder-layout` (PreToolUse on `Edit|Write|MultiEdit`,
|
|
142
|
+
`plugin/hooks/hooks.json`). When an agent is about to create a file that would add an entry the
|
|
143
|
+
layout does not allow — at a paper folder's root, or at the top of its `repro/`
|
|
144
|
+
([`repro/allowed-files-only`](../repro/allowed-files-only.md)) — the hook denies the call, and the reason it gives is the
|
|
145
|
+
rule's own message: where the entry goes, and the layout drawn. The agent writes the file where it
|
|
146
|
+
belongs instead, in the same turn. The text comes from the functions the rules report with
|
|
147
|
+
(`strayTexts`, `reproTexts`); there is no second list.
|
|
148
|
+
|
|
149
|
+
What it never refuses:
|
|
150
|
+
|
|
151
|
+
- **a file that already exists** — editing is never blocked, wherever the file is, so a paper whose
|
|
152
|
+
folder predates the layout keeps working and the rule reports what is out of place;
|
|
153
|
+
- a new file inside an entry that already exists, even a stray one: the entry is not new, and the
|
|
154
|
+
rule names it once;
|
|
155
|
+
- anything outside a paper folder.
|
|
156
|
+
|
|
157
|
+
A new study folder in `repro/` is started with its `README.md`; any other first file in it is
|
|
158
|
+
refused with `repro/allowed-files-only`'s "without a README.md" message.
|
|
159
|
+
|
|
160
|
+
🔴 **What it cannot see: the shell.** `mv`, `cp`, `git mv`, or a script that writes files are not an
|
|
161
|
+
Edit or a Write, so the hook never sees them. The guarantee is the lint: both rules fail `paperlint
|
|
162
|
+
lint`, and CI, whatever wrote the entry. The hook only saves the round trip. It does not read the
|
|
163
|
+
paper's `rules` either: a paper that switched a layout rule off is still refused a new stray entry
|
|
164
|
+
(an existing one is never refused).
|
|
165
|
+
|
|
166
|
+
Its first step is cheap: it looks for the nearest folder above the target file that holds
|
|
167
|
+
`PIPELINE-STATUS.md`, one file check per ancestor, and allows the call when there is none — no git,
|
|
168
|
+
no settings, no lint before that. Measured on 2026-10-12: that step takes about 19 µs. The process
|
|
169
|
+
around it is what every `paperlint hook` costs, the CLI's import: about 650 ms per write on the
|
|
170
|
+
measuring machine, with or without a paper folder (`paper-skills-nudge`, a vigiles hook, measured 730
|
|
171
|
+
to 850 ms on the same machine). The hook fails open: a payload it cannot read is reported on stderr
|
|
172
|
+
and the call goes ahead.
|
|
173
|
+
|
|
174
|
+
A project whose papers all live under one folder can skip the process for every other write by
|
|
175
|
+
giving the hook's entry in its own `.claude/settings.json` an `if`, Claude Code's permission-rule
|
|
176
|
+
filter, for example `"if": "Write(**/papers/**)"`. paperlint does not write one: where papers live is
|
|
177
|
+
a project's choice, and a filter that misses a papers folder would make the hook silently dead there.
|
|
178
|
+
|
|
179
|
+
## How to fix
|
|
180
|
+
|
|
181
|
+
Move the entry where its message says, and update whatever names its path:
|
|
182
|
+
|
|
183
|
+
```sh
|
|
184
|
+
git mv papers/agent-rule-drift/EMAILS.md papers/agent-rule-drift/submission/EMAILS.md
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
To keep something as it is, move it under `extra/`. To switch the rule off for one paper, set it in
|
|
188
|
+
that paper's `paperlint.json`:
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{ "rules": { "paper/allowed-files-only": "off" } }
|
|
192
|
+
```
|