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,182 @@
1
+ ---
2
+ name: plan-paper-timeline
3
+ description: Turn a venue's CFP dates into a scheduled, buffered plan on the Google Calendar — build the timeline backwards from the AoE submission deadline (harden + panel-review + artifact + anonymized host all land BEFORE it), create calendar events in the author's own time zone for submit-day / deadline / camera-ready, and apply the FILING-SPACING rule so multiple papers don't cluster in the month before a planned external filing. Use right after find-venue hands you real deadlines and before you start drafting. Compose with find-venue (deadline source) and submit-paper.
4
+ allowed-tools: [Read, Write, Grep, Glob, mcp__Google_Calendar__list_events, mcp__Google_Calendar__search_events, mcp__Google_Calendar__create_event, mcp__Google_Calendar__update_event, Bash(node .claude/skills/paper-pipeline/scripts/announce.mjs:*), Bash(node .claude/skills/paper-pipeline/scripts/ledger.mjs:*)]
5
+ ---
6
+
7
+ <!-- vigiles:sha256:9009b62e211cf421 compiled from skills/plan-paper-timeline/SKILL.md.spec.ts -->
8
+
9
+ # plan-paper-timeline — CFP dates → a buffered, filing-aware schedule
10
+
11
+ ## Run me
12
+
13
+ 🔴 FIRST, before any other step:
14
+
15
+ ```
16
+ node .claude/skills/paper-pipeline/scripts/announce.mjs plan-paper-timeline <paper-dir>
17
+ ```
18
+
19
+ An advisory pass cannot be observed failing — silence is both its error state and its normal
20
+ state — so starting is an event, and events get written down.
21
+
22
+ A deadline is not a plan. This skill converts the four dates every CFP gives you — **submission (AoE),
23
+ notification, camera-ready, event** — into calendar events with the buffers that actually keep a
24
+ submission from slipping, and sequences *multiple* papers so the cadence reads as sustained acclaim,
25
+ not a pre-filing burst.
26
+
27
+ ## 0. 🔴 CAN YOU PHYSICALLY SUBMIT? — do this in the FIRST week (scorecard row `access`)
28
+
29
+ **This skill owns the question no other unit of work owned**, and it is the one that bites. It is not
30
+ about the paper. It is about whether, on submit-day, there is anywhere to put it. Everything below in
31
+ this skill schedules *work*; this schedules *access*, and access has a lead time you do not control.
32
+
33
+ *Added 2026-08-03, after this was the sole critical-path item on a finished paper at **T−4 days**: the
34
+ science was done, the artifact was built, and the account could not submit.*
35
+
36
+ Concrete steps, all of them today, none of them deferrable:
37
+
38
+ 1. **Create the portal account** (OpenReview / HotCRP / EasyChair / the venue's own). Not "find the
39
+ URL" — create the account.
40
+ 2. **Drive the profile to ACTIVE.** 🔴 *"Pending moderation" is not an account you can submit from.*
41
+ Fill in every field moderation looks at — full name, affiliation history with dates, an ORCID or
42
+ DBLP/homepage link, and prior publications if the portal imports them; a sparse profile is what
43
+ gets queued rather than approved. Then **check back and confirm the state flipped**, and
44
+ **record the ISO date it went ACTIVE in the `access` row**, not just a tick.
45
+ 3. **Confirm the form's fields** — open the actual submission form and read it. Title, abstract
46
+ (character cap?), track/type selector, subject areas, conflicts-of-interest, mandatory
47
+ declarations (GenAI/LLM use, ethics, dual-use), authors' order, and whether upload alone counts as
48
+ submitted or a separate **"ready for review"** click does.
49
+ 4. **Confirm whether a supplementary-upload field EXISTS.** If it does not, the artifact needs an
50
+ **external anonymous host** (OSF view-only link / 4open) plus a `\url{}` in Availability — and that
51
+ host has to be minted before submit-day, not on it. This is a real branch in the plan, not a detail.
52
+ 5. **Re-check as the deadline approaches.** External state rots without telling you: a profile can be
53
+ flagged, a portal can open late or change its form. The CONTINUOUS trigger on the `access` row exists
54
+ for exactly this.
55
+
56
+ **The lead time, stated plainly:** portal moderation runs **up to two weeks**, and an author with **no
57
+ institutional email has no expedite route** — no support queue jump, no "I'm on a deadline" exception.
58
+ That is why `access` is scheduled in the first week rather than the last: it is the one item where the
59
+ only remedy for being late is having started earlier.
60
+
61
+ `pipeline-check.mjs` reports an `access` row still `☐` inside the moderation window as a finding, and
62
+ `submit-paper` is hard-blocked on `access` being green.
63
+
64
+ ## 1. Build the timeline backwards from the AoE deadline
65
+ Everything that must be done *before* you can submit gets its own slot ending before the deadline —
66
+ never "the day of." Working back from the submission AoE date, reserve, in this order:
67
+
68
+ - **Draft frozen** — prose done, `verify-citations` clean. Earliest.
69
+ - **Harden** — `paper-adversarial-review` → `pc-panel-review` venue-fit mode, fix blockers, re-render.
70
+ Loops; give it room.
71
+ - **Panel review** — `pc-panel-review` (multi-reviewer + artifact-runner) as the final gate. 1–2 days.
72
+ - **Artifact built + hosted anonymously** — build the self-checking artifact and mint the OSF/4open
73
+ anonymized link (see `submit-paper` + `paper-pipeline/references/artifact-checklist.md`). This step
74
+ reliably eats more time than expected; put it before, not on, submit-day.
75
+ - **Submit-day = one day before AoE.** The real submission happens here, with slack for a HotCRP
76
+ surprise (no supplementary field, a re-paste, a "ready for review" step you forgot). Never plan to
77
+ submit on the deadline itself.
78
+
79
+ Rule of thumb: if the CFP is <2 weeks out, the artifact + panel review are what you cut scope on, not
80
+ the claim-sizing.
81
+
82
+ ## 2. The AoE nuance (don't lose a day to it)
83
+ CFP deadline dates are **Anywhere-on-Earth (UTC−12)**. A deadline written "15 Jul AoE" does *not* expire
84
+ at your local midnight on the 15th — it expires when the last spot on Earth leaves the 15th:
85
+
86
+ ```
87
+ 15 Jul 23:59 AoE = 16 Jul 11:59 UTC ≈ 16 Jul ~17:00 local, in a UTC+5 zone
88
+ ```
89
+
90
+ So the calendar date on the CFP is effectively the **next day at home**. This is slack, not a plan —
91
+ treat submit-day as the day before the *printed* date regardless, and let the AoE hours be your safety
92
+ margin, not your runway.
93
+
94
+ ## 3. Create the calendar events (in the author's own zone)
95
+ Use `mcp__Google_Calendar__create_event` for three anchors per paper. 🔴 **Read the zone, do not
96
+ assume it** — it is the `timezone` carrier, and it is the author's, not the pipeline's:
97
+
98
+ ```
99
+ node -p "((p) => p.paperlint ?? p['research-paper-pipeline'])(require('./package.json'))?.timezone ?? 'UTC'"
100
+ ```
101
+
102
+ The example below shows the default, `UTC`; substitute whatever that command prints.
103
+ Title them so the venue and the phase are unmistakable at a glance:
104
+
105
+ - **`<Venue> — SUBMIT`** on submit-day (one day before the printed AoE date). This is the one you act on.
106
+ - **`<Venue> — deadline (AoE)`** on the printed date, as a hard backstop.
107
+ - **`<Venue> — camera-ready`** on the camera-ready date (from the CFP), so acceptance doesn't ambush you
108
+ in a busy week.
109
+
110
+ `summary`, `startTime` **and `endTime`** are all required — the API rejects the call without the end
111
+ time, and a deadline anchor has no natural duration, so give it an hour and move on. Exact shape:
112
+
113
+ <!-- harness:prescribed-call create_event -->
114
+ ```json
115
+ {
116
+ "summary": "AISec 2026 — SUBMIT",
117
+ "startTime": "2026-07-23T10:00:00",
118
+ "endTime": "2026-07-23T11:00:00",
119
+ "timeZone": "UTC"
120
+ }
121
+ ```
122
+
123
+ Optionally add **notification** and **event/workshop** days as markers with `"allDay": true` — which
124
+ does not excuse you from `startTime`/`endTime`; they are still required and are read as midnight. Keep
125
+ the two review/host milestones (§1) as your own working reminders; the three above are the immovable ones.
126
+
127
+ > The block above is not decoration. `plan-paper-timeline.effects.harness.mjs` parses it out of this
128
+ > file and replays it against a stand-in server carrying the REAL API's schema, so a prescription this
129
+ > section gets wrong fails in the harness rather than on a live run. Edit it like code.
130
+
131
+ ## 4. FILING-SPACING rule (the reason this skill exists)
132
+ Where the papers are the authorship pillar of some external filing — a grant, a tenure packet, an
133
+ immigration petition — **do not let multiple submissions or acceptances cluster in the month before
134
+ that filing.** A burst of activity right before a deadline reads as *manufactured* rather than
135
+ *sustained*, and that is a documented reason for refusal, not a stylistic worry. A steady cadence with
136
+ recent-but-not-crammed activity reads far better.
137
+
138
+ When scheduling paper N against papers already on the calendar and a known/likely filing date:
139
+ - Spread submission and (projected) notification dates across months; avoid two acceptances landing in
140
+ the same pre-filing window.
141
+ - If two CFPs force a cluster, stagger which one you *extend* later (workshop → conference) so the
142
+ second, stronger publication lands in a later month on purpose.
143
+ - Prefer a paper with an earlier, safely-past deadline over one that would pile onto the filing month.
144
+
145
+ ## 5. Record the verdict
146
+
147
+ 🔴 LAST step, once the deliverable exists:
148
+
149
+ ```
150
+ node .claude/skills/paper-pipeline/scripts/ledger.mjs record plan-paper-timeline <paper-dir> FINDING <count> <report-path>
151
+ node .claude/skills/paper-pipeline/scripts/ledger.mjs record plan-paper-timeline <paper-dir> ABSTAINED <reason> "<one line>"
152
+ ```
153
+
154
+ **FINDING** — `<count>` slots have no buffer, or collide with another paper's pre-filing window;
155
+ `<report-path>` is the plan. Add `--blocking` when the deadline cannot be met from here.
156
+ **ABSTAINED** — `no-witness`: the backwards plan is on the calendar, `access` is green and no slot
157
+ is tight. `input-missing`: `find-venue` has produced no dated deadline to plan against.
158
+
159
+ This is the one skill whose blocking finding is a fact about the calendar rather than about the
160
+ paper: portal moderation runs up to two weeks and an author with no institutional email has no
161
+ expedite route, so one recorded today is still true tomorrow no matter how good the science gets.
162
+
163
+ 🔴 **There is no PASS**, so "the plan is on the calendar" is not storable here. Put the event ids in
164
+ the `ABSTAINED` note — markdown is the source of truth and the calendar is its projection, and a row
165
+ with neither is a claim about a calendar nobody opened.
166
+
167
+ ## Scorecard rows this skill owns
168
+ Two, and they fail differently — see `paper-pipeline/references/pipeline-status-template.md`:
169
+ - **`access`** — *can you physically submit?* (§0). Account · profile ACTIVE since `<ISO>` · portal
170
+ reachable · form fields known · artifact-host requirement known. `submit-paper` is blocked on it.
171
+ - **`schedule`** — the schedule itself (§1–§4): the buffered backwards plan and its calendar events.
172
+
173
+ ## Compose with
174
+ - **`find-venue`** — the source of the four CFP dates this skill consumes; run it first.
175
+ - **`submit-paper`** (+ the venue subskill) — what submit-day actually executes; this skill just makes
176
+ sure the runway before it exists, **and that the account it executes through is already ACTIVE**.
177
+
178
+ ## Provenance
179
+ This session scheduled two real papers with spacing rather than back-to-back: **AgenticDev 2026 @ ASE**
180
+ (submission 15 Jul, notify 21 Aug, camera-ready 28 Aug, workshop 12 Oct) and **AISec 2026 @ ACM CCS**
181
+ (submission 24 Jul). Nine days apart, deliberately staggered so notifications and any camera-ready work
182
+ don't collapse into one pre-filing month — the FILING-SPACING rule applied in practice, not in theory.
@@ -0,0 +1,200 @@
1
+ // Compiled to SKILL.md by `vigiles compile`. Edit THIS file, never the markdown.
2
+ //
3
+ // Adopted 2026-08-17 (batch 3). Body carried over VERBATIM so the compiled diff shows
4
+ // only what the compiler adds. No `disallowedTools` fence yet — the field landed on
5
+ // `SkillSpec` in vigiles branch `claude/skill-disallowed-tools` and is not in a release
6
+ // this repo installs, so writing one here would not compile.
7
+ import { experimental_skill } from "vigiles/spec";
8
+
9
+ export default experimental_skill({
10
+ name: "plan-paper-timeline",
11
+ description:
12
+ "Turn a venue's CFP dates into a scheduled, buffered plan on the Google Calendar — build the timeline backwards from the AoE submission deadline (harden + panel-review + artifact + anonymized host all land BEFORE it), create calendar events in the author's own time zone for submit-day / deadline / camera-ready, and apply the FILING-SPACING rule so multiple papers don't cluster in the month before a planned external filing. Use right after find-venue hands you real deadlines and before you start drafting. Compose with find-venue (deadline source) and submit-paper.",
13
+ tools: [
14
+ "Read",
15
+ "Write",
16
+ "Grep",
17
+ "Glob",
18
+ "mcp__Google_Calendar__list_events",
19
+ "mcp__Google_Calendar__search_events",
20
+ "mcp__Google_Calendar__create_event",
21
+ "mcp__Google_Calendar__update_event",
22
+ "Bash(node .claude/skills/paper-pipeline/scripts/announce.mjs:*)",
23
+ "Bash(node .claude/skills/paper-pipeline/scripts/ledger.mjs:*)",
24
+ ],
25
+ body: `
26
+ # plan-paper-timeline — CFP dates → a buffered, filing-aware schedule
27
+
28
+ ## Run me
29
+
30
+ 🔴 FIRST, before any other step:
31
+
32
+ \`\`\`
33
+ node .claude/skills/paper-pipeline/scripts/announce.mjs plan-paper-timeline <paper-dir>
34
+ \`\`\`
35
+
36
+ An advisory pass cannot be observed failing — silence is both its error state and its normal
37
+ state — so starting is an event, and events get written down.
38
+
39
+ A deadline is not a plan. This skill converts the four dates every CFP gives you — **submission (AoE),
40
+ notification, camera-ready, event** — into calendar events with the buffers that actually keep a
41
+ submission from slipping, and sequences *multiple* papers so the cadence reads as sustained acclaim,
42
+ not a pre-filing burst.
43
+
44
+ ## 0. 🔴 CAN YOU PHYSICALLY SUBMIT? — do this in the FIRST week (scorecard row \`access\`)
45
+
46
+ **This skill owns the question no other unit of work owned**, and it is the one that bites. It is not
47
+ about the paper. It is about whether, on submit-day, there is anywhere to put it. Everything below in
48
+ this skill schedules *work*; this schedules *access*, and access has a lead time you do not control.
49
+
50
+ *Added 2026-08-03, after this was the sole critical-path item on a finished paper at **T−4 days**: the
51
+ science was done, the artifact was built, and the account could not submit.*
52
+
53
+ Concrete steps, all of them today, none of them deferrable:
54
+
55
+ 1. **Create the portal account** (OpenReview / HotCRP / EasyChair / the venue's own). Not "find the
56
+ URL" — create the account.
57
+ 2. **Drive the profile to ACTIVE.** 🔴 *"Pending moderation" is not an account you can submit from.*
58
+ Fill in every field moderation looks at — full name, affiliation history with dates, an ORCID or
59
+ DBLP/homepage link, and prior publications if the portal imports them; a sparse profile is what
60
+ gets queued rather than approved. Then **check back and confirm the state flipped**, and
61
+ **record the ISO date it went ACTIVE in the \`access\` row**, not just a tick.
62
+ 3. **Confirm the form's fields** — open the actual submission form and read it. Title, abstract
63
+ (character cap?), track/type selector, subject areas, conflicts-of-interest, mandatory
64
+ declarations (GenAI/LLM use, ethics, dual-use), authors' order, and whether upload alone counts as
65
+ submitted or a separate **"ready for review"** click does.
66
+ 4. **Confirm whether a supplementary-upload field EXISTS.** If it does not, the artifact needs an
67
+ **external anonymous host** (OSF view-only link / 4open) plus a \`\\url{}\` in Availability — and that
68
+ host has to be minted before submit-day, not on it. This is a real branch in the plan, not a detail.
69
+ 5. **Re-check as the deadline approaches.** External state rots without telling you: a profile can be
70
+ flagged, a portal can open late or change its form. The CONTINUOUS trigger on the \`access\` row exists
71
+ for exactly this.
72
+
73
+ **The lead time, stated plainly:** portal moderation runs **up to two weeks**, and an author with **no
74
+ institutional email has no expedite route** — no support queue jump, no "I'm on a deadline" exception.
75
+ That is why \`access\` is scheduled in the first week rather than the last: it is the one item where the
76
+ only remedy for being late is having started earlier.
77
+
78
+ \`pipeline-check.mjs\` reports an \`access\` row still \`☐\` inside the moderation window as a finding, and
79
+ \`submit-paper\` is hard-blocked on \`access\` being green.
80
+
81
+ ## 1. Build the timeline backwards from the AoE deadline
82
+ Everything that must be done *before* you can submit gets its own slot ending before the deadline —
83
+ never "the day of." Working back from the submission AoE date, reserve, in this order:
84
+
85
+ - **Draft frozen** — prose done, \`verify-citations\` clean. Earliest.
86
+ - **Harden** — \`paper-adversarial-review\` → \`pc-panel-review\` venue-fit mode, fix blockers, re-render.
87
+ Loops; give it room.
88
+ - **Panel review** — \`pc-panel-review\` (multi-reviewer + artifact-runner) as the final gate. 1–2 days.
89
+ - **Artifact built + hosted anonymously** — build the self-checking artifact and mint the OSF/4open
90
+ anonymized link (see \`submit-paper\` + \`paper-pipeline/references/artifact-checklist.md\`). This step
91
+ reliably eats more time than expected; put it before, not on, submit-day.
92
+ - **Submit-day = one day before AoE.** The real submission happens here, with slack for a HotCRP
93
+ surprise (no supplementary field, a re-paste, a "ready for review" step you forgot). Never plan to
94
+ submit on the deadline itself.
95
+
96
+ Rule of thumb: if the CFP is <2 weeks out, the artifact + panel review are what you cut scope on, not
97
+ the claim-sizing.
98
+
99
+ ## 2. The AoE nuance (don't lose a day to it)
100
+ CFP deadline dates are **Anywhere-on-Earth (UTC−12)**. A deadline written "15 Jul AoE" does *not* expire
101
+ at your local midnight on the 15th — it expires when the last spot on Earth leaves the 15th:
102
+
103
+ \`\`\`
104
+ 15 Jul 23:59 AoE = 16 Jul 11:59 UTC ≈ 16 Jul ~17:00 local, in a UTC+5 zone
105
+ \`\`\`
106
+
107
+ So the calendar date on the CFP is effectively the **next day at home**. This is slack, not a plan —
108
+ treat submit-day as the day before the *printed* date regardless, and let the AoE hours be your safety
109
+ margin, not your runway.
110
+
111
+ ## 3. Create the calendar events (in the author's own zone)
112
+ Use \`mcp__Google_Calendar__create_event\` for three anchors per paper. 🔴 **Read the zone, do not
113
+ assume it** — it is the \`timezone\` carrier, and it is the author's, not the pipeline's:
114
+
115
+ \`\`\`
116
+ node -p "((p) => p.paperlint ?? p['research-paper-pipeline'])(require('./package.json'))?.timezone ?? 'UTC'"
117
+ \`\`\`
118
+
119
+ The example below shows the default, \`UTC\`; substitute whatever that command prints.
120
+ Title them so the venue and the phase are unmistakable at a glance:
121
+
122
+ - **\`<Venue> — SUBMIT\`** on submit-day (one day before the printed AoE date). This is the one you act on.
123
+ - **\`<Venue> — deadline (AoE)\`** on the printed date, as a hard backstop.
124
+ - **\`<Venue> — camera-ready\`** on the camera-ready date (from the CFP), so acceptance doesn't ambush you
125
+ in a busy week.
126
+
127
+ \`summary\`, \`startTime\` **and \`endTime\`** are all required — the API rejects the call without the end
128
+ time, and a deadline anchor has no natural duration, so give it an hour and move on. Exact shape:
129
+
130
+ <!-- harness:prescribed-call create_event -->
131
+ \`\`\`json
132
+ {
133
+ "summary": "AISec 2026 — SUBMIT",
134
+ "startTime": "2026-07-23T10:00:00",
135
+ "endTime": "2026-07-23T11:00:00",
136
+ "timeZone": "UTC"
137
+ }
138
+ \`\`\`
139
+
140
+ Optionally add **notification** and **event/workshop** days as markers with \`"allDay": true\` — which
141
+ does not excuse you from \`startTime\`/\`endTime\`; they are still required and are read as midnight. Keep
142
+ the two review/host milestones (§1) as your own working reminders; the three above are the immovable ones.
143
+
144
+ > The block above is not decoration. \`plan-paper-timeline.effects.harness.mjs\` parses it out of this
145
+ > file and replays it against a stand-in server carrying the REAL API's schema, so a prescription this
146
+ > section gets wrong fails in the harness rather than on a live run. Edit it like code.
147
+
148
+ ## 4. FILING-SPACING rule (the reason this skill exists)
149
+ Where the papers are the authorship pillar of some external filing — a grant, a tenure packet, an
150
+ immigration petition — **do not let multiple submissions or acceptances cluster in the month before
151
+ that filing.** A burst of activity right before a deadline reads as *manufactured* rather than
152
+ *sustained*, and that is a documented reason for refusal, not a stylistic worry. A steady cadence with
153
+ recent-but-not-crammed activity reads far better.
154
+
155
+ When scheduling paper N against papers already on the calendar and a known/likely filing date:
156
+ - Spread submission and (projected) notification dates across months; avoid two acceptances landing in
157
+ the same pre-filing window.
158
+ - If two CFPs force a cluster, stagger which one you *extend* later (workshop → conference) so the
159
+ second, stronger publication lands in a later month on purpose.
160
+ - Prefer a paper with an earlier, safely-past deadline over one that would pile onto the filing month.
161
+
162
+ ## 5. Record the verdict
163
+
164
+ 🔴 LAST step, once the deliverable exists:
165
+
166
+ \`\`\`
167
+ node .claude/skills/paper-pipeline/scripts/ledger.mjs record plan-paper-timeline <paper-dir> FINDING <count> <report-path>
168
+ node .claude/skills/paper-pipeline/scripts/ledger.mjs record plan-paper-timeline <paper-dir> ABSTAINED <reason> "<one line>"
169
+ \`\`\`
170
+
171
+ **FINDING** — \`<count>\` slots have no buffer, or collide with another paper's pre-filing window;
172
+ \`<report-path>\` is the plan. Add \`--blocking\` when the deadline cannot be met from here.
173
+ **ABSTAINED** — \`no-witness\`: the backwards plan is on the calendar, \`access\` is green and no slot
174
+ is tight. \`input-missing\`: \`find-venue\` has produced no dated deadline to plan against.
175
+
176
+ This is the one skill whose blocking finding is a fact about the calendar rather than about the
177
+ paper: portal moderation runs up to two weeks and an author with no institutional email has no
178
+ expedite route, so one recorded today is still true tomorrow no matter how good the science gets.
179
+
180
+ 🔴 **There is no PASS**, so "the plan is on the calendar" is not storable here. Put the event ids in
181
+ the \`ABSTAINED\` note — markdown is the source of truth and the calendar is its projection, and a row
182
+ with neither is a claim about a calendar nobody opened.
183
+
184
+ ## Scorecard rows this skill owns
185
+ Two, and they fail differently — see \`paper-pipeline/references/pipeline-status-template.md\`:
186
+ - **\`access\`** — *can you physically submit?* (§0). Account · profile ACTIVE since \`<ISO>\` · portal
187
+ reachable · form fields known · artifact-host requirement known. \`submit-paper\` is blocked on it.
188
+ - **\`schedule\`** — the schedule itself (§1–§4): the buffered backwards plan and its calendar events.
189
+
190
+ ## Compose with
191
+ - **\`find-venue\`** — the source of the four CFP dates this skill consumes; run it first.
192
+ - **\`submit-paper\`** (+ the venue subskill) — what submit-day actually executes; this skill just makes
193
+ sure the runway before it exists, **and that the account it executes through is already ACTIVE**.
194
+
195
+ ## Provenance
196
+ This session scheduled two real papers with spacing rather than back-to-back: **AgenticDev 2026 @ ASE**
197
+ (submission 15 Jul, notify 21 Aug, camera-ready 28 Aug, workshop 12 Oct) and **AISec 2026 @ ACM CCS**
198
+ (submission 24 Jul). Nine days apart, deliberately staggered so notifications and any camera-ready work
199
+ don't collapse into one pre-filing month — the FILING-SPACING rule applied in practice, not in theory.`,
200
+ });
@@ -0,0 +1,239 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * A stand-in for the Google Calendar MCP server, used by
4
+ * `plan-paper-timeline.effects.harness.mjs` to make §3 of the skill EXECUTABLE in a
5
+ * test. Records every call instead of mutating anything.
6
+ *
7
+ * ── WHY A DOUBLE AND NOT A DENY ─────────────────────────────────────────────────
8
+ * A `PreToolUse` hook can only allow / deny / escalate — it cannot answer a call. A
9
+ * denied call ends the run, so nothing downstream of the effectful step is ever
10
+ * observed. Answering lets the run continue past it, which is the only way to see
11
+ * what the skill does with three events rather than one. Measured 2026-08-17,
12
+ * `vigiles/repro/mcp-double-2026-08-17/`.
13
+ *
14
+ * ── WHY IT IS STRICT, AND WHY THAT IS THE WHOLE POINT ───────────────────────────
15
+ * A permissive double would accept anything and the test would only be replaying its
16
+ * own script — a green light for arguments the real API rejects. So the schemas below
17
+ * are TRANSCRIBED FROM THE REAL SERVER (read off the live tool definitions on
18
+ * 2026-08-17) and the required-field sets are enforced. That makes the double an
19
+ * ORACLE: the unit under test is the CALL THE SKILL PRESCRIBES, and a prescription
20
+ * the real Google Calendar would reject fails here instead of at 3am on a real run.
21
+ *
22
+ * 🔴 If the real server's schema changes, this file is now WRONG in the quiet
23
+ * direction — it will keep passing calls the real API rejects. Re-read the live tool
24
+ * definitions when a calendar call fails in production despite a green harness.
25
+ *
26
+ * ── SAFETY ──────────────────────────────────────────────────────────────────────
27
+ * This process has no network and no credentials. It cannot reach a calendar even if
28
+ * asked. Under `--strict-mcp-config` (which the runner's `.mcp.json` path implies) the
29
+ * real `mcp__Google_Calendar__*` surface does not exist in the session at all, so the
30
+ * real calendar is not blocked here — it is UNREACHABLE. That is a stronger property
31
+ * than a deny rule and it is why this tier may finally call `create_event`, which the
32
+ * absence-pinning run next door deliberately never does.
33
+ *
34
+ * Calls are appended as JSONL to $FAKE_MCP_LOG.
35
+ */
36
+ import { appendFileSync } from "node:fs";
37
+
38
+ const LOG = process.env.FAKE_MCP_LOG;
39
+ if (!LOG) {
40
+ // Failing closed matters more than it looks: with no log there is nothing to assert
41
+ // on, and a test that asserts on an empty list passes for the wrong reason.
42
+ process.stderr.write(
43
+ "fake-google-calendar: FAKE_MCP_LOG is unset; refusing to run blind\n",
44
+ );
45
+ process.exit(1);
46
+ }
47
+
48
+ /**
49
+ * Transcribed from the live tool definitions, 2026-08-17. `required` is the field
50
+ * that carries the weight — everything else is here so an unknown key is detectable.
51
+ */
52
+ const TOOLS = {
53
+ create_event: {
54
+ description: "Creates an event on the given calendar.",
55
+ required: ["summary", "startTime", "endTime"],
56
+ known: [
57
+ "summary",
58
+ "startTime",
59
+ "endTime",
60
+ "timeZone",
61
+ "allDay",
62
+ "calendarId",
63
+ "description",
64
+ "location",
65
+ "colorId",
66
+ "visibility",
67
+ "availability",
68
+ "eventType",
69
+ "attendees",
70
+ "attachments",
71
+ "recurrenceData",
72
+ "overrideReminders",
73
+ "notificationLevel",
74
+ "addGoogleMeetUrl",
75
+ "googleMeetUrl",
76
+ "guestPermissions",
77
+ "workingLocationProperties",
78
+ "attendeeEmails",
79
+ ],
80
+ },
81
+ update_event: {
82
+ description: "Updates an event on the given calendar.",
83
+ required: ["eventId"],
84
+ known: [
85
+ "eventId",
86
+ "summary",
87
+ "startTime",
88
+ "endTime",
89
+ "timeZone",
90
+ "allDay",
91
+ "calendarId",
92
+ "description",
93
+ "location",
94
+ "colorId",
95
+ "visibility",
96
+ "availability",
97
+ "addedAttendees",
98
+ "removedAttendeeEmails",
99
+ "addedAttachments",
100
+ "removedAttachmentFileUrls",
101
+ "overrideReminders",
102
+ "notificationLevel",
103
+ "addGoogleMeetUrl",
104
+ "googleMeetUrl",
105
+ "guestPermissions",
106
+ "addedAttendeeEmails",
107
+ ],
108
+ },
109
+ list_events: {
110
+ description:
111
+ "Returns events on the given calendar matching all specified constraints.",
112
+ required: [],
113
+ known: [
114
+ "calendarId",
115
+ "startTime",
116
+ "endTime",
117
+ "timeZone",
118
+ "orderBy",
119
+ "pageSize",
120
+ "pageToken",
121
+ "fullText",
122
+ "eventType",
123
+ "eventTypeFilter",
124
+ ],
125
+ },
126
+ search_events: {
127
+ description: "Searches events on the primary calendar by keyword or topic.",
128
+ required: ["query"],
129
+ known: [
130
+ "query",
131
+ "calendarId",
132
+ "startTime",
133
+ "endTime",
134
+ "timeZone",
135
+ "pageSize",
136
+ "pageToken",
137
+ ],
138
+ },
139
+ };
140
+
141
+ const schemaOf = (spec) => ({
142
+ type: "object",
143
+ properties: Object.fromEntries(spec.known.map((k) => [k, {}])),
144
+ required: spec.required,
145
+ });
146
+
147
+ let created = 0;
148
+
149
+ /** @returns {string|null} the reason this call would be rejected, or null if it is well-formed. */
150
+ function reject(name, args) {
151
+ const spec = TOOLS[name];
152
+ if (!spec) return `no such tool: ${name}`;
153
+ const a = args ?? {};
154
+ const missing = spec.required.filter(
155
+ (k) => a[k] === undefined || a[k] === "",
156
+ );
157
+ if (missing.length)
158
+ return `missing required argument(s): ${missing.join(", ")} — the real Google Calendar API rejects this call`;
159
+ const unknown = Object.keys(a).filter((k) => !spec.known.includes(k));
160
+ if (unknown.length)
161
+ return `unknown argument(s): ${unknown.join(", ")} — not fields of ${name} on the real server`;
162
+ return null;
163
+ }
164
+
165
+ let buf = "";
166
+ process.stdin.on("data", (chunk) => {
167
+ buf += chunk;
168
+ let nl;
169
+ while ((nl = buf.indexOf("\n")) >= 0) {
170
+ const line = buf.slice(0, nl).trim();
171
+ buf = buf.slice(nl + 1);
172
+ if (!line) continue;
173
+ let msg;
174
+ try {
175
+ msg = JSON.parse(line);
176
+ } catch {
177
+ continue;
178
+ }
179
+ const send = (payload) =>
180
+ process.stdout.write(
181
+ JSON.stringify({ jsonrpc: "2.0", id: msg.id, ...payload }) + "\n",
182
+ );
183
+
184
+ if (msg.method === "initialize") {
185
+ send({
186
+ result: {
187
+ protocolVersion: "2024-11-05",
188
+ capabilities: { tools: {} },
189
+ serverInfo: { name: "fake-google-calendar", version: "1.0.0" },
190
+ },
191
+ });
192
+ } else if (msg.method === "tools/list") {
193
+ send({
194
+ result: {
195
+ tools: Object.entries(TOOLS).map(([name, spec]) => ({
196
+ name,
197
+ description: spec.description,
198
+ inputSchema: schemaOf(spec),
199
+ })),
200
+ },
201
+ });
202
+ } else if (msg.method === "tools/call") {
203
+ const name = msg.params?.name;
204
+ const args = msg.params?.arguments ?? {};
205
+ const why = reject(name, args);
206
+ // Every call is logged, accepted or not. A rejected call that left no trace
207
+ // would be indistinguishable from a call that never happened.
208
+ appendFileSync(
209
+ LOG,
210
+ JSON.stringify({ name, args, ok: why === null, why }) + "\n",
211
+ );
212
+ if (why) {
213
+ send({
214
+ result: {
215
+ isError: true,
216
+ content: [{ type: "text", text: `INVALID CALL: ${why}` }],
217
+ },
218
+ });
219
+ } else {
220
+ created += 1;
221
+ send({
222
+ result: {
223
+ content: [
224
+ {
225
+ type: "text",
226
+ text: JSON.stringify({
227
+ id: `evt_fake_${String(created)}`,
228
+ status: "confirmed",
229
+ }),
230
+ },
231
+ ],
232
+ },
233
+ });
234
+ }
235
+ } else if (msg.id !== undefined) {
236
+ send({ result: {} });
237
+ }
238
+ }
239
+ });