paperlint 4.0.0 → 4.2.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 (236) hide show
  1. package/.github/workflows/ci.yml +16 -23
  2. package/CLAUDE.md +55 -11
  3. package/CONTRIBUTING.md +30 -7
  4. package/README.md +11 -8
  5. package/dist/adapters/banal/invocation.d.ts +1 -1
  6. package/dist/adapters/latex/document-class.d.ts +23 -0
  7. package/dist/adapters/latex/document-class.d.ts.map +1 -0
  8. package/dist/adapters/latex/document-class.js +110 -0
  9. package/dist/adapters/latex/document-class.js.map +1 -0
  10. package/dist/adapters/latex/hidden.d.ts +16 -0
  11. package/dist/adapters/latex/hidden.d.ts.map +1 -0
  12. package/dist/adapters/latex/hidden.js +49 -0
  13. package/dist/adapters/latex/hidden.js.map +1 -0
  14. package/dist/adapters/latex/includes.d.ts +12 -0
  15. package/dist/adapters/latex/includes.d.ts.map +1 -0
  16. package/dist/adapters/latex/includes.js +42 -0
  17. package/dist/adapters/latex/includes.js.map +1 -0
  18. package/dist/adapters/latex/index.d.ts +33 -0
  19. package/dist/adapters/latex/index.d.ts.map +1 -0
  20. package/dist/adapters/latex/index.js +25 -0
  21. package/dist/adapters/latex/index.js.map +1 -0
  22. package/dist/adapters/latex/nodes.d.ts +58 -0
  23. package/dist/adapters/latex/nodes.d.ts.map +1 -0
  24. package/dist/adapters/latex/nodes.js +97 -0
  25. package/dist/adapters/latex/nodes.js.map +1 -0
  26. package/dist/adapters/latex/outline.d.ts +10 -0
  27. package/dist/adapters/latex/outline.d.ts.map +1 -0
  28. package/dist/adapters/latex/outline.js +39 -0
  29. package/dist/adapters/latex/outline.js.map +1 -0
  30. package/dist/adapters/latex/parse.d.ts +42 -0
  31. package/dist/adapters/latex/parse.d.ts.map +1 -0
  32. package/dist/adapters/latex/parse.js +95 -0
  33. package/dist/adapters/latex/parse.js.map +1 -0
  34. package/dist/adapters/latex/prose.d.ts +8 -0
  35. package/dist/adapters/latex/prose.d.ts.map +1 -0
  36. package/dist/adapters/latex/prose.js +353 -0
  37. package/dist/adapters/latex/prose.js.map +1 -0
  38. package/dist/adapters/latex/rendered.d.ts +10 -0
  39. package/dist/adapters/latex/rendered.d.ts.map +1 -0
  40. package/dist/adapters/latex/rendered.js +39 -0
  41. package/dist/adapters/latex/rendered.js.map +1 -0
  42. package/dist/adapters/pdfjs/fill.d.ts.map +1 -1
  43. package/dist/adapters/pdfjs/fill.js +12 -12
  44. package/dist/adapters/pdfjs/fill.js.map +1 -1
  45. package/dist/build.js +4 -4
  46. package/dist/build.js.map +1 -1
  47. package/dist/claim-provenance.d.ts +34 -0
  48. package/dist/claim-provenance.d.ts.map +1 -0
  49. package/dist/claim-provenance.js +146 -0
  50. package/dist/claim-provenance.js.map +1 -0
  51. package/dist/cli.d.ts.map +1 -1
  52. package/dist/cli.js +49 -5
  53. package/dist/cli.js.map +1 -1
  54. package/dist/domain/paper-source.d.ts +58 -0
  55. package/dist/domain/paper-source.d.ts.map +1 -0
  56. package/dist/domain/paper-source.js +109 -0
  57. package/dist/domain/paper-source.js.map +1 -0
  58. package/dist/domain/sentences.d.ts +26 -0
  59. package/dist/domain/sentences.d.ts.map +1 -0
  60. package/dist/domain/sentences.js +48 -0
  61. package/dist/domain/sentences.js.map +1 -0
  62. package/dist/domain/tex-document.d.ts +119 -0
  63. package/dist/domain/tex-document.d.ts.map +1 -0
  64. package/dist/domain/tex-document.js +43 -0
  65. package/dist/domain/tex-document.js.map +1 -0
  66. package/dist/eslint-rules/paper-typography.d.ts +7 -0
  67. package/dist/eslint-rules/paper-typography.d.ts.map +1 -1
  68. package/dist/eslint-rules/paper-typography.js +53 -1
  69. package/dist/eslint-rules/paper-typography.js.map +1 -1
  70. package/dist/facts-file.d.ts.map +1 -1
  71. package/dist/facts-file.js +2 -2
  72. package/dist/facts-file.js.map +1 -1
  73. package/dist/new-paper.d.ts +5 -0
  74. package/dist/new-paper.d.ts.map +1 -1
  75. package/dist/new-paper.js +3 -0
  76. package/dist/new-paper.js.map +1 -1
  77. package/dist/package-dirs.d.ts +5 -0
  78. package/dist/package-dirs.d.ts.map +1 -0
  79. package/dist/package-dirs.js +14 -0
  80. package/dist/package-dirs.js.map +1 -0
  81. package/dist/ports/latex.d.ts +35 -0
  82. package/dist/ports/latex.d.ts.map +1 -0
  83. package/dist/ports/latex.js +2 -0
  84. package/dist/ports/latex.js.map +1 -0
  85. package/dist/presets.d.ts +15 -2
  86. package/dist/presets.d.ts.map +1 -1
  87. package/dist/presets.js +19 -6
  88. package/dist/presets.js.map +1 -1
  89. package/dist/register.d.ts +40 -0
  90. package/dist/register.d.ts.map +1 -0
  91. package/dist/register.js +134 -0
  92. package/dist/register.js.map +1 -0
  93. package/dist/tex-paper.d.ts +19 -0
  94. package/dist/tex-paper.d.ts.map +1 -0
  95. package/dist/tex-paper.js +105 -0
  96. package/dist/tex-paper.js.map +1 -0
  97. package/dist/tex-requirements.d.ts +11 -0
  98. package/dist/tex-requirements.d.ts.map +1 -1
  99. package/dist/tex-requirements.js +14 -8
  100. package/dist/tex-requirements.js.map +1 -1
  101. package/dist/tex-venue-rules.d.ts +88 -0
  102. package/dist/tex-venue-rules.d.ts.map +1 -0
  103. package/dist/tex-venue-rules.js +268 -0
  104. package/dist/tex-venue-rules.js.map +1 -0
  105. package/dist/venue-rules.d.ts +1 -1
  106. package/dist/venue-rules.d.ts.map +1 -1
  107. package/docs/configuration.md +3 -2
  108. package/docs/e2e.md +69 -40
  109. package/docs/install.md +1 -1
  110. package/docs/prior-art/README.md +1 -1
  111. package/docs/prior-art/package-location.md +5 -5
  112. package/docs/prior-art/repro/README.md +1 -1
  113. package/docs/prior-art/test-tooling.md +21 -10
  114. package/docs/rules/tex/claim-provenance.md +83 -0
  115. package/docs/rules/tex/missing-input.md +58 -0
  116. package/docs/rules/tex/register.md +77 -0
  117. package/docs/rules/tex/required-section.md +75 -0
  118. package/docs/rules/tex/template.md +74 -0
  119. package/docs/rules/tex/venue-leftover.md +73 -0
  120. package/docs/rules.md +73 -48
  121. package/docs/testing.md +17 -4
  122. package/docs/texlive-install-decision.md +1 -1
  123. package/docs/toolchain.md +2 -2
  124. package/eslint-rules/paper-typography.test.mjs +48 -1
  125. package/eslint-rules/paper-typography.ts +72 -1
  126. package/eslint.config.mjs +156 -15
  127. package/fixtures/build-e2e/acmart/expect.json +26 -0
  128. package/fixtures/build-e2e/aidc/PIPELINE-STATUS.md +3 -0
  129. package/fixtures/build-e2e/aidc/expect.json +14 -0
  130. package/fixtures/build-e2e/aidc/paper.tex +46 -0
  131. package/fixtures/build-e2e/aidc/paperlint.json +1 -0
  132. package/fixtures/build-e2e/broken/expect.json +11 -0
  133. package/fixtures/build-e2e/cite/expect.json +27 -0
  134. package/fixtures/build-e2e/empty/expect.json +12 -0
  135. package/fixtures/build-e2e/fallback/expect.json +53 -0
  136. package/fixtures/build-e2e/guards/expect.json +6 -0
  137. package/fixtures/build-e2e/no-source/expect.json +10 -0
  138. package/fixtures/build-e2e/unbalanced/expect.json +60 -0
  139. package/fixtures/pdf-facts/README.md +1 -1
  140. package/fixtures/real-markdown-paper/baseline.ts +8 -7
  141. package/fixtures/render-paper/build-clean.sh +3 -5
  142. package/package.json +14 -2
  143. package/{skills/submit-paper/references/venues → presets}/acm-sigconf.jsonc +4 -1
  144. package/{skills/submit-paper/references/venues → presets}/agenticdev.jsonc +4 -0
  145. package/presets/aidc.jsonc +73 -0
  146. package/{skills/submit-paper/references/venues → presets}/aisec.jsonc +4 -0
  147. package/presets/ieee-conference.jsonc +66 -0
  148. package/{skills/submit-paper/references/venues → presets}/realm.jsonc +8 -1
  149. package/{skills/submit-paper/references/venues → presets/tex}/paper-guards.tex +1 -1
  150. package/{skills/submit-paper/references/venues → presets}/venue-profile.schema.json +30 -2
  151. package/scripts/check.harness.ts +64 -10
  152. package/scripts/check.test.ts +60 -13
  153. package/scripts/check.ts +83 -30
  154. package/scripts/test.test.ts +1 -1
  155. package/scripts/test.ts +1 -1
  156. package/skills/cold-read-diff/SKILL.md +31 -10
  157. package/skills/cold-read-diff/SKILL.md.spec.ts +30 -9
  158. package/skills/draft-paper/SKILL.md +2 -2
  159. package/skills/draft-paper/SKILL.md.spec.ts +1 -1
  160. package/skills/grade-paper-writing/SKILL.md +109 -44
  161. package/skills/grade-paper-writing/SKILL.md.spec.ts +108 -43
  162. package/skills/harden-paper/SKILL.md +35 -36
  163. package/skills/harden-paper/SKILL.md.spec.ts +34 -35
  164. package/skills/paper-adversarial-review/SKILL.md +2 -2
  165. package/skills/paper-adversarial-review/SKILL.md.spec.ts +1 -1
  166. package/skills/paper-pipeline/PIPELINE-MAP.md +2 -2
  167. package/skills/paper-pipeline/SKILL.md +4 -4
  168. package/skills/paper-pipeline/SKILL.md.spec.ts +3 -3
  169. package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +6 -6
  170. package/skills/paper-pipeline/references/writing-craft.md +53 -18
  171. package/skills/paper-pipeline/scripts/consumer.d.mts +0 -2
  172. package/skills/paper-pipeline/scripts/consumer.mjs +1 -15
  173. package/skills/pc-panel-review/SKILL.md +36 -11
  174. package/skills/pc-panel-review/SKILL.md.spec.ts +35 -10
  175. package/skills/render-paper/SKILL.md +2 -2
  176. package/skills/render-paper/SKILL.md.spec.ts +1 -1
  177. package/skills/render-paper/check-render.sh +16 -63
  178. package/skills/render-paper/render-paper.harness.mjs +17 -59
  179. package/src/adapters/banal/index.test.ts +1 -1
  180. package/src/adapters/banal/invocation.ts +1 -1
  181. package/src/adapters/banal/output.test.ts +1 -1
  182. package/src/adapters/banal/run.test.ts +1 -1
  183. package/src/adapters/banal/xml.test.ts +1 -1
  184. package/src/adapters/latex/document-class.ts +155 -0
  185. package/src/adapters/latex/hidden.ts +70 -0
  186. package/src/adapters/latex/includes.ts +66 -0
  187. package/src/adapters/latex/index.test.ts +535 -0
  188. package/src/adapters/latex/index.ts +65 -0
  189. package/src/adapters/latex/nodes.ts +150 -0
  190. package/src/adapters/latex/outline.ts +64 -0
  191. package/src/adapters/latex/parse.ts +137 -0
  192. package/src/adapters/latex/prose.ts +484 -0
  193. package/src/adapters/latex/rendered.ts +59 -0
  194. package/src/adapters/pdfjs/fill.test.ts +1 -1
  195. package/src/adapters/pdfjs/fill.ts +18 -10
  196. package/src/build.harness.mjs +5 -7
  197. package/src/build.ts +4 -4
  198. package/src/claim-provenance.test.ts +352 -0
  199. package/src/claim-provenance.ts +217 -0
  200. package/src/cli.harness.mjs +1 -1
  201. package/src/cli.test.ts +2 -8
  202. package/src/cli.ts +53 -8
  203. package/src/domain/paper-source.test.ts +156 -0
  204. package/src/domain/paper-source.ts +233 -0
  205. package/src/domain/sentences.ts +67 -0
  206. package/src/domain/tex-document.test.ts +80 -0
  207. package/src/domain/tex-document.ts +143 -0
  208. package/src/engine.harness.mjs +1 -1
  209. package/src/facts-file.test.ts +4 -7
  210. package/src/facts-file.ts +2 -2
  211. package/src/latex-loop.harness.ts +1 -1
  212. package/src/new-paper.test.ts +28 -3
  213. package/src/new-paper.ts +8 -0
  214. package/src/package-dirs.ts +17 -0
  215. package/src/paper-settings-commands.test.ts +6 -1
  216. package/src/ports/latex.ts +42 -0
  217. package/src/presets.test.ts +65 -4
  218. package/src/presets.ts +38 -15
  219. package/src/register.test.ts +165 -0
  220. package/src/register.ts +177 -0
  221. package/src/rule-cases.test.ts +524 -0
  222. package/src/rule-docs.test.ts +159 -0
  223. package/src/tex-paper.test.ts +131 -0
  224. package/src/tex-paper.ts +152 -0
  225. package/src/tex-requirements.harness.ts +13 -23
  226. package/src/tex-requirements.ts +35 -8
  227. package/src/tex-venue-rules.test.ts +741 -0
  228. package/src/tex-venue-rules.ts +461 -0
  229. package/src/toolchain.harness.mjs +1 -1
  230. package/src/venue-rules.test.ts +96 -4
  231. package/src/venue-rules.ts +1 -1
  232. package/templates/paper/paper.tex +2 -1
  233. package/skills/harden-paper/fixtures/uncited-assertions-sample.md +0 -43
  234. package/skills/harden-paper/fixtures/uncited-assertions-sample.tex +0 -77
  235. /package/{skills/submit-paper/references/venues → presets/tex}/agenticdev.tex +0 -0
  236. /package/{skills/submit-paper/references/venues → presets}/tex-base.jsonc +0 -0
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: pc-panel-review
3
- description: Use when asking "what would the program committee decide?" / "simulate the reviewers" / "what's this paper's accept probability?" on a drafted paper + artifact. Spawns N independent reviewers with DISTINCT lenses (one actually RUNS the artifact), then synthesizes a PC-chair meta-review into an accept/reject decision, a calibrated probability, and consensus must-fixes. Requires grade-paper-writing's persona stall inventory + tighten-paper's structural verdict as inputs (blocked until they exist — the panel cannot feel reader fatigue on its own). Also has a lightweight single-reviewer VENUE-FIT MODE for a quick CFP-fit spot-check. NOT a single fast defect hunt (paper-adversarial-review), a writing grade (grade-paper-writing), or the full pre-submit gate (harden-paper, which calls this) — this models the PC decision itself.
3
+ description: Use when asking "what would the program committee decide?" / "simulate the reviewers" / "what's this paper's accept probability?" on a drafted paper + artifact. Spawns N independent reviewers with DISTINCT lenses (one actually RUNS the artifact, one is a skimming genre/register reviewer), then synthesizes a PC-chair meta-review into an accept/reject decision reported as min/median/max, a calibrated probability, and consensus must-fixes. Requires grade-paper-writing's persona stall inventory + tighten-paper's structural verdict as inputs (blocked until they exist — the panel cannot feel reader fatigue on its own). Also has a lightweight single-reviewer VENUE-FIT MODE for a quick CFP-fit spot-check. NOT a single fast defect hunt (paper-adversarial-review), a writing grade (grade-paper-writing), or the full pre-submit gate (harden-paper, which calls this) — this models the PC decision itself.
4
4
  allowed-tools: [Read, Write, Grep, Glob, Bash, WebSearch, WebFetch, Agent, Skill]
