paperlint 9.0.0 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/.github/CLAUDE.md +22 -0
  2. package/.github/CLAUDE.md.spec.ts +20 -0
  3. package/CLAUDE.md +59 -515
  4. package/CONTRIBUTING.md +2 -2
  5. package/README.md +68 -128
  6. package/dist/CLAUDE.md.spec.d.ts +3 -0
  7. package/dist/CLAUDE.md.spec.d.ts.map +1 -0
  8. package/dist/CLAUDE.md.spec.js +62 -0
  9. package/dist/CLAUDE.md.spec.js.map +1 -0
  10. package/dist/build.d.ts +1 -1
  11. package/dist/build.js +2 -2
  12. package/dist/cli.d.ts +5 -1
  13. package/dist/cli.d.ts.map +1 -1
  14. package/dist/cli.js +52 -6
  15. package/dist/cli.js.map +1 -1
  16. package/dist/domain/layout-tree.d.ts +19 -0
  17. package/dist/domain/layout-tree.d.ts.map +1 -0
  18. package/dist/domain/layout-tree.js +32 -0
  19. package/dist/domain/layout-tree.js.map +1 -0
  20. package/dist/domain/lookup-cache.d.ts +2 -2
  21. package/dist/domain/lookup-cache.js +2 -2
  22. package/dist/domain/paper-layout.d.ts +81 -0
  23. package/dist/domain/paper-layout.d.ts.map +1 -0
  24. package/dist/domain/paper-layout.js +259 -0
  25. package/dist/domain/paper-layout.js.map +1 -0
  26. package/dist/domain/repro-layout.d.ts +80 -0
  27. package/dist/domain/repro-layout.d.ts.map +1 -0
  28. package/dist/domain/repro-layout.js +247 -0
  29. package/dist/domain/repro-layout.js.map +1 -0
  30. package/dist/domain/scaffold-layout.d.ts +28 -0
  31. package/dist/domain/scaffold-layout.d.ts.map +1 -0
  32. package/dist/domain/scaffold-layout.js +81 -0
  33. package/dist/domain/scaffold-layout.js.map +1 -0
  34. package/dist/eslint-rules/CLAUDE.md.spec.d.ts +3 -0
  35. package/dist/eslint-rules/CLAUDE.md.spec.d.ts.map +1 -0
  36. package/dist/eslint-rules/CLAUDE.md.spec.js +39 -0
  37. package/dist/eslint-rules/CLAUDE.md.spec.js.map +1 -0
  38. package/dist/eslint-rules/install-path-literals.d.ts +1 -1
  39. package/dist/eslint-rules/install-path-literals.js +4 -4
  40. package/dist/eslint-rules/install-path-literals.js.map +1 -1
  41. package/dist/eslint-rules/pdf-last-page-balance.js +1 -1
  42. package/dist/eslint-rules/prereg-coverage.d.ts +98 -0
  43. package/dist/eslint-rules/prereg-coverage.d.ts.map +1 -0
  44. package/dist/eslint-rules/prereg-coverage.js +274 -0
  45. package/dist/eslint-rules/prereg-coverage.js.map +1 -0
  46. package/dist/folder-layout-hook.d.ts +21 -0
  47. package/dist/folder-layout-hook.d.ts.map +1 -0
  48. package/dist/folder-layout-hook.js +117 -0
  49. package/dist/folder-layout-hook.js.map +1 -0
  50. package/dist/folder-layout-rule.d.ts +22 -0
  51. package/dist/folder-layout-rule.d.ts.map +1 -0
  52. package/dist/folder-layout-rule.js +107 -0
  53. package/dist/folder-layout-rule.js.map +1 -0
  54. package/dist/init.js +1 -1
  55. package/dist/init.js.map +1 -1
  56. package/dist/lib/agent-cli-version.js +1 -1
  57. package/dist/lib/agent-cli-version.js.map +1 -1
  58. package/dist/new-paper.d.ts +4 -1
  59. package/dist/new-paper.d.ts.map +1 -1
  60. package/dist/new-paper.js +40 -6
  61. package/dist/new-paper.js.map +1 -1
  62. package/dist/references.d.ts +1 -1
  63. package/dist/references.d.ts.map +1 -1
  64. package/dist/references.js +2 -2
  65. package/dist/references.js.map +1 -1
  66. package/dist/repro-layout-rule.d.ts +16 -0
  67. package/dist/repro-layout-rule.d.ts.map +1 -0
  68. package/dist/repro-layout-rule.js +81 -0
  69. package/dist/repro-layout-rule.js.map +1 -0
  70. package/dist/tex-requirements.js +1 -1
  71. package/dist/toolchain.js +1 -1
  72. package/docs/ci.md +60 -0
  73. package/docs/configuration.md +2 -0
  74. package/docs/e2e.md +3 -3
  75. package/docs/incidents.md +5 -5
  76. package/docs/install.md +20 -16
  77. package/docs/layout.md +154 -0
  78. package/docs/optional-rules.md +5 -4
  79. package/docs/references.md +5 -2
  80. package/docs/rules/paper/allowed-files-only.md +192 -0
  81. package/docs/rules/repro/allowed-files-only.md +128 -0
  82. package/docs/rules/study/declare-sample.md +146 -0
  83. package/docs/rules.md +53 -50
  84. package/docs/skills.md +6 -3
  85. package/docs/venues.md +94 -0
  86. package/eslint-rules/CLAUDE.md +44 -0
  87. package/eslint-rules/CLAUDE.md.spec.ts +43 -0
  88. package/eslint-rules/install-path-literals.harness.mjs +1 -1
  89. package/eslint-rules/install-path-literals.ts +4 -4
  90. package/eslint-rules/pdf-last-page-balance.ts +1 -1
  91. package/eslint-rules/prereg-coverage.test.ts +448 -0
  92. package/eslint-rules/prereg-coverage.ts +419 -0
  93. package/eslint.config.mjs +6 -3
  94. package/hooks/CLAUDE.md +34 -0
  95. package/hooks/CLAUDE.md.spec.ts +30 -0
  96. package/lib/agent-cli-version.ts +1 -1
  97. package/lib/skill-trigger-cases.harness.mjs +51 -5
  98. package/lib/skill-trigger-cases.mjs +53 -15
  99. package/package.json +2 -2
  100. package/plugin/CLAUDE.md +22 -0
  101. package/plugin/CLAUDE.md.spec.ts +22 -0
  102. package/plugin/hooks/hooks.json +9 -0
  103. package/scripts/coverage-config.test.ts +2 -1
  104. package/skills/CLAUDE.md +19 -0
  105. package/skills/CLAUDE.md.spec.ts +18 -0
  106. package/skills/analyze-sibling-paper/SKILL.md +2 -2
  107. package/skills/analyze-sibling-paper/SKILL.md.spec.ts +1 -1
  108. package/skills/build-benchmark/SKILL.md +14 -1
  109. package/skills/build-benchmark/SKILL.md.spec.ts +13 -0
  110. package/skills/build-label-page/SKILL.md +118 -0
  111. package/skills/build-label-page/SKILL.md.spec.ts +129 -0
  112. package/skills/build-label-page/build-label-page.eval.mjs +15 -0
  113. package/skills/build-label-page/build-label-page.test.ts +959 -0
  114. package/skills/build-label-page/references/checklist.md +148 -0
  115. package/skills/build-label-page/references/example-generator.py +803 -0
  116. package/skills/find-venue/SKILL.md +5 -3
  117. package/skills/find-venue/SKILL.md.spec.ts +4 -2
  118. package/skills/grade-paper-writing/SKILL.md +7 -7
  119. package/skills/grade-paper-writing/SKILL.md.spec.ts +6 -6
  120. package/skills/map-prior-work/SKILL.md +2 -2
  121. package/skills/map-prior-work/SKILL.md.spec.ts +1 -1
  122. package/skills/paper-pipeline/SKILL.md +8 -6
  123. package/skills/paper-pipeline/SKILL.md.spec.ts +7 -5
  124. package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +19 -9
  125. package/skills/paper-pipeline/references/acceptance-gate.md +19 -19
  126. package/skills/paper-pipeline/references/artifact-checklist.md +5 -0
  127. package/skills/paper-pipeline/references/pipeline-status-template.md +30 -1
  128. package/skills/paper-pipeline/repro/2026-08-07-fork-check.log +1 -1
  129. package/skills/paper-pipeline/repro/2026-08-07-fork-check2.log +1 -1
  130. package/skills/paper-pipeline/repro/2026-08-07-language-eval-pilot.log +1 -1
  131. package/skills/paper-pipeline/repro/2026-08-07-language-eval-raw.log +1 -1
  132. package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.log +48 -48
  133. package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-oracle.log +8 -8
  134. package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.log +48 -48
  135. package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.log +2 -2
  136. package/skills/paper-pipeline/repro/2026-08-08-parent-replication.mjs +3 -3
  137. package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-ablation-preflight-after.log +81 -0
  138. package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-ablation-preflight-before.log +81 -0
  139. package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-ablation-setupdiff.log +59 -0
  140. package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-trigger.jsonl +1 -0
  141. package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-trigger.log +22 -0
  142. package/skills/paper-pipeline/repro/README.md +4 -0
  143. package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +2 -2
  144. package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +2 -2
  145. package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +4 -2
  146. package/skills/paper-pipeline/scripts/generated-code.harness.mjs +7 -2
  147. package/skills/paper-pipeline/scripts/generated-code.mjs +8 -4
  148. package/skills/paper-pipeline/scripts/generated-code.test.ts +15 -1
  149. package/skills/paper-status/SKILL.md +2 -2
  150. package/skills/paper-status/SKILL.md.spec.ts +1 -1
  151. package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +21 -102
  152. package/skills/study-accepted-papers/SKILL.md +163 -161
  153. package/skills/study-accepted-papers/SKILL.md.spec.ts +167 -165
  154. package/skills/study-accepted-papers/references/base-rates.md +69 -0
  155. package/skills/study-accepted-papers/references/mechanisms.md +181 -0
  156. package/skills/verify-citations/SKILL.md +2 -2
  157. package/skills/verify-citations/SKILL.md.spec.ts +1 -1
  158. package/src/CLAUDE.md +23 -1
  159. package/src/CLAUDE.md.spec.ts +72 -0
  160. package/src/build.ts +2 -2
  161. package/src/cli.harness.mjs +42 -0
  162. package/src/cli.test.ts +90 -6
  163. package/src/cli.ts +78 -5
  164. package/src/domain/layout-tree.test.ts +32 -0
  165. package/src/domain/layout-tree.ts +47 -0
  166. package/src/domain/lookup-cache.ts +2 -2
  167. package/src/domain/paper-layout.test.ts +264 -0
  168. package/src/domain/paper-layout.ts +336 -0
  169. package/src/domain/repro-layout.test.ts +222 -0
  170. package/src/domain/repro-layout.ts +311 -0
  171. package/src/domain/scaffold-layout.ts +104 -0
  172. package/src/folder-layout-hook.test.ts +214 -0
  173. package/src/folder-layout-hook.ts +141 -0
  174. package/src/folder-layout-rule.test.ts +235 -0
  175. package/src/folder-layout-rule.ts +151 -0
  176. package/src/hooks-settings.harness.ts +9 -8
  177. package/src/init.ts +1 -1
  178. package/src/layout-docs.test.ts +52 -0
  179. package/src/new-paper.harness.mjs +23 -1
  180. package/src/new-paper.test.ts +299 -6
  181. package/src/new-paper.ts +51 -5
  182. package/src/paper-files.test.ts +9 -6
  183. package/src/references.test.ts +5 -5
  184. package/src/references.ts +2 -2
  185. package/src/repro-layout-rule.test.ts +114 -0
  186. package/src/repro-layout-rule.ts +121 -0
  187. package/src/rule-cases.test.ts +98 -0
  188. package/src/tex-requirements.ts +1 -1
  189. package/src/toolchain.ts +1 -1
  190. package/templates/paper/PIPELINE-STATUS.md +41 -7
