paperlint 2.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 (762) hide show
  1. package/.github/dependabot.yml +72 -0
  2. package/.github/workflows/ci.yml +297 -0
  3. package/.github/workflows/dependabot-automerge.yml +70 -0
  4. package/.github/workflows/pr-title.yml +59 -0
  5. package/.github/workflows/release.yml +54 -0
  6. package/CLAUDE.md +598 -0
  7. package/CONTRIBUTING.md +159 -0
  8. package/LICENSE +21 -0
  9. package/README.md +240 -0
  10. package/action.harness.mjs +287 -0
  11. package/action.mutations.mjs +162 -0
  12. package/action.yml +138 -0
  13. package/bin/rpp.mjs +43 -0
  14. package/dist/action-ref.d.ts +12 -0
  15. package/dist/action-ref.d.ts.map +1 -0
  16. package/dist/action-ref.js +16 -0
  17. package/dist/action-ref.js.map +1 -0
  18. package/dist/adapters/banal/failure.d.ts +73 -0
  19. package/dist/adapters/banal/failure.d.ts.map +1 -0
  20. package/dist/adapters/banal/failure.js +58 -0
  21. package/dist/adapters/banal/failure.js.map +1 -0
  22. package/dist/adapters/banal/index.d.ts +17 -0
  23. package/dist/adapters/banal/index.d.ts.map +1 -0
  24. package/dist/adapters/banal/index.js +56 -0
  25. package/dist/adapters/banal/index.js.map +1 -0
  26. package/dist/adapters/banal/install.d.ts +26 -0
  27. package/dist/adapters/banal/install.d.ts.map +1 -0
  28. package/dist/adapters/banal/install.js +15 -0
  29. package/dist/adapters/banal/install.js.map +1 -0
  30. package/dist/adapters/banal/invocation.d.ts +48 -0
  31. package/dist/adapters/banal/invocation.d.ts.map +1 -0
  32. package/dist/adapters/banal/invocation.js +43 -0
  33. package/dist/adapters/banal/invocation.js.map +1 -0
  34. package/dist/adapters/banal/locate.d.ts +50 -0
  35. package/dist/adapters/banal/locate.d.ts.map +1 -0
  36. package/dist/adapters/banal/locate.js +34 -0
  37. package/dist/adapters/banal/locate.js.map +1 -0
  38. package/dist/adapters/banal/output.d.ts +27 -0
  39. package/dist/adapters/banal/output.d.ts.map +1 -0
  40. package/dist/adapters/banal/output.js +112 -0
  41. package/dist/adapters/banal/output.js.map +1 -0
  42. package/dist/adapters/banal/pin.d.ts +19 -0
  43. package/dist/adapters/banal/pin.d.ts.map +1 -0
  44. package/dist/adapters/banal/pin.js +15 -0
  45. package/dist/adapters/banal/pin.js.map +1 -0
  46. package/dist/adapters/banal/probe.d.ts +12 -0
  47. package/dist/adapters/banal/probe.d.ts.map +1 -0
  48. package/dist/adapters/banal/probe.js +27 -0
  49. package/dist/adapters/banal/probe.js.map +1 -0
  50. package/dist/adapters/banal/run.d.ts +89 -0
  51. package/dist/adapters/banal/run.d.ts.map +1 -0
  52. package/dist/adapters/banal/run.js +104 -0
  53. package/dist/adapters/banal/run.js.map +1 -0
  54. package/dist/adapters/banal/settings.d.ts +18 -0
  55. package/dist/adapters/banal/settings.d.ts.map +1 -0
  56. package/dist/adapters/banal/settings.js +29 -0
  57. package/dist/adapters/banal/settings.js.map +1 -0
  58. package/dist/adapters/banal/xml.d.ts +48 -0
  59. package/dist/adapters/banal/xml.d.ts.map +1 -0
  60. package/dist/adapters/banal/xml.js +67 -0
  61. package/dist/adapters/banal/xml.js.map +1 -0
  62. package/dist/adapters/curl/download.io.d.ts +14 -0
  63. package/dist/adapters/curl/download.io.d.ts.map +1 -0
  64. package/dist/adapters/curl/download.io.js +69 -0
  65. package/dist/adapters/curl/download.io.js.map +1 -0
  66. package/dist/adapters/curl/index.d.ts +6 -0
  67. package/dist/adapters/curl/index.d.ts.map +1 -0
  68. package/dist/adapters/curl/index.js +6 -0
  69. package/dist/adapters/curl/index.js.map +1 -0
  70. package/dist/adapters/memory/index.d.ts +43 -0
  71. package/dist/adapters/memory/index.d.ts.map +1 -0
  72. package/dist/adapters/memory/index.js +79 -0
  73. package/dist/adapters/memory/index.js.map +1 -0
  74. package/dist/adapters/node/files.io.d.ts +3 -0
  75. package/dist/adapters/node/files.io.d.ts.map +1 -0
  76. package/dist/adapters/node/files.io.js +31 -0
  77. package/dist/adapters/node/files.io.js.map +1 -0
  78. package/dist/adapters/node/host.io.d.ts +3 -0
  79. package/dist/adapters/node/host.io.d.ts.map +1 -0
  80. package/dist/adapters/node/host.io.js +14 -0
  81. package/dist/adapters/node/host.io.js.map +1 -0
  82. package/dist/adapters/node/index.d.ts +25 -0
  83. package/dist/adapters/node/index.d.ts.map +1 -0
  84. package/dist/adapters/node/index.js +14 -0
  85. package/dist/adapters/node/index.js.map +1 -0
  86. package/dist/adapters/node/process.io.d.ts +14 -0
  87. package/dist/adapters/node/process.io.d.ts.map +1 -0
  88. package/dist/adapters/node/process.io.js +41 -0
  89. package/dist/adapters/node/process.io.js.map +1 -0
  90. package/dist/adapters/node/workspace.io.d.ts +4 -0
  91. package/dist/adapters/node/workspace.io.d.ts.map +1 -0
  92. package/dist/adapters/node/workspace.io.js +33 -0
  93. package/dist/adapters/node/workspace.io.js.map +1 -0
  94. package/dist/adapters/pdfjs/fill.d.ts +42 -0
  95. package/dist/adapters/pdfjs/fill.d.ts.map +1 -0
  96. package/dist/adapters/pdfjs/fill.js +91 -0
  97. package/dist/adapters/pdfjs/fill.js.map +1 -0
  98. package/dist/build-engine.d.ts +48 -0
  99. package/dist/build-engine.d.ts.map +1 -0
  100. package/dist/build-engine.js +148 -0
  101. package/dist/build-engine.js.map +1 -0
  102. package/dist/build.d.ts +163 -0
  103. package/dist/build.d.ts.map +1 -0
  104. package/dist/build.js +575 -0
  105. package/dist/build.js.map +1 -0
  106. package/dist/cli.d.ts +151 -0
  107. package/dist/cli.d.ts.map +1 -0
  108. package/dist/cli.js +951 -0
  109. package/dist/cli.js.map +1 -0
  110. package/dist/doctor.d.ts +42 -0
  111. package/dist/doctor.d.ts.map +1 -0
  112. package/dist/doctor.js +280 -0
  113. package/dist/doctor.js.map +1 -0
  114. package/dist/domain/geometry.d.ts +71 -0
  115. package/dist/domain/geometry.d.ts.map +1 -0
  116. package/dist/domain/geometry.js +35 -0
  117. package/dist/domain/geometry.js.map +1 -0
  118. package/dist/domain/host.d.ts +16 -0
  119. package/dist/domain/host.d.ts.map +1 -0
  120. package/dist/domain/host.js +8 -0
  121. package/dist/domain/host.js.map +1 -0
  122. package/dist/domain/page-layout.d.ts +34 -0
  123. package/dist/domain/page-layout.d.ts.map +1 -0
  124. package/dist/domain/page-layout.js +8 -0
  125. package/dist/domain/page-layout.js.map +1 -0
  126. package/dist/domain/paths.d.ts +5 -0
  127. package/dist/domain/paths.d.ts.map +1 -0
  128. package/dist/domain/paths.js +2 -0
  129. package/dist/domain/paths.js.map +1 -0
  130. package/dist/domain/result.d.ts +23 -0
  131. package/dist/domain/result.d.ts.map +1 -0
  132. package/dist/domain/result.js +10 -0
  133. package/dist/domain/result.js.map +1 -0
  134. package/dist/domain/sha256.d.ts +7 -0
  135. package/dist/domain/sha256.d.ts.map +1 -0
  136. package/dist/domain/sha256.js +14 -0
  137. package/dist/domain/sha256.js.map +1 -0
  138. package/dist/domain/text.d.ts +6 -0
  139. package/dist/domain/text.d.ts.map +1 -0
  140. package/dist/domain/text.js +7 -0
  141. package/dist/domain/text.js.map +1 -0
  142. package/dist/engine.d.ts +93 -0
  143. package/dist/engine.d.ts.map +1 -0
  144. package/dist/engine.js +119 -0
  145. package/dist/engine.js.map +1 -0
  146. package/dist/exit-code.d.ts +22 -0
  147. package/dist/exit-code.d.ts.map +1 -0
  148. package/dist/exit-code.js +10 -0
  149. package/dist/exit-code.js.map +1 -0
  150. package/dist/facts-file.d.ts +96 -0
  151. package/dist/facts-file.d.ts.map +1 -0
  152. package/dist/facts-file.js +134 -0
  153. package/dist/facts-file.js.map +1 -0
  154. package/dist/hooks-settings.d.ts +141 -0
  155. package/dist/hooks-settings.d.ts.map +1 -0
  156. package/dist/hooks-settings.js +306 -0
  157. package/dist/hooks-settings.js.map +1 -0
  158. package/dist/init.d.ts +201 -0
  159. package/dist/init.d.ts.map +1 -0
  160. package/dist/init.js +579 -0
  161. package/dist/init.js.map +1 -0
  162. package/dist/latex-log.d.ts +80 -0
  163. package/dist/latex-log.d.ts.map +1 -0
  164. package/dist/latex-log.js +187 -0
  165. package/dist/latex-log.js.map +1 -0
  166. package/dist/latex-loop.d.ts +129 -0
  167. package/dist/latex-loop.d.ts.map +1 -0
  168. package/dist/latex-loop.js +113 -0
  169. package/dist/latex-loop.js.map +1 -0
  170. package/dist/link-skills.d.ts +51 -0
  171. package/dist/link-skills.d.ts.map +1 -0
  172. package/dist/link-skills.js +199 -0
  173. package/dist/link-skills.js.map +1 -0
  174. package/dist/new-paper.d.ts +48 -0
  175. package/dist/new-paper.d.ts.map +1 -0
  176. package/dist/new-paper.js +110 -0
  177. package/dist/new-paper.js.map +1 -0
  178. package/dist/pdf-facts.d.ts +44 -0
  179. package/dist/pdf-facts.d.ts.map +1 -0
  180. package/dist/pdf-facts.js +239 -0
  181. package/dist/pdf-facts.js.map +1 -0
  182. package/dist/pdf-geometry.d.ts +170 -0
  183. package/dist/pdf-geometry.d.ts.map +1 -0
  184. package/dist/pdf-geometry.js +158 -0
  185. package/dist/pdf-geometry.js.map +1 -0
  186. package/dist/ports/download.d.ts +9 -0
  187. package/dist/ports/download.d.ts.map +1 -0
  188. package/dist/ports/download.js +2 -0
  189. package/dist/ports/download.js.map +1 -0
  190. package/dist/ports/files.d.ts +11 -0
  191. package/dist/ports/files.d.ts.map +1 -0
  192. package/dist/ports/files.js +2 -0
  193. package/dist/ports/files.js.map +1 -0
  194. package/dist/ports/measure-geometry.d.ts +8 -0
  195. package/dist/ports/measure-geometry.d.ts.map +1 -0
  196. package/dist/ports/measure-geometry.js +2 -0
  197. package/dist/ports/measure-geometry.js.map +1 -0
  198. package/dist/ports/process.d.ts +45 -0
  199. package/dist/ports/process.d.ts.map +1 -0
  200. package/dist/ports/process.js +2 -0
  201. package/dist/ports/process.js.map +1 -0
  202. package/dist/ports/tool-installer.d.ts +29 -0
  203. package/dist/ports/tool-installer.d.ts.map +1 -0
  204. package/dist/ports/tool-installer.js +2 -0
  205. package/dist/ports/tool-installer.js.map +1 -0
  206. package/dist/ports/workspace.d.ts +18 -0
  207. package/dist/ports/workspace.d.ts.map +1 -0
  208. package/dist/ports/workspace.js +2 -0
  209. package/dist/ports/workspace.js.map +1 -0
  210. package/dist/rules-config.d.ts +34 -0
  211. package/dist/rules-config.d.ts.map +1 -0
  212. package/dist/rules-config.js +132 -0
  213. package/dist/rules-config.js.map +1 -0
  214. package/dist/structure.d.ts +34 -0
  215. package/dist/structure.d.ts.map +1 -0
  216. package/dist/structure.js +149 -0
  217. package/dist/structure.js.map +1 -0
  218. package/dist/tex-requirements.d.ts +43 -0
  219. package/dist/tex-requirements.d.ts.map +1 -0
  220. package/dist/tex-requirements.js +127 -0
  221. package/dist/tex-requirements.js.map +1 -0
  222. package/dist/toolchain.d.ts +159 -0
  223. package/dist/toolchain.d.ts.map +1 -0
  224. package/dist/toolchain.js +542 -0
  225. package/dist/toolchain.js.map +1 -0
  226. package/dist/types.d.ts +110 -0
  227. package/dist/types.d.ts.map +1 -0
  228. package/dist/types.js +2 -0
  229. package/dist/types.js.map +1 -0
  230. package/docs/configuration.md +235 -0
  231. package/docs/e2e.md +152 -0
  232. package/docs/incidents.md +59 -0
  233. package/docs/install.md +170 -0
  234. package/docs/optional-rules.md +107 -0
  235. package/docs/package-shape-options.md +262 -0
  236. package/docs/prior-art/README.md +76 -0
  237. package/docs/prior-art/blocking-vs-advisory.md +83 -0
  238. package/docs/prior-art/content-delivery.md +124 -0
  239. package/docs/prior-art/multi-mode-tools.md +106 -0
  240. package/docs/prior-art/nondeterministic-checks.md +99 -0
  241. package/docs/prior-art/package-location.md +422 -0
  242. package/docs/prior-art/paper-folder-scaffolding.md +538 -0
  243. package/docs/prior-art/readme-structure.md +69 -0
  244. package/docs/prior-art/repro/README.md +92 -0
  245. package/docs/prior-art/repro/claim1-allowedtools.mjs +66 -0
  246. package/docs/prior-art/repro/claim1-at2.mjs +40 -0
  247. package/docs/prior-art/repro/claim1-crosschannel.mjs +54 -0
  248. package/docs/prior-art/repro/claim1-frontmatter.mjs +76 -0
  249. package/docs/prior-art/repro/claim1-hook-payload-reporter.mjs +10 -0
  250. package/docs/prior-art/repro/claim1-plugin-frontmatter.mjs +27 -0
  251. package/docs/prior-art/repro/claim1-plugin-skill.mjs +52 -0
  252. package/docs/prior-art/repro/claim1-project-skill.mjs +81 -0
  253. package/docs/prior-art/repro/claim2-marketplace-flat-asclaimed.json +1 -0
  254. package/docs/prior-art/repro/claim2-marketplace-negative-control.json +1 -0
  255. package/docs/prior-art/repro/claim2-marketplace-nested-exact.json +9 -0
  256. package/docs/prior-art/repro/claim2-marketplace-nested-noversion.json +9 -0
  257. package/docs/prior-art/repro/claim2-marketplace-nested-range.json +1 -0
  258. package/docs/prior-art/repro/claim3-imports.mjs +50 -0
  259. package/docs/prior-art/repro/claim4-find-package-json.mjs +8 -0
  260. package/docs/prior-art/repro/claim4-package-dir.mjs +39 -0
  261. package/docs/prior-art/repro/claim4-parent-arg.mjs +17 -0
  262. package/docs/prior-art/repro/claim4-resolve-apis.mjs +21 -0
  263. package/docs/prior-art/repro/claim4-setup-consumers.mjs +45 -0
  264. package/docs/prior-art/repro/claim4-yarn-pnp.mjs +70 -0
  265. package/docs/prior-art/repro/claim5-bin-launch.mjs +39 -0
  266. package/docs/prior-art/repro/claim5-exports-mutation.mjs +57 -0
  267. package/docs/prior-art/repro/claim5-resolved-location-and-bin.mjs +33 -0
  268. package/docs/prior-art/repro/claim6-candidate-ambiguity.mjs +17 -0
  269. package/docs/prior-art/repro/claim6-doc-path-candidates.mjs +27 -0
  270. package/docs/prior-art/test-tooling.md +131 -0
  271. package/docs/rules.md +58 -0
  272. package/docs/texlive-install-decision.md +230 -0
  273. package/docs/toolchain.md +152 -0
  274. package/eslint-rules/doc-fields.harness.mjs +336 -0
  275. package/eslint-rules/doc-fields.mjs +186 -0
  276. package/eslint-rules/doc-fields.mutations.mjs +96 -0
  277. package/eslint-rules/install-path-literals.harness.mjs +121 -0
  278. package/eslint-rules/install-path-literals.mjs +108 -0
  279. package/eslint-rules/install-path-literals.mutations.mjs +62 -0
  280. package/eslint-rules/latex-language.harness.mjs +599 -0
  281. package/eslint-rules/latex-language.mjs +591 -0
  282. package/eslint-rules/latex-language.mutations.mjs +196 -0
  283. package/eslint-rules/paper-research-question.harness.mjs +146 -0
  284. package/eslint-rules/paper-research-question.mjs +180 -0
  285. package/eslint-rules/paper-research-question.mutations.mjs +127 -0
  286. package/eslint-rules/paper-stages.harness.mjs +356 -0
  287. package/eslint-rules/paper-stages.mjs +455 -0
  288. package/eslint-rules/paper-stages.mutations.mjs +157 -0
  289. package/eslint-rules/paper-typography.harness.mjs +291 -0
  290. package/eslint-rules/paper-typography.mjs +313 -0
  291. package/eslint-rules/paper-typography.mutations.mjs +131 -0
  292. package/eslint-rules/papers.harness.mjs +259 -0
  293. package/eslint-rules/papers.mjs +166 -0
  294. package/eslint-rules/papers.mutations.mjs +186 -0
  295. package/eslint-rules/pdf-last-page-balance.harness.mjs +206 -0
  296. package/eslint-rules/pdf-last-page-balance.mjs +208 -0
  297. package/eslint-rules/review-findings-cause.harness.mjs +228 -0
  298. package/eslint-rules/review-findings-cause.mjs +135 -0
  299. package/eslint-rules/review-findings-cause.mutations.mjs +72 -0
  300. package/eslint-rules/temp-root-realpath.harness.mjs +176 -0
  301. package/eslint-rules/temp-root-realpath.mjs +129 -0
  302. package/eslint-rules/temp-root-realpath.mutations.mjs +99 -0
  303. package/eslint-rules/tex-build.harness.mjs +753 -0
  304. package/eslint-rules/tex-build.mjs +322 -0
  305. package/eslint-rules/tex-build.mutations.mjs +258 -0
  306. package/eslint.config.mjs +521 -0
  307. package/fixtures/build-e2e/acmart/PIPELINE-STATUS.md +3 -0
  308. package/fixtures/build-e2e/acmart/paper.tex +11 -0
  309. package/fixtures/build-e2e/acmart/venue.json +1 -0
  310. package/fixtures/build-e2e/broken/PIPELINE-STATUS.md +3 -0
  311. package/fixtures/build-e2e/broken/paper.tex +7 -0
  312. package/fixtures/build-e2e/cite/PIPELINE-STATUS.md +3 -0
  313. package/fixtures/build-e2e/cite/build.sh +5 -0
  314. package/fixtures/build-e2e/cite/paper.tex +10 -0
  315. package/fixtures/build-e2e/cite/refs.bib +9 -0
  316. package/fixtures/build-e2e/empty/PIPELINE-STATUS.md +3 -0
  317. package/fixtures/build-e2e/empty/paper.tex +6 -0
  318. package/fixtures/build-e2e/fallback/PIPELINE-STATUS.md +3 -0
  319. package/fixtures/build-e2e/fallback/paper.tex +11 -0
  320. package/fixtures/build-e2e/guards/PIPELINE-STATUS.md +3 -0
  321. package/fixtures/build-e2e/guards/paper.tex +10 -0
  322. package/fixtures/build-e2e/no-source/PIPELINE-STATUS.md +3 -0
  323. package/fixtures/build-e2e/unbalanced/PIPELINE-STATUS.md +3 -0
  324. package/fixtures/build-e2e/unbalanced/paper.tex +28 -0
  325. package/fixtures/build-e2e/unbalanced/refs.bib +269 -0
  326. package/fixtures/install-path-literals/clean.fixture.mjs +3 -0
  327. package/fixtures/install-path-literals/clean.md +15 -0
  328. package/fixtures/install-path-literals/defect.fixture.mjs +3 -0
  329. package/fixtures/install-path-literals/defect.md +14 -0
  330. package/fixtures/latex-language/clean.tex +50 -0
  331. package/fixtures/latex-language/defect.tex +52 -0
  332. package/fixtures/paper-research-question/comment-only/PIPELINE-STATUS.md +9 -0
  333. package/fixtures/paper-research-question/comment-only/paper.tex +7 -0
  334. package/fixtures/paper-research-question/declared-not-in-paper/PIPELINE-STATUS.md +10 -0
  335. package/fixtures/paper-research-question/declared-not-in-paper/paper.tex +6 -0
  336. package/fixtures/paper-research-question/draft/PIPELINE-STATUS.md +6 -0
  337. package/fixtures/paper-research-question/draft/paper.tex +2 -0
  338. package/fixtures/paper-research-question/markdown-no-rq/PIPELINE-STATUS.md +9 -0
  339. package/fixtures/paper-research-question/markdown-no-rq/paper.md +4 -0
  340. package/fixtures/paper-research-question/shipped-no-rq/PIPELINE-STATUS.md +12 -0
  341. package/fixtures/paper-research-question/shipped-no-rq/paper.tex +3 -0
  342. package/fixtures/paper-research-question/shipped-with-rq/PIPELINE-STATUS.md +10 -0
  343. package/fixtures/paper-research-question/shipped-with-rq/paper.tex +2 -0
  344. package/fixtures/paper-stages/authors-ran/PIPELINE-STATUS.md +16 -0
  345. package/fixtures/paper-stages/marker-in-prose/PIPELINE-STATUS.md +17 -0
  346. package/fixtures/paper-stages/nofile/PIPELINE-STATUS.md +8 -0
  347. package/fixtures/paper-stages/noheader/PIPELINE-STATUS.md +1 -0
  348. package/fixtures/paper-stages/noheader/versions/2026-07-22-submitted.pdf +0 -0
  349. package/fixtures/paper-stages/nothing/PIPELINE-STATUS.md +3 -0
  350. package/fixtures/paper-stages/ok/PIPELINE-STATUS.md +9 -0
  351. package/fixtures/paper-stages/ok/versions/2026-07-22-submitted.pdf +0 -0
  352. package/fixtures/paper-stages/stale/PIPELINE-STATUS.md +1 -0
  353. package/fixtures/paper-stages/stale/versions/2026-07-22-submitted.STALE-WRONG-FILE.pdf +0 -0
  354. package/fixtures/paper-stages/twice/PIPELINE-STATUS.md +14 -0
  355. package/fixtures/paper-stages/twice/versions/2026-08-06-submitted.pdf +0 -0
  356. package/fixtures/paper-stages/twice/versions/2026-10-24-submitted.pdf +0 -0
  357. package/fixtures/paper-stages/undeclared/PIPELINE-STATUS.md +8 -0
  358. package/fixtures/paper-stages/undeclared/versions/2026-07-22-submitted.pdf +0 -0
  359. package/fixtures/paper-stages/undeclared/versions/2026-08-29-camera-ready.pdf +0 -0
  360. package/fixtures/paper-stages/wrongsize/PIPELINE-STATUS.md +8 -0
  361. package/fixtures/paper-stages/wrongsize/versions/2026-07-22-submitted.pdf +0 -0
  362. package/fixtures/paper-typography/clean-paper/paper.tex +29 -0
  363. package/fixtures/paper-typography/messy-paper/paper.tex +27 -0
  364. package/fixtures/pdf-facts/README.md +22 -0
  365. package/fixtures/pdf-facts/corrupt-font.pdf +0 -0
  366. package/fixtures/pdf-facts/encrypted.pdf +0 -0
  367. package/fixtures/pdf-facts/hidden-text.pdf +0 -0
  368. package/fixtures/pdf-facts/hidden-text.tex +28 -0
  369. package/fixtures/pdf-facts/t3-all.pdf +0 -0
  370. package/fixtures/pdf-facts/t3-all.tex +8 -0
  371. package/fixtures/pdf-facts/t3-mixed.pdf +0 -0
  372. package/fixtures/pdf-facts/t3-mixed.tex +9 -0
  373. package/fixtures/pdf-facts/ttf.pdf +2240 -1
  374. package/fixtures/pdf-facts/ttf.tex +6 -0
  375. package/fixtures/real-markdown-paper/baseline.json +24 -0
  376. package/fixtures/real-markdown-paper/baseline.mjs +48 -0
  377. package/fixtures/render-paper/build-clean.sh +25 -0
  378. package/fixtures/render-paper/build-defect.sh +15 -0
  379. package/fixtures/review-findings-cause/clean.md +17 -0
  380. package/fixtures/review-findings-cause/defect.md +14 -0
  381. package/fixtures/review-findings-cause/old-debt.md +14 -0
  382. package/fixtures/review-findings-cause/quiet-in-fence.md +16 -0
  383. package/fixtures/tex-build/clean.tex +21 -0
  384. package/fixtures/tex-build/defect.tex +24 -0
  385. package/fixtures/tex-build/frontmatter-clean.tex +25 -0
  386. package/fixtures/tex-build/frontmatter-defect.tex +23 -0
  387. package/fixtures/toolchain-mirror/catalog.txt +5 -0
  388. package/fixtures/toolchain-mirror/install-tl +27 -0
  389. package/fixtures/toolchain-mirror/release-texlive.txt +3 -0
  390. package/fixtures/toolchain-mirror/release-year +1 -0
  391. package/fixtures/toolchain-mirror/stub-kpsewhich +8 -0
  392. package/fixtures/toolchain-mirror/stub-pdflatex +3 -0
  393. package/fixtures/toolchain-mirror/stub-tlmgr +44 -0
  394. package/hooks/hooks.harness.mjs +713 -0
  395. package/hooks/hooks.mutations.mjs +337 -0
  396. package/hooks/paper-edit-guard.hook.d.mts +13 -0
  397. package/hooks/paper-edit-guard.hook.mjs +457 -0
  398. package/hooks/paper-skills-nudge.hook.mjs +136 -0
  399. package/hooks/paper-status-gates.hook.mjs +156 -0
  400. package/hooks/paper-status-gates.sh +91 -0
  401. package/lib/agent-cli-version.harness.mjs +165 -0
  402. package/lib/agent-cli-version.mjs +106 -0
  403. package/lib/agent-cli-version.mutations.mjs +109 -0
  404. package/lib/markdown.mjs +386 -0
  405. package/lib/mutation-driver.harness.mjs +227 -0
  406. package/lib/mutation-driver.mjs +397 -0
  407. package/lib/mutation-driver.mutations.mjs +68 -0
  408. package/lib/paper-config.d.mts +34 -0
  409. package/lib/paper-config.harness.mjs +286 -0
  410. package/lib/paper-config.mjs +142 -0
  411. package/lib/paper-config.mutations.mjs +143 -0
  412. package/lib/skill-checks.mjs +701 -0
  413. package/lib/skill-corpus.mjs +403 -0
  414. package/lib/skill-eval-fixture.mjs +63 -0
  415. package/lib/skill-eval-kit.mjs +257 -0
  416. package/lib/skill-trigger-cases.harness.mjs +170 -0
  417. package/lib/skill-trigger-cases.mjs +446 -0
  418. package/lib/skill-trigger-cases.mutations.mjs +65 -0
  419. package/lib/trigger-ledger.mjs +215 -0
  420. package/package.json +97 -0
  421. package/plugin/.claude-plugin/plugin.json +8 -0
  422. package/plugin/hooks/hooks.json +30 -0
  423. package/scripts/check.harness.mjs +177 -0
  424. package/scripts/check.mjs +239 -0
  425. package/scripts/check.mutations.mjs +110 -0
  426. package/scripts/eslint-report-guard.mjs +82 -0
  427. package/scripts/exclusive.mjs +138 -0
  428. package/scripts/harness-api.frozen.json +76 -0
  429. package/scripts/harness-api.test.ts +175 -0
  430. package/scripts/layer-legacy-frozen.d.mts +28 -0
  431. package/scripts/layer-legacy-frozen.mjs +152 -0
  432. package/scripts/layer-legacy-frozen.test.ts +115 -0
  433. package/scripts/layer-legacy.frozen.json +50 -0
  434. package/scripts/mutation-batteries-frozen.harness.mjs +204 -0
  435. package/scripts/mutation-batteries-frozen.mjs +238 -0
  436. package/scripts/mutation-batteries.frozen.json +117 -0
  437. package/scripts/release-config.test.ts +90 -0
  438. package/scripts/rules-are-content-only.harness.mjs +113 -0
  439. package/scripts/rules-are-content-only.mjs +138 -0
  440. package/scripts/rules-are-content-only.mutations.mjs +81 -0
  441. package/scripts/rules-see-files.harness.mjs +115 -0
  442. package/scripts/rules-see-files.mjs +99 -0
  443. package/scripts/rules-see-files.mutations.mjs +131 -0
  444. package/scripts/run-mutations.mjs +100 -0
  445. package/scripts/semantic-release-plugins.d.ts +16 -0
  446. package/skills/README.md +15 -0
  447. package/skills/analyze-sibling-paper/SKILL.md +170 -0
  448. package/skills/analyze-sibling-paper/SKILL.md.spec.ts +186 -0
  449. package/skills/analyze-sibling-paper/analyze-sibling-paper.eval.mjs +19 -0
  450. package/skills/analyze-sibling-paper/analyze-sibling-paper.harness.mjs +23 -0
  451. package/skills/argument-arc/SKILL.md +177 -0
  452. package/skills/argument-arc/SKILL.md.spec.ts +192 -0
  453. package/skills/argument-arc/argument-arc.eval.mjs +19 -0
  454. package/skills/argument-arc/argument-arc.harness.mjs +23 -0
  455. package/skills/build-benchmark/SKILL.md +213 -0
  456. package/skills/build-benchmark/SKILL.md.spec.ts +220 -0
  457. package/skills/build-benchmark/build-benchmark.eval.mjs +19 -0
  458. package/skills/build-benchmark/build-benchmark.harness.mjs +23 -0
  459. package/skills/build-benchmark/references/adversarial-cold-repro.md +68 -0
  460. package/skills/camera-ready/SKILL.md +148 -0
  461. package/skills/camera-ready/SKILL.md.spec.ts +164 -0
  462. package/skills/camera-ready/camera-ready.eval.mjs +19 -0
  463. package/skills/camera-ready/camera-ready.harness.mjs +23 -0
  464. package/skills/cold-read-diff/SKILL.md +160 -0
  465. package/skills/cold-read-diff/SKILL.md.spec.ts +166 -0
  466. package/skills/cold-read-diff/cold-read-diff.eval.mjs +19 -0
  467. package/skills/cold-read-diff/cold-read-diff.harness.mjs +23 -0
  468. package/skills/draft-paper/SKILL.md +152 -0
  469. package/skills/draft-paper/SKILL.md.spec.ts +169 -0
  470. package/skills/draft-paper/draft-paper.eval.mjs +19 -0
  471. package/skills/draft-paper/draft-paper.harness.mjs +23 -0
  472. package/skills/extend-paper/SKILL.md +99 -0
  473. package/skills/extend-paper/SKILL.md.spec.ts +116 -0
  474. package/skills/extend-paper/extend-paper.eval.mjs +19 -0
  475. package/skills/extend-paper/extend-paper.harness.mjs +23 -0
  476. package/skills/find-venue/SKILL.md +128 -0
  477. package/skills/find-venue/SKILL.md.spec.ts +145 -0
  478. package/skills/find-venue/find-venue.eval.mjs +19 -0
  479. package/skills/find-venue/find-venue.harness.mjs +23 -0
  480. package/skills/grade-paper-writing/SKILL.md +436 -0
  481. package/skills/grade-paper-writing/SKILL.md.spec.ts +453 -0
  482. package/skills/grade-paper-writing/fixtures/control_gopen.txt +1 -0
  483. package/skills/grade-paper-writing/fixtures/control_human_paper.txt +1 -0
  484. package/skills/grade-paper-writing/fixtures/rewrite.txt +1 -0
  485. package/skills/grade-paper-writing/fixtures/specimen.txt +1 -0
  486. package/skills/grade-paper-writing/fixtures/structure-checks.md +22 -0
  487. package/skills/grade-paper-writing/grade-paper-writing.eval.mjs +19 -0
  488. package/skills/grade-paper-writing/grade-paper-writing.harness.mjs +23 -0
  489. package/skills/grade-paper-writing/prose-lint.mjs +713 -0
  490. package/skills/harden-paper/SKILL.md +318 -0
  491. package/skills/harden-paper/SKILL.md.spec.ts +336 -0
  492. package/skills/harden-paper/check-numbers.sh +33 -0
  493. package/skills/harden-paper/check-release-claims.sh +35 -0
  494. package/skills/harden-paper/fixtures/uncited-assertions-sample.md +43 -0
  495. package/skills/harden-paper/fixtures/uncited-assertions-sample.tex +77 -0
  496. package/skills/harden-paper/harden-paper.eval.mjs +19 -0
  497. package/skills/harden-paper/harden-paper.harness.mjs +23 -0
  498. package/skills/map-prior-work/SKILL.md +211 -0
  499. package/skills/map-prior-work/SKILL.md.spec.ts +227 -0
  500. package/skills/map-prior-work/map-prior-work.eval.mjs +19 -0
  501. package/skills/map-prior-work/map-prior-work.harness.mjs +23 -0
  502. package/skills/osf-artifact-upload/SKILL.md +52 -0
  503. package/skills/osf-artifact-upload/SKILL.md.spec.ts +59 -0
  504. package/skills/osf-artifact-upload/osf-artifact-upload.eval.mjs +22 -0
  505. package/skills/osf-artifact-upload/osf-artifact-upload.harness.mjs +103 -0
  506. package/skills/paper-adversarial-review/SKILL.md +126 -0
  507. package/skills/paper-adversarial-review/SKILL.md.spec.ts +142 -0
  508. package/skills/paper-adversarial-review/paper-adversarial-review.eval.mjs +19 -0
  509. package/skills/paper-adversarial-review/paper-adversarial-review.harness.mjs +23 -0
  510. package/skills/paper-pipeline/PIPELINE-MAP.md +371 -0
  511. package/skills/paper-pipeline/SKILL.md +499 -0
  512. package/skills/paper-pipeline/SKILL.md.spec.ts +517 -0
  513. package/skills/paper-pipeline/description-language.eval.mjs +347 -0
  514. package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +891 -0
  515. package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +1254 -0
  516. package/skills/paper-pipeline/paper-pipeline.eval.mjs +22 -0
  517. package/skills/paper-pipeline/paper-pipeline.harness.mjs +143 -0
  518. package/skills/paper-pipeline/pipeline-firing.baseline.json +270 -0
  519. package/skills/paper-pipeline/pipeline-firing.eval.mjs +664 -0
  520. package/skills/paper-pipeline/pipeline-language.eval.mjs +672 -0
  521. package/skills/paper-pipeline/references/acceptance-gate.md +329 -0
  522. package/skills/paper-pipeline/references/acl-venue-rules.md +142 -0
  523. package/skills/paper-pipeline/references/anonymization.md +68 -0
  524. package/skills/paper-pipeline/references/artifact-checklist.md +93 -0
  525. package/skills/paper-pipeline/references/body-vs-appendix.md +97 -0
  526. package/skills/paper-pipeline/references/credit-criteria.md +69 -0
  527. package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/README.md +35 -0
  528. package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/run_retext.mjs +24 -0
  529. package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/sentences.txt +11 -0
  530. package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/test_sentences.py +25 -0
  531. package/skills/paper-pipeline/references/occupancy-2026-08-06-prose-checkers.md +538 -0
  532. package/skills/paper-pipeline/references/occupancy-2026-08-06-reproducible-tooling.md +431 -0
  533. package/skills/paper-pipeline/references/occupancy-2026-08-06-staleness-and-orchestration.md +592 -0
  534. package/skills/paper-pipeline/references/pipeline-status-template.md +162 -0
  535. package/skills/paper-pipeline/references/review-ratchet.md +36 -0
  536. package/skills/paper-pipeline/references/sweep-2026-08-09-ideal-pipeline.md +585 -0
  537. package/skills/paper-pipeline/references/writing-craft.md +448 -0
  538. package/skills/paper-pipeline/repro/2026-08-07-description-language-control.log +63 -0
  539. package/skills/paper-pipeline/repro/2026-08-07-fork-check.log +52 -0
  540. package/skills/paper-pipeline/repro/2026-08-07-fork-check2.log +33 -0
  541. package/skills/paper-pipeline/repro/2026-08-07-language-eval-pilot.log +33 -0
  542. package/skills/paper-pipeline/repro/2026-08-07-language-eval-raw.log +166 -0
  543. package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.json +338 -0
  544. package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.log +118 -0
  545. package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-raw.log +245 -0
  546. package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary.json +776 -0
  547. package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-oracle.log +53 -0
  548. package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-raw.log +89 -0
  549. package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.json +450 -0
  550. package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.log +136 -0
  551. package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-raw.log +242 -0
  552. package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-setupdiff.log +59 -0
  553. package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation.json +1032 -0
  554. package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.json +139 -0
  555. package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.log +98 -0
  556. package/skills/paper-pipeline/repro/2026-08-08-parent-replication.mjs +92 -0
  557. package/skills/paper-pipeline/repro/README.md +129 -0
  558. package/skills/paper-pipeline/repro/analyze-language-eval.py +116 -0
  559. package/skills/paper-pipeline/scripts/README.md +344 -0
  560. package/skills/paper-pipeline/scripts/announce.mjs +67 -0
  561. package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +496 -0
  562. package/skills/paper-pipeline/scripts/artifact-coverage.mjs +397 -0
  563. package/skills/paper-pipeline/scripts/artifact-coverage.mutations.mjs +218 -0
  564. package/skills/paper-pipeline/scripts/check-provenance.mjs +184 -0
  565. package/skills/paper-pipeline/scripts/consumer.d.mts +32 -0
  566. package/skills/paper-pipeline/scripts/consumer.harness.mjs +562 -0
  567. package/skills/paper-pipeline/scripts/consumer.mjs +535 -0
  568. package/skills/paper-pipeline/scripts/consumer.mutations.mjs +190 -0
  569. package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +457 -0
  570. package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +656 -0
  571. package/skills/paper-pipeline/scripts/extract-ref-facts.mutations.mjs +54 -0
  572. package/skills/paper-pipeline/scripts/fixtures/clean/PIPELINE-STATUS.md +51 -0
  573. package/skills/paper-pipeline/scripts/fixtures/dirty/PIPELINE-STATUS.md +52 -0
  574. package/skills/paper-pipeline/scripts/fixtures/dirty/paper.md +6 -0
  575. package/skills/paper-pipeline/scripts/fixtures/real-bib/refs.bib +153 -0
  576. package/skills/paper-pipeline/scripts/generated-code.harness.mjs +466 -0
  577. package/skills/paper-pipeline/scripts/generated-code.mjs +338 -0
  578. package/skills/paper-pipeline/scripts/generated-code.mutations.mjs +254 -0
  579. package/skills/paper-pipeline/scripts/ledger.mjs +623 -0
  580. package/skills/paper-pipeline/scripts/ledger.selftest.mjs +286 -0
  581. package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +389 -0
  582. package/skills/paper-pipeline/scripts/pipeline-check.mjs +737 -0
  583. package/skills/paper-pipeline/scripts/pipeline-check.mutations.mjs +54 -0
  584. package/skills/paper-pipeline/scripts/pipeline-edges.mjs +169 -0
  585. package/skills/paper-pipeline/scripts/population-map.harness.mjs +178 -0
  586. package/skills/paper-pipeline/scripts/population-map.mjs +181 -0
  587. package/skills/paper-pipeline/scripts/population-map.mutations.mjs +65 -0
  588. package/skills/paper-pipeline/scripts/population-map.selftest.mjs +122 -0
  589. package/skills/paper-pipeline/scripts/provenance.harness.mjs +240 -0
  590. package/skills/paper-pipeline/scripts/provenance.mutations.mjs +59 -0
  591. package/skills/paper-pipeline/scripts/round-diff.harness.mjs +881 -0
  592. package/skills/paper-pipeline/scripts/round-diff.mjs +576 -0
  593. package/skills/paper-pipeline/scripts/round-diff.mutations.mjs +276 -0
  594. package/skills/paper-pipeline/scripts/run-mechanical.mjs +633 -0
  595. package/skills/paper-pipeline/scripts/status.mjs +295 -0
  596. package/skills/paper-status/SKILL.md +183 -0
  597. package/skills/paper-status/SKILL.md.spec.ts +190 -0
  598. package/skills/paper-status/paper-status.eval.mjs +22 -0
  599. package/skills/paper-status/paper-status.harness.mjs +25 -0
  600. package/skills/pc-panel-review/SKILL.md +263 -0
  601. package/skills/pc-panel-review/SKILL.md.spec.ts +280 -0
  602. package/skills/pc-panel-review/pc-panel-review.eval.mjs +19 -0
  603. package/skills/pc-panel-review/pc-panel-review.harness.mjs +23 -0
  604. package/skills/plan-paper-timeline/SKILL.md +182 -0
  605. package/skills/plan-paper-timeline/SKILL.md.spec.ts +200 -0
  606. package/skills/plan-paper-timeline/fixtures/fake-google-calendar.mjs +239 -0
  607. package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +431 -0
  608. package/skills/plan-paper-timeline/plan-paper-timeline.effects.mutations.mjs +65 -0
  609. package/skills/plan-paper-timeline/plan-paper-timeline.eval.mjs +19 -0
  610. package/skills/plan-paper-timeline/plan-paper-timeline.harness.mjs +23 -0
  611. package/skills/render-paper/SKILL.md +159 -0
  612. package/skills/render-paper/SKILL.md.spec.ts +166 -0
  613. package/skills/render-paper/check-render.sh +419 -0
  614. package/skills/render-paper/checkers-requirements.txt +55 -0
  615. package/skills/render-paper/ensure-checkers.sh +69 -0
  616. package/skills/render-paper/extract-pdf-facts.harness.mjs +166 -0
  617. package/skills/render-paper/extract-pdf-facts.mjs +144 -0
  618. package/skills/render-paper/render-paper.eval.mjs +19 -0
  619. package/skills/render-paper/render-paper.harness.mjs +339 -0
  620. package/skills/research-ideate/SKILL.md +136 -0
  621. package/skills/research-ideate/SKILL.md.spec.ts +152 -0
  622. package/skills/research-ideate/research-ideate.eval.mjs +19 -0
  623. package/skills/research-ideate/research-ideate.harness.mjs +23 -0
  624. package/skills/skill-contract.mutations.mjs +179 -0
  625. package/skills/study-accepted-papers/SKILL.md +206 -0
  626. package/skills/study-accepted-papers/SKILL.md.spec.ts +223 -0
  627. package/skills/study-accepted-papers/study-accepted-papers.eval.mjs +19 -0
  628. package/skills/study-accepted-papers/study-accepted-papers.harness.mjs +23 -0
  629. package/skills/submit-paper/SKILL.md +182 -0
  630. package/skills/submit-paper/SKILL.md.spec.ts +199 -0
  631. package/skills/submit-paper/check-deanon.sh +149 -0
  632. package/skills/submit-paper/references/publishers/acm.md +92 -0
  633. package/skills/submit-paper/references/venues/agenticdev.jsonc +108 -0
  634. package/skills/submit-paper/references/venues/agenticdev.md +139 -0
  635. package/skills/submit-paper/references/venues/agenticdev.tex +19 -0
  636. package/skills/submit-paper/references/venues/aisec.jsonc +101 -0
  637. package/skills/submit-paper/references/venues/aisec.md +105 -0
  638. package/skills/submit-paper/references/venues/paper-guards.tex +41 -0
  639. package/skills/submit-paper/references/venues/realm.jsonc +81 -0
  640. package/skills/submit-paper/references/venues/realm.md +155 -0
  641. package/skills/submit-paper/references/venues/tex-base.jsonc +50 -0
  642. package/skills/submit-paper/references/venues/venue-profile.schema.json +74 -0
  643. package/skills/submit-paper/submit-paper.eval.mjs +19 -0
  644. package/skills/submit-paper/submit-paper.harness.mjs +23 -0
  645. package/skills/sweep-design-space/SKILL.md +269 -0
  646. package/skills/sweep-design-space/SKILL.md.spec.ts +285 -0
  647. package/skills/sweep-design-space/sweep-design-space.eval.mjs +19 -0
  648. package/skills/sweep-design-space/sweep-design-space.harness.mjs +23 -0
  649. package/skills/tighten-paper/SKILL.md +368 -0
  650. package/skills/tighten-paper/SKILL.md.spec.ts +384 -0
  651. package/skills/tighten-paper/structure.mjs +371 -0
  652. package/skills/tighten-paper/tighten-paper.eval.mjs +19 -0
  653. package/skills/tighten-paper/tighten-paper.harness.mjs +23 -0
  654. package/skills/verify-citations/SKILL.md +328 -0
  655. package/skills/verify-citations/SKILL.md.spec.ts +345 -0
  656. package/skills/verify-citations/scripts/bib-authors.mjs +479 -0
  657. package/skills/verify-citations/scripts/bib-authors.test.mjs +175 -0
  658. package/skills/verify-citations/scripts/verify-cites.mjs +1108 -0
  659. package/skills/verify-citations/scripts/verify-cites.test.mjs +735 -0
  660. package/skills/verify-citations/verify-citations.eval.mjs +19 -0
  661. package/skills/verify-citations/verify-citations.harness.mjs +23 -0
  662. package/src/CLAUDE.md +51 -0
  663. package/src/action-ref.test.ts +26 -0
  664. package/src/action-ref.ts +15 -0
  665. package/src/adapters/banal/failure.test.ts +63 -0
  666. package/src/adapters/banal/failure.ts +118 -0
  667. package/src/adapters/banal/index.test.ts +119 -0
  668. package/src/adapters/banal/index.ts +100 -0
  669. package/src/adapters/banal/install.test.ts +20 -0
  670. package/src/adapters/banal/install.ts +41 -0
  671. package/src/adapters/banal/invocation.test.ts +74 -0
  672. package/src/adapters/banal/invocation.ts +95 -0
  673. package/src/adapters/banal/locate.test.ts +52 -0
  674. package/src/adapters/banal/locate.ts +84 -0
  675. package/src/adapters/banal/output.test.ts +140 -0
  676. package/src/adapters/banal/output.ts +141 -0
  677. package/src/adapters/banal/pin.ts +30 -0
  678. package/src/adapters/banal/probe.ts +35 -0
  679. package/src/adapters/banal/run.test.ts +191 -0
  680. package/src/adapters/banal/run.ts +244 -0
  681. package/src/adapters/banal/settings.test.ts +31 -0
  682. package/src/adapters/banal/settings.ts +55 -0
  683. package/src/adapters/banal/xml.test.ts +111 -0
  684. package/src/adapters/banal/xml.ts +112 -0
  685. package/src/adapters/curl/download.io.ts +73 -0
  686. package/src/adapters/curl/download.test.ts +55 -0
  687. package/src/adapters/curl/index.ts +5 -0
  688. package/src/adapters/memory/index.ts +131 -0
  689. package/src/adapters/node/files.io.ts +39 -0
  690. package/src/adapters/node/files.test.ts +28 -0
  691. package/src/adapters/node/host.io.ts +15 -0
  692. package/src/adapters/node/index.ts +36 -0
  693. package/src/adapters/node/process.io.ts +49 -0
  694. package/src/adapters/node/process.test.ts +46 -0
  695. package/src/adapters/node/workspace.io.ts +40 -0
  696. package/src/adapters/node/workspace.test.ts +58 -0
  697. package/src/adapters/pdfjs/fill.test.ts +111 -0
  698. package/src/adapters/pdfjs/fill.ts +141 -0
  699. package/src/build-engine.harness.mjs +314 -0
  700. package/src/build-engine.ts +219 -0
  701. package/src/build.harness.mjs +631 -0
  702. package/src/build.mutations.mjs +195 -0
  703. package/src/build.ts +793 -0
  704. package/src/cli.harness.mjs +2007 -0
  705. package/src/cli.mutations.mjs +448 -0
  706. package/src/cli.ts +1189 -0
  707. package/src/doctor.harness.mjs +396 -0
  708. package/src/doctor.mutations.mjs +175 -0
  709. package/src/doctor.ts +356 -0
  710. package/src/domain/geometry.ts +108 -0
  711. package/src/domain/host.ts +23 -0
  712. package/src/domain/page-layout.ts +32 -0
  713. package/src/domain/paths.ts +5 -0
  714. package/src/domain/result.test.ts +26 -0
  715. package/src/domain/result.ts +29 -0
  716. package/src/domain/sha256.test.ts +12 -0
  717. package/src/domain/sha256.ts +21 -0
  718. package/src/domain/text.ts +11 -0
  719. package/src/engine.harness.mjs +252 -0
  720. package/src/engine.ts +176 -0
  721. package/src/exit-code.test.ts +21 -0
  722. package/src/exit-code.ts +38 -0
  723. package/src/facts-file.test.ts +240 -0
  724. package/src/facts-file.ts +241 -0
  725. package/src/hooks-settings.harness.mjs +386 -0
  726. package/src/hooks-settings.mutations.mjs +116 -0
  727. package/src/hooks-settings.ts +434 -0
  728. package/src/init.ts +900 -0
  729. package/src/latex-log.harness.mjs +226 -0
  730. package/src/latex-log.ts +234 -0
  731. package/src/latex-loop.harness.mjs +449 -0
  732. package/src/latex-loop.ts +211 -0
  733. package/src/link-skills.harness.mjs +273 -0
  734. package/src/link-skills.mutations.mjs +136 -0
  735. package/src/link-skills.ts +258 -0
  736. package/src/new-paper.harness.mjs +216 -0
  737. package/src/new-paper.mutations.mjs +79 -0
  738. package/src/new-paper.ts +158 -0
  739. package/src/pdf-facts.harness.mjs +188 -0
  740. package/src/pdf-facts.ts +327 -0
  741. package/src/pdf-geometry.harness.mjs +254 -0
  742. package/src/pdf-geometry.ts +300 -0
  743. package/src/ports/download.ts +10 -0
  744. package/src/ports/files.ts +11 -0
  745. package/src/ports/measure-geometry.ts +8 -0
  746. package/src/ports/process.ts +46 -0
  747. package/src/ports/tool-installer.ts +33 -0
  748. package/src/ports/workspace.ts +20 -0
  749. package/src/rules-config.harness.mjs +114 -0
  750. package/src/rules-config.ts +178 -0
  751. package/src/structure.harness.mjs +179 -0
  752. package/src/structure.mutations.mjs +83 -0
  753. package/src/structure.ts +166 -0
  754. package/src/tex-requirements.harness.mjs +238 -0
  755. package/src/tex-requirements.ts +181 -0
  756. package/src/toolchain.harness.mjs +651 -0
  757. package/src/toolchain.ts +755 -0
  758. package/src/types.ts +106 -0
  759. package/templates/paper/PIPELINE-STATUS.md +72 -0
  760. package/templates/paper/paper.md +4 -0
  761. package/templates/paper/paper.tex +8 -0
  762. package/tsconfig.json +23 -0