5
5
  ---
6
6
 
7
- <!-- vigiles:sha256:b9feb099347291ff compiled from skills/pc-panel-review/SKILL.md.spec.ts -->
7
+ <!-- vigiles:sha256:7e5fb04d1cfa0548 compiled from skills/pc-panel-review/SKILL.md.spec.ts -->
8
8
 
9
9
  # pc-panel-review — model the whole PC, not one reviewer
10
10
 
@@ -51,7 +51,15 @@ prose-only read can.
51
51
  panel's only legitimate source for the readability/structure signal.
52
52
  *(Scorecard: this gate is row **`panel`**, and it declares **`structure`** (tighten-paper) + **`writing`**
53
53
  (grade-paper-writing) as its required inputs.)*
54
- 3. **Pick 3 (or 4) reviewer lenses** that a real PC for THIS venue would field. Menu — choose by fit:
54
+ 3. **Pick 3 (or 4) reviewer lenses** that a real PC for THIS venue would field. One is fixed; the rest
55
+ come from the menu, by fit:
56
+ - **Genre/register reviewer — MANDATORY on every panel, at every venue.** Confidence 3 (knows the
57
+ area, not this topic), reads ONLY the abstract, the introduction and the conclusion, and judges
58
+ whether this reads as a paper at this venue: whose result each sentence states, whether the
59
+ relations between claims are signposted or left to infer, and whether anything is an aphorism,
60
+ a fragment or a colloquial turn. This is the reviewer who writes one short review, scores low,
61
+ and calls the paper a blog post — the one reviewer a panel of careful full readers never
62
+ predicts. Give it the three sections as text and the venue's name, nothing else.
55
63
  - **Methods/stats skeptic** — sample size, construct validity, multiple comparisons, overclaims,