@@ -4,7 +4,7 @@ description: "Discover and rank real venues for a given paper, scored by authors
4
4
  allowed-tools: [Read, Write, Grep, Glob, WebSearch, WebFetch, Agent]
5
5
  ---
6
6
 
7
- <!-- vigiles:sha256:8b332cbc072b46ee compiled from skills/find-venue/SKILL.md.spec.ts -->
7
+ <!-- vigiles:sha256:42d8e283fb7045f1 compiled from skills/find-venue/SKILL.md.spec.ts -->
8
8
 
9
9
  # find-venue — rank real venues by what earns the credit, then keep or switch
10
10
 
@@ -43,7 +43,7 @@ rank = credit-weight × topic-fit × accept-probability × deadline-feasibility
43
43
  prestige (a bonus, not a gate). **Non-archival ⇒ credit-weight ≈ 0** — it earns no authorship credit,
44
44
  so it sinks to the bottom no matter how nice the CFP reads.
45
45
  - **topic-fit** — how squarely the paper matches the CFP scope (a stretch fit raises reject risk).
46
- - **accept-probability** — realistic odds given the paper's maturity and the venue's bar.
46
+ - **accept-probability** — realistic odds given the paper's maturity and how selective the venue is.
47
47
  - **deadline-feasibility** — can the paper (and artifact) be ready by the deadline? A great venue you
48
48
  miss is a zero.
49
49
  - **remote-friendliness** — remote-OK is a real weight here, not a footnote.
@@ -84,7 +84,9 @@ rank = credit-weight × topic-fit × accept-probability × deadline-feasibility
84
84
  - the venue cards, `presets/<venue>.md` in the paperlint package beside each venue preset (e.g.
85
85
  `agenticdev.md`, `aisec.md`) — the winner gets a card with its HotCRP quirks beside its preset;
86
86
  for a venue with no shipped preset, the project's own `./venues/<name>.md` beside its
87
- `./venues/<name>.jsonc`. Plain data, not a new skill.
87
+ `./venues/<name>.jsonc`. Plain data, not a new skill. That `./venues/` is not the venue's corpus
88
+ of accepted papers, `<papers-root>/venues/<venue>/` (`study-accepted-papers`): the three places are
89
+ told apart in `docs/venues.md` in the paperlint package.
88
90
  - `extend-paper` — the prestige-upgrade target this skill names but defers.
89
91
 
90
92
  ## Provenance
@@ -49,7 +49,7 @@ rank = credit-weight × topic-fit × accept-probability × deadline-feasibility
49
49
  prestige (a bonus, not a gate). **Non-archival ⇒ credit-weight ≈ 0** — it earns no authorship credit,
50
50
  so it sinks to the bottom no matter how nice the CFP reads.
51
51
  - **topic-fit** — how squarely the paper matches the CFP scope (a stretch fit raises reject risk).
52
- - **accept-probability** — realistic odds given the paper's maturity and the venue's bar.
52
+ - **accept-probability** — realistic odds given the paper's maturity and how selective the venue is.
53
53
  - **deadline-feasibility** — can the paper (and artifact) be ready by the deadline? A great venue you
54
54
  miss is a zero.
55
55
  - **remote-friendliness** — remote-OK is a real weight here, not a footnote.
