paperlint 13.1.0 → 13.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 (141) hide show
  1. package/.github/workflows/ci.yml +10 -0
  2. package/CLAUDE.md +9 -1
  3. package/dist/adapters/git/index.d.ts.map +1 -1
  4. package/dist/adapters/git/index.js +13 -0
  5. package/dist/adapters/git/index.js.map +1 -1
  6. package/dist/adapters/study-registry/index.d.ts +142 -1
  7. package/dist/adapters/study-registry/index.d.ts.map +1 -1
  8. package/dist/adapters/study-registry/index.js +160 -1
  9. package/dist/adapters/study-registry/index.js.map +1 -1
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/cli.js +3 -0
  12. package/dist/cli.js.map +1 -1
  13. package/dist/doctor.d.ts.map +1 -1
  14. package/dist/doctor.js +2 -2
  15. package/dist/doctor.js.map +1 -1
  16. package/dist/domain/decide-first.d.ts +43 -0
  17. package/dist/domain/decide-first.d.ts.map +1 -1
  18. package/dist/domain/decide-first.js +45 -2
  19. package/dist/domain/decide-first.js.map +1 -1
  20. package/dist/domain/study-gates.d.ts +96 -0
  21. package/dist/domain/study-gates.d.ts.map +1 -0
  22. package/dist/domain/study-gates.js +56 -0
  23. package/dist/domain/study-gates.js.map +1 -0
  24. package/dist/domain/study-mode.d.ts +6 -0
  25. package/dist/domain/study-mode.d.ts.map +1 -1
  26. package/dist/domain/study-mode.js.map +1 -1
  27. package/dist/domain/study-registry.d.ts +24 -4
  28. package/dist/domain/study-registry.d.ts.map +1 -1
  29. package/dist/domain/study-registry.js +39 -2
  30. package/dist/domain/study-registry.js.map +1 -1
  31. package/dist/domain/study-result.d.ts +151 -0
  32. package/dist/domain/study-result.d.ts.map +1 -0
  33. package/dist/domain/study-result.js +175 -0
  34. package/dist/domain/study-result.js.map +1 -0
  35. package/dist/outputs-before-decision-rule.d.ts.map +1 -1
  36. package/dist/outputs-before-decision-rule.js +37 -3
  37. package/dist/outputs-before-decision-rule.js.map +1 -1
  38. package/dist/ports/git-history.d.ts +12 -0
  39. package/dist/ports/git-history.d.ts.map +1 -1
  40. package/dist/ports/study-registry-codec.d.ts +3 -0
  41. package/dist/ports/study-registry-codec.d.ts.map +1 -1
  42. package/dist/skills/assess-novelty/scripts/candidate_page.d.ts +1 -0
  43. package/dist/skills/assess-novelty/scripts/novelty_reader_brief.d.ts +25 -0
  44. package/dist/skills/build-label-page/scripts/label-page-cli.d.ts +6 -0
  45. package/dist/skills/build-label-page/scripts/label-page-commands.d.ts +14 -0
  46. package/dist/skills/build-label-page/scripts/label-page-core.d.ts +149 -0
  47. package/dist/skills/build-label-page/scripts/label-page-html.d.ts +5 -0
  48. package/dist/skills/build-label-page/scripts/label-page.d.ts +2 -0
  49. package/dist/study-modes.d.ts +3 -0
  50. package/dist/study-modes.d.ts.map +1 -1
  51. package/dist/study-modes.js +8 -1
  52. package/dist/study-modes.js.map +1 -1
  53. package/dist/study-registry-rule.d.ts.map +1 -1
  54. package/dist/study-registry-rule.js +1 -0
  55. package/dist/study-registry-rule.js.map +1 -1
  56. package/dist/study-result-rule.d.ts +12 -0
  57. package/dist/study-result-rule.d.ts.map +1 -0
  58. package/dist/study-result-rule.js +87 -0
  59. package/dist/study-result-rule.js.map +1 -0
  60. package/docs/design/study-gates.md +208 -0
  61. package/docs/rules/repro/outputs-before-decision.md +8 -0
  62. package/docs/rules/repro/study-registered.md +38 -15
  63. package/docs/rules/repro/study-result.md +100 -0
  64. package/docs/rules.md +1 -0
  65. package/docs/toolchain.md +1 -1
  66. package/eslint.config.mjs +52 -0
  67. package/package.json +3 -1
  68. package/presets/agentverify.md +15 -4
  69. package/schemas/plan.v2.schema.json +341 -0
  70. package/skills/assess-novelty/scripts/candidate_page.css +31 -0
  71. package/skills/assess-novelty/scripts/candidate_page.html +934 -0
  72. package/skills/assess-novelty/scripts/candidate_page.js +165 -0
  73. package/skills/assess-novelty/scripts/candidate_page.test.ts +401 -0
  74. package/skills/assess-novelty/scripts/candidate_page.ts +194 -0
  75. package/skills/assess-novelty/scripts/check_novelty.py +230 -0
  76. package/skills/assess-novelty/scripts/check_novelty.test.ts +490 -0
  77. package/skills/assess-novelty/scripts/check_novelty_coverage.test.ts +176 -0
  78. package/skills/assess-novelty/scripts/crop_quote.py +104 -0
  79. package/skills/assess-novelty/scripts/crop_quote.test.ts +130 -0
  80. package/skills/assess-novelty/scripts/fixtures/crop-quote/make_sample.py +42 -0
  81. package/skills/assess-novelty/scripts/fixtures/crop-quote/sample.pdf +106 -0
  82. package/skills/assess-novelty/scripts/fixtures/novelty/CLAIMS.md +11 -0
  83. package/skills/assess-novelty/scripts/fixtures/novelty/paper.tex +10 -0
  84. package/skills/assess-novelty/scripts/fixtures/novelty/siblings/2026-10-27-novelty-refutation.md +8 -0
  85. package/skills/assess-novelty/scripts/fixtures/novelty/siblings/data/doe-preprint.txt +63 -0
  86. package/skills/assess-novelty/scripts/fixtures/novelty/siblings/novelty.json +54 -0
  87. package/skills/assess-novelty/scripts/novelty_candidates.py +136 -0
  88. package/skills/assess-novelty/scripts/novelty_candidates.test.ts +222 -0
  89. package/skills/assess-novelty/scripts/novelty_cells.py +88 -0
  90. package/skills/assess-novelty/scripts/novelty_cells.test.ts +159 -0
  91. package/skills/assess-novelty/scripts/novelty_coverage.py +55 -0
  92. package/skills/assess-novelty/scripts/novelty_reader_brief.js +165 -0
  93. package/skills/assess-novelty/scripts/novelty_reader_brief.test.ts +251 -0
  94. package/skills/assess-novelty/scripts/novelty_reader_brief.ts +215 -0
  95. package/skills/assess-novelty/scripts/quote_match.py +34 -0
  96. package/skills/assess-novelty/scripts/script_edges.test.ts +174 -0
  97. package/skills/build-label-page/SKILL.md +85 -54
  98. package/skills/build-label-page/SKILL.md.spec.ts +85 -53
  99. package/skills/build-label-page/build-label-page.test.ts +10 -941
  100. package/skills/build-label-page/references/checklist.md +45 -3
  101. package/skills/build-label-page/references/closed-choice-generator.md +88 -0
  102. package/skills/build-label-page/scripts/label-page-cli.js +72 -0
  103. package/skills/build-label-page/scripts/label-page-cli.ts +85 -0
  104. package/skills/build-label-page/scripts/label-page-commands.js +14 -0
  105. package/skills/build-label-page/scripts/label-page-commands.ts +14 -0
  106. package/skills/build-label-page/scripts/label-page-core.js +850 -0
  107. package/skills/build-label-page/scripts/label-page-core.test.ts +1164 -0
  108. package/skills/build-label-page/scripts/label-page-core.ts +1210 -0
  109. package/skills/build-label-page/scripts/label-page-html.js +33 -0
  110. package/skills/build-label-page/scripts/label-page-html.test.ts +413 -0
  111. package/skills/build-label-page/scripts/label-page-html.ts +43 -0
  112. package/skills/build-label-page/scripts/label-page.js +70 -0
  113. package/skills/build-label-page/scripts/label-page.test.ts +71 -0
  114. package/skills/build-label-page/scripts/label-page.ts +71 -0
  115. package/skills/build-label-page/scripts/page-template.html +513 -0
  116. package/src/adapters/git/index.test.ts +33 -0
  117. package/src/adapters/git/index.ts +19 -0
  118. package/src/adapters/study-registry/gates.test.ts +181 -0
  119. package/src/adapters/study-registry/index.ts +219 -1
  120. package/src/adapters/study-registry/mode.test.ts +6 -0
  121. package/src/cli.ts +3 -0
  122. package/src/doctor.ts +4 -2
  123. package/src/domain/decide-first.test.ts +131 -0
  124. package/src/domain/decide-first.ts +92 -2
  125. package/src/domain/study-gates.ts +125 -0
  126. package/src/domain/study-measurements.test.ts +71 -0
  127. package/src/domain/study-mode.ts +6 -0
  128. package/src/domain/study-registry.ts +64 -2
  129. package/src/domain/study-result.test.ts +300 -0
  130. package/src/domain/study-result.ts +358 -0
  131. package/src/outputs-before-decision-rule.test.ts +115 -2
  132. package/src/outputs-before-decision-rule.ts +50 -2
  133. package/src/ports/git-history.ts +12 -0
  134. package/src/ports/study-registry-codec.ts +3 -0
  135. package/src/rule-cases.test.ts +74 -0
  136. package/src/studies.test.ts +3 -0
  137. package/src/study-modes.ts +10 -1
  138. package/src/study-registry-rule.ts +1 -0
  139. package/src/study-result-rule.test.ts +281 -0
  140. package/src/study-result-rule.ts +123 -0
  141. package/skills/build-label-page/references/example-generator.py +0 -803
