paperlint 10.1.1 → 10.3.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.
Files changed (72) hide show
  1. package/README.md +2 -0
  2. package/dist/adapters/study-registry/index.d.ts +357 -0
  3. package/dist/adapters/study-registry/index.d.ts.map +1 -0
  4. package/dist/adapters/study-registry/index.js +221 -0
  5. package/dist/adapters/study-registry/index.js.map +1 -0
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +42 -2
  8. package/dist/cli.js.map +1 -1
  9. package/dist/domain/repro-layout.d.ts +12 -4
  10. package/dist/domain/repro-layout.d.ts.map +1 -1
  11. package/dist/domain/repro-layout.js +49 -18
  12. package/dist/domain/repro-layout.js.map +1 -1
  13. package/dist/domain/scaffold-layout.d.ts +4 -3
  14. package/dist/domain/scaffold-layout.d.ts.map +1 -1
  15. package/dist/domain/scaffold-layout.js +7 -4
  16. package/dist/domain/scaffold-layout.js.map +1 -1
  17. package/dist/domain/study-registry.d.ts +372 -0
  18. package/dist/domain/study-registry.d.ts.map +1 -0
  19. package/dist/domain/study-registry.js +593 -0
  20. package/dist/domain/study-registry.js.map +1 -0
  21. package/dist/ports/study-registry-codec.d.ts +15 -0
  22. package/dist/ports/study-registry-codec.d.ts.map +1 -0
  23. package/dist/ports/study-registry-codec.js +2 -0
  24. package/dist/ports/study-registry-codec.js.map +1 -0
  25. package/dist/repro-layout-rule.d.ts.map +1 -1
  26. package/dist/repro-layout-rule.js +4 -3
  27. package/dist/repro-layout-rule.js.map +1 -1
  28. package/dist/studies.d.ts +27 -0
  29. package/dist/studies.d.ts.map +1 -0
  30. package/dist/studies.js +163 -0
  31. package/dist/studies.js.map +1 -0
  32. package/dist/study-registry-rule.d.ts +24 -0
  33. package/dist/study-registry-rule.d.ts.map +1 -0
  34. package/dist/study-registry-rule.js +145 -0
  35. package/dist/study-registry-rule.js.map +1 -0
  36. package/docs/design/study-registry.md +246 -0
  37. package/docs/layout.md +51 -46
  38. package/docs/rules/paper/allowed-files-only.md +3 -2
  39. package/docs/rules/repro/allowed-files-only.md +40 -30
  40. package/docs/rules/repro/study-plan-name.md +70 -0
  41. package/docs/rules/repro/study-registered.md +320 -0
  42. package/docs/rules/study/declare-sample.md +1 -1
  43. package/docs/rules.md +53 -51
  44. package/package.json +1 -1
  45. package/skills/build-benchmark/SKILL.md +39 -8
  46. package/skills/build-benchmark/SKILL.md.spec.ts +38 -7
  47. package/skills/build-label-page/SKILL.md +14 -9
  48. package/skills/build-label-page/SKILL.md.spec.ts +13 -8
  49. package/skills/paper-pipeline/references/artifact-checklist.md +2 -1
  50. package/skills/paper-pipeline/references/pipeline-status-template.md +5 -3
  51. package/skills/run-agent-grid/SKILL.md +13 -1
  52. package/skills/run-agent-grid/SKILL.md.spec.ts +12 -0
  53. package/src/adapters/study-registry/index.test.ts +484 -0
  54. package/src/adapters/study-registry/index.ts +328 -0
  55. package/src/cli.ts +53 -2
  56. package/src/domain/paper-layout.test.ts +1 -1
  57. package/src/domain/repro-layout.test.ts +18 -9
  58. package/src/domain/repro-layout.ts +58 -18
  59. package/src/domain/scaffold-layout.ts +7 -4
  60. package/src/domain/study-measurements.test.ts +629 -0
  61. package/src/domain/study-registry.test.ts +666 -0
  62. package/src/domain/study-registry.ts +1044 -0
  63. package/src/folder-layout-hook.test.ts +10 -0
  64. package/src/new-paper.test.ts +29 -2
  65. package/src/ports/study-registry-codec.ts +15 -0
  66. package/src/repro-layout-rule.ts +4 -3
  67. package/src/rule-cases.test.ts +68 -0
  68. package/src/studies-cli.test.ts +113 -0
  69. package/src/studies.test.ts +359 -0
  70. package/src/studies.ts +268 -0
  71. package/src/study-registry-rule.test.ts +456 -0
  72. package/src/study-registry-rule.ts +239 -0
package/docs/rules.md CHANGED
@@ -3,57 +3,59 @@
3
3
  The README lists what each check catches in one line. This page says exactly which file each one
4
4
  reads and when it fails. Errors fail `paperlint lint`; warnings print and do not.
5
5
 