56
64
  whether the CIs support the claims.
57
65
  - **Domain practitioner** — relevance to the CFP, novelty vs the nearest prior work, does the
@@ -67,7 +75,13 @@ prose-only read can.
67
75
  4. **Spawn them in PARALLEL, each on a separate model instance** (e.g. Fable via subagent), each blind
68
76
  to the others — independence is the whole point. Give each: the paper (`.tex`/PDF), the artifact
69
77
  path, the quoted rubric, its lens, **and the two step-2 artifacts (persona stall inventory +
70
- tighten-paper structural verdict)**. Force a filled review form (below).
78
+ tighten-paper structural verdict)** — the genre/register reviewer gets only its three sections.
79
+ Force a filled review form (below).
80
+
81
+ 🔴 **No reviewer is shown a previous round** — not its scores, not its reviews, not "the panel
82
+ gave 3.25 last time", not a list of what changed since. A reviewer who is told the last score
83
+ anchors on it (*"up from the 3.25 the earlier rounds reported"*), and the rounds then converge on
84
+ each other instead of on the paper. Comparing rounds is the chair's job, after the reviews land.
71
85
  5. **Synthesize the meta-review yourself** (PC chair) once all land — see output.
72
86
 
73
87
  ## Each reviewer returns a review form
@@ -90,10 +104,19 @@ prose-only read can.
90
104
  - One-line meta: does this belong at this venue?