@@ -132,6 +132,7 @@ author's check of the drawn units. `paperlint studies new` writes the template b
132
132
 
133
133
  ```json
134
134
  {
135
+ "$schema": "https://unpkg.com/paperlint/schemas/plan.v2.schema.json",
135
136
  "question": "",
136
137
  "population": {
137
138
  "source": "",
@@ -143,23 +144,36 @@ author's check of the drawn units. `paperlint studies new` writes the template b
143
144
  "mode": {
144
145
  "kind": "decide_before_run",
145
146
  "outputs": [],
146
- "decision": { "file": "repro/P1-S02-census/decision.md", "note": "" }
147
- }
147
+ "decision": { "file": "repro/P1-S02-census/decision.md", "note": "" },
148
+ "we_lose_if": null
149
+ },
150
+ "second_check": null,
151
+ "self_test": null,
152
+ "funnel": null,
153
+ "recall_audit": null
148
154
  }
149
155
  ```
150
156
 
151
- | field | what it holds |
152
- | ------------------------ | -------------------------------------------------------------------------------- |
153
- | `question` | what the study measures, one sentence |
154
- | `population.source` | where the units come from: the census, crawl or registry, with its date |
155
- | `population.draw.kind` | `all` (the whole population, a census: no seed) or `seeded` (a sample) |
156
- | `population.draw.method` | how the sample is drawn: `census`, `random`, `stratified` … |
157
- | `population.draw.seed` | `seeded` only, and required there: the draw's seed, a string or a number |
158
- | `arms` | the conditions compared, each `{ "name": … }`, each name once; `[]` for a census |
159
- | `primary_outcome` | the one number the study reports first |
160
- | `author_check` | `null` until the author has seen the drawn units; then `by`, `date`, `quote` |
161
- | `extra` | optional: the paper's own keys (hypotheses, models, budget), kept unread |
162
- | `mode` | a measurement's: exactly one of `decide_before_run` or `exploratory` |
157
+ That is a measurement's template, a plan v2. Another kind's has no `$schema`, `mode` or gates.
158
+
159
+ | field | what it holds |
160
+ | ------------------------ | ------------------------------------------------------------------------------------ |
161
+ | `question` | what the study measures, one sentence |
162
+ | `population.source` | where the units come from: the census, crawl or registry, with its date |
163
+ | `population.draw.kind` | `all` (the whole population, a census: no seed) or `seeded` (a sample) |
164
+ | `population.draw.method` | how the sample is drawn: `census`, `random`, `stratified` … |
165
+ | `population.draw.seed` | `seeded` only, and required there: the draw's seed, a string or a number |
166
+ | `arms` | the conditions compared, each `{ "name": … }`, each name once; `[]` for a census |
167
+ | `primary_outcome` | the one number the study reports first |
168
+ | `author_check` | `null` until the author has seen the drawn units; then `by`, `date`, `quote` |
169
+ | `extra` | optional: the paper's own keys (hypotheses, models, budget), kept unread |
170
+ | `mode` | a measurement's: exactly one of `decide_before_run` or `exploratory` |
171
+ | `$schema` | a plan v2 says so here; without it the plan is v1 |
172
+ | `mode.we_lose_if` | v2, decide-first only: the number its claim loses by, `below` or `above` a threshold |
173
+ | `second_check` | v2: per labelling step, the least kappa between two raters, or `"none"` |
174
+ | `self_test` | v2: how the study's check is run on known answers first, or `"none"` |
175
+ | `funnel` | v2: the reasons a drawn unit may be dropped, or `"none"` |
176
+ | `recall_audit` | v2: files a person reads in full, and the least recall of the extractor, or `"none"` |
163
177
 