@@ -90,7 +90,9 @@ rank = credit-weight × topic-fit × accept-probability × deadline-feasibility
90
90
  - the venue cards, \`presets/<venue>.md\` in the paperlint package beside each venue preset (e.g.
91
91
  \`agenticdev.md\`, \`aisec.md\`) — the winner gets a card with its HotCRP quirks beside its preset;
92
92
  for a venue with no shipped preset, the project's own \`./venues/<name>.md\` beside its
93
- \`./venues/<name>.jsonc\`. Plain data, not a new skill.
93
+ \`./venues/<name>.jsonc\`. Plain data, not a new skill. That \`./venues/\` is not the venue's corpus
94
+ of accepted papers, \`<papers-root>/venues/<venue>/\` (\`study-accepted-papers\`): the three places are
95
+ told apart in \`docs/venues.md\` in the paperlint package.
94
96
  - \`extend-paper\` — the prestige-upgrade target this skill names but defers.
95
97
 
96
98
  ## Provenance
@@ -1,17 +1,17 @@
1
1
  ---
2
2
  name: grade-paper-writing
3
- description: Use when asking "does this paper read well?" / "is the writing any good?" / "it reads like a wall of text and jargon" / "grade the writing" on a draft. Grades WRITING CRAFT only — ten dimensions 1–5 (Title, Abstract, Intro architecture, Structure, Prose clarity, Jargon discipline, Landing-the-point, Figure economy, Honesty-without-hedge-stacking, Register distance to the target venue) against how the best-written papers read (Peyton Jones, McEnerney, Gopen & Swan; exemplars Trusting Trust, Carlini, Greshake) AND against 2–3 accepted papers of the target venue, naming the offending sentence and the fix for each. Runs the cold-read stall pass with TWO committed personas (a non-academic engineer per section, and a workshop reviewer skimming abstract, introduction and conclusion) whose stall inventory — not the rubric number — is the readability gate other skills consume (pc-panel-review, paper-adversarial-review, harden-paper). Defaults to a blind multi-grader panel; after fixes, mandates the claims-preservation diff (row `claims`). NOT a content/defect review (paper-adversarial-review / pc-panel-review), NOT a structural cut plan (tighten-paper — run that first on a bloated draft), NOT venue-bar content strength (study-accepted-papers). Compose with harden-paper (which calls it), draft-paper (generative counterpart), render-paper.
3
+ description: Grade how a paper reads and fix it sentence by sentence. Use when asked if a paper reads well or to grade the prose, or when the writing is a wall of text, jargon-heavy, or reads like a blog post. Returns 1–5 scores, the sentences cold readers could not follow, and a rewrite for each. Not for cuts (tighten-paper), one edited paragraph (cold-read-diff), scientific flaws (paper-adversarial-review, pc-panel-review), submit readiness (harden-paper) or what the venue rewards (study-accepted-papers).
4
4
  allowed-tools: [Read, Write, Edit, Grep, Glob, Agent, Skill]
5
5
  ---
6
6
 
7
- <!-- vigiles:sha256:21c8899648c9b223 compiled from skills/grade-paper-writing/SKILL.md.spec.ts -->
7
+ <!-- vigiles:sha256:1897016b69f3f9b8 compiled from skills/grade-paper-writing/SKILL.md.spec.ts -->
8
8
 
9
9
  # grade-paper-writing — grade how the paper READS, then fix it sentence by sentence
10
10
 
11
- The review skills hunt scientific defects; `study-accepted-papers` checks whether the contribution clears
12
- the venue's bar. Neither tells you the paper is a slog to read. This skill does exactly that: it grades
13
- the **writing craft** against how the best-written papers actually read, and turns each low score into a
14
- concrete, sentence-level fix. Its target failure mode is the common one — *"a wall of text and jargon that
11
+ The review skills hunt scientific defects; `study-accepted-papers` scores the contribution on the
12
+ strong-accept scorecard. Neither tells you the paper is a slog to read. This skill does exactly that: it
13
+ grades the **writing craft** against how the best-written papers actually read, and turns each low score
14
+ into a concrete, sentence-level fix. Its target failure mode is the common one — *"a wall of text and jargon that
15
15
  doesn't drive home how crazy the situation is"* — which is a writing problem, not a science problem, and
16
16
  which the defect-hunting skills will pass right over.
17
17
 
@@ -67,7 +67,7 @@ Things that exist only in the render, every one of which produces a real stall:
67
67
  - how much of page 1 is numbers, which is what a reviewer's first impression is actually made of;
68
68
  - line numbers and two-column measure, which change where the eye stops.
69
69
 
70
- `repro/build-submission.sh` produces the PDF; `render-paper` turns pages into images. Judge from
70
+ `repro/tools/build-submission.sh` produces the PDF; `render-paper` turns pages into images. Judge from
71
71
  those. If only the source is available, say so in the report — a stall inventory taken from source
72
72
  is a weaker artifact and must not be presented as the same thing.
73
73
 
@@ -9,15 +9,15 @@ import { experimental_skill } from "vigiles/spec";
9
9
  export default experimental_skill({
10
10
  name: "grade-paper-writing",
11
11
  description:
12
- 'Use when asking "does this paper read well?" / "is the writing any good?" / "it reads like a wall of text and jargon" / "grade the writing" on a draft. Grades WRITING CRAFT only — ten dimensions 1–5 (Title, Abstract, Intro architecture, Structure, Prose clarity, Jargon discipline, Landing-the-point, Figure economy, Honesty-without-hedge-stacking, Register distance to the target venue) against how the best-written papers read (Peyton Jones, McEnerney, Gopen & Swan; exemplars Trusting Trust, Carlini, Greshake) AND against 2–3 accepted papers of the target venue, naming the offending sentence and the fix for each. Runs the cold-read stall pass with TWO committed personas (a non-academic engineer per section, and a workshop reviewer skimming abstract, introduction and conclusion) whose stall inventory — not the rubric number — is the readability gate other skills consume (pc-panel-review, paper-adversarial-review, harden-paper). Defaults to a blind multi-grader panel; after fixes, mandates the claims-preservation diff (row `claims`). NOT a content/defect review (paper-adversarial-review / pc-panel-review), NOT a structural cut plan (tighten-paper — run that first on a bloated draft), NOT venue-bar content strength (study-accepted-papers). Compose with harden-paper (which calls it), draft-paper (generative counterpart), render-paper.',
12
+ "Grade how a paper reads and fix it sentence by sentence. Use when asked if a paper reads well or to grade the prose, or when the writing is a wall of text, jargon-heavy, or reads like a blog post. Returns 1–5 scores, the sentences cold readers could not follow, and a rewrite for each. Not for cuts (tighten-paper), one edited paragraph (cold-read-diff), scientific flaws (paper-adversarial-review, pc-panel-review), submit readiness (harden-paper) or what the venue rewards (study-accepted-papers).",
13
13
  tools: ["Read", "Write", "Edit", "Grep", "Glob", "Agent", "Skill"],
14
14
  body: `
15
15
  # grade-paper-writing — grade how the paper READS, then fix it sentence by sentence
16
16
 