6
- | Rule | Level | Reads | Fails when |
7
- | --------------------------------------------------------------------- | -------------------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
8
- | `paper/stages` | error | `PIPELINE-STATUS.md` | a declared stage's PDF is missing, or the `bytes:` it names does not match the file's real size, or a frozen PDF exists that no stage declares |
9
- | `paper/source` | error | `PIPELINE-STATUS.md` | a declared stage has no frozen `.tex` beside its PDF (a commit hash does not count — squash and gc destroy it) |
10
- | [`study/declare-sample`](rules/study/declare-sample.md) | experimental; off unless a paper turns it on (`"error"`) | each study's `repro/<study>/prereg.json` | a study's pre-registration does not declare its `population` (what the claim is about, source, size), the `strata` and what each of its `screens` removed per stratum value, its `coverage` (share 0–1 or null with a reason, and the strata it cannot reach), or the `author_check` of the drawn units (a doubtful verdict needs a recorded `decision`); reported on `PIPELINE-STATUS.md`, once per problem — [page](rules/study/declare-sample.md) |
11
- | [`cycle/record`](rules/cycle/record.md) | error | `paperlint.json` (`cycles`) | the paper's attempts at venues (`cycles`, one entry per attempt) hold one source's deadlines out of order (registration ≤ submission ≤ resubmission < notification < camera-ready), or copy a portal reading the venue's preset carries; reported on `PIPELINE-STATUS.md`. A record that does not parse — a bare date, two open cycles, an unknown key — is refused by `paperlint lint` itself before any rule runs, exit 2 — [page](rules/cycle/record.md) |
12
- | [`cycle/evidence`](rules/cycle/evidence.md) | error | `paperlint.json` (`cycles`) → the paper folder | a closed cycle (`accepted`, `rejected`, `withdrawn`) or a deadline's `override` names an `evidence` file — the decision mail, the reviews, the withdrawal note, the chairs' mail — that is not in the paper folder; reported on `PIPELINE-STATUS.md` — [page](rules/cycle/evidence.md) |
13
- | [`cycle/stage`](rules/cycle/stage.md) | error | `PIPELINE-STATUS.md` → `paperlint.json` (`cycles`) | with `cycles` declared, a stage names no `cycle: <id>`, names one no cycle has, or a `submitted`/`camera-ready` stage belongs to a cycle whose `phase` is still `porting` — [page](rules/cycle/stage.md) |
14
- | [`cycle/port-done`](rules/cycle/port-done.md) | error | `paper.tex` → `paperlint.json` (`cycles`) → the preset's `template` | the current cycle still declares `"phase": "porting"` (which silences the venue's format rules) while the `\documentclass` is already the venue's template: the port is done, drop the phase — [page](rules/cycle/port-done.md) |
15
- | [`paper/folder-venue-leftover`](rules/paper/folder-venue-leftover.md) | warn | the folder name → `paperlint.json` | the paper's folder is named after another shipped venue than the one it extends (`papers/aisec-2026/` for a paper now set for AIDC); reported on `PIPELINE-STATUS.md` — [page](rules/paper/folder-venue-leftover.md) |
16
- | [`paper/allowed-files-only`](rules/paper/allowed-files-only.md) | error | the paper folder's root listing → `paperlint.json` | a file or folder at the paper's root is not part of the layout (`PLAN.md`, `archive/`, a stray PDF); reported on `PIPELINE-STATUS.md`, once per entry, with where it goes; `extra/` takes anything — [page](rules/paper/allowed-files-only.md) |
17
- | [`repro/allowed-files-only`](rules/repro/allowed-files-only.md) | error | the listing of the paper's `repro/` | an entry at the top of `repro/` is not `README.md`, `.gitignore`, `tools/`, `data/` or a study folder named `<slug>-YYYY-MM-DD` (or `<slug>-YYYY-MM`), or a study folder has no `README.md`; reported on `PIPELINE-STATUS.md`, once per entry, with where it goes by kind (a script → `repro/tools/`, data → `repro/data/`, a note → its study or `research/`) — [page](rules/repro/allowed-files-only.md) |
18
- | [`paper/sources-fresh`](rules/paper/sources-fresh.md) | warn | `paper.tex` → `_build/sources.json` | the paper has not been built (no record of the files TeX read), or a file changed since the last build (its SHA-256 differs) — run `npx paperlint build` |
19
- | `paper/author-list` | error | `paper.tex` → `_build/references.json`, `_build/sources.json` | an entry's authors are not those of the version it cites — typically the arXiv preprint's list under a published venue. Checked online by `paperlint build` (DBLP), reported on the entry's own line (or at the top of `paper.tex`, naming the entry's file); silent without a current build record |
20
- | `paper/section-word` | warn | `paper.tex` and its body files | `§` or `\S\ref` instead of the word Section. **Fixable:** `--fix` writes `Section~\ref{…}` / `Section 5` |
21
- | `paper/leading-zero` | warn | `paper.tex` and its body files | a decimal below 1 without its leading zero (`.05` instead of `0.05`, IEEE / ISO 80000-1 style), counted only where the reader sees it — prose, math and table cells, not macro options, column specs, comments, code or tikz. A number right after `¶` or `§` (`¶¶.42`) is a paragraph designator and is not reported; in a paragraph that cites numbers that way, another bare decimal is reported without a fix, with the zero as a suggestion. **Fixable** |
22
- | `paper/figure-ref-style` | warn | `paper.tex` and its body files | `Fig.~\ref` and `Figure~\ref` mixed in one document; each minority occurrence is reported. **Fixable:** `--fix` writes the majority form |
23
- | [`bib/reachable-entry`](rules/bib/reachable-entry.md) | warn | `paper.tex` → `_build/sources.json`: the databases the last build's bibtex opened | an entry has no doi, url or arXiv id — a reader has nothing to follow. Reported on the entry in the `filecontents` block of `paper.tex` that wrote its `.bib`, or at the top of `paper.tex` with the entry's file and line first; silent until a build records the paper; no fix, the link has to be looked up — [page](rules/bib/reachable-entry.md) |
24
- | `paper/cite-exists` | error | `paper.tex` → `_build/references.json` | an entry's doi or arXiv id resolves to nothing, or to a different work. Checked online by `paperlint build`; a work that was merely not found is not a finding |
25
- | `paper/refs-fresh` | error | `paper.tex` → `_build/references.json` | the databases bibtex opened changed since the build checked them (their SHA-256 differs) — run `npx paperlint build` |
26
- | `paper/refs-checked` | warn | `paper.tex` → `_build/references.json` | a build recorded the paper's databases and none has checked their references, or the last build could not (no network), or its bibtex typeset a key paperlint's reader did not read (an entry behind `%`, inside `@comment{…}`), or bibtex found a database outside the paper directory (`BIBINPUTS`) — so the two rules above did not run on it. Silent without a record of the build (`paper/sources-fresh` speaks) |
27
- | `tex/future-promise` | warn | `paper.tex` and its body files | a camera-ready build still says "will be released" about something already handed over |
28
- | `tex/acm-frontmatter-override` | error | `paper.tex` | an `acmart` build overrides ACM's front-matter commands and drops template elements from page 1 |
29
- | [`tex/template`](rules/tex/template.md) | error | `paper.tex` → `paperlint.json` | the `\documentclass` is not the preset's `template` class, or lacks an option it names — [page](rules/tex/template.md) |
30
- | [`tex/required-section`](rules/tex/required-section.md) | error | `paper.tex` → `paperlint.json` | a section the preset requires (AIDC: «LLM Usage Statement», last) is missing or not where it must be — [page](rules/tex/required-section.md) |
31
- | [`tex/heading-case`](rules/tex/heading-case.md) | optional; the AgenticDev preset sets `error` | `paper.tex` and the files it includes | a word in the paper's `\title` or in a `\section`, `\subsection`, `\subsubsection`, `\paragraph` or `\subparagraph` title is not in the style its scope asks: `{ "title", "headings", "levels"? }`, each `chicago-headline` (Conference Publishing's quote of the Chicago Manual of Style), `sentence` (first word only) or `any`. Words that are a preposition in one title and a particle in another (`up`, `on`, `in`) are not read mid-title. **Fixable:** `--fix` — [page](rules/tex/heading-case.md) |
32
- | [`format/layout-override`](rules/format/layout-override.md) | error | `paper.tex` → `paperlint.json` | the source changes the page layout the venue's template sets for the whole document — `geometry`, a layout `\setlength`, `\linespread`, `\baselinestretch` — [page](rules/format/layout-override.md) |
33
- | [`anonymity/identity`](rules/anonymity/identity.md) | error | `paper.tex` → `_build/paper.facts.json` | a double-blind venue (`"blind": true`) and no `identity` declared, or a declared name, handle, email or project in the PDF's text, metadata or link targets — [page](rules/anonymity/identity.md) |
34
- | [`tex/venue-leftover`](rules/tex/venue-leftover.md) | warn | `paper.tex` → `paperlint.json` | the text names another shipped venue than the one the paper extends («Submission to VenueA 2026» in a paper now set for VenueB) — [page](rules/tex/venue-leftover.md) |
35
- | [`tex/claim-provenance`](rules/tex/claim-provenance.md) | warn | `paper.tex` | a sentence of the body states a number and does not say whose it is — no citation or link, no _we_/_our_, no `\ref` or "Table 2", no sample ("n = 134", "48 runs") — [page](rules/tex/claim-provenance.md) |
36
- | [`tex/register`](rules/tex/register.md) | warn | `paper.tex` and the files it includes | the body runs to a post's register: more than 0.8 sentences per 1000 words open with _And_, _So_, _But_, _Nor_, _Or_ or _Yet_ — the limit is an option — [page](rules/tex/register.md) |
37
- | [`tex/contrast-frames`](rules/tex/contrast-frames.md) | warn | `paper.tex` and the files it includes → `paperlint.json` | contrast frames (_X, not Y_, _not X but Y_, _rather than_, _instead of_, _as opposed to_) per 10,000 words fall outside the band the venue preset's anchors set (AIDC: 0–10.4, from five accepted ACSAC papers); above it, each frame is reported too — [page](rules/tex/contrast-frames.md) |
38
- | [`tex/claim-emphasis`](rules/tex/claim-emphasis.md) | warn | `paper.tex` and the files it includes → `paperlint.json` | claims set in bold inside a sentence (six words or more, or holding a number; not a term, not a label opening a paragraph) per 10,000 words fall outside the preset's band (AIDC: 0–17.8); above it, each is reported too — [page](rules/tex/claim-emphasis.md) |
39
- | [`tex/relation-markers`](rules/tex/relation-markers.md) | warn | `paper.tex` and the files it includes → `paperlint.json` | words that name how a sentence relates to the one before (_because_, _therefore_, _however_, _for example_ …) per 10,000 words fall outside the preset's band (AIDC: 25.4–135.0) — the floor that catches relations left to infer — [page](rules/tex/relation-markers.md) |
40
- | `review/frontmatter` | error | `reviews/*.md` | the review's frontmatter fails paperlint's JSON Schema — a `findings` record without `id`/`status`, an unknown field, or an **open** finding with no `cause` (`skill-defect` · `missing-skill` · `hook` · `rule`). A review with no `findings` key is not checked for findings |
41
- | `sibling/frontmatter` | warn | `siblings/*.md` (not `README.md`) | a sibling card's frontmatter has no `read:` saying how much of the competing paper was read (`full` · `abstract` · `none`) |
42
- | `pdf/fresh` | error | `paper.tex` → `_build/paper.facts.json` | the paper names a venue and the facts cannot be judged: not JSON, a schema other than 3, or they describe a PDF that is gone or differs from the one on disk (its SHA-256) |
43
- | `pdf/profile` | error | `paper.tex` → `paperlint.json` | `paperlint.json` is not JSON or has an unknown key, its `extends` does not resolve (not found, a cycle, a chain longer than four, a preset that fails the schema, an npm name), it names no `kind` while the preset has kinds, or names a kind the preset does not have |
44
- | `pdf/fonts` | error | `paper.tex` → `_build/paper.facts.json` | a font the pages draw is Type 3 or not embedded, or no font starts with the family the venue preset names for body text (`fonts_text`) or headings (`fonts_title`) |
45
- | `pdf/geometry` | error | `paper.tex` → `_build/paper.facts.json` | the page width or height is more than `dimTol` (default 0.05 in) off the preset's, or the column count differs |
46
- | [`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; with `appendix_in_body` too (MSR) the appendix is body wherever it sits and the reference pages are the ones holding only references; an end it cannot find, or signals that disagree, are reported — [page](rules/format/page-limit.md) |
47
- | [`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) |
48
- | `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 |
49
- | [`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) |
50
- | [`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) |
51
- | [`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) |
52
- | [`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) |
53
- | [`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) |
54
- | [`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) |
55
- | [`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) |
56
- | [`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) |
6
+ | Rule | Level | Reads | Fails when |
7
+ | --------------------------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
8
+ | `paper/stages` | error | `PIPELINE-STATUS.md` | a declared stage's PDF is missing, or the `bytes:` it names does not match the file's real size, or a frozen PDF exists that no stage declares |
9
+ | `paper/source` | error | `PIPELINE-STATUS.md` | a declared stage has no frozen `.tex` beside its PDF (a commit hash does not count — squash and gc destroy it) |
10
+ | [`study/declare-sample`](rules/study/declare-sample.md) | experimental; off unless a paper turns it on (`"error"`) | each study's `repro/<study>/prereg.json` | a study's pre-registration does not declare its `population` (what the claim is about, source, size), the `strata` and what each of its `screens` removed per stratum value, its `coverage` (share 0–1 or null with a reason, and the strata it cannot reach), or the `author_check` of the drawn units (a doubtful verdict needs a recorded `decision`); reported on `PIPELINE-STATUS.md`, once per problem — [page](rules/study/declare-sample.md) |
11
+ | [`cycle/record`](rules/cycle/record.md) | error | `paperlint.json` (`cycles`) | the paper's attempts at venues (`cycles`, one entry per attempt) hold one source's deadlines out of order (registration ≤ submission ≤ resubmission < notification < camera-ready), or copy a portal reading the venue's preset carries; reported on `PIPELINE-STATUS.md`. A record that does not parse — a bare date, two open cycles, an unknown key — is refused by `paperlint lint` itself before any rule runs, exit 2 — [page](rules/cycle/record.md) |
12
+ | [`cycle/evidence`](rules/cycle/evidence.md) | error | `paperlint.json` (`cycles`) → the paper folder | a closed cycle (`accepted`, `rejected`, `withdrawn`) or a deadline's `override` names an `evidence` file — the decision mail, the reviews, the withdrawal note, the chairs' mail — that is not in the paper folder; reported on `PIPELINE-STATUS.md` — [page](rules/cycle/evidence.md) |
13
+ | [`cycle/stage`](rules/cycle/stage.md) | error | `PIPELINE-STATUS.md` → `paperlint.json` (`cycles`) | with `cycles` declared, a stage names no `cycle: <id>`, names one no cycle has, or a `submitted`/`camera-ready` stage belongs to a cycle whose `phase` is still `porting` — [page](rules/cycle/stage.md) |
14
+ | [`cycle/port-done`](rules/cycle/port-done.md) | error | `paper.tex` → `paperlint.json` (`cycles`) → the preset's `template` | the current cycle still declares `"phase": "porting"` (which silences the venue's format rules) while the `\documentclass` is already the venue's template: the port is done, drop the phase — [page](rules/cycle/port-done.md) |
15
+ | [`paper/folder-venue-leftover`](rules/paper/folder-venue-leftover.md) | warn | the folder name → `paperlint.json` | the paper's folder is named after another shipped venue than the one it extends (`papers/aisec-2026/` for a paper now set for AIDC); reported on `PIPELINE-STATUS.md` — [page](rules/paper/folder-venue-leftover.md) |
16
+ | [`paper/allowed-files-only`](rules/paper/allowed-files-only.md) | error | the paper folder's root listing → `paperlint.json` | a file or folder at the paper's root is not part of the layout (`PLAN.md`, `archive/`, a stray PDF); reported on `PIPELINE-STATUS.md`, once per entry, with where it goes; `extra/` takes anything — [page](rules/paper/allowed-files-only.md) |
17
+ | [`repro/allowed-files-only`](rules/repro/allowed-files-only.md) | error | the listing of the paper's `repro/` | an entry at the top of `repro/` is not `README.md`, `studies.json`, `.gitignore`, `tools/`, `data/` or a study folder named `<id>-<slug>` (or, in a paper without the registry, `<slug>-YYYY-MM-DD`), or a study folder has no `README.md`; reported on `PIPELINE-STATUS.md`, once per entry, with where it goes by kind (a script → `repro/tools/`, data → `repro/data/`, a note → its study or `research/`) — [page](rules/repro/allowed-files-only.md) |
18
+ | [`repro/study-registered`](rules/repro/study-registered.md) | error | the listing of the paper's `repro/`, `repro/studies.json`, `repro/README.md`, each study's `plan.json` | a study folder with no row in `repro/studies.json`, a row whose folder is missing or not `<id>-<slug>`, a bad or repeated id, a title over 8 words, `done` without a result and an existing result file, `superseded` without an existing successor, a row naming a measurement the registry does not declare, a measurement with no study or an unknown requirement, a headline measurement not approved by the author, a plan that is missing, malformed or — once the study runs — unfilled, a headline study that has run without the author's check in its plan, or the index in `repro/README.md` out of sync with the registry; `paperlint studies new` and `paperlint studies index` write both — [page](rules/repro/study-registered.md) |
19
+ | [`repro/study-plan-name`](rules/repro/study-plan-name.md) | warn | `repro/studies.json` | a study's plan still named `prereg.json`, its old name: read as the plan, and to be renamed `plan.json` — [page](rules/repro/study-plan-name.md) |
20
+ | [`paper/sources-fresh`](rules/paper/sources-fresh.md) | warn | `paper.tex` → `_build/sources.json` | the paper has not been built (no record of the files TeX read), or a file changed since the last build (its SHA-256 differs) — run `npx paperlint build` |
21
+ | `paper/author-list` | error | `paper.tex` → `_build/references.json`, `_build/sources.json` | an entry's authors are not those of the version it cites — typically the arXiv preprint's list under a published venue. Checked online by `paperlint build` (DBLP), reported on the entry's own line (or at the top of `paper.tex`, naming the entry's file); silent without a current build record |
22
+ | `paper/section-word` | warn | `paper.tex` and its body files | `§` or `\S\ref` instead of the word Section. **Fixable:** `--fix` writes `Section~\ref{…}` / `Section 5` |
23
+ | `paper/leading-zero` | warn | `paper.tex` and its body files | a decimal below 1 without its leading zero (`.05` instead of `0.05`, IEEE / ISO 80000-1 style), counted only where the reader sees it — prose, math and table cells, not macro options, column specs, comments, code or tikz. A number right after `¶` or `§` (`¶¶.42`) is a paragraph designator and is not reported; in a paragraph that cites numbers that way, another bare decimal is reported without a fix, with the zero as a suggestion. **Fixable** |
24
+ | `paper/figure-ref-style` | warn | `paper.tex` and its body files | `Fig.~\ref` and `Figure~\ref` mixed in one document; each minority occurrence is reported. **Fixable:** `--fix` writes the majority form |
25
+ | [`bib/reachable-entry`](rules/bib/reachable-entry.md) | warn | `paper.tex` → `_build/sources.json`: the databases the last build's bibtex opened | an entry has no doi, url or arXiv id — a reader has nothing to follow. Reported on the entry in the `filecontents` block of `paper.tex` that wrote its `.bib`, or at the top of `paper.tex` with the entry's file and line first; silent until a build records the paper; no fix, the link has to be looked up — [page](rules/bib/reachable-entry.md) |
26
+ | `paper/cite-exists` | error | `paper.tex` → `_build/references.json` | an entry's doi or arXiv id resolves to nothing, or to a different work. Checked online by `paperlint build`; a work that was merely not found is not a finding |
27
+ | `paper/refs-fresh` | error | `paper.tex` → `_build/references.json` | the databases bibtex opened changed since the build checked them (their SHA-256 differs) — run `npx paperlint build` |
28
+ | `paper/refs-checked` | warn | `paper.tex` → `_build/references.json` | a build recorded the paper's databases and none has checked their references, or the last build could not (no network), or its bibtex typeset a key paperlint's reader did not read (an entry behind `%`, inside `@comment{…}`), or bibtex found a database outside the paper directory (`BIBINPUTS`) — so the two rules above did not run on it. Silent without a record of the build (`paper/sources-fresh` speaks) |
29
+ | `tex/future-promise` | warn | `paper.tex` and its body files | a camera-ready build still says "will be released" about something already handed over |
30
+ | `tex/acm-frontmatter-override` | error | `paper.tex` | an `acmart` build overrides ACM's front-matter commands and drops template elements from page 1 |
31
+ | [`tex/template`](rules/tex/template.md) | error | `paper.tex` → `paperlint.json` | the `\documentclass` is not the preset's `template` class, or lacks an option it names — [page](rules/tex/template.md) |
32
+ | [`tex/required-section`](rules/tex/required-section.md) | error | `paper.tex` → `paperlint.json` | a section the preset requires (AIDC: «LLM Usage Statement», last) is missing or not where it must be — [page](rules/tex/required-section.md) |
33
+ | [`tex/heading-case`](rules/tex/heading-case.md) | optional; the AgenticDev preset sets `error` | `paper.tex` and the files it includes | a word in the paper's `\title` or in a `\section`, `\subsection`, `\subsubsection`, `\paragraph` or `\subparagraph` title is not in the style its scope asks: `{ "title", "headings", "levels"? }`, each `chicago-headline` (Conference Publishing's quote of the Chicago Manual of Style), `sentence` (first word only) or `any`. Words that are a preposition in one title and a particle in another (`up`, `on`, `in`) are not read mid-title. **Fixable:** `--fix` — [page](rules/tex/heading-case.md) |
34
+ | [`format/layout-override`](rules/format/layout-override.md) | error | `paper.tex` → `paperlint.json` | the source changes the page layout the venue's template sets for the whole document — `geometry`, a layout `\setlength`, `\linespread`, `\baselinestretch` — [page](rules/format/layout-override.md) |
35
+ | [`anonymity/identity`](rules/anonymity/identity.md) | error | `paper.tex` → `_build/paper.facts.json` | a double-blind venue (`"blind": true`) and no `identity` declared, or a declared name, handle, email or project in the PDF's text, metadata or link targets — [page](rules/anonymity/identity.md) |
36
+ | [`tex/venue-leftover`](rules/tex/venue-leftover.md) | warn | `paper.tex` → `paperlint.json` | the text names another shipped venue than the one the paper extends («Submission to VenueA 2026» in a paper now set for VenueB) — [page](rules/tex/venue-leftover.md) |
37
+ | [`tex/claim-provenance`](rules/tex/claim-provenance.md) | warn | `paper.tex` | a sentence of the body states a number and does not say whose it is — no citation or link, no _we_/_our_, no `\ref` or "Table 2", no sample ("n = 134", "48 runs") — [page](rules/tex/claim-provenance.md) |
38
+ | [`tex/register`](rules/tex/register.md) | warn | `paper.tex` and the files it includes | the body runs to a post's register: more than 0.8 sentences per 1000 words open with _And_, _So_, _But_, _Nor_, _Or_ or _Yet_ — the limit is an option — [page](rules/tex/register.md) |
39
+ | [`tex/contrast-frames`](rules/tex/contrast-frames.md) | warn | `paper.tex` and the files it includes → `paperlint.json` | contrast frames (_X, not Y_, _not X but Y_, _rather than_, _instead of_, _as opposed to_) per 10,000 words fall outside the band the venue preset's anchors set (AIDC: 0–10.4, from five accepted ACSAC papers); above it, each frame is reported too — [page](rules/tex/contrast-frames.md) |
40
+ | [`tex/claim-emphasis`](rules/tex/claim-emphasis.md) | warn | `paper.tex` and the files it includes → `paperlint.json` | claims set in bold inside a sentence (six words or more, or holding a number; not a term, not a label opening a paragraph) per 10,000 words fall outside the preset's band (AIDC: 0–17.8); above it, each is reported too — [page](rules/tex/claim-emphasis.md) |
41
+ | [`tex/relation-markers`](rules/tex/relation-markers.md) | warn | `paper.tex` and the files it includes → `paperlint.json` | words that name how a sentence relates to the one before (_because_, _therefore_, _however_, _for example_ …) per 10,000 words fall outside the preset's band (AIDC: 25.4–135.0) — the floor that catches relations left to infer — [page](rules/tex/relation-markers.md) |
42
+ | `review/frontmatter` | error | `reviews/*.md` | the review's frontmatter fails paperlint's JSON Schema — a `findings` record without `id`/`status`, an unknown field, or an **open** finding with no `cause` (`skill-defect` · `missing-skill` · `hook` · `rule`). A review with no `findings` key is not checked for findings |
43
+ | `sibling/frontmatter` | warn | `siblings/*.md` (not `README.md`) | a sibling card's frontmatter has no `read:` saying how much of the competing paper was read (`full` · `abstract` · `none`) |
44
+ | `pdf/fresh` | error | `paper.tex` → `_build/paper.facts.json` | the paper names a venue and the facts cannot be judged: not JSON, a schema other than 3, or they describe a PDF that is gone or differs from the one on disk (its SHA-256) |
45
+ | `pdf/profile` | error | `paper.tex` → `paperlint.json` | `paperlint.json` is not JSON or has an unknown key, its `extends` does not resolve (not found, a cycle, a chain longer than four, a preset that fails the schema, an npm name), it names no `kind` while the preset has kinds, or names a kind the preset does not have |
46
+ | `pdf/fonts` | error | `paper.tex` → `_build/paper.facts.json` | a font the pages draw is Type 3 or not embedded, or no font starts with the family the venue preset names for body text (`fonts_text`) or headings (`fonts_title`) |
47
+ | `pdf/geometry` | error | `paper.tex` → `_build/paper.facts.json` | the page width or height is more than `dimTol` (default 0.05 in) off the preset's, or the column count differs |
48
+ | [`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; with `appendix_in_body` too (MSR) the appendix is body wherever it sits and the reference pages are the ones holding only references; an end it cannot find, or signals that disagree, are reported — [page](rules/format/page-limit.md) |
49
+ | [`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) |
50
+ | `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 |
51
+ | [`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) |
52
+ | [`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) |
53
+ | [`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) |
54
+ | [`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) |
55
+ | [`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) |
56
+ | [`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) |
57
+ | [`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) |
58
+ | [`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) |
57
59
 
58
60
  **Body files** are the files of the paper's directory that TeX read after `\begin{document}` in the
59
61
  last build, as `paperlint build` recorded them in `_build/sources.json` — not a preamble file, not a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paperlint",
3
- "version": "10.1.1",
3
+ "version": "10.3.0",
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",
@@ -5,7 +5,7 @@ context: fork
5
5
  allowed-tools: [Read, Write, Edit, Grep, Glob, Bash, Agent]
6
6
  ---
7
7
 
8
- <!-- vigiles:sha256:eed7d0afdfee87a6 compiled from skills/build-benchmark/SKILL.md.spec.ts -->
8
+ <!-- vigiles:sha256:ed855c5f3aa34b10 compiled from skills/build-benchmark/SKILL.md.spec.ts -->
9
9
 
10
10
  # build-benchmark — the study + the artifact reviewers can run
11
11
 
@@ -36,6 +36,34 @@ about *preventing* something, write the one sentence saying **what physically st
36
36
  the experimental arm**. "There is different text in it" means you are measuring persuasion, not
37
37
  prevention, and the design is wrong before a single run.
38
38
 
39
+ ## 0.5 Register the study before its first file
40
+
41
+ Every study gets its folder, its id and its row from one command, never from a folder made by hand:
42
+
43
+ ```bash
44
+ npx paperlint studies new <paper-dir> "<plain title, at most 8 words>" --kind measurement
45
+ ```
46
+
47
+ It takes the next id in `repro/studies.json`, creates `repro/<id>-<slug>/README.md` and the study's
48
+ plan, `plan.json`, appends the row (`status: designing`, `plan: "plan.json"`) and regenerates the index
49
+ in `repro/README.md`. The kinds: `measurement`, `spike`, `tooling`, `data`, `artifact`, `salvage`.
50
+ Write the study's one-sentence `question` into its row at once. Never write a plan by hand under
51
+ another name: the template's fields are the ones `repro/study-registered` reads.
52
+
53
+ **A measurement that spans studies** (a pilot, the main run, a replication) is declared once, in
54
+ `measurements` at the top of `repro/studies.json` — `id`, `title`, `rq`, `role` (`headline` or
55
+ `baseline`) — and each of its studies' rows names it in `measurement`. The author's requirements on
56
+ the measurements go, verbatim, into `requirements` (`id`, `text`, `date`, `source`), and each
57
+ measurement lists the ones it must meet; never in a separate note. A `headline` measurement carries
58
+ the author's `approval` (`by` the registry's `author`, `date`, their words in `quote`), written
59
+ from the author's own message, never by the agent that designed the study.
60
+
61
+ As the study moves, edit its row in `repro/studies.json`: `running` when the first counted run starts;
62
+ when it ends, `done` with `result` (one line) and `result_file` (the file inside the folder the result
63
+ is read from) — or `stopped`, `dropped`, or `superseded` with `superseded_by` — and `in_paper`. Then
64
+ run `npx paperlint studies index <paper-dir>`. The rule `repro/study-registered` fails `paperlint lint`
65
+ on a folder without a row, a `done` row without its result, and an index out of sync.
66
+
39
67
  ## 1. Design that inverts the pitfall you're critiquing
40
68
 
41
69
  The strongest measurement papers are a corrected version of the mistake they name. Pick the design that
@@ -54,13 +82,16 @@ Write the design as the answer to "what would make the critiqued pitfall impossi
54
82
  **Declare the population, the screens and the coverage before freezing the sample.** Each screen
55
83
  between the population and the sample (fits one excerpt, one classifier lane, a model-picked item)
56
84
  changes what the sample can say, not only its size; screens that are reasonable one by one can leave a
57
- sample nobody meets in practice. So, before the draw is frozen, the study's `prereg.json` says what
58
- the claim is about, what each screen removed per stratum and what share the sample can represent; then
59
- show the author the drawn **units** (the rules or cases, not the items made from them) and ask: *is
60
- this what you meet in practice?* The fields are the experimental rule
61
- `study/declare-sample` (`docs/rules/study/declare-sample.md`), off by default: do this step either way,
62
- and turn the check on with `"rules": { "study/declare-sample": "error" }` in the paper's `paperlint.json`
63
- for `paperlint lint` to fail until they are there.
85
+ sample nobody meets in practice. So, before the draw is frozen, fill the study's `plan.json`: the
86
+ `question`, the `population` (its `source`, and the `draw`: `{ "kind": "all", "method" }` for a census,
87
+ or `{ "kind": "seeded", "method", "seed" }` for a sample),
88
+ the `arms` (`[]` for a census) and the `primary_outcome`; what each screen removed per stratum and
89
+ what share the sample can represent go into the same file, under `extra` (the plan is closed: any
90
+ other unknown key is an error). Then show the
91
+ author the drawn **units** (the rules or cases, not the items made from them), ask *is this what you
92
+ meet in practice?*, and record the answer in the plan's `author_check` (`by`, `date`, their words in
93
+ `quote`). Once the study is `running`, `repro/study-registered` fails the lint on an empty plan field,
94
+ and, for a study of a `headline` measurement, on a plan without the author's check.
64
95
 
65
96
  ## 2. Honest statistics — the part reviewers attack first
66
97
 
@@ -42,6 +42,34 @@ about *preventing* something, write the one sentence saying **what physically st
42
42
  the experimental arm**. "There is different text in it" means you are measuring persuasion, not
43
43
  prevention, and the design is wrong before a single run.
44
44
 
45
+ ## 0.5 Register the study before its first file
46
+
47
+ Every study gets its folder, its id and its row from one command, never from a folder made by hand:
48
+
49
+ \`\`\`bash
50
+ npx paperlint studies new <paper-dir> "<plain title, at most 8 words>" --kind measurement
51
+ \`\`\`
52
+
53
+ It takes the next id in \`repro/studies.json\`, creates \`repro/<id>-<slug>/README.md\` and the study's
54
+ plan, \`plan.json\`, appends the row (\`status: designing\`, \`plan: "plan.json"\`) and regenerates the index
55
+ in \`repro/README.md\`. The kinds: \`measurement\`, \`spike\`, \`tooling\`, \`data\`, \`artifact\`, \`salvage\`.
56
+ Write the study's one-sentence \`question\` into its row at once. Never write a plan by hand under
57
+ another name: the template's fields are the ones \`repro/study-registered\` reads.
58
+
59
+ **A measurement that spans studies** (a pilot, the main run, a replication) is declared once, in
60
+ \`measurements\` at the top of \`repro/studies.json\` — \`id\`, \`title\`, \`rq\`, \`role\` (\`headline\` or
61
+ \`baseline\`) — and each of its studies' rows names it in \`measurement\`. The author's requirements on
62
+ the measurements go, verbatim, into \`requirements\` (\`id\`, \`text\`, \`date\`, \`source\`), and each
63
+ measurement lists the ones it must meet; never in a separate note. A \`headline\` measurement carries
64
+ the author's \`approval\` (\`by\` the registry's \`author\`, \`date\`, their words in \`quote\`), written
65
+ from the author's own message, never by the agent that designed the study.
66
+
67
+ As the study moves, edit its row in \`repro/studies.json\`: \`running\` when the first counted run starts;
68
+ when it ends, \`done\` with \`result\` (one line) and \`result_file\` (the file inside the folder the result
69
+ is read from) — or \`stopped\`, \`dropped\`, or \`superseded\` with \`superseded_by\` — and \`in_paper\`. Then
70
+ run \`npx paperlint studies index <paper-dir>\`. The rule \`repro/study-registered\` fails \`paperlint lint\`
71
+ on a folder without a row, a \`done\` row without its result, and an index out of sync.
72
+
45
73
  ## 1. Design that inverts the pitfall you're critiquing
46
74
 
47
75
  The strongest measurement papers are a corrected version of the mistake they name. Pick the design that
@@ -60,13 +88,16 @@ Write the design as the answer to "what would make the critiqued pitfall impossi
60
88
  **Declare the population, the screens and the coverage before freezing the sample.** Each screen
61
89
  between the population and the sample (fits one excerpt, one classifier lane, a model-picked item)
62
90
  changes what the sample can say, not only its size; screens that are reasonable one by one can leave a
63
- sample nobody meets in practice. So, before the draw is frozen, the study's \`prereg.json\` says what
64
- the claim is about, what each screen removed per stratum and what share the sample can represent; then
65
- show the author the drawn **units** (the rules or cases, not the items made from them) and ask: *is
66
- this what you meet in practice?* The fields are the experimental rule
67
- \`study/declare-sample\` (\`docs/rules/study/declare-sample.md\`), off by default: do this step either way,
68
- and turn the check on with \`"rules": { "study/declare-sample": "error" }\` in the paper's \`paperlint.json\`
69
- for \`paperlint lint\` to fail until they are there.
91
+ sample nobody meets in practice. So, before the draw is frozen, fill the study's \`plan.json\`: the
92
+ \`question\`, the \`population\` (its \`source\`, and the \`draw\`: \`{ "kind": "all", "method" }\` for a census,
93
+ or \`{ "kind": "seeded", "method", "seed" }\` for a sample),
94
+ the \`arms\` (\`[]\` for a census) and the \`primary_outcome\`; what each screen removed per stratum and
95
+ what share the sample can represent go into the same file, under \`extra\` (the plan is closed: any
96
+ other unknown key is an error). Then show the
97
+ author the drawn **units** (the rules or cases, not the items made from them), ask *is this what you
98
+ meet in practice?*, and record the answer in the plan's \`author_check\` (\`by\`, \`date\`, their words in
99
+ \`quote\`). Once the study is \`running\`, \`repro/study-registered\` fails the lint on an empty plan field,
100
+ and, for a study of a \`headline\` measurement, on a plan without the author's check.
70
101
 
71
102
  ## 2. Honest statistics — the part reviewers attack first
72
103
 
@@ -4,7 +4,7 @@ description: Build a phone labelling page published as a claude.ai Artifact with
4
4
  allowed-tools: [Read, Write, Edit, Grep, Glob, Bash, Artifact, ArtifactData]
5
5
  ---
6
6
 
7
- <!-- vigiles:sha256:592d7b4f8f27c4ce compiled from skills/build-label-page/SKILL.md.spec.ts -->
7
+ <!-- vigiles:sha256:dd742c1fcb34cfd6 compiled from skills/build-label-page/SKILL.md.spec.ts -->
8
8
 
9
9
  # build-label-page — a page the person labels on a phone, and the labels back in the repo
10
10
 
@@ -49,15 +49,20 @@ The full text, each with its reason and its check, is
49
49
  fit the time they have on a phone? Who is the labeller: a page with a store opens only for people
50
50
  with access to the artifact, so the labeller is the owner or has been shared as a Contributor.
51
51
  If the study design is not written yet, `build-benchmark` comes first.
52
- If the items come from a drawn sample, the study's `prereg.json` declares the population, the
53
- screens and the coverage before the sample is frozen (the experimental rule `study/declare-sample` checks it, off by default), and the
54
- author has been shown the drawn **units** (the rules or cases, not the items) with "is this what
55
- you meet in practice?". The labelling page is not that check: items made from easy units each look
56
- fine on their own.
52
+ If the items come from a drawn sample, the study's `plan.json` (written by
53
+ `paperlint studies new`) declares the population and its draw before the sample is frozen, and its
54
+ `author_check` records that the author has been shown the drawn **units** (the rules or cases, not
55
+ the items) with "is this what you meet in practice?". The labelling page is not that check: items
56
+ made from easy units each look fine on their own.
57
57
  2. **Copy the generator into the study and write the items.** The study folder is
58
- `<paper-dir>/repro/<slug>-YYYY-MM-DD/` (`gold-labels-2026-10-12`; `repro/allowed-files-only` wants the
59
- date, a `README.md` in it saying what the study asks, and a line in `repro/README.md`; where this
60
- sits in stage 3, and what checks each place: `docs/layout.md` in the paperlint package).
58
+ `<paper-dir>/repro/<id>-<slug>/`, made by `npx paperlint studies new <paper-dir> "Gold labels"
59
+ --kind data` when the labelling is a study of its own (`P1-S04-gold-labels`): the command writes
60
+ its `README.md`, its `plan.json` and its row in `repro/studies.json`, and `repro/study-registered`
61
+ checks them; fill the plan (what is labelled, the items' source, `arms: []`, the primary outcome)
62
+ before the first label. When
63
+ the labels are in, update the row (`status`, `result`, `result_file`) and run
64
+ `npx paperlint studies index <paper-dir>`. Where this sits in stage 3, and what checks each place:
65
+ `docs/layout.md` in the paperlint package.
61
66
  `items.json` follows the header of the generator. Source ids in `items.json` are the study's own;
62
67
  the page never shows them.
63
68
 
@@ -59,15 +59,20 @@ The full text, each with its reason and its check, is
59
59
  fit the time they have on a phone? Who is the labeller: a page with a store opens only for people
60
60
  with access to the artifact, so the labeller is the owner or has been shared as a Contributor.
61
61
  If the study design is not written yet, \`build-benchmark\` comes first.
62
- If the items come from a drawn sample, the study's \`prereg.json\` declares the population, the
63
- screens and the coverage before the sample is frozen (the experimental rule \`study/declare-sample\` checks it, off by default), and the
64
- author has been shown the drawn **units** (the rules or cases, not the items) with "is this what
65
- you meet in practice?". The labelling page is not that check: items made from easy units each look
66
- fine on their own.
62
+ If the items come from a drawn sample, the study's \`plan.json\` (written by
63
+ \`paperlint studies new\`) declares the population and its draw before the sample is frozen, and its
64
+ \`author_check\` records that the author has been shown the drawn **units** (the rules or cases, not
65
+ the items) with "is this what you meet in practice?". The labelling page is not that check: items
66
+ made from easy units each look fine on their own.
67
67
  2. **Copy the generator into the study and write the items.** The study folder is
68
- \`<paper-dir>/repro/<slug>-YYYY-MM-DD/\` (\`gold-labels-2026-10-12\`; \`repro/allowed-files-only\` wants the
69
- date, a \`README.md\` in it saying what the study asks, and a line in \`repro/README.md\`; where this
70
- sits in stage 3, and what checks each place: \`docs/layout.md\` in the paperlint package).
68
+ \`<paper-dir>/repro/<id>-<slug>/\`, made by \`npx paperlint studies new <paper-dir> "Gold labels"
69
+ --kind data\` when the labelling is a study of its own (\`P1-S04-gold-labels\`): the command writes
70
+ its \`README.md\`, its \`plan.json\` and its row in \`repro/studies.json\`, and \`repro/study-registered\`
71
+ checks them; fill the plan (what is labelled, the items' source, \`arms: []\`, the primary outcome)
72
+ before the first label. When
73
+ the labels are in, update the row (\`status\`, \`result\`, \`result_file\`) and run
74
+ \`npx paperlint studies index <paper-dir>\`. Where this sits in stage 3, and what checks each place:
75
+ \`docs/layout.md\` in the paperlint package.
71
76
  \`items.json\` follows the header of the generator. Source ids in \`items.json\` are the study's own;
72
77
  the page never shows them.
73
78
 
@@ -29,7 +29,8 @@ the 140-run `reproduce.py` (AgenticDev) and the 46-guard `evaluate.py`/`mutate.p
29
29
 
30
30
  In the paper folder the artifact lives in `repro/`, laid out as `repro/allowed-files-only` requires: the
31
31
  scripts the whole paper uses in `repro/tools/`, shared data in `repro/data/`, each study in
32
- `repro/<slug>-YYYY-MM-DD/` with its `README.md`, and `repro/README.md` as the index. The shipped
32
+ `repro/<id>-<slug>/` with its `README.md` and its row in `repro/studies.json`
33
+ (`repro/study-registered`), and `repro/README.md` as the index generated from it. The shipped
33
34
  zip below is built from it; the anonymized copy that was uploaded goes in the paper's `submission/`.
34
35
 
35
36
  - `reproduce.py` / the harness scripts — self-check, print `RESULT: PASS`, exit non-zero on drift.