164
178
  `mode` is required of a `measurement` (other kinds may omit it): how its outcomes are kept from the
165
179
  decisions on its inputs — decided before the run (the default the template writes; its `outputs`,
@@ -168,6 +182,12 @@ and its `decision` file relative to the paper, committed before the run) or `exp
168
182
  rule that holds the order in git, [`repro/outputs-before-decision`](outputs-before-decision.md), are
169
183
  in [`docs/design/study-modes.md`](../../design/study-modes.md).
170
184
 
185
+ The four gates of a plan v2 — what each is for, what the machine checks and what it cannot — are in
186
+ [`docs/design/study-gates.md`](../../design/study-gates.md). Each is `null` until decided (the plan is
187
+ a draft), `"none"` when the study has none of it, or its value; a plan v2 that leaves one out is not
188
+ a plan. A plan without `$schema` is v1 and is judged as before, and may carry none of the gates. The
189
+ result a done study's plan v2 asks for is judged by [`repro/study-result`](study-result.md).
190
+
171
191
  The template's draw is of all the population; a sampled study changes it to
172
192
  `{ "kind": "seeded", "method": "random", "seed": 7 }`. The population's size is not stored: it is
173
193
  counted from the source, and a stored copy could only contradict it.
@@ -212,7 +232,10 @@ The rule reports:
212
232
  - a row's `plan` that is outside its folder, not a file, reached through a symbolic link, or not of the plan's shape — and, once the
213
233
  study has run (`running`, `done`, `stopped`, `superseded`), a plan that leaves `question`,
214
234
  `population.source`, `population.draw.method` or `primary_outcome` empty, or — for a measurement —
215
- declares no `mode` or leaves one of its texts empty (`bad-plan`);
235
+ declares no `mode` or leaves one of its texts empty, or — for a plan v2 — leaves a gate `null`
236
+ (`bad-plan`);
237
+ - a measurement registered on or after 2026-10-31 whose plan is v1, without `$schema`
238
+ (`plan-version`): its gates are not declared. A measurement registered earlier keeps its plan v1;
216
239
  - a study of a `headline` measurement that has run with no `author_check` in its plan — or no plan —
217
240
  or, when the registry names its `author`, a check by anyone else (`author-check`).
218
241
 
@@ -0,0 +1,100 @@
1
+ # repro/study-result
2
+
3
+ **Level:** error · **Reads:** `repro/studies.json`, each `done` study's `plan.json` (v2) and
4
+ `result.json` · **Reported on:** `PIPELINE-STATUS.md`, line 1
5
+
6
+ A study whose plan is v2 fixed, before its run, what makes its number believable: when its claim
7
+ loses, how well two raters must agree, that its checker is tried on known answers, and why a unit may
8
+ be dropped ([the gates](../../design/study-gates.md)). Once the study is `done`, its `result.json`
9
+ must show each of them.
10
+
11
+ ## What it catches
12
+
13
+ For each study that is `done`, whose plan is ready and v2:
14
+
15
+ | problem | when |
16
+ | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
17
+ | `no-result-file` | the study's folder has no `result.json` |
18
+ | `result-linked` | `result.json` is reached through a symbolic link |
19
+ | `result-unreadable` | `result.json` is not JSON, or `kappa`, `self_test`, `funnel`, `recall_audit` or `claim` is not of its shape (a kappa outside -1..1, such as a percentage written as `75`, is not one; an audit counting more units extracted than the person found, or no unit found, is not one) |
20
+ | `no-number` | the number `mode.we_lose_if.result` names is not a number in `result.json` |
21
+ | `claim-lost` | that number crosses the threshold, and `result.json` does not say `"claim": "lost"` |
22
+ | `claim-holds` | `result.json` says `"claim": "lost"`, and the number does not cross the threshold |
23
+ | `kappa` | a step of `second_check` has no kappa in `result.json`'s `kappa`, or one below its `min_kappa` |
24
+ | `self-test` | `self_test` is declared, and `result.json`'s `self_test` is missing or `failed` is not 0 |
25
+ | `self-test-empty` | `self_test` is declared, and `result.json`'s `self_test` ran no case: `"passed": 0, "failed": 0` |
26
+ | `funnel` | a reason of `funnel` has no count in `result.json`'s `funnel` |
27
+ | `no-recall-audit` | `recall_audit` is declared, and `result.json` has no `recall_audit` counts |
28
+ | `recall-sample` | the audit read fewer `files` than the plan's `n_files` |
29
+ | `recall` | `extracted / units_in_files` is below the plan's `min_recall`: the extractor misses too much |
30
+
31
+ A gate that is `"none"` asks nothing; an exploratory study has no `we_lose_if`.
32
+
33
+ ## Why
34
+
35
+ A threshold fixed before the run and checked by hand after it can be moved, forgotten or quietly
36
+ reinterpreted. Here the plan holds the threshold and the result holds the number, and the engine
37
+ compares them on every lint. A claim that lost stays red until the result says so in so many words —
38
+ `"claim": "lost"` — where the paper reads its numbers from, so the loss cannot sit unnoticed in a
39
+ table. The same holds for a kappa below the bar set before labelling, a checker that missed a known
40
+ answer, and a dropped unit nobody counted.
41
+
42
+ ## Examples
43
+
44
+ Failing — the plan declares the funnel reason `timed out`, the result counts none:
45
+
46
+ ```json
47
+ {
48
+ "pass_rate": 0.7,
49
+ "kappa": { "pass": 0.71 },
50
+ "self_test": { "passed": 4, "failed": 0 },
51
+ "funnel": {}
52
+ }
53
+ ```
54
+
55
+ ```text
56
+ PIPELINE-STATUS.md
57
+ 1:1 error The `result.json` of `P1-S01` counts no drop for `timed out`, declared in its plan's
58
+ `funnel`. What to do: write each in `funnel`, 0 when none was dropped for it.
59
+ ```
60
+
61
+ Failing — `we_lose_if` is `{ "result": "pass_rate", "below": 0.5 }` and the rate is 0.4:
62
+
63
+ ```text
64
+ 1:1 error `P1-S01`'s claim LOST: its `pass_rate` is 0.4, below 0.5, the threshold its plan fixed
65
+ before the run. What to do: report the loss in the paper, and say so in `result.json`
66
+ with `"claim": "lost"`; the threshold is not moved after the run.
67
+ ```
68
+
69
+ Passing — every gate shown:
70
+
71
+ ```json
72
+ {
73
+ "pass_rate": 0.7,
74
+ "kappa": { "pass": 0.71 },
75
+ "self_test": { "passed": 4, "failed": 0 },
76
+ "funnel": { "timed out": 2 }
77
+ }
78
+ ```
79
+
80
+ ## Options / preset fields
81
+
82
+ None. The rule is on at `error` in paperlint's config; a paper turns it off in its `paperlint.json`.
83
+
84
+ ## What it does not check
85
+
86
+ - A study that is not `done`, a plan v1, a draft or a plan that does not parse
87
+ ([`repro/study-registered`](study-registered.md) reports those).
88
+ - Whether a gate's `"none"` is true, whether the threshold is a sensible one, or whether the number
89
+ is computed right: the author's check of the plan, and the study's own artifact.
90
+ - Whether the gates were fixed before the outputs: [`repro/outputs-before-decision`](outputs-before-decision.md)
91
+ holds them to the plan as it stood at the first output.
92
+ - Whether the person of the recall audit read the files in full, or counted right: the audit's own
93
+ labels, exported and committed like any other.
94
+
95
+ ## How to fix
96
+
97
+ Have the study's script write `result.json` beside its plan, with the number `we_lose_if` names at
98
+ the top, and `kappa`, `self_test`, `funnel` and `recall_audit` as the plan declares them
99
+ ([the shape](../../design/study-gates.md#resultjson)). A lost claim is reported, not hidden: add
100
+ `"claim": "lost"` and say so in the paper. Never edit the plan's thresholds after the run.
package/docs/rules.md CHANGED
@@ -21,6 +21,7 @@ reads and when it fails. Errors fail `paperlint lint`; warnings print and do not
21
21
  | [`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) |