17
- The review skills hunt scientific defects; \`study-accepted-papers\` checks whether the contribution clears
18
- the venue's bar. Neither tells you the paper is a slog to read. This skill does exactly that: it grades
19
- the **writing craft** against how the best-written papers actually read, and turns each low score into a
20
- concrete, sentence-level fix. Its target failure mode is the common one — *"a wall of text and jargon that
17
+ The review skills hunt scientific defects; \`study-accepted-papers\` scores the contribution on the
18
+ strong-accept scorecard. Neither tells you the paper is a slog to read. This skill does exactly that: it
19
+ grades the **writing craft** against how the best-written papers actually read, and turns each low score
20
+ into a concrete, sentence-level fix. Its target failure mode is the common one — *"a wall of text and jargon that
21
21
  doesn't drive home how crazy the situation is"* — which is a writing problem, not a science problem, and
22
22
  which the defect-hunting skills will pass right over.
23
23
 
@@ -73,7 +73,7 @@ Things that exist only in the render, every one of which produces a real stall:
73
73
  - how much of page 1 is numbers, which is what a reviewer's first impression is actually made of;
74
74
  - line numbers and two-column measure, which change where the eye stops.
75
75
 
76
- \`repro/build-submission.sh\` produces the PDF; \`render-paper\` turns pages into images. Judge from
76
+ \`repro/tools/build-submission.sh\` produces the PDF; \`render-paper\` turns pages into images. Judge from
77
77
  those. If only the source is available, say so in the report — a stall inventory taken from source
78
78
  is a weaker artifact and must not be presented as the same thing.
79
79
 
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: map-prior-work
3
- description: Sweep the competitive landscape for a paper idea BEFORE drafting — find everyone already working on it, date each against your submission (prior vs concurrent is a five-month question, not a vibe), triage by whether they threaten your claimed contribution, deep-read the ones that do, and emit a verdict on WHAT YOU CAN STILL CLAIM plus a related-work skeleton. Saves the full map, not a summary. Use right after research-ideate and before draft-paper, and re-run as a cheap delta pass before submit (hot fields move). Distinct from analyze-sibling-paper (one known competitor, deep) — this one FINDS the competitors and decides which deserve that treatment; distinct from verify-citations (checks cites you already wrote, too late to reshape a contribution); distinct from study-accepted-papers (mines a venue's bar, not your rivals). Compose with research-ideate (upstream), analyze-sibling-paper (it hands off), draft-paper and find-venue (downstream).
3
+ description: Sweep the competitive landscape for a paper idea BEFORE drafting — find everyone already working on it, date each against your submission (prior vs concurrent is a five-month question, not a vibe), triage by whether they threaten your claimed contribution, deep-read the ones that do, and emit a verdict on WHAT YOU CAN STILL CLAIM plus a related-work skeleton. Saves the full map, not a summary. Use right after research-ideate and before draft-paper, and re-run as a cheap delta pass before submit (hot fields move). Distinct from analyze-sibling-paper (one known competitor, deep) — this one FINDS the competitors and decides which deserve that treatment; distinct from verify-citations (checks cites you already wrote, too late to reshape a contribution); distinct from study-accepted-papers (mines what a venue rewards, not your rivals). Compose with research-ideate (upstream), analyze-sibling-paper (it hands off), draft-paper and find-venue (downstream).
4
4
  context: fork
5
5
  allowed-tools: [WebSearch, WebFetch, Read, Write, Grep, Glob, Bash, Agent]
6
6
  ---
7
7
 
8
- <!-- vigiles:sha256:55971644308c470f compiled from skills/map-prior-work/SKILL.md.spec.ts -->
8
+ <!-- vigiles:sha256:474b3d7b8e0542ae compiled from skills/map-prior-work/SKILL.md.spec.ts -->
9
9
 
10
10
  # map-prior-work — find out who already did it, while you can still change course
11
11
 
@@ -9,7 +9,7 @@ import { experimental_skill } from "vigiles/spec";
9
9
  export default experimental_skill({
10
10
  name: "map-prior-work",
11
11
  description:
12
- "Sweep the competitive landscape for a paper idea BEFORE drafting — find everyone already working on it, date each against your submission (prior vs concurrent is a five-month question, not a vibe), triage by whether they threaten your claimed contribution, deep-read the ones that do, and emit a verdict on WHAT YOU CAN STILL CLAIM plus a related-work skeleton. Saves the full map, not a summary. Use right after research-ideate and before draft-paper, and re-run as a cheap delta pass before submit (hot fields move). Distinct from analyze-sibling-paper (one known competitor, deep) — this one FINDS the competitors and decides which deserve that treatment; distinct from verify-citations (checks cites you already wrote, too late to reshape a contribution); distinct from study-accepted-papers (mines a venue's bar, not your rivals). Compose with research-ideate (upstream), analyze-sibling-paper (it hands off), draft-paper and find-venue (downstream).",
12
+ "Sweep the competitive landscape for a paper idea BEFORE drafting — find everyone already working on it, date each against your submission (prior vs concurrent is a five-month question, not a vibe), triage by whether they threaten your claimed contribution, deep-read the ones that do, and emit a verdict on WHAT YOU CAN STILL CLAIM plus a related-work skeleton. Saves the full map, not a summary. Use right after research-ideate and before draft-paper, and re-run as a cheap delta pass before submit (hot fields move). Distinct from analyze-sibling-paper (one known competitor, deep) — this one FINDS the competitors and decides which deserve that treatment; distinct from verify-citations (checks cites you already wrote, too late to reshape a contribution); distinct from study-accepted-papers (mines what a venue rewards, not your rivals). Compose with research-ideate (upstream), analyze-sibling-paper (it hands off), draft-paper and find-venue (downstream).",
13
13
  context: "fork",
14
14
  tools: [
15
15
  "WebSearch",
@@ -4,7 +4,7 @@ description: The orchestrator for writing a research paper end-to-end, from idea
4
4
  allowed-tools: [Read, Write, Edit, Grep, Glob, Bash, WebSearch, WebFetch, Agent, Skill]
5
5
  ---
6
6
 
7
- <!-- vigiles:sha256:67a3989629952dc1 compiled from skills/paper-pipeline/SKILL.md.spec.ts -->
7
+ <!-- vigiles:sha256:cb2eecdb81ba766d compiled from skills/paper-pipeline/SKILL.md.spec.ts -->
8
8
 
9
9
  # paper-pipeline — the conductor for the whole organism
10
10
 
@@ -150,7 +150,7 @@ Ordered, because each really does gate the next. Two pairs run in parallel; that
150
150
  | **Validate the idea** | **`research-ideate`** | Is it a reusable method (survives review) in your lane, and does it feed the credit criteria? Go/no-go + the MVP finding. |
151
151
  | **Map the competition** ‖ | **`map-prior-work`** | 🔴 **Who already did this?** Multi-angle sweep; date each hit against your submission (5 months = prior, not concurrent); triage by which layer of your claim it threatens; deep-read the dangerous ones. Output: **what you can still claim** + a related-work skeleton. *Added 2026-07-27 after a five-month-older paper surfaced at T−10 days on a finished draft.* |
152
152
  | **Pick the venue** ‖ | **`find-venue`** | Rank by credit-weight (peer-reviewed + indexed) × fit × accept-odds × deadline × remote. |
153
- | **Learn the venue's bar** | **`study-accepted-papers`** | 🔴 **What does an ACCEPTED paper look like HERE?** Measure the venue's whole accepted volume, deep-read 8–12 of your contribution type, and extract what made them strong: evidence scale, figures, artifact release, how the abstract is built. File the venue card (`presets/<venue>.md` in the package, or the project's own beside its preset) and confirm family rules (`references/acl-venue-rules.md`). *Promoted to its own step 2026-08-03: on `compile-rules-2026` every judgement of the paper's quality — rubric, persona, simulated panel, accept probability — came from instruments this pipeline itself generated, with no external grounding, and the author had to say so at T−3 days. A closed loop optimises against its own model of a reviewer.* |
153
+ | **Learn what the venue rewards** | **`study-accepted-papers`** (mode A idea, B design) | 🔴 **What does this venue REWARD, before the study is designed?** The venue's award winners against its ordinary accepted papers over 5–7 years (plus award papers on the topic at other venues, labelled apart), coded for substance as well as form, written as **requirements for the study**: what the oracle under the headline is, which belief stated on record the claim targets, which factor is a column. The corpus goes to `<papers-root>/venues/<venue>/`, shared by every paper for that venue; the stage is row `venuebar`. **The same skill comes back once text exists** (mode C draft: the strong-accept scorecard, M1–M10, in `PIPELINE-STATUS.md`; mode D final: the abstract and introduction). The skill always asks which mode. Venue format facts go in its preset (`presets/<venue>.jsonc` in the package, or the project's own) and the venue card beside it (`presets/<venue>.md`); confirm family rules (`references/acl-venue-rules.md`). *Promoted to its own step 2026-08-03: every judgement of a paper's quality had come from instruments this pipeline itself generated, with no external grounding. Moved to modes on 2026-10-12 (#207): run only on a draft, its main findings arrived after they could change the design.* |
154
154
  | 🔴 **Can you physically submit?** | **`plan-paper-timeline`** | **The step that owns the question no stage used to own.** Create the portal account NOW; drive the profile to **ACTIVE**, not "pending moderation"; confirm the form's fields and the artifact-hosting requirement. OpenReview moderation runs **up to two weeks**, and an author with no institutional email has no expedite route. Scorecard row `access`. *Added 2026-08-03, when this was the sole critical-path item on a finished paper at T−4 days: the science was done and there was nowhere to put it.* |
155
155
  | **Schedule it** | **`plan-paper-timeline`** | Deadlines → calendar with buffers, in the author's own zone (AoE − 12h ≠ your clock). Space papers apart. |
156
156
  | 🔴 **State the claim** | **`argument-arc`** (frame mode) | **One paragraph: what will this paper claim, and what would have to be true?** Before any run. The frame decides which results matter, and reframing after the data is collected is how experiments get thrown away — observed on `compile-rules-2026`, and the reason `argument-arc` has a rebuild mode at all. Costs a paragraph; saves a study. Scorecard row `frame`. |
@@ -170,6 +170,8 @@ No numbers. Nothing here happens once, and nothing here is "next".
170
170
 
171
171
  - **`build-benchmark`** — a design that inverts the pitfall; honest stats; a self-checking artifact
172
172
  (`references/artifact-checklist.md`).
173
+ - **`build-label-page`** — when the study needs a person to label items (gold labels, a blind review):
174
+ a phone page with a shared answer store, the answers exported into the study's `data/`.
173
175
  - **`draft-paper`** — section skeleton + the accept-and-get-cited properties
174
176
  (`references/writing-craft.md`).
175
177
  - **`argument-arc`** — 🔴 **does the draft carry the reader to ONE conclusion?** One sentence per
@@ -221,7 +223,7 @@ Use an atom for a quick spot-check; **run the panel for the decision.**
221
223
  skill: 🔴 **argument architecture** — does the whole thing carry the reader to one conclusion
222
224
  (`argument-arc`, in the LOOP; the axis that failed on `compile-rules-2026` precisely because it was
223
225
  nobody's) · **content strength** — does the contribution clear *this venue's* bar (`study-accepted-papers`,
224
- mines the accepted corpus for Accept→Strong levers; in SETUP, before the prose is set) ·
226
+ from the venue's own award winners: in SETUP as requirements for the study, and again on the draft as the strong-accept scorecard, M1–M10) ·
225
227
  **scientific defects** — what a hostile reviewer attacks (`paper-adversarial-review` / `pc-panel-review`)
226
228
  · **writing craft** — does it read like a human wrote it (`grade-paper-writing`, the 10-dim rubric in
227
229
  `references/writing-craft.md`; called by `harden-paper`). A paper can ace one and fail another — grade all four.
@@ -294,7 +296,7 @@ Over {{corpus.n}} real rules files, with the extractor's proposed mapping
294
296
  taken as if an author had written it, {{annotated.failedContradicted}} fail admission.
295
297
  ```
