paperlint 4.1.0 → 5.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.
Files changed (202) hide show
  1. package/CLAUDE.md +26 -2
  2. package/CONTRIBUTING.md +7 -0
  3. package/README.md +15 -4
  4. package/action.harness.mjs +54 -36
  5. package/action.yml +12 -27
  6. package/dist/adapters/latex/document-class.d.ts +6 -5
  7. package/dist/adapters/latex/document-class.d.ts.map +1 -1
  8. package/dist/adapters/latex/document-class.js +18 -10
  9. package/dist/adapters/latex/document-class.js.map +1 -1
  10. package/dist/adapters/latex/includes.d.ts +16 -0
  11. package/dist/adapters/latex/includes.d.ts.map +1 -0
  12. package/dist/adapters/latex/includes.js +48 -0
  13. package/dist/adapters/latex/includes.js.map +1 -0
  14. package/dist/adapters/latex/index.d.ts +9 -5
  15. package/dist/adapters/latex/index.d.ts.map +1 -1
  16. package/dist/adapters/latex/index.js +8 -1
  17. package/dist/adapters/latex/index.js.map +1 -1
  18. package/dist/adapters/latex/nodes.d.ts +5 -1
  19. package/dist/adapters/latex/nodes.d.ts.map +1 -1
  20. package/dist/adapters/latex/nodes.js +6 -2
  21. package/dist/adapters/latex/nodes.js.map +1 -1
  22. package/dist/adapters/latex/parse.d.ts.map +1 -1
  23. package/dist/adapters/latex/parse.js +1 -0
  24. package/dist/adapters/latex/parse.js.map +1 -1
  25. package/dist/adapters/latex/prose.d.ts +8 -0
  26. package/dist/adapters/latex/prose.d.ts.map +1 -0
  27. package/dist/adapters/latex/prose.js +353 -0
  28. package/dist/adapters/latex/prose.js.map +1 -0
  29. package/dist/build.d.ts.map +1 -1
  30. package/dist/build.js +7 -3
  31. package/dist/build.js.map +1 -1
  32. package/dist/claim-provenance.d.ts +34 -0
  33. package/dist/claim-provenance.d.ts.map +1 -0
  34. package/dist/claim-provenance.js +146 -0
  35. package/dist/claim-provenance.js.map +1 -0
  36. package/dist/cli.d.ts +2 -8
  37. package/dist/cli.d.ts.map +1 -1
  38. package/dist/cli.js +81 -31
  39. package/dist/cli.js.map +1 -1
  40. package/dist/domain/paper-source.d.ts +58 -0
  41. package/dist/domain/paper-source.d.ts.map +1 -0
  42. package/dist/domain/paper-source.js +109 -0
  43. package/dist/domain/paper-source.js.map +1 -0
  44. package/dist/domain/sentences.d.ts +26 -0
  45. package/dist/domain/sentences.d.ts.map +1 -0
  46. package/dist/domain/sentences.js +48 -0
  47. package/dist/domain/sentences.js.map +1 -0
  48. package/dist/domain/tex-document.d.ts +39 -4
  49. package/dist/domain/tex-document.d.ts.map +1 -1
  50. package/dist/domain/tex-document.js.map +1 -1
  51. package/dist/eslint-rules/latex-language.d.ts +5 -1
  52. package/dist/eslint-rules/latex-language.d.ts.map +1 -1
  53. package/dist/eslint-rules/latex-language.js +6 -3
  54. package/dist/eslint-rules/latex-language.js.map +1 -1
  55. package/dist/eslint-rules/paper-context.d.ts +20 -0
  56. package/dist/eslint-rules/paper-context.d.ts.map +1 -0
  57. package/dist/eslint-rules/paper-context.js +44 -0
  58. package/dist/eslint-rules/paper-context.js.map +1 -0
  59. package/dist/eslint-rules/paper-typography.d.ts +8 -1
  60. package/dist/eslint-rules/paper-typography.d.ts.map +1 -1
  61. package/dist/eslint-rules/paper-typography.js +85 -19
  62. package/dist/eslint-rules/paper-typography.js.map +1 -1
  63. package/dist/eslint-rules/tex-build.d.ts.map +1 -1
  64. package/dist/eslint-rules/tex-build.js +7 -1
  65. package/dist/eslint-rules/tex-build.js.map +1 -1
  66. package/dist/init.d.ts +4 -19
  67. package/dist/init.d.ts.map +1 -1
  68. package/dist/init.js +24 -29
  69. package/dist/init.js.map +1 -1
  70. package/dist/package-dirs.d.ts +2 -0
  71. package/dist/package-dirs.d.ts.map +1 -1
  72. package/dist/package-dirs.js +8 -0
  73. package/dist/package-dirs.js.map +1 -1
  74. package/dist/paper-files.d.ts +2 -2
  75. package/dist/paper-includes.d.ts +17 -0
  76. package/dist/paper-includes.d.ts.map +1 -0
  77. package/dist/paper-includes.js +51 -0
  78. package/dist/paper-includes.js.map +1 -0
  79. package/dist/ports/latex.d.ts +12 -1
  80. package/dist/ports/latex.d.ts.map +1 -1
  81. package/dist/register.d.ts +40 -0
  82. package/dist/register.d.ts.map +1 -0
  83. package/dist/register.js +134 -0
  84. package/dist/register.js.map +1 -0
  85. package/dist/rules-config.d.ts +1 -0
  86. package/dist/rules-config.d.ts.map +1 -1
  87. package/dist/rules-config.js.map +1 -1
  88. package/dist/structure.js +1 -1
  89. package/dist/structure.js.map +1 -1
  90. package/dist/tex-paper.d.ts +33 -0
  91. package/dist/tex-paper.d.ts.map +1 -0
  92. package/dist/tex-paper.js +103 -0
  93. package/dist/tex-paper.js.map +1 -0
  94. package/dist/tex-venue-rules.d.ts +7 -2
  95. package/dist/tex-venue-rules.d.ts.map +1 -1
  96. package/dist/tex-venue-rules.js +31 -17
  97. package/dist/tex-venue-rules.js.map +1 -1
  98. package/docs/configuration.md +5 -4
  99. package/docs/install.md +8 -8
  100. package/docs/prior-art/paper-folder-scaffolding.md +4 -7
  101. package/docs/rules/tex/claim-provenance.md +83 -0
  102. package/docs/rules/tex/register.md +77 -0
  103. package/docs/rules/tex/required-section.md +3 -1
  104. package/docs/rules/tex/template.md +12 -0
  105. package/docs/rules/tex/venue-leftover.md +4 -2
  106. package/docs/rules.md +17 -10
  107. package/eslint-rules/latex-language.ts +10 -4
  108. package/eslint-rules/paper-context.test.ts +47 -0
  109. package/eslint-rules/paper-context.ts +57 -0
  110. package/eslint-rules/paper-typography.test.mjs +48 -1
  111. package/eslint-rules/paper-typography.ts +139 -24
  112. package/eslint-rules/tex-build.ts +8 -1
  113. package/eslint.config.mjs +1 -0
  114. package/fixtures/real-markdown-paper/baseline.json +1 -2
  115. package/fixtures/real-markdown-paper/baseline.ts +8 -7
  116. package/package.json +2 -1
  117. package/scripts/check.ts +2 -2
  118. package/scripts/eslint-report-guard.mjs +5 -2
  119. package/scripts/harness-api.frozen.json +0 -1
  120. package/skills/cold-read-diff/SKILL.md +31 -10
  121. package/skills/cold-read-diff/SKILL.md.spec.ts +30 -9
  122. package/skills/draft-paper/SKILL.md +2 -2
  123. package/skills/draft-paper/SKILL.md.spec.ts +1 -1
  124. package/skills/grade-paper-writing/SKILL.md +109 -44
  125. package/skills/grade-paper-writing/SKILL.md.spec.ts +108 -43
  126. package/skills/harden-paper/SKILL.md +35 -36
  127. package/skills/harden-paper/SKILL.md.spec.ts +34 -35
  128. package/skills/paper-adversarial-review/SKILL.md +2 -2
  129. package/skills/paper-adversarial-review/SKILL.md.spec.ts +1 -1
  130. package/skills/paper-pipeline/SKILL.md +3 -3
  131. package/skills/paper-pipeline/SKILL.md.spec.ts +2 -2
  132. package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +6 -6
  133. package/skills/paper-pipeline/references/pipeline-status-template.md +2 -16
  134. package/skills/paper-pipeline/references/writing-craft.md +53 -18
  135. package/skills/pc-panel-review/SKILL.md +36 -11
  136. package/skills/pc-panel-review/SKILL.md.spec.ts +35 -10
  137. package/src/adapters/latex/document-class.ts +20 -12
  138. package/src/adapters/latex/includes.ts +74 -0
  139. package/src/adapters/latex/index.test.ts +183 -1
  140. package/src/adapters/latex/index.ts +14 -4
  141. package/src/adapters/latex/nodes.ts +11 -2
  142. package/src/adapters/latex/parse.ts +1 -0
  143. package/src/adapters/latex/prose.ts +484 -0
  144. package/src/build.harness.mjs +4 -2
  145. package/src/build.test.ts +18 -1
  146. package/src/build.ts +7 -3
  147. package/src/claim-provenance.test.ts +352 -0
  148. package/src/claim-provenance.ts +217 -0
  149. package/src/cli.harness.mjs +22 -54
  150. package/src/cli.test.ts +2 -18
  151. package/src/cli.ts +92 -36
  152. package/src/domain/paper-source.test.ts +156 -0
  153. package/src/domain/paper-source.ts +233 -0
  154. package/src/domain/sentences.ts +67 -0
  155. package/src/domain/tex-document.ts +36 -3
  156. package/src/init.test.ts +33 -13
  157. package/src/init.ts +25 -48
  158. package/src/linted-set.test.ts +11 -1
  159. package/src/new-paper.harness.mjs +1 -5
  160. package/src/package-dirs.ts +9 -0
  161. package/src/paper-files.ts +2 -2
  162. package/src/paper-includes.test.ts +161 -0
  163. package/src/paper-includes.ts +77 -0
  164. package/src/ports/latex.ts +13 -0
  165. package/src/register.test.ts +165 -0
  166. package/src/register.ts +177 -0
  167. package/src/rule-cases.test.ts +43 -23
  168. package/src/rule-docs.test.ts +0 -1
  169. package/src/rules-config.ts +1 -0
  170. package/src/structure.harness.ts +1 -3
  171. package/src/structure.ts +1 -1
  172. package/src/tex-paper.test.ts +148 -0
  173. package/src/tex-paper.ts +161 -0
  174. package/src/tex-venue-rules.test.ts +87 -5
  175. package/src/tex-venue-rules.ts +62 -30
  176. package/templates/paper/PIPELINE-STATUS.md +0 -4
  177. package/dist/action-ref.d.ts +0 -12
  178. package/dist/action-ref.d.ts.map +0 -1
  179. package/dist/action-ref.js +0 -16
  180. package/dist/action-ref.js.map +0 -1
  181. package/dist/eslint-rules/paper-research-question.d.ts +0 -31
  182. package/dist/eslint-rules/paper-research-question.d.ts.map +0 -1
  183. package/dist/eslint-rules/paper-research-question.js +0 -188
  184. package/dist/eslint-rules/paper-research-question.js.map +0 -1
  185. package/eslint-rules/paper-research-question.harness.mjs +0 -212
  186. package/eslint-rules/paper-research-question.ts +0 -199
  187. package/fixtures/paper-research-question/comment-only/PIPELINE-STATUS.md +0 -9
  188. package/fixtures/paper-research-question/comment-only/paper.tex +0 -7
  189. package/fixtures/paper-research-question/declared-not-in-paper/PIPELINE-STATUS.md +0 -10
  190. package/fixtures/paper-research-question/declared-not-in-paper/paper.tex +0 -6
  191. package/fixtures/paper-research-question/draft/PIPELINE-STATUS.md +0 -6
  192. package/fixtures/paper-research-question/draft/paper.tex +0 -2
  193. package/fixtures/paper-research-question/markdown-no-rq/PIPELINE-STATUS.md +0 -9
  194. package/fixtures/paper-research-question/markdown-no-rq/paper.md +0 -4
  195. package/fixtures/paper-research-question/shipped-no-rq/PIPELINE-STATUS.md +0 -12
  196. package/fixtures/paper-research-question/shipped-no-rq/paper.tex +0 -3
  197. package/fixtures/paper-research-question/shipped-with-rq/PIPELINE-STATUS.md +0 -10
  198. package/fixtures/paper-research-question/shipped-with-rq/paper.tex +0 -2
  199. package/skills/harden-paper/fixtures/uncited-assertions-sample.md +0 -43
  200. package/skills/harden-paper/fixtures/uncited-assertions-sample.tex +0 -77
  201. package/src/action-ref.test.ts +0 -26
  202. package/src/action-ref.ts +0 -15