91
105
 
92
106
  ## The meta-review (PC-chair synthesis) — the actual deliverable
93
- - **Score table:** each reviewer × each criterion + overall + confidence, at a glance.
94
- - **Decision:** the rec the PC discussion converges on (weight by confidence; a lone low-confidence
95
- outlier doesn't sink two confident accepts), + a **calibrated accept probability** for this venue
96
- and edition.
107
+ - **Score table:** each reviewer × each criterion + overall + confidence, at a glance, and under it
108
+ the overall score's **min / median / max** across reviewers — never a mean alone, which hides the
109
+ one low review that decides a workshop.
110
+ - **Decision:** for a **workshop**, the decision follows the **MIN**: a workshop PC with three
111
+ reviews and a short discussion does not overrule a reject, so one Reject or Strong Reject sinks the
112
+ paper however confident the others are. For a conference, the rec the PC discussion converges on,
113
+ weighted by confidence, with the min named beside it. Either way, a **calibrated accept probability**
114
+ for this venue and edition.
115
+ - 🔴 **One reviewer is not a panel.** A run with fewer than three reviews and a meta-review — a
116
+ Venue-fit spot-check, a single re-review, a panel whose other seats did not return — is recorded as
117
+ what it is (`venue-fit`, `single review`), never in the scorecard's `panel` row. A single
118
+ review recorded as a panel verdict is how a paper reaches submission on an 88% that three real
119
+ reviewers turned into a reject.
97
120
  - **Consensus must-fixes:** issues **≥2 reviewers independently raised** — these are the real ones;
98
121
  apply before submitting.
99
122
  - **Single-reviewer flags:** noted, triaged (fix if cheap, else defer to camera-ready).
@@ -199,9 +222,11 @@ reject-risk without rubber-stamping. For the real decision, escalate to the full
199
222
  ## The reproduce loop (why this beats a prose review)
200
223
  This is "double-blind review as a real venue does it, WITH reproduction." The artifact-runner reviewer
201
224
  is the difference-maker on both runs so far: it doesn't judge the paper's numbers, it re-derives them.
202
- Run the panel, apply the consensus fixes, then **re-run the panel** (same lenses, told what changed) to
203
- confirm the fixes landed and nothing regressed — the artifact-runner re-executes every harness each
204
- round. Track the accept-probability progression in the review ledger. Stop when the panel converges to
225
+ Run the panel, apply the consensus fixes, then **re-run the panel** — same lenses, FRESH reviewers,
226
+ told nothing about the previous round — to confirm the fixes landed and nothing regressed; the
227
+ artifact-runner re-executes every harness each round. The chair, not the reviewers, sets the new
228
+ reviews beside the old ones and checks that each earlier must-fix is gone. Track the min / median / max
229
+ progression in the review ledger. Stop when the panel converges to
205
230
  accept and the artifact reproduces clean; don't loop past one confirming round.
206
231
 
207
232
  ## Provenance (two papers, battle-tested)
@@ -9,7 +9,7 @@ import { experimental_skill } from "vigiles/spec";
9
9
  export default experimental_skill({
10
10
  name: "pc-panel-review",
11
11
  description:
12
- 'Use when asking "what would the program committee decide?" / "simulate the reviewers" / "what\'s this paper\'s accept probability?" on a drafted paper + artifact. Spawns N independent reviewers with DISTINCT lenses (one actually RUNS the artifact), then synthesizes a PC-chair meta-review into an accept/reject decision, a calibrated probability, and consensus must-fixes. Requires grade-paper-writing\'s persona stall inventory + tighten-paper\'s structural verdict as inputs (blocked until they exist — the panel cannot feel reader fatigue on its own). Also has a lightweight single-reviewer VENUE-FIT MODE for a quick CFP-fit spot-check. NOT a single fast defect hunt (paper-adversarial-review), a writing grade (grade-paper-writing), or the full pre-submit gate (harden-paper, which calls this) — this models the PC decision itself.',
12
+ 'Use when asking "what would the program committee decide?" / "simulate the reviewers" / "what\'s this paper\'s accept probability?" on a drafted paper + artifact. Spawns N independent reviewers with DISTINCT lenses (one actually RUNS the artifact, one is a skimming genre/register reviewer), then synthesizes a PC-chair meta-review into an accept/reject decision reported as min/median/max, a calibrated probability, and consensus must-fixes. Requires grade-paper-writing\'s persona stall inventory + tighten-paper\'s structural verdict as inputs (blocked until they exist — the panel cannot feel reader fatigue on its own). Also has a lightweight single-reviewer VENUE-FIT MODE for a quick CFP-fit spot-check. NOT a single fast defect hunt (paper-adversarial-review), a writing grade (grade-paper-writing), or the full pre-submit gate (harden-paper, which calls this) — this models the PC decision itself.',
13
13
  tools: [
14
14
  "Read",
15
15
  "Write",
@@ -67,7 +67,15 @@ prose-only read can.
67
67
  panel's only legitimate source for the readability/structure signal.
68
68
  *(Scorecard: this gate is row **\`panel\`**, and it declares **\`structure\`** (tighten-paper) + **\`writing\`**
69
69
  (grade-paper-writing) as its required inputs.)*
70
- 3. **Pick 3 (or 4) reviewer lenses** that a real PC for THIS venue would field. Menu — choose by fit:
70
+ 3. **Pick 3 (or 4) reviewer lenses** that a real PC for THIS venue would field. One is fixed; the rest
71
+ come from the menu, by fit:
72
+ - **Genre/register reviewer — MANDATORY on every panel, at every venue.** Confidence 3 (knows the
73
+ area, not this topic), reads ONLY the abstract, the introduction and the conclusion, and judges
74
+ whether this reads as a paper at this venue: whose result each sentence states, whether the
75
+ relations between claims are signposted or left to infer, and whether anything is an aphorism,
76
+ a fragment or a colloquial turn. This is the reviewer who writes one short review, scores low,
77
+ and calls the paper a blog post — the one reviewer a panel of careful full readers never
78
+ predicts. Give it the three sections as text and the venue's name, nothing else.
71
79
  - **Methods/stats skeptic** — sample size, construct validity, multiple comparisons, overclaims,
72
80
  whether the CIs support the claims.
73
81
  - **Domain practitioner** — relevance to the CFP, novelty vs the nearest prior work, does the
@@ -83,7 +91,13 @@ prose-only read can.
83
91
  4. **Spawn them in PARALLEL, each on a separate model instance** (e.g. Fable via subagent), each blind
84
92
  to the others — independence is the whole point. Give each: the paper (\`.tex\`/PDF), the artifact
85
93
  path, the quoted rubric, its lens, **and the two step-2 artifacts (persona stall inventory +
86
- tighten-paper structural verdict)**. Force a filled review form (below).
94
+ tighten-paper structural verdict)** — the genre/register reviewer gets only its three sections.
95
+ Force a filled review form (below).
96
+
97
+ 🔴 **No reviewer is shown a previous round** — not its scores, not its reviews, not "the panel
98
+ gave 3.25 last time", not a list of what changed since. A reviewer who is told the last score
99
+ anchors on it (*"up from the 3.25 the earlier rounds reported"*), and the rounds then converge on
100
+ each other instead of on the paper. Comparing rounds is the chair's job, after the reviews land.
87
101
  5. **Synthesize the meta-review yourself** (PC chair) once all land — see output.
88
102
 
89
103
  ## Each reviewer returns a review form
@@ -106,10 +120,19 @@ prose-only read can.
106
120
  - One-line meta: does this belong at this venue?
107
121
 
108
122
  ## The meta-review (PC-chair synthesis) — the actual deliverable
109
- - **Score table:** each reviewer × each criterion + overall + confidence, at a glance.
110
- - **Decision:** the rec the PC discussion converges on (weight by confidence; a lone low-confidence
111
- outlier doesn't sink two confident accepts), + a **calibrated accept probability** for this venue
112
- and edition.
123
+ - **Score table:** each reviewer × each criterion + overall + confidence, at a glance, and under it
124
+ the overall score's **min / median / max** across reviewers — never a mean alone, which hides the
125
+ one low review that decides a workshop.
126
+ - **Decision:** for a **workshop**, the decision follows the **MIN**: a workshop PC with three
127
+ reviews and a short discussion does not overrule a reject, so one Reject or Strong Reject sinks the
128
+ paper however confident the others are. For a conference, the rec the PC discussion converges on,
129
+ weighted by confidence, with the min named beside it. Either way, a **calibrated accept probability**
130
+ for this venue and edition.
131
+ - 🔴 **One reviewer is not a panel.** A run with fewer than three reviews and a meta-review — a
132
+ Venue-fit spot-check, a single re-review, a panel whose other seats did not return — is recorded as
133
+ what it is (\`venue-fit\`, \`single review\`), never in the scorecard's \`panel\` row. A single
134
+ review recorded as a panel verdict is how a paper reaches submission on an 88% that three real
135
+ reviewers turned into a reject.
113
136
  - **Consensus must-fixes:** issues **≥2 reviewers independently raised** — these are the real ones;
114
137
  apply before submitting.
115
138
  - **Single-reviewer flags:** noted, triaged (fix if cheap, else defer to camera-ready).
@@ -215,9 +238,11 @@ reject-risk without rubber-stamping. For the real decision, escalate to the full
215
238
  ## The reproduce loop (why this beats a prose review)
216
239
  This is "double-blind review as a real venue does it, WITH reproduction." The artifact-runner reviewer
217
240
  is the difference-maker on both runs so far: it doesn't judge the paper's numbers, it re-derives them.
218
- Run the panel, apply the consensus fixes, then **re-run the panel** (same lenses, told what changed) to
219
- confirm the fixes landed and nothing regressed — the artifact-runner re-executes every harness each
220
- round. Track the accept-probability progression in the review ledger. Stop when the panel converges to
241
+ Run the panel, apply the consensus fixes, then **re-run the panel** — same lenses, FRESH reviewers,
242
+ told nothing about the previous round — to confirm the fixes landed and nothing regressed; the
243
+ artifact-runner re-executes every harness each round. The chair, not the reviewers, sets the new
244
+ reviews beside the old ones and checks that each earlier must-fix is gone. Track the min / median / max
245
+ progression in the review ledger. Stop when the panel converges to
221
246
  accept and the artifact reproduces clean; don't loop past one confirming round.
222
247
 
223
248
  ## Provenance (two papers, battle-tested)
@@ -4,7 +4,7 @@ description: Compile a LaTeX paper (ACM/IEEE/arXiv) to PDF and render its pages
4
4
  allowed-tools: [Read, Write, Edit, Grep, Glob, Bash, SendUserFile]
5
5
  ---
6
6
 
7
- <!-- vigiles:sha256:a1ec1d392f144c80 compiled from skills/render-paper/SKILL.md.spec.ts -->
7
+ <!-- vigiles:sha256:5e2ee8850c098375 compiled from skills/render-paper/SKILL.md.spec.ts -->
8
8
 
9
9
  # render-paper — .tex → PDF → readable page PNGs
10
10
 
@@ -34,7 +34,7 @@ That list was replaced on 2026-08-03; nothing about the toolchain below changed.
34
34
  by `kpsewhich` against the files each profile names. The last line it prints is `bin: <dir>` —
35
35
  put that directory first on PATH before calling `pdflatex` or `check-render.sh` by hand.
36
36
 
37
- 🔴 The package list lives in the venue profiles (`submit-paper/references/venues/*.jsonc`, the
37
+ 🔴 The package list lives in the venue profiles (`presets/*.jsonc` in the package, the
38
38
  `tex` block; `tex-base.jsonc` for every paper) and nowhere else. A new venue that needs a new
39
39
  package gets it there — never in a script, never here. A TeX Live without `libertine` builds an
40
40
  acmart paper GREEN in Computer Modern; that is why the files are checked, not the exit code.
@@ -40,7 +40,7 @@ That list was replaced on 2026-08-03; nothing about the toolchain below changed.
40
40
  by \`kpsewhich\` against the files each profile names. The last line it prints is \`bin: <dir>\` —
41
41
  put that directory first on PATH before calling \`pdflatex\` or \`check-render.sh\` by hand.
42
42
 
43
- 🔴 The package list lives in the venue profiles (\`submit-paper/references/venues/*.jsonc\`, the
43
+ 🔴 The package list lives in the venue profiles (\`presets/*.jsonc\` in the package, the
44
44
  \`tex\` block; \`tex-base.jsonc\` for every paper) and nowhere else. A new venue that needs a new
45
45
  package gets it there — never in a script, never here. A TeX Live without \`libertine\` builds an
46
46
  acmart paper GREEN in Computer Modern; that is why the files are checked, not the exit code.
@@ -25,75 +25,28 @@ set -uo pipefail
25
25
  # check — that is, the defect the guard was set up for comes back together with a green report.
26
26
  # A broken `import` in JS fails loudly; here we have to fail on our own.
27
27
  #
28
- # A ladder of THREE candidates, because the directory MOVED (12.09): `venues/` is now carried by the
29
- # `paperlint` package, and at the consumer's old location there is a symlink into it.
30
- # The order "the declared package → a sibling inside the package → the consumer's directory" is
31
- # deliberate: the package must win silently. It only gets loud when there is NOT A SINGLE ONE.
32
- # 🔴 THE SCRIPT'S OWN DIRECTORY — TAKEN ABSOLUTE AND BEFORE ANY `cd` (12.09.2026, evening).
33
- # Found by a RED HARNESS, `gates.harness.mjs`, not by proofreading, and it is my own regression from
34
- # the same day: below, the script does `cd "$DIR"` into the paper directory, and the venues resolve
35
- # stood AFTER that `cd`. So `git rev-parse --show-toplevel` was already running in the fixture's
36
- # temporary directory, the root was not found, and the build honestly stopped — on a test that had
37
- # been passing until then.
28
+ # TWO candidates, in this order: the package by its NAME, from the current directory (a consumer's
29
+ # installed paperlint wins), then the package THIS script ships in. Loud only when neither holds it.
38
30
  #
39
- # ⚠️ The previous version was wrong THE SAME WAY, but silently: it did not check the result and built
40
- # the PDF without the reference guard. That is, going red is not a new breakage but a manifestation
41
- # of an old one; still, a test that was green yesterday and is red today is mine to fix.
42
- #
43
- # The neighbouring `build.sh` already carries this same lesson in a comment ("The script directory is
44
- # taken ONCE and ABSOLUTE, before cd") — CI failed on it there on 30.08. A second instance of the same
45
- # class in a file right next door.
31
+ # 🔴 THE SCRIPT'S OWN DIRECTORY IS TAKEN ABSOLUTE AND BEFORE ANY `cd`: below, the script `cd`s into
32
+ # the paper directory, and a lookup after that would start from the paper, not from here.
46
33
  SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
47
34
 
48
35
  resolve_venues() {
49
- local root pkg
50
- # Three sources for the root, from the most explicit to the most reliable. The last one is the
51
- # SCRIPT's location (`.claude/skills/render-paper/` ⇒ three levels up), and it is the only one that
52
- # depends neither on the current directory nor on whether the caller sits inside a git tree.
53
- # ⚠️ THE PARENTHESES ARE MANDATORY, and the harness caught this a minute after I wrote the line
54
- # without them. In bash `A || B && C` parses as `(A || B) && C`, so on a SUCCESSFUL `git rev-parse`
55
- # the `pwd` ran as well, and the substitution returned TWO lines — the repository root plus the
56
- # current directory. By hand from a directory outside git this did not reproduce: there git failed,
57
- # and only one branch ran.
58
- # ⚠️ THE LAST RUNG SEARCHES, IT DOES NOT COUNT LEVELS. `cd "$SELF_DIR/../../.."` used to stand here
59
- # — correct for `.claude/skills/render-paper/` (three levels to the root) and silently wrong after
60
- # the skill moved into the package, where it lies in `skills/render-paper/` (two): the climb
61
- # overshot ABOVE the repository, and rung 2 looked for venues in someone else's directory. Climbing
62
- # up to the directory that holds `.claude` does not depend on the depth.
63
- local probe="$SELF_DIR" fallback=""
64
- while [ "$probe" != "/" ]; do
65
- if [ -d "$probe/.claude" ]; then fallback="$probe"; break; fi
66
- probe=$(dirname "$probe")
67
- done
68
- root="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || printf '%s' "$fallback")}"
69
-
70
- # 1) the package, if it already carries venues/ (after step 5)
71
- pkg=$(node -e 'try{process.stdout.write(require.resolve("paperlint/venues/paper-guards.tex"))}catch{}' 2>/dev/null || true)
36
+ local pkg own
37
+ # 1) the package by its name. `node -e` resolves from the current directory.
38
+ pkg=$(node -e 'try{process.stdout.write(require.resolve("paperlint/presets/tex/paper-guards.tex"))}catch{}' 2>/dev/null || true)
72
39
  if [ -n "$pkg" ] && [ -f "$pkg" ]; then
73
40
  dirname "$pkg"
74
41
  return 0
75
42
  fi
76
43
 
77
- # 2) THE SIBLING SKILL, from THIS script's directory. Added 12.09 together with the move of
78
- # `submit-paper` into the package, and it is the only one of the three that works in BOTH worlds at
79
- # once:
80
- # • package checkout `skills/render-paper/..` → `skills/submit-paper/references/venues`
81
- # • consumer `.claude/skills/render-paper/..` → `.claude/skills/submit-paper/...`
82
- # (both directories are symlinks into the package, `-f` goes through them, and `cd`+`pwd`
83
- # leaves the logical path)
84
- # Rung 1 depends on cwd (`node -e` resolves from the current directory), rung 3 depends on the
85
- # consumer's root having been found at all. This one depends on neither — exactly the anchor whose
86
- # absence had `gates.harness.mjs` red on 12.09.
87
- local sibling="$SELF_DIR/../submit-paper/references/venues"
88
- if [ -f "$sibling/paper-guards.tex" ]; then
89
- (cd "$sibling" && pwd)
90
- return 0
91
- fi
92
-
93
- # 3) the skill directory in the consumer — for a consumer that mounted the skills differently
94
- local local_dir="$root/.claude/skills/submit-paper/references/venues"
95
- if [ -f "$local_dir/paper-guards.tex" ]; then
96
- printf '%s\n' "$local_dir"
44
+ # 2) the package this script is part of: `skills/render-paper/` → `presets/tex/`. `cd -P` follows
45
+ # the symlinks a consumer's `.claude/skills/render-paper` is made of, so `../..` is the PACKAGE's
46
+ # root, not the consumer's `.claude/`. Depends neither on the cwd nor on a git tree.
47
+ own="$(cd -P "$SELF_DIR" && pwd)/../../presets/tex"
48
+ if [ -f "$own/paper-guards.tex" ]; then
49
+ (cd "$own" && pwd)
97
50
  return 0
98
51
  fi
99
52
 
@@ -107,8 +60,8 @@ if [ "${1:-}" = "--print-venues" ]; then
107
60
  printf '%s\n' "$VD"
108
61
  exit 0
109
62
  fi
110
- echo "✗ paper-guards.tex not found: not under the package name paperlint, not beside the script" >&2
111
- echo " (skills/submit-paper/references/venues), not at the consumer (.claude/skills/submit-paper/references/venues)." >&2
63
+ echo "✗ paper-guards.tex not found: not under the package name paperlint (paperlint/presets/tex)," >&2
64
+ echo " not in the package this script ships in (../../presets/tex from it)." >&2
112
65
  echo " Building the paper without it is not allowed: \\input{paper-guards} will not fail, it will vanish silently," >&2
113
66
  echo " and the PDF will be built WITHOUT the check for dangling \\ref and \\cite." >&2
114
67
  exit 2
@@ -135,7 +88,7 @@ if [ "${NO_COMPILE:-0}" != "1" ] && [ -f "$BASE.tex" ]; then
135
88
  # Two passes: overfull boxes surface on every pass; a second pass settles refs
136
89
  # so Reference/Citation-undefined warnings are accurate.
137
90
  # 🔴 TEXINPUTS is mandatory, and that was found by a crash on 26.08. Papers pull in their venue's
138
- # numbers via `\input{<venue>}`, and the file lies in `.claude/skills/submit-paper/references/venues/`.
91
+ # numbers via `\input{<venue>}`, and the file lies in the package's `presets/tex/`.
139
92
  # Without that path the build fails with `File \`agenticdev.tex' not found` — and the `|| true`
140
93
  # below swallows it, after which the script reads a log of UNKNOWN origin and reports on it. That
141
94
  # is, compiling this paper here silently did not work, and it only became visible once a pass
@@ -68,34 +68,11 @@ await checkSkill("render-paper");
68
68
  const run = (args, opts = {}) =>
69
69
  spawnSync("bash", [CHECK, ...args], { encoding: "utf8", ...opts });
70
70
 
71
- // First half — on TODAY's tree, resolution must succeed IF THERE IS ANYTHING TO
72
- // resolve.
73
- //
74
- // 🔴 THERE USED TO BE AN UNCONDITIONAL `assert.equal(status, 0)` HERE, AND THE MOVE BROKE IT —
75
- // for a real reason, not by accident. It relied on the SURROUNDING tree: while the skill lived
76
- // at the consumer, the second rung of the ladder won
77
- // (`.claude/skills/submit-paper/references/venues/`). The package checkout has neither that nor
78
- // the first rung — `venues/` arrives together with `submit-paper`, which hasn't moved yet. So
79
- // the assertion wasn't checking resolution, it was checking for the presence of an unrelated
80
- // directory nearby.
81
- //
82
- // The form now is: resolution must succeed IF AND ONLY IF at least one candidate exists on
83
- // disk. This is still a real assertion — it fails if a candidate exists and the script didn't
84
- // find it — but it stops requiring the existence of something this repository doesn't have yet.
85
- // The absence of both candidates gets PRINTED, not swallowed: "the check found nothing to
86
- // check" and "the check passed" have to look different.
71
+ // First half — on this tree, resolution must succeed IF AND ONLY IF a candidate exists on disk:
72
+ // the package's own `presets/tex/`. The absence of it is PRINTED, not swallowed: "the check found
73
+ // nothing to check" and "the check passed" have to look different.
87
74
  const candidates = [
88
- // rung 1 — the package
89
- join(HERE, "..", "..", "venues", "paper-guards.tex"),
90
- // rung 2 — the skill directory at the consumer
91
- join(
92
- HERE,
93
- "..",
94
- "submit-paper",
95
- "references",
96
- "venues",
97
- "paper-guards.tex",
98
- ),
75
+ join(HERE, "..", "..", "presets", "tex", "paper-guards.tex"),
99
76
  ].filter((c) => existsSync(c));
100
77
 
101
78
  const ok = run(["--print-venues"]);
@@ -108,9 +85,8 @@ await checkSkill("render-paper");
108
85
  `silent success this whole block was written to catch.`,
109
86
  );
110
87
  console.log(
111
- ` --print-venues: NOT VERIFIED on the live tree — paper-guards.tex is absent from both ` +
112
- `the package (venues/) and next to it (submit-paper/references/venues/). It arrives with ` +
113
- `\`submit-paper\`; until then only the fixtures below hold this half.`,
88
+ ` --print-venues: NOT VERIFIED on the live tree — paper-guards.tex is absent from the ` +
89
+ `package's presets/tex/; only the fixtures below hold this half.`,
114
90
  );
115
91
  } else {
116
92
  assert.equal(
@@ -159,18 +135,9 @@ await checkSkill("render-paper");
159
135
  // When the file is NOWHERE, the script must exit with code 2 and say why. A silent success here
160
136
  // is exactly the defect the whole ladder was written for.
161
137
  //
162
- // 🔴 RUN AGAINST A COPY OF THE SCRIPT, AND THIS IS NOT A TEST WORKAROUND, IT'S A CONSEQUENCE OF
163
- // THE MOVE (09-12, wave ③). Before `submit-paper` moved, "nowhere" was reproduced by an empty
164
- // consumer root: both former rungs looked outward (the package name from cwd · `.claude/` under
165
- // the root), and an empty directory found nothing at either. Now the script has a rung anchored
166
- // to ITS OWN directory (`$SELF_DIR/../submit-paper/references/venues`) — added for
167
- // cwd-independence — so as long as the script sits inside the package, venues next to it exist
168
- // ALWAYS, and "nowhere" can no longer be reached through the environment. The only honest way to
169
- // demonstrate that state is to move the script itself out of the package. The property being
170
- // checked is otherwise identical, word for word: nothing resolved ⇒ exit 2 + the name of the
171
- // missing file. ⚠️ What this form no longer checks: that an empty consumer root won't make the
172
- // script lie — and it shouldn't, because such a root is now legitimately shadowed by the copy in
173
- // the package.
138
+ // 🔴 RUN AGAINST A COPY OF THE SCRIPT. Inside the package its second rung (the package it ships
139
+ // in) always holds the file, so "nowhere" is reachable only by moving the script out of the
140
+ // package: nothing resolved ⇒ exit 2 + the name of the missing file.
174
141
  const empty = realpathSync(mkdtempSync(join(tmpdir(), "venues-none-")));
175
142
  const lonely = join(empty, "check-render.sh");
176
143
  copyFileSync(CHECK, lonely);
@@ -190,30 +157,21 @@ await checkSkill("render-paper");
190
157
  "the failure message must name the missing file",
191
158
  );
192
159
 
193
- // Third half — the ORDER of the rungs. Not cosmetic: on the day of the move, both rungs will be
194
- // true at once, and the package has to win, otherwise the consumer silently keeps building with
195
- // their old copy. Plant BOTH candidates and see which one is chosen.
160
+ // Third half — the ORDER of the rungs. The package the consumer installed, found by its name,
161
+ // must win over the copy this script ships in; otherwise a consumer silently builds with a stale
162
+ // copy. Plant a package by name beside the real script and see which one is chosen.
196
163
  const both = realpathSync(mkdtempSync(join(tmpdir(), "venues-both-")));
197
- const pkgVenues = join(both, "node_modules", "paperlint", "venues");
198
- const skillVenues = join(
199
- both,
200
- ".claude",
201
- "skills",
202
- "submit-paper",
203
- "references",
204
- "venues",
205
- );
206
- for (const d of [pkgVenues, skillVenues]) mkdirSync(d, { recursive: true });
164
+ const pkgVenues = join(both, "node_modules", "paperlint", "presets", "tex");
165
+ mkdirSync(pkgVenues, { recursive: true });
207
166
  writeFileSync(
208
167
  join(both, "node_modules", "paperlint", "package.json"),
209
168
  JSON.stringify({
210
169
  name: "paperlint",
211
170
  version: "0.0.0",
212
- exports: { "./venues/*": "./venues/*" },
171
+ exports: { "./*": "./*" },
213
172
  }),
214
173
  );
215
174
  writeFileSync(join(pkgVenues, "paper-guards.tex"), "% from package\n");
216
- writeFileSync(join(skillVenues, "paper-guards.tex"), "% from skill dir\n");
217
175
  const race = run(["--print-venues"], {
218
176
  cwd: both,
219
177
  env: { ...process.env, CLAUDE_PROJECT_DIR: both },
@@ -226,7 +184,7 @@ await checkSkill("render-paper");
226
184
  assert.equal(
227
185
  race.stdout.trim(),
228
186
  pkgVenues,
229
- "with both candidates present, the PACKAGE wins, not the skill directory",
187
+ "with both candidates present, the INSTALLED package wins, not the copy the script ships in",
230
188
  );
231
189
  }
232
190
 
@@ -276,7 +234,7 @@ await checkSkill("render-paper");
276
234
  );
277
235
  assert.match(
278
236
  build,
279
- /require\.resolve\("paperlint\/venues\/paper-guards\.tex"\)/,
237
+ /require\.resolve\("paperlint\/presets\/tex\/paper-guards\.tex"\)/,
280
238
  `${where}: the ladder's first rung (the package) must remain`,
281
239
  );
282
240
  assert.match(
@@ -2,7 +2,7 @@
2
2
  * `adapters/banal/index.ts` — banal as the two ports the app sees. What `run.test.ts` checks in
3
3
  * banal's own terms is checked here as the domain receives it: a `Geometry` whose provenance names
4
4
  * banal and the rule that found it, a reason that is one line of text, a `Ready` that says what was
5
- * verified. In-memory ports throughout; the real banal is `test/e2e/banal.ts`.
5
+ * verified. In-memory ports throughout; the real banal is `test/e2e/tex/banal.e2e.ts`.
6
6
  */
7
7
  import assert from "node:assert/strict";
8
8
  import { test } from "vitest";
@@ -7,7 +7,7 @@
7
7
  * (`Error: Failed to run pdftohtml`, measured); told a version below 0.85 it applies a larger
8
8
  * correction and every font size moves. So `$PDFTOHTML` (banal's own override, line 166) points at a
9
9
  * stub that answers `-v` with `XML_DIALECT.version` — the pdftohtml whose XML `xml.ts` writes — and
10
- * refuses anything else. `test/e2e/banal.ts` shows both failures with the real banal.
10
+ * refuses anything else. `test/e2e/tex/banal.e2e.ts` shows both failures with the real banal.
11
11
  */
12
12
  import type { Opaque } from "ts-essentials";
13
13
  import type { AbsolutePath } from "../../domain/paths.ts";
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * `output.ts` — every way a banal run can end, as a table of `ProcessExit` → `Result`, and the
3
- * measurement → geometry projection. Pure: no perl, no disk. The real banal is `test/e2e/banal.ts`.
3
+ * measurement → geometry projection. Pure: no perl, no disk. The real banal is `test/e2e/tex/banal.e2e.ts`.
4
4
  */
5
5
  import assert from "node:assert/strict";
6
6
  import { test } from "vitest";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * `adapters/banal/run.ts` — running and installing banal, on in-memory ports: which `Command` it builds, what it stages,
3
3
  * what it downloads and writes, and what it concludes. No perl, no disk, no network: the adapters
4
- * record, and each assertion reads the record. Real perl and the real banal are `test/e2e/banal.ts`;
4
+ * record, and each assertion reads the record. Real perl and the real banal are `test/e2e/tex/banal.e2e.ts`;
5
5
  * the adapters' own behaviour is `src/adapters/node/*.test.ts`.
6
6
  */
7
7
  import assert from "node:assert/strict";
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Every page here is written by hand. The real-PDF half — pdf.js boxes of a committed fixture,
6
6
  * written as XML, measured by the real banal and compared with banal on real pdftohtml — is
7
- * `test/e2e/banal.ts`, which runs where `paperlint toolchain` has installed banal.
7
+ * `test/e2e/tex/banal.e2e.ts`, which runs where `paperlint toolchain` has installed banal.
8
8
  */
9
9
  import assert from "node:assert/strict";
10
10
  import { test } from "vitest";