296
298
 
297
- `<paper>/repro/numbers.tsv` gives each name its value, the JSON path or **formula** that recomputes
299
+ `<paper>/repro/data/numbers.tsv` gives each name its value, the JSON path or **formula** that recomputes
298
300
  it, and two guards. The build refuses a PDF unless: the name resolves · the value still matches the
299
301
  data · the **paragraph** names the arm (`requires`) · the **sentence** does not claim a different one
300
302
  (`forbids`). Requires and forbids are separate on purpose — the pairing is auditing's, where a control
@@ -310,7 +312,7 @@ binds every row from now on, and the existing rows are *enumerated rather than e
310
312
  says out loud how many figures still have no machine-checked path to data. On the paper that has it:
311
313
  7 guarded, 248 grandfathered. That ratio is a finding, not a score.
312
314
 
313
- **Adopting it in another paper:** copy `repro/paper_numbers.py`, `repro/paper_numbers.selftest.py`
315
+ **Adopting it in another paper:** copy `repro/tools/paper_numbers.py`, `repro/tools/paper_numbers.selftest.py`
314
316
  and the build step that calls `expand()` on `paper.tex` before `pdflatex`; generate the
315
317
  grandfather list from the current draft; register the numbers a reviewer will actually check first.
316
318
 
@@ -350,7 +352,7 @@ printing two unrelated 22s broke it the same hour. Key on the occurrence.
350
352
  🔴 **Hand the gate to a red team the same day you build it.** This one was, and seven holes came
351
353
  back, the worst being that the original defect could simply be **retyped as digits** — its value was
352
354
  grandfathered, so the guard applied to the macro and not to the claim. Every hole is now a case in