22
22
  | [`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) |
23
23
  | [`repro/outputs-before-decision`](rules/repro/outputs-before-decision.md) | error | `repro/studies.json`, each decide-first study's `plan.json` and folder, git's history | an output of a study decided before its run whose first commit does not descend from its decision's first commit, an output while the decision is uncommitted, a decision its outputs match, a shallow or unreadable history; `paperlint studies gate` opens the run only once the decision is committed — [page](rules/repro/outputs-before-decision.md) |
24
+ | [`repro/study-result`](rules/repro/study-result.md) | error | `repro/studies.json`, each done study's `plan.json` (v2) and `result.json` | a done study whose plan is v2 and whose `result.json` is missing, lacks the number its claim is judged by, crosses that threshold without saying `"claim": "lost"`, lacks a labelling step's kappa or has one below the plan's bar, shows a failed self-test, counts no drop for a declared funnel reason, or audits its extractor on too few files or finds its recall below the plan's bar — [page](rules/repro/study-result.md) |
24
25
  | [`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` |
25
26
  | `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 |
26
27
  | `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` |
package/docs/toolchain.md CHANGED
@@ -16,7 +16,7 @@ after deciding to use the tool, not before.
16
16
  | `texcount` | TeX Live (`paperlint toolchain`) | render-paper, grade-paper-writing | the length checks cannot run |
17
17
  | `checkcites` | TeX Live (`paperlint toolchain`) | render-paper | nothing asks whether a bibliography entry is uncited |
18
18
  | `java` | any JRE (21 works) | render-paper | TeXtidote does not run, and **nothing else spell-checks the text** |
19
- | `python3` | your system | the analysis and report scripts | those scripts do not start |
19
+ | `python3` | your system | assess-novelty, the analysis scripts | those scripts do not start (`crop_quote.py` also needs `pymupdf`) |
20
20
  | `tlmgr` | TeX Live | the TeX installer itself | you cannot add a TeX package |
21
21
  | `banal` | HotCRP (`paperlint toolchain`) | `paperlint build`, extract-pdf-facts | page size, columns and font sizes are `null` in the facts file |
22
22
  | `perl` | your system (macOS has it) | banal | banal cannot run — the same `null`, and `paperlint toolchain` refuses |
package/eslint.config.mjs CHANGED
@@ -135,6 +135,36 @@ const RATCHET = {
135
135
  "skills/plan-paper-timeline/fixtures/fake-google-calendar.ts": {
136
136
  "max-lines-per-function": 31,
137
137
  },
138
+ "skills/assess-novelty/scripts/candidate_page.test.ts": {
139
+ "max-lines-per-function": 343,
140
+ "max-nested-callbacks": 4,
141
+ },
142
+ "skills/assess-novelty/scripts/check_novelty.test.ts": {
143
+ "max-lines-per-function": 419,
144
+ },
145
+ "skills/assess-novelty/scripts/check_novelty_coverage.test.ts": {
146
+ "max-lines-per-function": 131,
147
+ },
148
+ "skills/assess-novelty/scripts/crop_quote.test.ts": {
149
+ "max-lines-per-function": 96,
150
+ },
151
+ "skills/assess-novelty/scripts/novelty_candidates.test.ts": {
152
+ "max-lines-per-function": 179,
153
+ },
154
+ "skills/assess-novelty/scripts/novelty_cells.test.ts": {
155
+ "max-lines-per-function": 117,
156
+ },
157
+ "skills/assess-novelty/scripts/novelty_reader_brief.test.ts": {
158
+ "max-lines-per-function": 180,
159
+ complexity: 11,
160
+ },
161
+ "skills/assess-novelty/scripts/candidate_page.ts": {
162
+ "max-lines-per-function": 55,
163
+ },
164
+ "skills/assess-novelty/scripts/novelty_reader_brief.ts": {
165
+ "max-lines-per-function": 32,
166
+ "max-params": 5,
167
+ },
138
168
  "src/adapters/curl/download.io.ts": { "max-lines-per-function": 39 },
139
169
  "src/adapters/references/index.ts": { "max-lines-per-function": 36 },
140
170
  "src/build-engine.ts": { "max-lines-per-function": 33 },
@@ -667,6 +697,28 @@ export default [
667
697
  files: TEST_FILES,
668
698
  rules: { "max-lines-per-function": ["error", MAX_LINES_TESTS] },
669
699
  },
700
+ // 🔴 DEBT, NOT A PERMISSION. The novelty-check scripts and their harnesses were moved from a consumer
701
+ // repository AS THEY WERE (the 2026-11-02 decision): untyped JSON in, `any` throughout. Making them pass the
702
+ // type-safety rules below means rewriting them, which is paperlint#339 (TypeScript rewrite of the Python
703
+ // checkers) and paperlint#338 (the regex priority-sentence detector). Only the rules that need typed data
704
+ // are off, only here; the rules about behaviour (`complexity`, `max-depth`, `no-empty`, ...) still apply.
705
+ // Delete this block with the rewrite.
706
+ {
707
+ files: ["skills/assess-novelty/scripts/*.ts"],
708
+ rules: {
709
+ "@typescript-eslint/no-unsafe-member-access": "off",
710
+ "@typescript-eslint/no-unsafe-call": "off",
711
+ "@typescript-eslint/no-unsafe-assignment": "off",
712
+ "@typescript-eslint/no-unsafe-argument": "off",
713
+ "@typescript-eslint/no-unsafe-return": "off",
714
+ "@typescript-eslint/no-explicit-any": "off",
715
+ "@typescript-eslint/consistent-type-assertions": "off",
716
+ "@typescript-eslint/restrict-template-expressions": "off",
717
+ "@typescript-eslint/no-unnecessary-condition": "off",
718
+ "@typescript-eslint/no-confusing-void-expression": "off",
719
+ "@typescript-eslint/no-non-null-assertion": "off",
720
+ },
721
+ },
670
722
  // Hexagonal layers, both axes (src/CLAUDE.md): knowledge by element, purity by file category.
671
723
  IO_GLOBALS,
672
724
  NO_CLOCK,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paperlint",
3
- "version": "13.1.0",
3
+ "version": "13.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",
@@ -58,6 +58,7 @@
58
58
  "eslint-plugin-project-structure": "^3.14.5",
59
59
  "eslint-plugin-sonarjs": "^4.2.1",
60
60
  "jsonc-parser": "^3.3.1",
61
+ "playwright-core": "^1.64.1",
61
62
  "prettier": "^3.8.1",
62
63
  "semantic-release": "25.0.9",
63
64
  "typescript-eslint": "^8.70.1",
@@ -119,6 +120,7 @@
119
120
  "presets",
120
121
  "plugin",
121
122
  "scripts",
123
+ "schemas",
122
124
  "skills",
123
125
  "src",
124
126
  "templates",
@@ -26,8 +26,10 @@ researchr page `conf.researchr.org/home/agentverify-2027` only redirects to the
26
26
  preset keeps the family's `\documentclass[conference]{IEEEtran}`.
27
27
  - **Publisher**: «Accepted papers will appear in the ICSE 2027 workshop proceedings by default.»
28
28
  The ICSE workshops page: «The workshop papers will be published by the IEEE.» The CFP does not
29
- say what a non-default route would be, or whether it is archival.
30
- - **Presenting**: «At least one author of each accepted paper must register and present.»
29
+ say what a non-default route would be, or whether it is archival. The organizers: no other route
30
+ is offered (below).
31
+ - **Presenting**: «At least one author of each accepted paper must register and present.» Remote
32
+ presentation is allowed (below).
31
33
  - **Deadlines (AoE)**: submission 13 Nov 2026, reviews due 5 Dec, final notification 11 Dec 2026,
32
34
  camera-ready 29 Jan 2027.
33
35
 
@@ -65,10 +67,19 @@ and are over the limit. Count the PDF's pages yourself before submitting.
65
67
  scheduled workflow; notification and camera-ready are the site's. No `registration`: there is no
66
68
  abstract deadline.
67
69
 
70
+ ## Answered by the organizers (emails to an author, not public)
71
+
72
+ - **Remote presentation** (2026-10-23): «In general, remote presentations will be allowed for
73
+ authors who are unable to attend the workshop in person. However, at least one author of each
74
+ accepted paper must register for the workshop to ensure its inclusion in the proceedings.»
75
+ - **APC** (2026-10-27), for every paper kind: «APC. It is free completely.»
76
+ - **Proceedings** (2026-10-27): «Workshop papers are normally published in an ICSE companion
77
+ IEEE/ACM proceedings.» Asked whether a non-archival option exists: «The non-trivial [sic:
78
+ non-archival] option is not offered.» So every accepted paper is archival.
79
+
68
80
  ## Not verified
69
81
 
70
- - **Remote presentation**: not stated (only «must register and present»).
71
- - **Archival / non-archival choice**: «by default» suggests an alternative; none is described.
82
+ - **Digital library**: whether the 2027 companion volume goes to IEEE Xplore or the ACM DL.
72
83
  - **AI / LLM usage policy and required sections**: none stated.
73
84
 
74
85
  ## What the preset resolves to