@@ -0,0 +1,422 @@
1
+ # How a package finds its own installed files, and its own bin
2
+
3
+ **Question this file answers:** `test/e2e/install.mjs` inspects the tree it just installed by
4
+ spelling the layout out — `node_modules/research-paper-pipeline` (line 172),
5
+ `node_modules/.bin/rpp` (line 276), and `node_modules/.../plugin/hooks/hooks.json` (line 135).
6
+ `contentDelivery()` separately tries THREE candidate base directories for a path written as prose
7
+ inside a skill. Both are guesses. What is the supported way, and does using it buy anything?
8
+
9
+ **Verdict, in two parts, because the two hardcodes are not the same kind of thing:**
10
+
11
+ 1. **The package directory: replace the three spellings with ONE resolved anchor** —
12
+ `dirname(createRequire(pathToFileURL(join(consumer, "__placeholder__.js")).href)
13
+ .resolve("research-paper-pipeline/package.json"))`. Not because the hardcode is currently
14
+ wrong: it is not, under either manager measured. Because resolution additionally _asserts the
15
+ package is reachable by name from the consumer_, which is what the README's documented import
16
+ depends on and which `existsSync` cannot see — demonstrated by a mutation below.
17
+ 2. **The `.bin` launch: keep it.** `node_modules/.bin/rpp` is not the script guessing where a
18
+ file is; it is the script checking that the manager created the shim, which is a real property
19
+ of the install and the one thing resolution cannot tell you. Read the bin's _real_ path from
20
+ the resolved manifest as well, and launch both — they answer different questions.
21
+
22
+ **Yarn PnP is unaffected by any of this**, and for a harder reason than
23
+ [`test-tooling.md`](test-tooling.md) currently records. See § "Correcting the PnP note".
24
+
25
+ **The doc-relative base (`contentDelivery`): declare it, do not add a fourth candidate.** One of
26
+ the three candidates is measurably dead (0 hits of 104). The other two are disjoint by spelling,
27
+ so today the ladder hides nothing — but the package already declares this base once, in
28
+ `.claude-plugin/plugin.json` (`"skills": "./skills/"`), and the platform exposes
29
+ `${CLAUDE_PLUGIN_ROOT}` for exactly this.
30
+
31
+ Everything below was measured on this machine on 2026-09-19: Node v22.22.2 (CI pins
32
+ `node-version: 22`, `.github/workflows/ci.yml`), npm 10.9.7, pnpm 10.33.0, Yarn 4.9.2 via
33
+ corepack. Scripts: [`repro/`](repro/README.md), CLAIM 4–6.
34
+
35
+ ---
36
+
37
+ ## 1. The three APIs, and which one this script can use
38
+
39
+ | API | available on Node 22 | anchored at | our package | `exports`-closed package |
40
+ | -------------------------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------- | ------------------------------------------ | ---------------------------------- |
41
+ | `createRequire(url).resolve("<pkg>/package.json")` | yes, since v12.2.0, stable | **the URL you pass** | ✅ npm + pnpm | ❌ `ERR_PACKAGE_PATH_NOT_EXPORTED` |
42
+ | `require.resolve(spec, { paths: [dir] })` | yes, stable | the `paths` array | ✅ npm + pnpm | ❌ same |
43
+ | `import.meta.resolve(spec)` | yes — sync and unflagged since v20.6.0/v18.19.0, Stability **1.2 Release candidate** | **the calling file, always** | ✅ only if the caller sits in the consumer | ❌ same |
44
+ | `import.meta.resolve(spec, parent)` | **flagged** — `--experimental-import-meta-resolve` | the `parent` you pass | — | — |
45
+ | `module.findPackageJSON(spec, base)` | v22.14.0, Stability **1.1 Active development** | the `base` you pass | ✅ npm + pnpm | ✅ **ignores `exports`** |
46
+
47
+ Sources for the version and stability columns, fetched 2026-09-19:
48
+ <https://nodejs.org/docs/latest-v22.x/api/module.html> ·
49
+ <https://nodejs.org/docs/latest-v22.x/api/esm.html>. The Node docs are explicit that the second
50
+ argument of `import.meta.resolve` is the non-standard part:
51
+
52
+ > **v20.6.0, v18.19.0** — No longer behind `--experimental-import-meta-resolve` CLI flag, except
53
+ > for the non-standard `parentURL` parameter
54
+
55
+ ### 🔴 `import.meta.resolve` is the wrong tool here, and it fails SILENTLY
56
+
57
+ `test/e2e/install.mjs` runs from the repository and inspects a consumer somewhere in `os.tmpdir()`.
58
+ `import.meta.resolve` always resolves from **the file that calls it** — `cwd` is irrelevant and
59
+ the `parent` argument is ignored without the flag rather than rejected:
60
+
61
+ ```console
62
+ $ node docs/prior-art/repro/claim4-parent-arg.mjs <WORK>/consumer-npm
63
+ running from: file:///home/user/research-paper-pipeline/docs/prior-art/repro/claim4-parent-arg.mjs
64
+ 1-arg FAIL: ERR_MODULE_NOT_FOUND Cannot find package 'research-paper-pipeline' imported from …/claim4-parent-arg.mjs
65
+ 2-arg FAIL: ERR_MODULE_NOT_FOUND Cannot find package 'research-paper-pipeline' imported from …/claim4-parent-arg.mjs
66
+ require.resolve(..., {paths:[consumer]}) -> <WORK>/consumer-npm/node_modules/research-paper-pipeline/package.json
67
+ createRequire(consumer/x.js).resolve(...) -> <WORK>/consumer-npm/node_modules/research-paper-pipeline/package.json
68
+ ```
69
+
70
+ Note the second line: the parent URL pointed at the consumer, and the error still names the
71
+ _script's own path_. With the flag, the same line answers correctly:
72
+
73
+ ```console
74
+ $ node --experimental-import-meta-resolve docs/prior-art/repro/claim4-parent-arg.mjs <WORK>/consumer-npm
75
+ import.meta.resolve(..., parent=consumer) -> file://<WORK>/consumer-npm/node_modules/research-paper-pipeline/package.json
76
+ ```
77
+
78
+ **A flag that turns a wrong answer into a right one, rather than an error into an answer, is
79
+ exactly the shape this repository keeps writing tests against.** `createRequire` takes its anchor
80
+ as an argument and has no such mode.
81
+
82
+ ### The same probe, proving the anchor is the FILE and not the process
83
+
84
+ `claim4-resolve-apis.mjs` is kept as it was run: it must be **copied into the consumer** and run
85
+ there. Run from the repository instead, every name-based row fails — which is the point:
86
+
87
+ ```console
88
+ $ (cd <WORK>/consumer-npm && node claim4-resolve-apis.mjs) # copied in — as run
89
+ OK createRequire.resolve('<pkg>/package.json')
90
+ -> <WORK>/consumer-npm/node_modules/research-paper-pipeline/package.json
91
+
92
+ $ node docs/prior-art/repro/claim4-resolve-apis.mjs # run from the repo
93
+ FAIL createRequire.resolve('<pkg>/package.json')
94
+ MODULE_NOT_FOUND Cannot find module 'research-paper-pipeline/package.json'
95
+ ```
96
+
97
+ ## 2. Why `<pkg>/package.json` and not the bare name — and it is NOT the usual reason
98
+
99
+ The textbook trap is that `exports` blocks `./package.json`. **For this package it falls the
100
+ other way**, and the bare name is the spelling that fails:
101
+
102
+ ```console
103
+ OK createRequire.resolve('<pkg>/package.json')
104
+ -> …/node_modules/research-paper-pipeline/package.json
105
+ FAIL createRequire.resolve('<pkg>')
106
+ MODULE_NOT_FOUND Cannot find module 'research-paper-pipeline'
107
+ FAIL import.meta.resolve('<pkg>')
108
+ ERR_MODULE_NOT_FOUND Cannot find package '…/node_modules/research-paper-pipeline/index.js'
109
+ ```
110
+
111
+ The cause is in our own manifest, printed by `claim4-package-dir.mjs`:
112
+
113
+ ```console
114
+ manifest.main : undefined
115
+ manifest.exports : undefined
116
+ manifest.bin : {"research-paper-pipeline":"bin/rpp.mjs","rpp":"bin/rpp.mjs"}
117
+ ```
118
+
119
+ No `main`, no `exports`, so the bare specifier falls through to `index.js`, which does not exist.
120
+ `./package.json` is not blocked because there is no map to block it.
121
+
122
+ **The textbook trap is real, though — it is our own dependency.** In the same run:
123
+
124
+ ```console
125
+ FAIL createRequire.resolve('vigiles/package.json')
126
+ ERR_PACKAGE_PATH_NOT_EXPORTED Package subpath './package.json' is not defined by "exports"
127
+ OK createRequire.resolve('vigiles')
128
+ -> …/node_modules/vigiles/dist/test.js
129
+ FAIL createRequire.resolve('vigiles/dist/cli.js')
130
+ ERR_PACKAGE_PATH_NOT_EXPORTED Package subpath './dist/cli.js' is not defined by "exports"
131
+ ```
132
+
133
+ which is precisely the reason `src/cli.ts` (`runHook`, the comment above the injected `resolve`)
134
+ resolves `"vigiles"` and puts `cli.js` beside the result rather than naming the file. That note is
135
+ correct and this run confirms it.
136
+
137
+ 🔴 **`module.findPackageJSON` is the API that does not have this problem.** Same tree, same
138
+ moment, `claim4-find-package-json.mjs`:
139
+
140
+ ```console
141
+ findPackageJSON('research-paper-pipeline', consumer/x.js) -> …/node_modules/research-paper-pipeline/package.json
142
+ findPackageJSON('vigiles', consumer/x.js) -> …/node_modules/vigiles/package.json
143
+ ```
144
+
145
+ It returned `vigiles/package.json` where `require.resolve` threw. The Node docs do not state that
146
+ it bypasses `exports`; this run does. ⚠️ It is Stability 1.1 and Node ≥22.14 only, so it is
147
+ recorded as the right long-term answer and **not** recommended for a gate that must run on the
148
+ `>=22.13` this package declares in `engines`.
149
+
150
+ ## 3. What pnpm and npm actually return, and why it does not matter here
151
+
152
+ ```console
153
+ # npm
154
+ => package dir : <WORK>/consumer-npm/node_modules/research-paper-pipeline
155
+ isSymbolicLink? : false
156
+ SAME as resolved? : true
157
+
158
+ # pnpm
159
+ => package dir : <WORK>/consumer-pnpm/node_modules/.pnpm/research-paper-pipeline@file+…/node_modules/research-paper-pipeline
160
+ literal node_modules/ : <WORK>/consumer-pnpm/node_modules/research-paper-pipeline
161
+ isSymbolicLink? : true -> .pnpm/research-paper-pipeline@file+…/node_modules/research-paper-pipeline
162
+ SAME as resolved? : true
163
+ ```
164
+
165
+ Under pnpm the hardcode addresses the **symlink** and resolution addresses the **store realpath**
166
+ (Node realpaths by default; `--preserve-symlinks` flips it, and `resolve-package-path` carries
167
+ `lib/should-preserve-symlinks.js` for exactly that). `realpathSync` of one equals the other, so
168
+ every `existsSync` / `readdirSync` / `readFileSync` in `test/e2e/install.mjs` gives the same answer
169
+ either way. Two interesting asymmetries, both measured:
170
+
171
+ - `module.findPackageJSON` returns the **symlink** path under pnpm, not the store path — the
172
+ opposite of `createRequire`.
173
+ - `findPackageJSON('vigiles', …)` **fails** under pnpm from the consumer root, because pnpm does
174
+ not hoist transitive dependencies. That is the same fact that broke the hook wiring and is
175
+ recorded in `src/cli.ts`; this is an independent reproduction of it.
176
+
177
+ ## 4. The mutation: what resolution catches that `existsSync` cannot
178
+
179
+ A directory existing and a package being **reachable by name** are different claims. Add a closed
180
+ `exports` map to the installed manifest and the two diverge:
181
+
182
+ ```console
183
+ $ node docs/prior-art/repro/claim5-exports-mutation.mjs <WORK>/consumer-npm
184
+ MUTATION applied: exports = {".":"./bin/rpp.mjs"} (no ./package.json, no ./bin/rpp.mjs)
185
+ 1) the HARDCODE still sees everything:
186
+ dir exists : true
187
+ plugin/hooks/h.json : true
188
+ skills/ : true
189
+ 2) the documented public import is BROKEN:
190
+ import FAILS: ERR_PACKAGE_PATH_NOT_EXPORTED Package subpath './bin/rpp.mjs' is not defined by "exports"
191
+ 3) resolution-based location reports it:
192
+ resolve FAILS: ERR_PACKAGE_PATH_NOT_EXPORTED Package subpath './package.json' is not defined by "exports"
193
+ 4) node:module.findPackageJSON, same mutation:
194
+ findPackageJSON -> /tmp/rpp-mutation-X4jYrD/node_modules/research-paper-pipeline/package.json
195
+ ```
196
+
197
+ `bin/rpp.mjs` documents `import { buildConfig } from "research-paper-pipeline/bin/rpp.mjs"` as a
198
+ public contract. Row 1 is the current test: green. Row 2 is the contract: broken. Row 3 is the
199
+ proposed replacement: red, for the right reason.
200
+
201
+ ⚠️ **Row 4 is the honest caveat on the recommendation.** `findPackageJSON` is the better locator
202
+ and the _worse_ canary: it answers past a broken map. Use `createRequire` for the gate.
203
+
204
+ **This is the whole case for the change.** Without this mutation the swap is cosmetic, and this
205
+ repository's rule is that a working tool is replaced when it is shown not to work, not when it
206
+ looks inelegant.
207
+
208
+ ## 5. The bin — keep launching the shim, and also resolve the real file
209
+
210
+ The recorded reason for not using `node <path>` is exact, and reproduces:
211
+
212
+ ```console
213
+ $ node docs/prior-art/repro/claim5-bin-launch.mjs <WORK>/consumer-pnpm
214
+ <.bin shim> --help rc=0 253ms
215
+ node <.bin shim> --help rc=1 46ms …/node_modules/.bin/rpp:2 basedir=$(dirname …)
216
+ node <manifest bin> --help rc=0 244ms
217
+ npx --no-install rpp --help rc=0 524ms
218
+ ```
219
+
220
+ Under npm the shim is a symlink (`.bin/rpp -> ../research-paper-pipeline/bin/rpp.mjs`); under
221
+ pnpm it is a `#!/bin/sh` wrapper, and `node` chokes on its second line. But the `bin` value in the
222
+ resolved manifest is the **real file under both**, so `node <that>` works everywhere.
223
+
224
+ | way to get the bin | uniform across npm/pnpm? | cost | what it proves |
225
+ | ---------------------------------------------------- | ------------------------------------------------------ | -------------------------------- | ---------------------------------------------- |
226
+ | `join(consumer, "node_modules/.bin", "rpp")` — today | yes (both managers write it) | none | **the manager created the shim** |
227
+ | `join(installed, manifest.bin.rpp)` | yes | one JSON read already being done | the file the manifest promises exists and runs |
228
+ | `npx --no-install <name>` | yes | ~0.5 s, one process | resolution by name through npm |
229
+ | `npm exec` / `pnpm exec` / `yarn bin` | per-manager commands | ~0.3–0.5 s each | nothing extra |
230
+ | `npm bin` | **gone** — `Unknown command: "bin"` on npm 10.9.7 | — | — |
231
+ | `npm ls <name> --parseable` | prints the path but exits `ELSPROBLEMS` on a pnpm tree | one process | npm's view only |
232
+
233
+ So the shim is not a guess to be removed; it is the only row that observes the manager's own
234
+ work. **What the manifest buys is the second row, free**, plus independence from the bin's _name_
235
+ (`rpp` versus `research-paper-pipeline`, and `.cmd` on Windows — a suffix `netlify/cli` has to
236
+ spell out and this script would not).
237
+
238
+ ## 6. Named prior art
239
+
240
+ ### Does exactly this
241
+
242
+ | project | file | mechanism |
243
+ | ------------------------------ | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
244
+ | **Playwright** | `playwright/lib/util.js:73` | `path.dirname(require.resolve("playwright-core/package.json"))` — the literal recommendation, in a shipped product |
245
+ | **ESLint** | `lib/shared/relative-module-resolver.js:24` | `createRequire(relativeToPath).resolve(moduleName)`, documented as _"This must be a file rather than a directory, but the file need not actually exist"_; the caller is `getPlaceholderPath(cwd) => path.join(cwd, "__placeholder__.js")` (`lib/eslint/eslint-helpers.js:648`) |
246
+ | **resolve-pkg 3.0.1** | `index.js:24–51` | the three-tier ladder: `resolve("<pkg>/package.json")`, else `resolve("<pkg>")` and walk **up** until the directory ends with `node_modules/<pkg>` or its manifest `name` matches, else `findUpSync("node_modules/<pkg>/package.json")`. The ladder exists _because_ tier 1 dies on a closed `exports` map |
247
+ | **resolve-from 4.0.0** | `index.js:27` | the same placeholder trick, spelled `noop.js`, via `Module._resolveFilename` |
248
+ | **lint-staged 17.5.1** | `lib/resolveConfig.js:7` | `createRequire(import.meta.url)` with the comment _"require() does not exist for ESM, so we must create it to use require.resolve()"_ |
249
+ | **resolve-package-path 4.0.3** | `lib/index.js:93` | `pnp ? pnp.resolveToUnqualified(target + "/package.json", baseDir) : …` — the only one of these that handles PnP, and it does so by `require("pnpapi")`, which only exists inside a PnP process |
250
+
251
+ ### Adjacent — finds _a_ package root, not _that_ package
252
+
253
+ `pkg-dir` (4.2.0 and 8.0.0) and `read-package-up` are both one line of `findUp("package.json")`
254
+ plus `dirname`. They answer "which package am I inside", walking **up** from a directory. The
255
+ question here is "where did package X land", which walks **down** a resolution path. Different
256
+ question; neither is a candidate.
257
+
258
+ ### Solves it by not asking — worth more than the rest
259
+
260
+ **husky 9.1.7**, `index.js:21`:
261
+
262
+ ```js
263
+ f.copyFileSync(new URL("husky", import.meta.url), _("h"));
264
+ ```
265
+
266
+ husky's git hooks never address `node_modules/husky/…`. On install it **copies its runtime out of
267
+ the package** into `.husky/_/h` and points `core.hooksPath` there, so the hook resolves nothing at
268
+ run time. That is the structural answer to the same problem `plugin/hooks/hooks.json` has, and it
269
+ is the reason husky works under PnP while our hook wiring does not.
270
+
271
+ ### Self-location from INSIDE is not the hard case
272
+
273
+ `new URL("…", import.meta.url)` — husky above, `lint-staged/lib/version.js:4`, and our own
274
+ `bin/rpp.mjs:27` (`new URL("../dist/cli.js", import.meta.url)`). No resolution needed. The hard
275
+ case is only the one this script has: locating a package from a **third-party observer process**.
276
+
277
+ ## 7. What other packages' own install e2e tests do — they hardcode
278
+
279
+ This is the closest analogue and the answer is plain: **they all hardcode**, and none of them
280
+ resolve.
281
+
282
+ - **netlify/cli**, `e2e/install.e2e.ts:256` — publishes to a local registry, installs under npm,
283
+ pnpm and yarn (classic), then:
284
+ ```ts
285
+ const binary = path.resolve(
286
+ path.join(
287
+ cwd,
288
+ `./node_modules/.bin/netlify${platform() === "win32" ? ".cmd" : ""}`,
289
+ ),
290
+ );
291
+ ```
292
+ Same three-manager matrix as `WANTED`, same `.bin` hardcode, plus the Windows suffix.
293
+ <https://github.com/netlify/cli/blob/main/e2e/install.e2e.ts>
294
+ - **nestjs/terminus**, `tools/pack-check.mjs:32` — `npm pack` → install into `mkdtempSync` →
295
+ ```js
296
+ const installed = join(dir, "node_modules", "@nestjs", "terminus");
297
+ ```
298
+ and it verifies the public surface by running `node --input-type=module -e "import …"` **with
299
+ `cwd` set to the consumer**, letting Node resolve. That is the resolution check recommended in
300
+ § 4, arrived at from the other direction.
301
+ <https://github.com/nestjs/terminus/blob/master/tools/pack-check.mjs>
302
+ - **npm/cli** smoke tests, `smoke-tests/test/fixtures/setup.js:22` —
303
+ `const GLOBAL_NODE_MODULES = join(WINDOWS ? '' : 'lib', 'node_modules')`.
304
+ <https://github.com/npm/cli/blob/latest/smoke-tests/test/fixtures/setup.js>
305
+ - A GitHub code search for `"npm pack" "node_modules" "tmpdir" extension:mjs` returns **4 552**
306
+ files; every one sampled builds the path with `join(tmp, "node_modules", …)`.
307
+
308
+ **So the hardcode is the industry norm and is not, by itself, a defect.** The case for changing it
309
+ rests entirely on § 4, not on this section.
310
+
311
+ ## 8. Correcting the PnP note in `test-tooling.md`
312
+
313
+ [`test-tooling.md`](test-tooling.md) § "One known limit" says:
314
+
315
+ > **Yarn Berry (PnP) cannot be supported as things stand.** `plugin/hooks/hooks.json` addresses
316
+ > `${CLAUDE_PROJECT_DIR}/node_modules/research-paper-pipeline/bin/rpp.mjs` literally, and PnP has
317
+ > no `node_modules` directory at all. Adding a third `managers()` row for it would be a red test,
318
+ > not a feature.
319
+
320
+ **The conclusion holds. The reason given is incomplete in three ways**, all measured by
321
+ `claim4-yarn-pnp.mjs`:
322
+
323
+ ```console
324
+ $ node docs/prior-art/repro/claim4-yarn-pnp.mjs
325
+ yarn add: ok
326
+ node_modules present? NO
327
+ 1) plain node, inside the PnP project (no PnP runtime):
328
+ resolve FAILS: MODULE_NOT_FOUND Cannot find module 'research-paper-pipeline/package.json'
329
+ 2) yarn node (PnP runtime loaded):
330
+ resolve(pkg/package.json) -> …/.yarn/__virtual__/…/.yarn/berry/cache/research-paper-pipeline-file-919de88475-10c0.zip/node_modules/research-paper-pipeline/package.json
331
+ 3) can the OUTSIDE process read the path PnP handed back?
332
+ plain node existsSync: false
333
+ yarn node existsSync: true
334
+ 4) `yarn bin rpp`:
335
+ …/cache/research-paper-pipeline-file-919de88475-10c0.zip/node_modules/research-paper-pipeline/bin/rpp.mjs
336
+ 5) does the CLI itself run under PnP?
337
+ research-paper-pipeline — machine-checkable gates for a paper kept in git
338
+ ```
339
+
340
+ 1. 🔴 **The package is not PnP-incompatible — line 5 runs the CLI under PnP and it answers.** The
341
+ note reads as if the package could not work there; what cannot work is one wiring file.
342
+ 2. 🔴 **The blocker the note names is the MOVABLE one.** Claude Code exposes
343
+ `${CLAUDE_PLUGIN_ROOT}` — _"Absolute path to the plugin's installation directory … Use it for
344
+ scripts, binaries, and config files bundled with the plugin"_, exported to hook processes
345
+ (<https://code.claude.com/docs/en/plugins-reference>). Rewriting the hook command with it
346
+ removes the literal `node_modules` from `hooks.json`. Whether that is _desirable_ is a separate
347
+ question — the variable is only set when the package is loaded through the plugin channel, and
348
+ today the npm channel is the one that delivers the runnable code — but it is not impossible.
349
+ 3. 🔴 **The blocker the note does NOT name is the immovable one, and no resolution API fixes it.**
350
+ PnP resolves to a path **inside a zip**. Line 3: the outside process gets `existsSync: false`
351
+ for the very path PnP just handed it; only PnP's patched `fs` can open it. `test/e2e/install.mjs`
352
+ inspects the installed tree with `existsSync`, `readdirSync` and `readFileSync` **from its own
353
+ process**. Under PnP those read nothing, whatever API produced the path — and `yarn bin rpp`
354
+ (line 4) hands back the same unreadable path, so the supported bin lookup does not help either.
355
+
356
+ **Supporting PnP therefore means re-executing the inspection half of the e2e under `yarn node`**,
357
+ not swapping a path for a resolve call. That is a real piece of work with a real payoff question,
358
+ and it should be recorded as such rather than as "PnP has no node_modules".
359
+
360
+ ## 9. The second guess: the base directory for a path written in prose
361
+
362
+ `contentDelivery()` (in `test/e2e/install.mjs`) tries three bases per reference.
363
+ Measured over the real corpus with `claim6-doc-path-candidates.mjs`:
364
+
365
+ ```console
366
+ skills : 24
367
+ distinct raw spellings: .claude/skills/paper-pipeline/scripts/announce.mjs
368
+ …
369
+ scripts/pipeline-check.mjs
370
+ ../verify-citations/scripts/verify-cites.mjs
371
+ which candidate resolved: {"c1":0,"c2":15,"c3":89,"none":0}
372
+ ```
373
+
374
+ and `claim6-candidate-ambiguity.mjs`:
375
+
376
+ ```console
377
+ total refs 104 ambiguous 0 {"c3":89,"c2":15}
378
+ ```
379
+
380
+ - **Candidate 1 (`join(installed, raw)`) is dead: 0 of 104.** No skill writes a package-root
381
+ relative path, and none has since the corpus existed.
382
+ - **Candidates 2 and 3 are disjoint by spelling**, zero references resolve under both. So the
383
+ `some()` is not currently masking a wrong-base reference — an honest negative.
384
+ - The corpus carries **two conventions at once**: project-root-relative
385
+ (`.claude/skills/paper-pipeline/scripts/x.mjs`, 89 refs) and skill-relative (`scripts/x.mjs`
386
+ and `../other-skill/scripts/x.mjs`, 15 refs).
387
+
388
+ ### Prior art for validating file references inside markdown
389
+
390
+ There is essentially one pattern, and it is not a ladder of candidates.
391
+
392
+ - **remark-validate-links 13.1.0** resolves every link against the **file's own directory** —
393
+ `lib/index.js:282`, `base: absolute ? path.dirname(absolute) : file.cwd` — and takes the one
394
+ thing it cannot derive as an explicit option: `root` _("path to Git root folder; if both `root`
395
+ and `repository` are nullish, the Git root is detected")_. Detection is a single documented
396
+ fallback with a stated failure mode, not a list of guesses.
397
+ <https://github.com/remarkjs/remark-validate-links>
398
+ - **Claude Code plugins** declare it: _"All paths must be relative to the plugin root and start
399
+ with `./`"_, plus `${CLAUDE_PLUGIN_ROOT}` for scripts.
400
+ <https://code.claude.com/docs/en/plugins-reference>
401
+ - **This package already declares it once** — `.claude-plugin/plugin.json`:
402
+ `"skills": "./skills/"`.
403
+
404
+ **What declaring it would look like here:** one convention for the prose — skill-relative, since
405
+ that is what survives both channels — and the test resolving against exactly one base, with
406
+ candidate 1 deleted and candidate 3 kept only as an explicitly named legacy spelling with its own
407
+ counter, so that "89 references still use the retired spelling" is a number the run prints rather
408
+ than a fact it hides behind an `||`. That is the same shape as the `managers()` rewrite: a
409
+ declared skip beats a silent one.
410
+
411
+ ---
412
+
413
+ ## Status
414
+
415
+ Written 2026-09-19. Every table cell above is either a pasted run from [`repro/`](repro/README.md)
416
+ or a quotation from the URL named beside it. Nothing here is recalled.
417
+
418
+ **What is NOT measured, and so not claimed:** behaviour on Windows (the `.cmd` shim, the pnpm
419
+ `.ps1`/`.cmd` wrappers); npm workspaces, where a hoisted install would put the package at the
420
+ workspace root rather than in the consumer — the e2e builds a standalone temp project, so this is
421
+ untested and merely plausible; yarn _classic_, which has a `node_modules` layout and is one
422
+ `managers()` row away but was not run.