353
- `repro/paper_numbers.selftest.py` (18/18, including three that must stay **quiet** — a gate that
355
+ `repro/tools/paper_numbers.selftest.py` (18/18, including three that must stay **quiet** — a gate that
354
356
  fires on ordinary writing gets muted, and then its silence reads as coverage).
355
357
 
356
358
  ## 🔴 The page limit is a SUBMISSION gate, not an EDIT gate
@@ -167,7 +167,7 @@ Ordered, because each really does gate the next. Two pairs run in parallel; that
167
167
  | **Validate the idea** | **\`research-ideate\`** | Is it a reusable method (survives review) in your lane, and does it feed the credit criteria? Go/no-go + the MVP finding. |
168
168
  | **Map the competition** ‖ | **\`map-prior-work\`** | 🔴 **Who already did this?** Multi-angle sweep; date each hit against your submission (5 months = prior, not concurrent); triage by which layer of your claim it threatens; deep-read the dangerous ones. Output: **what you can still claim** + a related-work skeleton. *Added 2026-07-27 after a five-month-older paper surfaced at T−10 days on a finished draft.* |
169
169
  | **Pick the venue** ‖ | **\`find-venue\`** | Rank by credit-weight (peer-reviewed + indexed) × fit × accept-odds × deadline × remote. |
170
- | **Learn the venue's bar** | **\`study-accepted-papers\`** | 🔴 **What does an ACCEPTED paper look like HERE?** Measure the venue's whole accepted volume, deep-read 8–12 of your contribution type, and extract what made them strong: evidence scale, figures, artifact release, how the abstract is built. File the venue card (\`presets/<venue>.md\` in the package, or the project's own beside its preset) and confirm family rules (\`references/acl-venue-rules.md\`). *Promoted to its own step 2026-08-03: on \`compile-rules-2026\` every judgement of the paper's quality — rubric, persona, simulated panel, accept probability — came from instruments this pipeline itself generated, with no external grounding, and the author had to say so at T−3 days. A closed loop optimises against its own model of a reviewer.* |
170
+ | **Learn what the venue rewards** | **\`study-accepted-papers\`** (mode A idea, B design) | 🔴 **What does this venue REWARD, before the study is designed?** The venue's award winners against its ordinary accepted papers over 5–7 years (plus award papers on the topic at other venues, labelled apart), coded for substance as well as form, written as **requirements for the study**: what the oracle under the headline is, which belief stated on record the claim targets, which factor is a column. The corpus goes to \`<papers-root>/venues/<venue>/\`, shared by every paper for that venue; the stage is row \`venuebar\`. **The same skill comes back once text exists** (mode C draft: the strong-accept scorecard, M1–M10, in \`PIPELINE-STATUS.md\`; mode D final: the abstract and introduction). The skill always asks which mode. Venue format facts go in its preset (\`presets/<venue>.jsonc\` in the package, or the project's own) and the venue card beside it (\`presets/<venue>.md\`); confirm family rules (\`references/acl-venue-rules.md\`). *Promoted to its own step 2026-08-03: every judgement of a paper's quality had come from instruments this pipeline itself generated, with no external grounding. Moved to modes on 2026-10-12 (#207): run only on a draft, its main findings arrived after they could change the design.* |
171
171
  | 🔴 **Can you physically submit?** | **\`plan-paper-timeline\`** | **The step that owns the question no stage used to own.** Create the portal account NOW; drive the profile to **ACTIVE**, not "pending moderation"; confirm the form's fields and the artifact-hosting requirement. OpenReview moderation runs **up to two weeks**, and an author with no institutional email has no expedite route. Scorecard row \`access\`. *Added 2026-08-03, when this was the sole critical-path item on a finished paper at T−4 days: the science was done and there was nowhere to put it.* |
172
172
  | **Schedule it** | **\`plan-paper-timeline\`** | Deadlines → calendar with buffers, in the author's own zone (AoE − 12h ≠ your clock). Space papers apart. |
173
173
  | 🔴 **State the claim** | **\`argument-arc\`** (frame mode) | **One paragraph: what will this paper claim, and what would have to be true?** Before any run. The frame decides which results matter, and reframing after the data is collected is how experiments get thrown away — observed on \`compile-rules-2026\`, and the reason \`argument-arc\` has a rebuild mode at all. Costs a paragraph; saves a study. Scorecard row \`frame\`. |
@@ -187,6 +187,8 @@ No numbers. Nothing here happens once, and nothing here is "next".
187
187
 
188
188
  - **\`build-benchmark\`** — a design that inverts the pitfall; honest stats; a self-checking artifact
189
189
  (\`references/artifact-checklist.md\`).
190
+ - **\`build-label-page\`** — when the study needs a person to label items (gold labels, a blind review):
191
+ a phone page with a shared answer store, the answers exported into the study's \`data/\`.
190
192
  - **\`draft-paper\`** — section skeleton + the accept-and-get-cited properties
191
193
  (\`references/writing-craft.md\`).
192
194
  - **\`argument-arc\`** — 🔴 **does the draft carry the reader to ONE conclusion?** One sentence per
@@ -238,7 +240,7 @@ Use an atom for a quick spot-check; **run the panel for the decision.**
238
240
  skill: 🔴 **argument architecture** — does the whole thing carry the reader to one conclusion
239
241
  (\`argument-arc\`, in the LOOP; the axis that failed on \`compile-rules-2026\` precisely because it was
240
242
  nobody's) · **content strength** — does the contribution clear *this venue's* bar (\`study-accepted-papers\`,
241
- mines the accepted corpus for Accept→Strong levers; in SETUP, before the prose is set) ·
243
+ from the venue's own award winners: in SETUP as requirements for the study, and again on the draft as the strong-accept scorecard, M1–M10) ·
242
244
  **scientific defects** — what a hostile reviewer attacks (\`paper-adversarial-review\` / \`pc-panel-review\`)
243
245
  · **writing craft** — does it read like a human wrote it (\`grade-paper-writing\`, the 10-dim rubric in
244
246
  \`references/writing-craft.md\`; called by \`harden-paper\`). A paper can ace one and fail another — grade all four.
@@ -311,7 +313,7 @@ Over {{corpus.n}} real rules files, with the extractor's proposed mapping
311
313
  taken as if an author had written it, {{annotated.failedContradicted}} fail admission.
312
314
  \`\`\`
313
315
 
314
- \`<paper>/repro/numbers.tsv\` gives each name its value, the JSON path or **formula** that recomputes
316
+ \`<paper>/repro/data/numbers.tsv\` gives each name its value, the JSON path or **formula** that recomputes
315
317
  it, and two guards. The build refuses a PDF unless: the name resolves · the value still matches the
316
318
  data · the **paragraph** names the arm (\`requires\`) · the **sentence** does not claim a different one
317
319
  (\`forbids\`). Requires and forbids are separate on purpose — the pairing is auditing's, where a control
@@ -327,7 +329,7 @@ binds every row from now on, and the existing rows are *enumerated rather than e
327
329
  says out loud how many figures still have no machine-checked path to data. On the paper that has it:
328
330
  7 guarded, 248 grandfathered. That ratio is a finding, not a score.
329
331
 
330
- **Adopting it in another paper:** copy \`repro/paper_numbers.py\`, \`repro/paper_numbers.selftest.py\`
332
+ **Adopting it in another paper:** copy \`repro/tools/paper_numbers.py\`, \`repro/tools/paper_numbers.selftest.py\`
331
333
  and the build step that calls \`expand()\` on \`paper.tex\` before \`pdflatex\`; generate the
332
334
  grandfather list from the current draft; register the numbers a reviewer will actually check first.
333
335
 
@@ -367,7 +369,7 @@ printing two unrelated 22s broke it the same hour. Key on the occurrence.
367
369
  🔴 **Hand the gate to a red team the same day you build it.** This one was, and seven holes came
368
370
  back, the worst being that the original defect could simply be **retyped as digits** — its value was
369
371
  grandfathered, so the guard applied to the macro and not to the claim. Every hole is now a case in
370
- \`repro/paper_numbers.selftest.py\` (18/18, including three that must stay **quiet** — a gate that
372
+ \`repro/tools/paper_numbers.selftest.py\` (18/18, including three that must stay **quiet** — a gate that
371
373
  fires on ordinary writing gets muted, and then its silence reads as coverage).
372
374
 
373
375
  ## 🔴 The page limit is a SUBMISSION gate, not an EDIT gate
@@ -233,8 +233,9 @@
233
233
  * renamed from two-letter codes to words (`Hn` → `claims`) because the two-letter space had
234
234
  * collided. The target's description carried one such
235
235
  * reference, so "…mandates the Hn claim-preservation diff." became "…mandates the
236
- * claims-preservation diff (row `claims`)." A0 is still read from disk and every guard still
237
- * passes; the numbers below were measured on the pre-rename bytes and are left as recorded.
236
+ * claims-preservation diff (row `claims`)." Every guard still passes on A0, which is pinned in
237
+ * this file since the live description was rewritten (2026-10-13); the numbers below were
238
+ * measured on the pre-rename bytes and are left as recorded.
238
239
  *
239
240
  * measurement AH SH total
240
241
  * parent, this morning 3/12 25% 2/12 17% 5/24 21%
@@ -380,6 +381,7 @@ import {
380
381
  realpathSync,
381
382
  } from "node:fs";
382
383
  import { join } from "node:path";
384
+ import { tmpdir } from "node:os";
383
385
  import { frontmatterBlock } from "#lib/markdown";
384
386
  import { parseFm } from "../../lib/skill-corpus.mjs";
385
387
  import { installedSkills } from "./scripts/consumer.mjs";
@@ -395,9 +397,7 @@ const PARENT = join(
395
397
  "paper-pipeline",
396
398
  "framing-vs-vocabulary.eval.mjs",
397
399
  );
398
- const SCRATCH =
399
- process.env.ABLATION_SCRATCH ??
400
- "/tmp/claude-0/-home-user-mine/8268acb1-66a2-55ac-858e-2b9e3c81f84e/scratchpad/gpw-ablation";
400
+ const SCRATCH = process.env.ABLATION_SCRATCH ?? join(tmpdir(), "gpw-ablation");
401
401
  const NS = "vigiles-loose-skills";
402
402
  const TARGET = "grade-paper-writing";
403
403
  const STEM = "2026-08-08-grade-paper-writing-ablation";
@@ -493,7 +493,18 @@ const allPrompts = CELLS.flatMap((k) =>
493
493
  PROMPTS[k].map((p) => ({ cell: k, prompt: p })),
494
494
  );
495
495
 
496
- // ── the baseline description, read from disk (never hardcoded — it would drift) ──
496
+ // ── the baseline description, PINNED ─────────────────────────────────────────
497
+ // This used to be read from the installed SKILL.md, so the arms followed every edit of it. On
498
+ // 2026-10-13 the description was rewritten (499 chars: what it does, when, when not, what it
499
+ // returns), and every arm below is a set of EXACT SUBSTRING DELETIONS: cut from the new text, they
500
+ // find nothing to delete and the file throws before preflight. The arms are an experiment on THIS
501
+ // text, so the text is pinned and the arms build exactly as before. It is the last description the
502
+ // deletion strings were written against (1371 chars), which already differs from the 1174 chars the
503
+ // 2026-08-08 result measured (see "A0 IS TWO TOKENS OFF" in the header, and the register clauses
504
+ // added since). How the LIVE description fires is measured by
505
+ // `grade-paper-writing/grade-paper-writing.eval.mjs` on the shared trigger cases, not here.
506
+ const A0 =
507
+ 'Use when asking "does this paper read well?" / "is the writing any good?" / "it reads like a wall of text and jargon" / "grade the writing" on a draft. Grades WRITING CRAFT only — ten dimensions 1–5 (Title, Abstract, Intro architecture, Structure, Prose clarity, Jargon discipline, Landing-the-point, Figure economy, Honesty-without-hedge-stacking, Register distance to the target venue) against how the best-written papers read (Peyton Jones, McEnerney, Gopen & Swan; exemplars Trusting Trust, Carlini, Greshake) AND against 2–3 accepted papers of the target venue, naming the offending sentence and the fix for each. Runs the cold-read stall pass with TWO committed personas (a non-academic engineer per section, and a workshop reviewer skimming abstract, introduction and conclusion) whose stall inventory — not the rubric number — is the readability gate other skills consume (pc-panel-review, paper-adversarial-review, harden-paper). Defaults to a blind multi-grader panel; after fixes, mandates the claims-preservation diff (row `claims`). NOT a content/defect review (paper-adversarial-review / pc-panel-review), NOT a structural cut plan (tighten-paper — run that first on a bloated draft), NOT content strength against what the venue rewards (study-accepted-papers). Compose with harden-paper (which calls it), draft-paper (generative counterpart), render-paper.';
497
508
  if (!existsSync(join(SKILLS_DIR, TARGET, "SKILL.md")))
498
509
  throw new Error(`${TARGET} is not installed under ${SKILLS_DIR}`);
499
510
  // READING a field is a parser's job. The hand-rolled version below used a lookahead
@@ -511,7 +522,6 @@ const parseDesc = (md) =>
511
522
  .trim();
512
523
  const parseName = (md) => fmOf(md).name;
513
524
  const BASE_MD = readFileSync(join(SKILLS_DIR, TARGET, "SKILL.md"), "utf-8");
514
- const A0 = parseDesc(BASE_MD);
515
525
  if (parseName(BASE_MD) !== TARGET)
516
526
  throw new Error(`${TARGET}/SKILL.md declares name: ${parseName(BASE_MD)}`);
517
527
 
@@ -525,7 +535,7 @@ const D_SUBORDINATION = [
525
535
  "harden-paper (which calls it), ",
526
536
  ];
527
537
  const D_NEGATION = [
528
- " NOT a content/defect review (paper-adversarial-review / pc-panel-review), NOT a structural cut plan (tighten-paper — run that first on a bloated draft), NOT venue-bar content strength (study-accepted-papers).",
538
+ " NOT a content/defect review (paper-adversarial-review / pc-panel-review), NOT a structural cut plan (tighten-paper — run that first on a bloated draft), NOT content strength against what the venue rewards (study-accepted-papers).",
529
539
  ];
530
540
  const D_MACHINERY = [
531
541
  " (Title, Abstract, Intro architecture, Structure, Prose clarity, Jargon discipline, Landing-the-point, Figure economy, Honesty-without-hedge-stacking, Register distance to the target venue)",
@@ -559,7 +569,7 @@ const D_PERSONA_ONLY = [
559
569
  " Grades WRITING CRAFT only — ten dimensions 1–5 (Title, Abstract, Intro architecture, Structure, Prose clarity, Jargon discipline, Landing-the-point, Figure economy, Honesty-without-hedge-stacking, Register distance to the target venue) against how the best-written papers read (Peyton Jones, McEnerney, Gopen & Swan; exemplars Trusting Trust, Carlini, Greshake) AND against 2–3 accepted papers of the target venue, naming the offending sentence and the fix for each.",
560
570
  " whose stall inventory — not the rubric number — is the readability gate other skills consume (pc-panel-review, paper-adversarial-review, harden-paper)",
561
571
  " Defaults to a blind multi-grader panel; after fixes, mandates the claims-preservation diff (row `claims`).",
562
- " NOT a content/defect review (paper-adversarial-review / pc-panel-review), NOT a structural cut plan (tighten-paper — run that first on a bloated draft), NOT venue-bar content strength (study-accepted-papers).",
572
+ " NOT a content/defect review (paper-adversarial-review / pc-panel-review), NOT a structural cut plan (tighten-paper — run that first on a bloated draft), NOT content strength against what the venue rewards (study-accepted-papers).",
563
573
  " Compose with harden-paper (which calls it), draft-paper (generative counterpart), render-paper.",
564
574
  ];
565
575
 
@@ -57,7 +57,7 @@ drive**.
57
57
  - ❌ this section earns its place
58
58
  - ❌ the reader could follow that sentence
59
59
  - ❌ the delta over the nearest prior work is real
60
- - ❌ the draft sits at this venue's bar
60
+ - ❌ the draft passes the strong-accept scorecard
61
61
  - ❌ the idea is worth doing
62
62
  - ❌ the arc carries the reader end to end
63
63
  - ❌ the accept probability is 88%
@@ -105,7 +105,7 @@ at all** — and it has four rungs:
105
105
  Type A lives on rung 3 and sometimes 2. **Rung 1 is not in their taxonomy at all**, and it is the
106
106
  rung we are actually best at:
107
107
 
108
- - `repro/paper_numbers.py` — a registered quantity is written `{{annotated.failedContradicted}}`, not
108
+ - `repro/tools/paper_numbers.py` — a registered quantity is written `{{annotated.failedContradicted}}`, not
109
109
  `22`. The build refuses to emit a PDF when the name does not resolve, when the value has drifted
110
110
  from the data, or when the paragraph does not declare which experimental arm it is reporting. This
111
111
  is not a gate that judges the number. **The wrong number has no representation in the source.**
@@ -117,12 +117,12 @@ not to find a second model family we do not have — it is to ask whether the qu
117
117
  or rung-1-shaped surrogate.** For measurement papers it very often does, and every time we found one
118
118
  it caught a defect the judgement gates had passed:
119
119
 
120
- | the taste question | the surrogate we built | what it caught |
121
- | ----------------------------------------------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
122
- | does this number support this sentence | `repro/numbers.tsv` guards — the arm word the paragraph must name, the condition it must not claim | the 2026-08-05 abstract defect, 29 hours before a deadline; five judgement gates had passed it |
123
- | is this quantity really bound to its data | `repro/arm_permutation.py` — permute the arm labels, rebuild, an unchanged quantity was never bound | a whole class the artifact reviewer recomputes and passes, because the number is _correct_ |
124
- | did the number survive into the delivered paper | `repro/delivered_pdf.py` — read the built PDF back | a superscript minus lost in typesetting |
125
- | is this reference real | `extract-ref-facts.mjs` + rules `refs/*` — arXiv / CrossRef | nine fabricated titles carrying correct arXiv ids, and one invented award |
120
+ | the taste question | the surrogate we built | what it caught |
121
+ | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
122
+ | does this number support this sentence | `repro/data/numbers.tsv` guards — the arm word the paragraph must name, the condition it must not claim | the 2026-08-05 abstract defect, 29 hours before a deadline; five judgement gates had passed it |
123
+ | is this quantity really bound to its data | `repro/tools/arm_permutation.py` — permute the arm labels, rebuild, an unchanged quantity was never bound | a whole class the artifact reviewer recomputes and passes, because the number is _correct_ |
124
+ | did the number survive into the delivered paper | `repro/tools/delivered_pdf.py` — read the built PDF back | a superscript minus lost in typesetting |
125
+ | is this reference real | `extract-ref-facts.mjs` + rules `refs/*` — arXiv / CrossRef | nine fabricated titles carrying correct arXiv ids, and one invented award |
126
126
 
127
127
  ---
128
128
 
@@ -188,7 +188,7 @@ we want more of.
188
188
  | `tighten-paper` | a cover-to-cover read produced no action | **B** | ❌ drive only |
189
189
  | `grade-paper-writing` | the stall inventory came back empty | **B** | ❌ drive only |
190
190
  | `analyze-sibling-paper` | the delta is real and no text has to change | **B** | ❌ drive only |
191
- | `study-accepted-papers` | the draft already sits at the venue's bar | **B** | ❌ drive only (the corpus is external; the comparison is ours) |
191
+ | `study-accepted-papers` | the draft already passes the strong-accept scorecard | **B** | ❌ drive only (the corpus is external; the comparison is ours) |
192
192
  | `paper-adversarial-review` | a hostile read found nothing | **B** | ❌ drive only |
193
193
  | `pc-panel-review` | accept with no must-fix | **B** | ❌ drive only — the flagship offender |
194
194
  | `harden-paper` | every gate green and every axis PASS/FIXED — _the only verdict that hands off to `submit-paper`_ | **A over B** | ❌ the wrapper is A, the thing wrapped is B; the handoff is an acquittal |
@@ -248,16 +248,16 @@ Everything in `.claude/skills/paper-pipeline/scripts/`, `.claude/lib/` and `<pap
248
248
  construction — that is why it is trustworthy, and it is not a coincidence but a selection effect: a
249
249
  program is what you can write only when the question is Type A.
250
250
 
251
- | unit | question | rung |
252
- | ---------------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
253
- | `repro/report-submission.py` | body pages, overfull boxes, unresolved refs, dropped characters | A (3) |
254
- | `repro/check-anon.sh`, `check-figure.sh` | deanonymising strings; does the figure fit the column | A (3) |
255
- | `prose-lint.mjs` | thresholds with published baselines | A (3) — _measuring_ a B question, which is legitimate and is not the same as answering it |
256
- | rules `refs/*` (facts by `extract-ref-facts.mjs`) | does the registry have this record | A (3), **external adjudicator** |
257
- | `paper-lint.mjs` | appendix ratio, shaved passages | A (3) |
258
- | `repro/arm_permutation.py`, `repro/delivered_pdf.py` | is the quantity bound to its arm; did it survive typesetting | A (3), metamorphic |
259
- | **`repro/paper_numbers.py` + `numbers.tsv`** | — | **rung 1: no PDF exists in which the number is wrong** |
260
- | `paper-edit-guard.hook.ts` | writing to a paper from Bash | rung 2 — **leaky**, an interpreter reading its program from stdin still gets through (`papers/CLAUDE.md`, 2026-08-06) |
251
+ | unit | question | rung |
252
+ | ---------------------------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
253
+ | `repro/tools/report-submission.py` | body pages, overfull boxes, unresolved refs, dropped characters | A (3) |
254
+ | `repro/tools/check-anon.sh`, `check-figure.sh` | deanonymising strings; does the figure fit the column | A (3) |
255
+ | `prose-lint.mjs` | thresholds with published baselines | A (3) — _measuring_ a B question, which is legitimate and is not the same as answering it |
256
+ | rules `refs/*` (facts by `extract-ref-facts.mjs`) | does the registry have this record | A (3), **external adjudicator** |
257
+ | `paper-lint.mjs` | appendix ratio, shaved passages | A (3) |
258
+ | `repro/tools/arm_permutation.py`, `repro/tools/delivered_pdf.py` | is the quantity bound to its arm; did it survive typesetting | A (3), metamorphic |
259
+ | **`repro/tools/paper_numbers.py` + `numbers.tsv`** | — | **rung 1: no PDF exists in which the number is wrong** |
260
+ | `paper-edit-guard.hook.ts` | writing to a paper from Bash | rung 2 — **leaky**, an interpreter reading its program from stdin still gets through (`papers/CLAUDE.md`, 2026-08-06) |
261
261
 
262
262
  `prose-lint.mjs` deserves its note. It puts a **number with a threshold** on a question that is
263
263
  Type B — is this sentence readable. That is not
@@ -27,6 +27,11 @@ the 140-run `reproduce.py` (AgenticDev) and the 46-guard `evaluate.py`/`mutate.p
27
27
 
28
28
  ## Contents (mirror this file map)
29
29
 
30
+ In the paper folder the artifact lives in `repro/`, laid out as `repro/allowed-files-only` requires: the
31
+ scripts the whole paper uses in `repro/tools/`, shared data in `repro/data/`, each study in
32
+ `repro/<slug>-YYYY-MM-DD/` with its `README.md`, and `repro/README.md` as the index. The shipped
33
+ zip below is built from it; the anonymized copy that was uploaded goes in the paper's `submission/`.
34
+
30
35
  - `reproduce.py` / the harness scripts — self-check, print `RESULT: PASS`, exit non-zero on drift.
31
36
  - `data/…` — the raw measurements the numbers derive from.
32
37
  - `README.md` — a **"what reproduces which paper number"** table (paper location → the line that prints
@@ -25,6 +25,35 @@ stages:
25
25
  ---
26
26
  ```
27
27
 
28
+ ## One live-state file: the plan, the scorecard, the stages
29
+
30
+ Everything about a paper that is _live_ (true now, and rewritten when it changes) is a section of this
31
+ one file, so there is one place to read and nothing to keep in step:
32
+
33
+ | section | holds | written by |
34
+ | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
35
+ | **Plan / now** | what is being done now and next, the decisions in force (who, when, a link to the record), the schedule | whoever changes the plan, in the same commit |
36
+ | **Strong-accept scorecard (M1–M10)** | a row per strong-accept mechanism: status (`pass` / `partial` / `fail` / `—`), evidence as `file:line`, owner, plan or `accept ceiling: <why>` | `study-accepted-papers` (mode C: every row; mode D: M2 and M4) |
37
+ | **Stages** (SETUP … AFTER) | a row per unit of pipeline work | the skill that ran it |
38
+
39
+ - **No `PLAN.md` beside it.** A plan in a second file drifts from the status it plans against;
40
+ `paper/allowed-files-only` reports a `PLAN.md` at the paper's root. A decision's _history_ (why it was
41
+ taken, what was rejected) is a record, not live state: it goes to `research/` as a dated file.
42
+ - **Each study gets a dated folder in `repro/`.** The `study` stage's runs go in
43
+ `repro/<slug>-YYYY-MM-DD/` (or `<slug>-YYYY-MM` for one that ran over weeks), with a `README.md`
44
+ saying what it asks, how to run it and what it found, and a line in `repro/README.md`. Scripts the
45
+ whole paper uses go in `repro/tools/`, shared data in `repro/data/`; `repro/allowed-files-only` reports
46
+ anything else at the top of `repro/`.
47
+ - **The scorecard's verdicts live only here.** The dated report `study-accepted-papers` writes under
48
+ `reviews/` keeps the reasoning and links to this section; it never copies the table, so there is one
49
+ truth to update. The mechanisms themselves, with what each is not, are in
50
+ `study-accepted-papers/references/mechanisms.md`. Nothing checks the table mechanically yet: keep its
51
+ columns as the template has them.
52
+ - 🔴 **Only stage tables have a first column headed `id`.** A stage-table parser reads every
53
+ `id`-headed table as stage rows, and a row under a heading that names no stage section is an error
54
+ there: a fix-list table headed `id` gave a real paper two such errors (2026-10-12). Head every other
55
+ table with `#` or a word, as the scorecard does. `src/new-paper.test.ts` holds the template to it.
56
+
28
57
  ## The row ids, and what each one runs
29
58
 
30
59
  The `id` cell is the **join key** — the same string appears in this template, in every paper's
@@ -36,7 +65,7 @@ the scorecard explains itself where it lives:
36
65
  | `idea` | SETUP | is the idea worth a paper? | `research-ideate` |
37
66
  | `priorwork` | SETUP | full competitor sweep, before drafting | `map-prior-work` |
38
67
  | `venue` | SETUP | pick where it goes | `find-venue` |
39
- | `venuebar` | SETUP | what this venue rewards; strengthening levers | `study-accepted-papers` |
68
+ | `venuebar` | SETUP | what this venue rewards (modes A/B), and later the draft scored (C/D) | `study-accepted-papers` |
40
69
  | `access` | SETUP | 🔴 _can you physically upload?_ — account, ACTIVE profile, portal, form | `plan-paper-timeline` |
41
70
  | `schedule` | SETUP | the backwards plan and its calendar events | `plan-paper-timeline` |
42
71
  | `frame` | SETUP | the claim, stated **before** the runs | `argument-arc` (frame mode) |
@@ -49,4 +49,4 @@ ru-original 67% 53% -13pp (10/15 vs 8/15)
49
49
  No gate: this file measures an effect, it does not enforce one. Read OVERALL first, then SPLIT BY ORIGIN — a gap that appears only on translated Russian is a finding about translation, not about Russian.
50
50
 
51
51
  total spend: $9.53 API-equivalent
52
- no baseline at /home/user/mine/.claude/skills/paper-pipeline/pipeline-language.baseline.json — record one with --update-baseline
52
+ no baseline at <project>/.claude/skills/paper-pipeline/pipeline-language.baseline.json — record one with --update-baseline
@@ -30,4 +30,4 @@ ru-original 100% 100% +0pp (6/6 vs 6/6)
30
30
  No gate: this file measures an effect, it does not enforce one. Read OVERALL first, then SPLIT BY ORIGIN — a gap that appears only on translated Russian is a finding about translation, not about Russian.
31
31
 
32
32
  total spend: $5.61 API-equivalent
33
- no baseline at /home/user/mine/.claude/skills/paper-pipeline/pipeline-language.baseline.json — record one with --update-baseline
33
+ no baseline at <project>/.claude/skills/paper-pipeline/pipeline-language.baseline.json — record one with --update-baseline
@@ -30,4 +30,4 @@ ru-original 100% 100% +0pp (4/4 vs 4/4)
30
30
  No gate: this file measures an effect, it does not enforce one. Read OVERALL first, then SPLIT BY ORIGIN — a gap that appears only on translated Russian is a finding about translation, not about Russian.
31
31
 
32
32
  total spend: $2.03 API-equivalent
33
- no baseline at /home/user/mine/.claude/skills/paper-pipeline/pipeline-language.baseline.json — record one with --update-baseline
33
+ no baseline at <project>/.claude/skills/paper-pipeline/pipeline-language.baseline.json — record one with --update-baseline
@@ -163,4 +163,4 @@ ru-original 76% 61% -15pp (41/54 vs 33/54)
163
163
  No gate: this file measures an effect, it does not enforce one. Read OVERALL first, then SPLIT BY ORIGIN — a gap that appears only on translated Russian is a finding about translation, not about Russian.
164
164
 
165
165
  total spend: $26.59 API-equivalent
166
- no baseline at /home/user/mine/.claude/skills/paper-pipeline/pipeline-language.baseline.json — record one with --update-baseline
166
+ no baseline at <project>/.claude/skills/paper-pipeline/pipeline-language.baseline.json — record one with --update-baseline