@@ -1,199 +0,0 @@
1
- /**
2
- * `paper/research-question` — a shipped paper states its research question EXPLICITLY.
3
- *
4
- * Reviewer A's verbatim point on agenticdev (#20), as it was parsed: "State the goal as a
5
- * research question." This is a venue expectation, not taste: an empirical paper that never
6
- * names the question it answers forces the reviewer to reconstruct it — and they say so.
7
- *
8
- * ── WHY THE SCOPE IS SHIPPED PAPERS ONLY ─────────────────────────────────────────────────
9
- * A draft never asked anyone to read it. The scope deliberately matches `paper/author-list`:
10
- * both are about the DEBT of a shipped paper, and an unshipped one has no debt. Measured on the
11
- * live corpus before the move:
12
- * agenticdev-2026 has RQ, shipped -> silent (added because of this very review)
13
- * aisec-2026 no RQ, shipped -> a finding
14
- * compile-rules no RQ, shipped -> a finding
15
- * scored-2026 no RQ, NOT shipped -> silent
16
- *
17
- * ── WHAT THE MOVE CHANGED ─────────────────────────────────────────────────────────────────
18
- * The predecessor read the stage with a REGEX OVER THE SCORECARD'S PROSE. Remeasured 2026-09-17:
19
- * for `agenticdev-2026` the prose reads `submitted`, while the `stages` field declares
20
- * `submitted, camera-ready`. Here the stage comes from the FIELD — the same one `paper/stages`
21
- * checks against the bytes in both directions.
22
- *
23
- * ⚠️ WHAT THE MOVE DOES NOT FIX, AND THIS IS MEASURED, NOT ASSUMED. The rule reads RAW text, not
24
- * the parsed tree, so a mention of RQ inside a LaTeX comment (`% add an RQ`) will put it to
25
- * sleep. Measured 2026-09-17 across all four papers: ZERO such cases — not a single match inside
26
- * a comment. So the hole is LATENT, not observed, and closing it with a tree walk would
27
- * complicate the rule without a measured difference. The neighboring `paper/typography` is built
28
- * the same way and for the same reason. Once a real case shows up, there will be something to
29
- * cite here.
30
- *
31
- * The rule is ADVISORY by decision, not by coincidence: a position paper may legitimately have
32
- * no question. Then its absence is the author's decision, and the rule says exactly that,
33
- * rather than declaring the absence a defect on its own.
34
- */
35
- import { existsSync, readFileSync } from "node:fs";
36
- import { join, dirname } from "node:path";
37
- import { load } from "js-yaml";
38
- import { z } from "zod";
39
- import type { RuleContext } from "./rule-context.ts";
40
-
41
- /**
42
- * The two scorecard fields this rule reads, parsed rather than trusted: a field of the wrong shape
43
- * reads as absent, the way `data?.stages` on a string or a number always did.
44
- */
45
- const Scorecard = z
46
- .looseObject({
47
- stages: z.array(z.unknown()).catch([]),
48
- researchQuestion: z.string().catch(""),
49
- })
50
- .catch({ stages: [], researchQuestion: "" });
51
- const StageEntry = z.looseObject({ stage: z.unknown().optional() }).catch({});
52
- const RuleOptions = z
53
- .object({ statusFile: z.string().default("PIPELINE-STATUS.md") })
54
- .default({ statusFile: "PIPELINE-STATUS.md" });
55
-
56
- /**
57
- * 🔴 THE REGEX IS GONE, AND THE REASON IS NOT "regexes are bad" — IT IS THAT IT ANSWERED THE
58
- * WRONG QUESTION (2026-09-19).
59
- *
60
- * What stood here was `/\\textbf\{RQ|\bRQ[0-9]?\b|research question/i`, defended in this very
61
- * file with: "a regex is LEGITIMATE here: the subject is human prose, which has no structure by
62
- * definition, and no parser can tell a question's statement apart from a paragraph about it."
63
- *
64
- * The premise is true. The conclusion does not follow. From "the text cannot decide this" the
65
- * answer is not "so pattern-match harder" — it is "so the HUMAN declares it, and the check
66
- * compares the declaration against the text." The three spellings were an undeclared schema
67
- * field: someone added a fact and invented a convention for how it would look in prose, which is
68
- * the design running backwards.
69
- *
70
- * What the pattern actually decided, in both directions:
71
- * • a paper that says "we leave the research question to future work" — MATCHED, and the rule
72
- * went silent on a paper that states no question at all;
73
- * • a paper whose question is written plainly ("Does pruning reduce review cost?") with no
74
- * "RQ" anywhere — NOT matched, and the rule reported a paper that does exactly what is asked.
75
- * An open list of spellings cannot be completed, so both failures are structural, not bugs.
76
- *
77
- * ── WHAT REPLACES IT: ONE UNDECIDABLE QUESTION BECOMES TWO DECIDABLE ONES ──────────────────
78
- * 1. Is the question WRITTEN DOWN? — `researchQuestion` in the scorecard's front matter. A
79
- * field, parsed by js-yaml, not a phrase hunted for in prose.
80
- * 2. Does the paper CONTAIN what was written down? — the declared sentence, whitespace
81
- * collapsed, compared against the source. Bytes, not spelling.
82
- *
83
- * This is the shape this package already uses twice, and for the same reason: `bytes`/
84
- * `sourceBytes` (a recorded number compared against the file on disk) and `paper/author-list`
85
- * (a recorded marker, because whether a cross-check ran is not visible in the paper). Neither
86
- * asks the prose a question the prose cannot answer.
87
- *
88
- * ⚠️ WHAT THIS STILL CANNOT DO, said plainly rather than implied: nothing here decides that the
89
- * declared sentence IS a research question. A declaration of "banana" passes step 1 and, if the
90
- * word appears in the paper, step 2. The rule guarantees that the author wrote a question down
91
- * and that the paper carries it — it does not grade the question. That is a human's job, and
92
- * pretending otherwise is what the regex was doing.
93
- */
94
-
95
- /** Whitespace is the only thing normalised: a sentence wrapped across lines is the same sentence. */
96
- const flatten = (t: string) => t.replace(/\s+/g, " ").trim().toLowerCase();
97
-
98
- /**
99
- * Both facts come out of ONE parse of the scorecard's front matter: the stages (has this paper
100
- * shipped?) and the declared question. Reading them separately would invite the two to be taken
101
- * from different revisions of the same file.
102
- */
103
- function scorecard(
104
- dir: string,
105
- statusName: string,
106
- ): { stages: unknown[]; question: string } {
107
- const p = join(dir, statusName);
108
- const none = { stages: [], question: "" };
109
- if (!existsSync(p)) return none;
110
- let text: string;
111
- try {
112
- text = readFileSync(p, "utf-8");
113
- } catch {
114
- return none;
115
- }
116
- const m = /^---\n([\s\S]*?)\n---/.exec(text);
117
- if (!m) return none;
118
- const [, front = ""] = m;
119
- let data: unknown;
120
- try {
121
- data = load(front);
122
- } catch {
123
- return none; // the unreadable YAML is already reported by `paper/stages`, on its own file
124
- }
125
- const { stages: raw, researchQuestion: question } = Scorecard.parse(data);
126
- const stages = raw.map((r) => StageEntry.parse(r).stage).filter(Boolean);
127
- return { stages, question };
128
- }
129
-
130
- export default {
131
- rules: {
132
- "research-question": {
133
- meta: {
134
- type: "suggestion" as const,
135
- docs: {
136
- description:
137
- "a paper that declares a stage states its research question explicitly — otherwise the reviewer reconstructs it themselves, and says so",
138
- },
139
- schema: [
140
- {
141
- type: "object",
142
- properties: {
143
- // The scorecard's name is a CONSUMER convention. The rule is declared on the
144
- // paper's source, so it has to name its neighbor itself rather than get it via a
145
- // config glob.
146
- statusFile: { type: "string" },
147
- },
148
- additionalProperties: false,
149
- },
150
- ],
151
- messages: {
152
- notDeclared:
153
- "the paper shipped (stage «{{stages}}») and its scorecard declares no `researchQuestion`. This is reviewer A's verbatim point on agenticdev (#20), not the linter's taste. Write the question down as a field, in your own words — the check cannot infer it from the prose, and the pattern that used to try matched «we leave the research question to future work» while missing a question stated plainly. Advisory: a position paper may legitimately have none, and then leaving the field out is a DECISION — record it as one",
154
- notInPaper:
155
- "the scorecard declares a research question the paper does not contain. Looked for «{{needle}}» with whitespace collapsed, and the source has no such run of text. Either the paper dropped it or the declaration drifted from what was written — and which of the two it is, only you know",
156
- },
157
- },
158
- create(context: RuleContext) {
159
- const { statusFile: statusName } = RuleOptions.parse(
160
- context.options[0],
161
- );
162
- return {
163
- // `root:exit` exists for both markdown and the `tex/latex` language — the same place
164
- // `paper/typography` lives, and for the same reason: a paper in this corpus can be
165
- // either.
166
- "root:exit"(node: object) {
167
- const raw = context.sourceCode.raw ?? context.sourceCode.text;
168
- if (typeof raw !== "string") return;
169
- const { stages, question } = scorecard(
170
- dirname(context.filename),
171
- statusName,
172
- );
173
- if (stages.length === 0) return; // not shipped — owes nothing
174
-
175
- // Step 1 — is it written down? A field, not a phrase hunted for in prose.
176
- if (question.trim() === "") {
177
- context.report({
178
- node,
179
- messageId: "notDeclared",
180
- data: { stages: stages.join("/") },
181
- });
182
- return;
183
- }
184
-
185
- // Step 2 — does the paper carry what was written down? Bytes, not spelling. Only
186
- // whitespace is normalised, because a sentence wrapped across source lines is the
187
- // same sentence; everything else stays the author's.
188
- if (flatten(raw).includes(flatten(question))) return;
189
- context.report({
190
- node,
191
- messageId: "notInPaper",
192
- data: { needle: flatten(question) },
193
- });
194
- },
195
- };
196
- },
197
- },
198
- },
199
- };
@@ -1,9 +0,0 @@
1
- ---
2
- stages:
3
- - stage: submitted
4
- date: 2026-07-22
5
- venue: A Venue 2026
6
- pdf: versions/2026-07-22-submitted.pdf
7
- bytes: 100
8
- ---
9
- # PIPELINE-STATUS
@@ -1,7 +0,0 @@
1
- \section{Introduction}
2
- % TODO: state the research question — this is a LaTeX COMMENT.
3
- % The rule reads RAW text and today does not tell it apart from the paper, so here it STAYS
4
- % SILENT. The hole is named in the rule's header and measured: on the live corpus there are
5
- % zero such cases, so it is latent. The fixture keeps it visible — the first real case turns
6
- % up and the assertion below has to be rewritten deliberately, not discovered after the fact.
7
- Body text of the paper, in which no question is stated.
@@ -1,10 +0,0 @@
1
- ---
2
- stages:
3
- - stage: submitted
4
- date: 2026-07-22
5
- venue: A Venue 2026
6
- pdf: versions/2026-07-22-submitted.pdf
7
- bytes: 100
8
- researchQuestion: "Does pruning reduce review cost?"
9
- ---
10
- # PIPELINE-STATUS
@@ -1,6 +0,0 @@
1
- \section{Introduction}
2
- % This fixture is the one the predecessor could not express. The scorecard declares a question;
3
- % the paper does not carry it. And the words "research question" DO appear below — so the old
4
- % pattern `/\textbf{RQ|\bRQ[0-9]?\b|research question/i` matched and went silent on a paper that
5
- % states no question at all. Matching a phrase ABOUT the thing is not finding the thing.
6
- We leave the research question to future work.
@@ -1,6 +0,0 @@
1
- ---
2
- stages: []
3
- ---
4
- # PIPELINE-STATUS
5
-
6
- A draft, never submitted anywhere.
@@ -1,2 +0,0 @@
1
- \section{Introduction}
2
- A draft with no question. Nobody was asked to read it.
@@ -1,9 +0,0 @@
1
- ---
2
- stages:
3
- - stage: submitted
4
- date: 2026-07-22
5
- venue: A Venue 2026
6
- pdf: versions/2026-07-22-submitted.pdf
7
- bytes: 100
8
- ---
9
- # PIPELINE-STATUS
@@ -1,4 +0,0 @@
1
- # Introduction
2
-
3
- The paper is written in markdown, not LaTeX — that happens in this corpus, and the rule must
4
- see it the same way. No question is stated here.
@@ -1,12 +0,0 @@
1
- ---
2
- stages:
3
- - stage: submitted
4
- date: 2026-07-22
5
- pdf: versions/a.pdf
6
- bytes: 1
7
- - stage: camera-ready
8
- date: 2026-08-29
9
- pdf: versions/b.pdf
10
- bytes: 2
11
- ---
12
- # PIPELINE-STATUS
@@ -1,3 +0,0 @@
1
- \section{Introduction}
2
- We study how agents behave and measure how many rules survive to the run.
3
- The question is named nowhere — neither by a macro nor in words.
@@ -1,10 +0,0 @@
1
- ---
2
- stages:
3
- - stage: submitted
4
- date: 2026-07-22
5
- venue: A Venue 2026
6
- pdf: versions/2026-07-22-submitted.pdf
7
- bytes: 100
8
- researchQuestion: "does a rule written in prose survive to the run?"
9
- ---
10
- # PIPELINE-STATUS
@@ -1,2 +0,0 @@
1
- \section{Introduction}
2
- \textbf{RQ1:} does a rule written in prose survive to the run?
@@ -1,43 +0,0 @@
1
- # Uncited-assertion fixture (markdown path)
2
-
3
- Documents the `.md` behaviour of the uncited-assertion checker. Keep in the repo (it IS the contract).
4
- <!-- Since 2026-08-27 the checker is the ESLint rule `paper/uncited-assertion`
5
- (`eslint-rules/paper-claims.mjs`); this file is its test — see `eslint-rules/paper-claims.harness.mjs`. -->
6
-
7
- Transformers outperform RNNs by 12%.
8
- <!-- MUST FLAG: uncited empirical claim about prior work -->
9
-
10
- Transformers outperform RNNs by 12% [@vaswani2017].
11
- <!-- MUST NOT FLAG: pandoc [@key] citation attached -->
12
-
13
- Prior work reports a large improvement over baselines.[^1]
14
- <!-- MUST NOT FLAG: markdown footnote marker counts as a citation -->
15
-
16
- The majority of detectors fail on adversarial inputs, per a recent survey ([link](https://example.org/survey)).
17
- <!-- MUST NOT FLAG: markdown link/URL counts as a citation -->
18
-
19
- Most prior detectors silently miss adversarial inputs.
20
- <!-- MUST FLAG: bare fuzzy quantifier, no citation -->
21
-
22
- We observe a 6% reduction in false positives.
23
- <!-- MUST NOT FLAG: paper's own result (we-guard) -->
24
-
25
- Transformers outperform RNNs by 12% [12].
26
- <!-- MUST NOT FLAG (S6): numeric citation [12] counts as a cite -->
27
-
28
- Prior detectors reduce error by 20% [3, 4].
29
- <!-- MUST NOT FLAG (S6): citation list [3, 4] counts -->
30
-
31
- US-based detectors reject 40% of benign inputs.
32
- <!-- MUST FLAG (S5): bare country "US" is not the paper's own result -->
33
-
34
- Our reading of the literature shows transformers outperform every prior baseline.
35
- <!-- MUST FLAG (S5): claim about OTHERS, "our reading" is not an own result -->
36
-
37
- Our method reduces false positives by 6% over the strongest baseline.
38
- <!-- MUST NOT FLAG (S5): genuine own result, "our method" is the subject -->
39
-
40
- 2. Prior detectors silently miss most adversarial inputs.
41
- <!-- MUST FLAG (S10): ordered-list marker "2." must not be a false boundary / stray number; the claim itself still flags -->
42
-
43
- [^1]: Some prior study, 2021.
@@ -1,77 +0,0 @@
1
- % fixtures/uncited-assertions-sample.tex
2
- % Contract fixture for the uncited-assertion checker — documents exactly what MUST and MUST NOT flag.
3
- % Since 2026-08-27 that checker is the ESLint rule `paper/uncited-assertion` (eslint-rules/paper-claims.mjs);
4
- % this file is its test, run by eslint-rules/paper-claims.harness.mjs.
5
- % Each numbered case is annotated with the expected verdict. Keep in the repo (it IS the contract).
6
- \documentclass{article}
7
- \begin{document}
8
-
9
- % (1) MUST FLAG — uncited empirical claim about prior work (percentage + verb, no cite, no "we").
10
- Transformers outperform RNNs by 12\%.
11
-
12
- % (2) MUST NOT FLAG — same claim, now carrying a citation.
13
- Transformers outperform RNNs by 12\% \cite{vaswani2017}.
14
-
15
- % (3) MUST NOT FLAG — float pointer; the verb refers to a Table, not to prior work.
16
- Table 2 shows the aggregated results across all runs.
17
-
18
- % (4) MUST NOT FLAG — the only number is a year (year guard).
19
- In 2026, the field continued to attract new benchmarks.
20
-
21
- % (5) MUST NOT FLAG — the paper's OWN reported result (we/our guard); numbers allowed uncited.
22
- We find a 6\% reduction in false positives (p<0.05) over the baseline.
23
-
24
- % (6) MUST NOT FLAG — a definition sentence, even though it contains a number.
25
- A transformer block is defined as a stack of 6 attention sub-layers.
26
-
27
- % (7) MUST NOT FLAG — version-number guard (v2.1) and a math-mode number ($n = 512$) both skipped;
28
- % no "we", so this isolates the version + math guards (no empirical signal survives).
29
- The baseline model ships as release v2.1 with a hidden size of $n = 512$.
30
-
31
- % (8) MUST FLAG — bare fuzzy quantifier making a claim about prior work, no cite.
32
- Most prior detectors silently miss adversarial inputs.
33
-
34
- % ── regression cases for the adversarial-review fixes (S5–S10) ──────────────────
35
-
36
- % (S6) MUST NOT FLAG — numeric-style citation [12] is a citation (dominant CS style).
37
- Transformers outperform RNNs by 12\% [12].
38
-
39
- % (S6b) MUST NOT FLAG — citation range/list [3, 4] and [1]--[3] both count.
40
- Prior detectors reduce error by 20\% [3, 4].
41
-
42
- % (S7) MUST NOT FLAG — biblatex \footcite is a citation macro.
43
- Prior work improves accuracy by 15\% \footcite{smith2020}.
44
-
45
- % (S7b) MUST NOT FLAG — \citeauthor / \parencite are citation macros too.
46
- \citeauthor{jones2019} shows a 9\% reduction over the baseline \parencite{jones2019}.
47
-
48
- % (S8) MUST NOT FLAG — "cf." must NOT sever the trailing \cite into its own segment.
49
- RNN baselines underperform on long sequences, cf. \cite{vaswani2017}.
50
-
51
- % (S8b) MUST NOT FLAG — "e.g." before a capital must not break the sentence off its cite.
52
- Several detectors miss adversarial inputs, e.g. Gradient attacks \cite{goodfellow2015}.
53
-
54
- % (S9) MUST NOT FLAG — float pointer via \ref-macro (no literal digit); verb refers to a Table.
55
- Table~\ref{tab:results} shows the aggregated numbers across all runs.
56
-
57
- % (S9b) MUST NOT FLAG — \autoref / \Cref float pointers, verb-only.
58
- \Cref{fig:overview} demonstrates the pipeline end to end.
59
-
60
- % (S10) MUST NOT FLAG — tabular body: numeric rows are data, not a prose claim.
61
- The results appear below.
62
- \begin{tabular}{lll}
63
- Model & Acc & F1 \\
64
- BERT & 0.91 & 0.89 \\
65
- GPT & 0.94 & 0.93 \\
66
- \end{tabular}
67
-
68
- % (S5) MUST FLAG — bare country "US" must NOT be read as the paper's own result ("us").
69
- US-based detectors reject 40\% of benign inputs.
70
-
71
- % (S5b) MUST FLAG — "Our reading of the literature" is a claim about OTHERS, not an own result.
72
- Our reading of the literature shows transformers outperform every prior baseline.
73
-
74
- % (S5c) MUST NOT FLAG — genuine own result: "our method" is the subject.
75
- Our method reduces false positives by 6\% over the strongest baseline.
76
-
77
- \end{document}
@@ -1,26 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { test } from "vitest";
3
- import { actionRef } from "./action-ref.ts";
4
-
5
- /** [the running package's version, the ref `paperlint init` may pin the action to]. */
6
- const TABLE: readonly (readonly [string | undefined, string | null])[] = [
7
- ["1.2.3", "v1.2.3"],
8
- ["1.0.0", "v1.0.0"],
9
- ["10.20.30", "v10.20.30"],
10
- // Guards: a git checkout / `npm link` carries the unreleased placeholder — there is no tag for it.
11
- ["0.0.0-semantically-released", null],
12
- // Guards: only a plain release is known to have a tag; anything else stays the placeholder.
13
- ["1.2.3-beta.1", null],
14
- ["v1.2.3", null],
15
- ["1.2", null],
16
- ["", null],
17
- ["latest", null],
18
- [" 1.2.3", null],
19
- // Guards: the CLI could not read its own manifest — no version, no pin.
20
- [undefined, null],
21
- ];
22
-
23
- for (const [version, ref] of TABLE)
24
- test(`actionRef(${JSON.stringify(version)})`, () => {
25
- assert.equal(actionRef(version), ref);
26
- });
package/src/action-ref.ts DELETED
@@ -1,15 +0,0 @@
1
- /**
2
- * The ref `paperlint init` pins the GitHub Action to, decided from the version of the package that is
3
- * running. semantic-release tags every release `v<version>` on the commit it publishes, so the
4
- * action at that tag is the same code as the installed package.
5
- *
6
- * Only a plain `X.Y.Z` has such a tag. A git checkout or `npm link` carries the unreleased
7
- * placeholder `0.0.0-semantically-released`, a version that could not be read is `undefined`, and
8
- * anything else is not known to be tagged: those return `null` ("no pin known"), and the caller
9
- * keeps the placeholder. A wrong tag written confidently is worse than an obvious placeholder.
10
- */
11
- export function actionRef(version: string | undefined): string | null {
12
- return version !== undefined && /^\d+\.\d+\.\d+$/.test(version)
13
- ? `v${version}`
14
- : null;
15
- }