paperlint 3.2.0 → 4.1.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 (442) hide show
  1. package/.github/workflows/ci.yml +16 -23
  2. package/CLAUDE.md +33 -13
  3. package/CONTRIBUTING.md +23 -7
  4. package/README.md +11 -8
  5. package/dist/adapters/banal/failure.d.ts +1 -7
  6. package/dist/adapters/banal/failure.d.ts.map +1 -1
  7. package/dist/adapters/banal/failure.js +5 -7
  8. package/dist/adapters/banal/failure.js.map +1 -1
  9. package/dist/adapters/banal/index.d.ts.map +1 -1
  10. package/dist/adapters/banal/index.js +8 -11
  11. package/dist/adapters/banal/index.js.map +1 -1
  12. package/dist/adapters/banal/install.d.ts.map +1 -1
  13. package/dist/adapters/banal/install.js +4 -3
  14. package/dist/adapters/banal/install.js.map +1 -1
  15. package/dist/adapters/banal/invocation.d.ts +10 -5
  16. package/dist/adapters/banal/invocation.d.ts.map +1 -1
  17. package/dist/adapters/banal/invocation.js +13 -1
  18. package/dist/adapters/banal/invocation.js.map +1 -1
  19. package/dist/adapters/banal/locate.d.ts +12 -9
  20. package/dist/adapters/banal/locate.d.ts.map +1 -1
  21. package/dist/adapters/banal/locate.js +19 -12
  22. package/dist/adapters/banal/locate.js.map +1 -1
  23. package/dist/adapters/banal/run.d.ts +10 -1
  24. package/dist/adapters/banal/run.d.ts.map +1 -1
  25. package/dist/adapters/banal/run.js +8 -4
  26. package/dist/adapters/banal/run.js.map +1 -1
  27. package/dist/adapters/banal/settings.d.ts +1 -1
  28. package/dist/adapters/banal/settings.d.ts.map +1 -1
  29. package/dist/adapters/banal/settings.js +2 -1
  30. package/dist/adapters/banal/settings.js.map +1 -1
  31. package/dist/adapters/latex/document-class.d.ts +22 -0
  32. package/dist/adapters/latex/document-class.d.ts.map +1 -0
  33. package/dist/adapters/latex/document-class.js +102 -0
  34. package/dist/adapters/latex/document-class.js.map +1 -0
  35. package/dist/adapters/latex/hidden.d.ts +16 -0
  36. package/dist/adapters/latex/hidden.d.ts.map +1 -0
  37. package/dist/adapters/latex/hidden.js +49 -0
  38. package/dist/adapters/latex/hidden.js.map +1 -0
  39. package/dist/adapters/latex/index.d.ts +29 -0
  40. package/dist/adapters/latex/index.d.ts.map +1 -0
  41. package/dist/adapters/latex/index.js +18 -0
  42. package/dist/adapters/latex/index.js.map +1 -0
  43. package/dist/adapters/latex/nodes.d.ts +54 -0
  44. package/dist/adapters/latex/nodes.d.ts.map +1 -0
  45. package/dist/adapters/latex/nodes.js +93 -0
  46. package/dist/adapters/latex/nodes.js.map +1 -0
  47. package/dist/adapters/latex/outline.d.ts +10 -0
  48. package/dist/adapters/latex/outline.d.ts.map +1 -0
  49. package/dist/adapters/latex/outline.js +39 -0
  50. package/dist/adapters/latex/outline.js.map +1 -0
  51. package/dist/adapters/latex/parse.d.ts +42 -0
  52. package/dist/adapters/latex/parse.d.ts.map +1 -0
  53. package/dist/adapters/latex/parse.js +94 -0
  54. package/dist/adapters/latex/parse.js.map +1 -0
  55. package/dist/adapters/latex/rendered.d.ts +10 -0
  56. package/dist/adapters/latex/rendered.d.ts.map +1 -0
  57. package/dist/adapters/latex/rendered.js +39 -0
  58. package/dist/adapters/latex/rendered.js.map +1 -0
  59. package/dist/adapters/memory/index.d.ts.map +1 -1
  60. package/dist/adapters/memory/index.js +8 -2
  61. package/dist/adapters/memory/index.js.map +1 -1
  62. package/dist/adapters/node/process.io.d.ts +6 -3
  63. package/dist/adapters/node/process.io.d.ts.map +1 -1
  64. package/dist/adapters/node/process.io.js.map +1 -1
  65. package/dist/adapters/node/workspace.io.d.ts.map +1 -1
  66. package/dist/adapters/node/workspace.io.js +4 -2
  67. package/dist/adapters/node/workspace.io.js.map +1 -1
  68. package/dist/adapters/pdfjs/fill.d.ts.map +1 -1
  69. package/dist/adapters/pdfjs/fill.js +12 -12
  70. package/dist/adapters/pdfjs/fill.js.map +1 -1
  71. package/dist/adapters/references/index.d.ts.map +1 -1
  72. package/dist/adapters/references/index.js +4 -2
  73. package/dist/adapters/references/index.js.map +1 -1
  74. package/dist/build-engine.d.ts +1 -1
  75. package/dist/build-engine.d.ts.map +1 -1
  76. package/dist/build-engine.js +8 -1
  77. package/dist/build-engine.js.map +1 -1
  78. package/dist/build.d.ts +2 -3
  79. package/dist/build.d.ts.map +1 -1
  80. package/dist/build.js +9 -10
  81. package/dist/build.js.map +1 -1
  82. package/dist/caller-path.d.ts +3 -0
  83. package/dist/caller-path.d.ts.map +1 -0
  84. package/dist/caller-path.js +13 -0
  85. package/dist/caller-path.js.map +1 -0
  86. package/dist/cli.d.ts +10 -17
  87. package/dist/cli.d.ts.map +1 -1
  88. package/dist/cli.js +91 -17
  89. package/dist/cli.js.map +1 -1
  90. package/dist/doctor.d.ts +12 -3
  91. package/dist/doctor.d.ts.map +1 -1
  92. package/dist/doctor.js +2 -1
  93. package/dist/doctor.js.map +1 -1
  94. package/dist/domain/paths.d.ts +8 -2
  95. package/dist/domain/paths.d.ts.map +1 -1
  96. package/dist/domain/paths.js +12 -1
  97. package/dist/domain/paths.js.map +1 -1
  98. package/dist/domain/ready.d.ts +24 -0
  99. package/dist/domain/ready.d.ts.map +1 -0
  100. package/dist/domain/ready.js +11 -0
  101. package/dist/domain/ready.js.map +1 -0
  102. package/dist/domain/record.d.ts +8 -0
  103. package/dist/domain/record.d.ts.map +1 -0
  104. package/dist/domain/record.js +8 -0
  105. package/dist/domain/record.js.map +1 -0
  106. package/dist/domain/sha256.d.ts.map +1 -1
  107. package/dist/domain/sha256.js +4 -2
  108. package/dist/domain/sha256.js.map +1 -1
  109. package/dist/domain/tex-document.d.ts +84 -0
  110. package/dist/domain/tex-document.d.ts.map +1 -0
  111. package/dist/domain/tex-document.js +43 -0
  112. package/dist/domain/tex-document.js.map +1 -0
  113. package/dist/engine.d.ts +23 -22
  114. package/dist/engine.d.ts.map +1 -1
  115. package/dist/engine.js.map +1 -1
  116. package/dist/eslint-rules/bib-reachable-entry.d.ts +1 -1
  117. package/dist/eslint-rules/bib-reachable-entry.js.map +1 -1
  118. package/dist/eslint-rules/install-path-literals.d.ts +2 -2
  119. package/dist/eslint-rules/install-path-literals.js.map +1 -1
  120. package/dist/eslint-rules/paper-research-question.d.ts +1 -1
  121. package/dist/eslint-rules/paper-research-question.js.map +1 -1
  122. package/dist/eslint-rules/paper-stages.d.ts +2 -2
  123. package/dist/eslint-rules/paper-stages.js.map +1 -1
  124. package/dist/eslint-rules/paper-typography.d.ts +6 -6
  125. package/dist/eslint-rules/paper-typography.js.map +1 -1
  126. package/dist/eslint-rules/pdf-last-page-balance.d.ts +1 -1
  127. package/dist/eslint-rules/pdf-last-page-balance.js.map +1 -1
  128. package/dist/eslint-rules/review-frontmatter.d.ts +2 -2
  129. package/dist/eslint-rules/review-frontmatter.js.map +1 -1
  130. package/dist/eslint-rules/sibling-frontmatter.d.ts +1 -1
  131. package/dist/eslint-rules/sibling-frontmatter.d.ts.map +1 -1
  132. package/dist/eslint-rules/temp-root-realpath.d.ts +1 -1
  133. package/dist/eslint-rules/temp-root-realpath.js.map +1 -1
  134. package/dist/eslint-rules/tex-build.d.ts +2 -2
  135. package/dist/eslint-rules/tex-build.js.map +1 -1
  136. package/dist/facts-file.d.ts.map +1 -1
  137. package/dist/facts-file.js +31 -28
  138. package/dist/facts-file.js.map +1 -1
  139. package/dist/hooks-settings.d.ts.map +1 -1
  140. package/dist/hooks-settings.js +17 -2
  141. package/dist/hooks-settings.js.map +1 -1
  142. package/dist/init.d.ts +3 -4
  143. package/dist/init.d.ts.map +1 -1
  144. package/dist/init.js +18 -8
  145. package/dist/init.js.map +1 -1
  146. package/dist/lib/paper-config.d.ts.map +1 -1
  147. package/dist/lib/paper-config.js +1 -5
  148. package/dist/lib/paper-config.js.map +1 -1
  149. package/dist/new-paper.d.ts +5 -0
  150. package/dist/new-paper.d.ts.map +1 -1
  151. package/dist/new-paper.js +8 -1
  152. package/dist/new-paper.js.map +1 -1
  153. package/dist/package-dirs.d.ts +5 -0
  154. package/dist/package-dirs.d.ts.map +1 -0
  155. package/dist/package-dirs.js +14 -0
  156. package/dist/package-dirs.js.map +1 -0
  157. package/dist/paper-files.d.ts +4 -4
  158. package/dist/paper-files.d.ts.map +1 -1
  159. package/dist/paper-files.js +6 -6
  160. package/dist/paper-files.js.map +1 -1
  161. package/dist/paper-settings.js +3 -3
  162. package/dist/paper-settings.js.map +1 -1
  163. package/dist/pdf-facts.d.ts +51 -1
  164. package/dist/pdf-facts.d.ts.map +1 -1
  165. package/dist/pdf-facts.js +23 -15
  166. package/dist/pdf-facts.js.map +1 -1
  167. package/dist/pdf-geometry.d.ts.map +1 -1
  168. package/dist/pdf-geometry.js +2 -3
  169. package/dist/pdf-geometry.js.map +1 -1
  170. package/dist/ports/latex.d.ts +24 -0
  171. package/dist/ports/latex.d.ts.map +1 -0
  172. package/dist/ports/latex.js +2 -0
  173. package/dist/ports/latex.js.map +1 -0
  174. package/dist/ports/tool-installer.d.ts +2 -14
  175. package/dist/ports/tool-installer.d.ts.map +1 -1
  176. package/dist/presets.d.ts +15 -2
  177. package/dist/presets.d.ts.map +1 -1
  178. package/dist/presets.js +35 -10
  179. package/dist/presets.js.map +1 -1
  180. package/dist/references.d.ts.map +1 -1
  181. package/dist/references.js +4 -4
  182. package/dist/references.js.map +1 -1
  183. package/dist/rules-config.d.ts +26 -1
  184. package/dist/rules-config.d.ts.map +1 -1
  185. package/dist/rules-config.js +9 -9
  186. package/dist/rules-config.js.map +1 -1
  187. package/dist/tex-requirements.d.ts +15 -0
  188. package/dist/tex-requirements.d.ts.map +1 -1
  189. package/dist/tex-requirements.js +28 -10
  190. package/dist/tex-requirements.js.map +1 -1
  191. package/dist/tex-venue-rules.d.ts +83 -0
  192. package/dist/tex-venue-rules.d.ts.map +1 -0
  193. package/dist/tex-venue-rules.js +254 -0
  194. package/dist/tex-venue-rules.js.map +1 -0
  195. package/dist/types.d.ts +5 -3
  196. package/dist/types.d.ts.map +1 -1
  197. package/dist/venue-rules.d.ts +1 -1
  198. package/dist/venue-rules.d.ts.map +1 -1
  199. package/dist/venue-rules.js +20 -7
  200. package/dist/venue-rules.js.map +1 -1
  201. package/docs/configuration.md +5 -4
  202. package/docs/e2e.md +69 -40
  203. package/docs/install.md +1 -1
  204. package/docs/prior-art/README.md +1 -1
  205. package/docs/prior-art/package-location.md +5 -5
  206. package/docs/prior-art/repro/README.md +1 -1
  207. package/docs/prior-art/test-tooling.md +21 -10
  208. package/docs/rules/tex/required-section.md +73 -0
  209. package/docs/rules/tex/template.md +62 -0
  210. package/docs/rules/tex/venue-leftover.md +71 -0
  211. package/docs/rules.md +70 -48
  212. package/docs/testing.md +17 -4
  213. package/docs/texlive-install-decision.md +1 -1
  214. package/docs/toolchain.md +2 -2
  215. package/eslint-rules/bib-reachable-entry.ts +1 -1
  216. package/eslint-rules/install-path-literals.ts +2 -2
  217. package/eslint-rules/paper-research-question.ts +1 -1
  218. package/eslint-rules/paper-stages.ts +2 -2
  219. package/eslint-rules/paper-typography.ts +2 -2
  220. package/eslint-rules/pdf-last-page-balance.ts +1 -1
  221. package/eslint-rules/review-frontmatter.ts +1 -1
  222. package/eslint-rules/temp-root-realpath.ts +1 -1
  223. package/eslint-rules/tex-build.ts +2 -2
  224. package/eslint.config.mjs +162 -15
  225. package/fixtures/build-e2e/acmart/expect.json +26 -0
  226. package/fixtures/build-e2e/aidc/PIPELINE-STATUS.md +3 -0
  227. package/fixtures/build-e2e/aidc/expect.json +14 -0
  228. package/fixtures/build-e2e/aidc/paper.tex +46 -0
  229. package/fixtures/build-e2e/aidc/paperlint.json +1 -0
  230. package/fixtures/build-e2e/broken/expect.json +11 -0
  231. package/fixtures/build-e2e/cite/expect.json +27 -0
  232. package/fixtures/build-e2e/empty/expect.json +12 -0
  233. package/fixtures/build-e2e/fallback/expect.json +53 -0
  234. package/fixtures/build-e2e/guards/expect.json +6 -0
  235. package/fixtures/build-e2e/no-source/expect.json +10 -0
  236. package/fixtures/build-e2e/unbalanced/expect.json +60 -0
  237. package/fixtures/install-path-literals/clean.fixture.mjs +1 -1
  238. package/fixtures/install-path-literals/clean.md +2 -2
  239. package/fixtures/install-path-literals/defect.fixture.mjs +1 -1
  240. package/fixtures/install-path-literals/defect.md +2 -2
  241. package/fixtures/pdf-facts/README.md +1 -1
  242. package/fixtures/real-markdown-paper/baseline.ts +1 -1
  243. package/fixtures/render-paper/build-clean.sh +3 -5
  244. package/hooks/paper-status-gates.sh +9 -19
  245. package/lib/paper-config.harness.mjs +1 -1
  246. package/lib/paper-config.ts +1 -5
  247. package/lib/skill-checks.mjs +24 -381
  248. package/lib/skill-checks.test.mjs +63 -115
  249. package/lib/skill-corpus.mjs +10 -251
  250. package/lib/skill-corpus.test.mjs +2 -13
  251. package/lib/skill-trigger-cases.mjs +1 -1
  252. package/package.json +13 -2
  253. package/{skills/submit-paper/references/venues → presets}/acm-sigconf.jsonc +4 -1
  254. package/{skills/submit-paper/references/venues → presets}/agenticdev.jsonc +4 -0
  255. package/presets/aidc.jsonc +73 -0
  256. package/{skills/submit-paper/references/venues → presets}/aisec.jsonc +4 -0
  257. package/presets/ieee-conference.jsonc +66 -0
  258. package/{skills/submit-paper/references/venues → presets}/realm.jsonc +8 -1
  259. package/{skills/submit-paper/references/venues → presets/tex}/paper-guards.tex +1 -1
  260. package/{skills/submit-paper/references/venues → presets}/venue-profile.schema.json +30 -2
  261. package/scripts/check.harness.ts +64 -10
  262. package/scripts/check.test.ts +60 -13
  263. package/scripts/check.ts +83 -30
  264. package/scripts/coverage-config.test.ts +0 -1
  265. package/scripts/harness-api.frozen.json +0 -1
  266. package/scripts/test.test.ts +1 -1
  267. package/scripts/test.ts +1 -1
  268. package/skills/analyze-sibling-paper/SKILL.md +1 -39
  269. package/skills/analyze-sibling-paper/SKILL.md.spec.ts +0 -38
  270. package/skills/argument-arc/SKILL.md +2 -36
  271. package/skills/argument-arc/SKILL.md.spec.ts +1 -43
  272. package/skills/build-benchmark/SKILL.md +1 -41
  273. package/skills/build-benchmark/SKILL.md.spec.ts +0 -40
  274. package/skills/camera-ready/SKILL.md +1 -34
  275. package/skills/camera-ready/SKILL.md.spec.ts +0 -33
  276. package/skills/cold-read-diff/SKILL.md +1 -35
  277. package/skills/cold-read-diff/SKILL.md.spec.ts +0 -34
  278. package/skills/draft-paper/SKILL.md +2 -41
  279. package/skills/draft-paper/SKILL.md.spec.ts +1 -50
  280. package/skills/extend-paper/SKILL.md +2 -33
  281. package/skills/extend-paper/SKILL.md.spec.ts +1 -42
  282. package/skills/find-venue/SKILL.md +2 -38
  283. package/skills/find-venue/SKILL.md.spec.ts +1 -47
  284. package/skills/grade-paper-writing/SKILL.md +3 -38
  285. package/skills/grade-paper-writing/SKILL.md.spec.ts +2 -47
  286. package/skills/grade-paper-writing/prose-lint.mjs +2 -3
  287. package/skills/grade-paper-writing/prose-lint.test.ts +1 -1
  288. package/skills/harden-paper/SKILL.md +1 -39
  289. package/skills/harden-paper/SKILL.md.spec.ts +0 -38
  290. package/skills/map-prior-work/SKILL.md +1 -35
  291. package/skills/map-prior-work/SKILL.md.spec.ts +0 -34
  292. package/skills/osf-artifact-upload/osf-artifact-upload.harness.ts +3 -4
  293. package/skills/paper-adversarial-review/SKILL.md +2 -35
  294. package/skills/paper-adversarial-review/SKILL.md.spec.ts +1 -43
  295. package/skills/paper-pipeline/PIPELINE-MAP.md +2 -2
  296. package/skills/paper-pipeline/SKILL.md +3 -29
  297. package/skills/paper-pipeline/SKILL.md.spec.ts +2 -28
  298. package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +1 -1
  299. package/skills/paper-pipeline/paper-pipeline.harness.mjs +25 -40
  300. package/skills/paper-pipeline/references/acceptance-gate.md +1 -6
  301. package/skills/paper-pipeline/references/pipeline-status-template.md +8 -35
  302. package/skills/paper-pipeline/scripts/README.md +30 -225
  303. package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +4 -8
  304. package/skills/paper-pipeline/scripts/consumer.d.mts +0 -2
  305. package/skills/paper-pipeline/scripts/consumer.harness.mjs +6 -104
  306. package/skills/paper-pipeline/scripts/consumer.mjs +24 -119
  307. package/skills/paper-pipeline/scripts/generated-code.harness.mjs +2 -3
  308. package/skills/paper-pipeline/scripts/population-map.harness.mjs +3 -3
  309. package/skills/paper-status/SKILL.md +2 -42
  310. package/skills/paper-status/SKILL.md.spec.ts +1 -41
  311. package/skills/pc-panel-review/SKILL.md +2 -46
  312. package/skills/pc-panel-review/SKILL.md.spec.ts +1 -45
  313. package/skills/plan-paper-timeline/SKILL.md +2 -36
  314. package/skills/plan-paper-timeline/SKILL.md.spec.ts +0 -36
  315. package/skills/render-paper/SKILL.md +2 -38
  316. package/skills/render-paper/SKILL.md.spec.ts +1 -37
  317. package/skills/render-paper/check-render.sh +16 -63
  318. package/skills/render-paper/render-paper.harness.mjs +17 -59
  319. package/skills/research-ideate/SKILL.md +2 -39
  320. package/skills/research-ideate/SKILL.md.spec.ts +1 -47
  321. package/skills/study-accepted-papers/SKILL.md +1 -34
  322. package/skills/study-accepted-papers/SKILL.md.spec.ts +0 -33
  323. package/skills/submit-paper/SKILL.md +1 -33
  324. package/skills/submit-paper/SKILL.md.spec.ts +0 -32
  325. package/skills/sweep-design-space/SKILL.md +1 -38
  326. package/skills/sweep-design-space/SKILL.md.spec.ts +0 -37
  327. package/skills/tighten-paper/SKILL.md +2 -39
  328. package/skills/tighten-paper/SKILL.md.spec.ts +1 -47
  329. package/skills/tighten-paper/structure.mjs +4 -11
  330. package/skills/verify-citations/SKILL.md +2 -38
  331. package/skills/verify-citations/SKILL.md.spec.ts +1 -37
  332. package/src/adapters/banal/failure.ts +9 -10
  333. package/src/adapters/banal/index.test.ts +3 -3
  334. package/src/adapters/banal/index.ts +8 -10
  335. package/src/adapters/banal/install.ts +9 -4
  336. package/src/adapters/banal/invocation.test.ts +20 -8
  337. package/src/adapters/banal/invocation.ts +24 -9
  338. package/src/adapters/banal/locate.test.ts +2 -2
  339. package/src/adapters/banal/locate.ts +27 -21
  340. package/src/adapters/banal/output.test.ts +1 -1
  341. package/src/adapters/banal/run.test.ts +3 -3
  342. package/src/adapters/banal/run.ts +16 -10
  343. package/src/adapters/banal/settings.ts +2 -2
  344. package/src/adapters/banal/xml.test.ts +1 -1
  345. package/src/adapters/curl/download.test.ts +4 -4
  346. package/src/adapters/latex/document-class.ts +147 -0
  347. package/src/adapters/latex/hidden.ts +70 -0
  348. package/src/adapters/latex/index.test.ts +361 -0
  349. package/src/adapters/latex/index.ts +55 -0
  350. package/src/adapters/latex/nodes.ts +141 -0
  351. package/src/adapters/latex/outline.ts +64 -0
  352. package/src/adapters/latex/parse.ts +136 -0
  353. package/src/adapters/latex/rendered.ts +59 -0
  354. package/src/adapters/memory/index.ts +4 -3
  355. package/src/adapters/node/files.test.ts +5 -3
  356. package/src/adapters/node/process.io.ts +9 -2
  357. package/src/adapters/node/process.test.ts +4 -4
  358. package/src/adapters/node/workspace.io.ts +9 -4
  359. package/src/adapters/pdfjs/fill.test.ts +1 -1
  360. package/src/adapters/pdfjs/fill.ts +18 -10
  361. package/src/adapters/references/index.ts +7 -2
  362. package/src/build-engine.test.ts +11 -6
  363. package/src/build-engine.ts +10 -5
  364. package/src/build.harness.mjs +5 -7
  365. package/src/build.test.ts +10 -11
  366. package/src/build.ts +12 -13
  367. package/src/caller-path.ts +13 -0
  368. package/src/cli.harness.mjs +1 -3
  369. package/src/cli.test.ts +21 -16
  370. package/src/cli.ts +115 -33
  371. package/src/doctor.test.ts +1 -1
  372. package/src/doctor.ts +14 -5
  373. package/src/domain/paths.test.ts +15 -0
  374. package/src/domain/paths.ts +20 -1
  375. package/src/domain/ready.ts +31 -0
  376. package/src/domain/record.test.ts +20 -0
  377. package/src/domain/record.ts +12 -0
  378. package/src/domain/sha256.ts +5 -3
  379. package/src/domain/tex-document.test.ts +80 -0
  380. package/src/domain/tex-document.ts +110 -0
  381. package/src/engine.harness.mjs +1 -1
  382. package/src/engine.ts +30 -2
  383. package/src/facts-file.test.ts +4 -7
  384. package/src/facts-file.ts +45 -30
  385. package/src/hooks-settings.harness.ts +17 -0
  386. package/src/hooks-settings.ts +28 -4
  387. package/src/init.test.ts +25 -5
  388. package/src/init.ts +26 -12
  389. package/src/latex-loop.harness.ts +1 -1
  390. package/src/new-paper.harness.mjs +7 -23
  391. package/src/new-paper.test.ts +28 -3
  392. package/src/new-paper.ts +15 -3
  393. package/src/package-dirs.ts +17 -0
  394. package/src/paper-files.test.ts +22 -19
  395. package/src/paper-files.ts +16 -27
  396. package/src/paper-settings-commands.test.ts +6 -1
  397. package/src/paper-settings.test.ts +2 -2
  398. package/src/paper-settings.ts +5 -5
  399. package/src/pdf-facts.test.ts +16 -5
  400. package/src/pdf-facts.ts +79 -22
  401. package/src/pdf-geometry.ts +5 -3
  402. package/src/ports/latex.ts +29 -0
  403. package/src/ports/tool-installer.ts +2 -17
  404. package/src/presets.test.ts +65 -4
  405. package/src/presets.ts +65 -28
  406. package/src/references.test.ts +25 -19
  407. package/src/references.ts +4 -5
  408. package/src/rule-cases.test.ts +474 -0
  409. package/src/rule-docs.test.ts +159 -0
  410. package/src/rules-config.ts +40 -11
  411. package/src/tex-requirements.harness.ts +13 -23
  412. package/src/tex-requirements.test.ts +21 -0
  413. package/src/tex-requirements.ts +64 -14
  414. package/src/tex-venue-rules.test.ts +659 -0
  415. package/src/tex-venue-rules.ts +429 -0
  416. package/src/toolchain.harness.mjs +1 -1
  417. package/src/toolchain.test.ts +9 -5
  418. package/src/toolchain.ts +2 -2
  419. package/src/types.ts +5 -3
  420. package/src/venue-rules.test.ts +104 -8
  421. package/src/venue-rules.ts +22 -12
  422. package/templates/paper/paper.tex +2 -1
  423. package/skills/paper-pipeline/scripts/announce.mjs +0 -67
  424. package/skills/paper-pipeline/scripts/announce.test.mjs +0 -102
  425. package/skills/paper-pipeline/scripts/fixtures/clean/PIPELINE-STATUS.md +0 -51
  426. package/skills/paper-pipeline/scripts/fixtures/dirty/PIPELINE-STATUS.md +0 -52
  427. package/skills/paper-pipeline/scripts/fixtures/dirty/paper.md +0 -6
  428. package/skills/paper-pipeline/scripts/fixtures/scorecard.ts +0 -100
  429. package/skills/paper-pipeline/scripts/ledger.cli.test.mjs +0 -302
  430. package/skills/paper-pipeline/scripts/ledger.mjs +0 -623
  431. package/skills/paper-pipeline/scripts/ledger.test.mjs +0 -254
  432. package/skills/paper-pipeline/scripts/pipeline-check.cli.test.mjs +0 -474
  433. package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +0 -328
  434. package/skills/paper-pipeline/scripts/pipeline-check.mjs +0 -750
  435. package/skills/paper-pipeline/scripts/pipeline-edges.mjs +0 -169
  436. package/skills/paper-pipeline/scripts/pipeline-edges.test.mjs +0 -73
  437. package/skills/paper-pipeline/scripts/run-mechanical.mjs +0 -648
  438. package/skills/paper-pipeline/scripts/run-mechanical.test.mjs +0 -468
  439. package/skills/paper-pipeline/scripts/status.mjs +0 -295
  440. package/skills/paper-pipeline/scripts/status.test.mjs +0 -232
  441. /package/{skills/submit-paper/references/venues → presets/tex}/agenticdev.tex +0 -0
  442. /package/{skills/submit-paper/references/venues → presets}/tex-base.jsonc +0 -0
@@ -45,8 +45,9 @@ import {
45
45
  import type { KindLimits, VenueFormat } from "./tex-requirements.ts";
46
46
  import { paperPreset, presetProblemText } from "./presets.ts";
47
47
  import type { FlatGeometry } from "./domain/geometry.ts";
48
- import type { AbsolutePath } from "./domain/paths.ts";
48
+ import { callerPath } from "./caller-path.ts";
49
49
  import { sha256Hex } from "./domain/sha256.ts";
50
+ import { fieldOf } from "./domain/record.ts";
50
51
  import type { Files } from "./ports/files.ts";
51
52
  import { CONFIG_FILE } from "#lib/paper-config";
52
53
 
@@ -88,13 +89,12 @@ export type Assessment =
88
89
 
89
90
  export interface VenueRuleDeps {
90
91
  readonly files: Files;
91
- /** The package's venues directory (`packageVenuesDir()`): the shipped presets and their schema. */
92
+ /** The package's presets directory (`presetsDir()`): the shipped presets and their schema. */
92
93
  readonly venuesDir: string;
93
94
  }
94
95
 
95
- const at = (p: string): AbsolutePath => p as AbsolutePath;
96
96
  const text = (files: Files, p: string): string | null => {
97
- const b = files.readBytes(at(p));
97
+ const b = files.readBytes(callerPath(p));
98
98
  return b === null ? null : new TextDecoder().decode(b);
99
99
  };
100
100
 
@@ -138,7 +138,7 @@ function freshFacts(
138
138
  : finding("factsBroken", { why: parsed.error.why });
139
139
  const f = parsed.value;
140
140
  const pdf = files.readBytes(
141
- at(isAbsolute(f.pdf) ? f.pdf : join(paperDir, f.pdf)),
141
+ callerPath(isAbsolute(f.pdf) ? f.pdf : join(paperDir, f.pdf)),
142
142
  );
143
143
  if (pdf === null) return finding("pdfMissing", { pdf: f.pdf });
144
144
  return sha256Hex(pdf) === f.sha ? f : finding("stale", { pdf: f.pdf });
@@ -373,8 +373,7 @@ const JUDGES: Readonly<Record<VenueRuleName, Judge>> = {
373
373
  a.facts.geometry,
374
374
  a.venue.format,
375
375
  a.venue.venue,
376
- (options[0] as { dimTol?: number } | undefined)?.dimTol ??
377
- DEFAULT_DIM_TOL_IN,
376
+ dimTolOf(options[0]),
378
377
  )
379
378
  : [],
380
379
  limits: (a) =>
@@ -390,6 +389,12 @@ const JUDGES: Readonly<Record<VenueRuleName, Judge>> = {
390
389
  /** How far, in inches, a measured page dimension may be from the preset's. */
391
390
  export const DEFAULT_DIM_TOL_IN = 0.05;
392
391
 
392
+ /** `geometry`'s `dimTol` option in inches, or the default when the rule was given none. */
393
+ function dimTolOf(options: unknown): number {
394
+ const tol = fieldOf(options, "dimTol");
395
+ return typeof tol === "number" ? tol : DEFAULT_DIM_TOL_IN;
396
+ }
397
+
393
398
  type Meta = Pick<VenueRuleModule["meta"], "docs" | "messages"> & {
394
399
  readonly type?: "suggestion";
395
400
  readonly schema?: readonly object[];
@@ -565,9 +570,14 @@ function rule(name: VenueRuleName, deps: VenueRuleDeps): VenueRuleModule {
565
570
  export function venueRules(
566
571
  deps: VenueRuleDeps,
567
572
  ): Record<VenueRuleName, VenueRuleModule> {
568
- const names = Object.keys(META) as VenueRuleName[];
569
- return Object.fromEntries(names.map((n) => [n, rule(n, deps)])) as Record<
570
- VenueRuleName,
571
- VenueRuleModule
572
- >;
573
+ // Every name listed: a record missing one is a compile error.
574
+ return {
575
+ fresh: rule("fresh", deps),
576
+ profile: rule("profile", deps),
577
+ measured: rule("measured", deps),
578
+ fonts: rule("fonts", deps),
579
+ geometry: rule("geometry", deps),
580
+ limits: rule("limits", deps),
581
+ "body-size": rule("body-size", deps),
582
+ };
573
583
  }
@@ -1,5 +1,6 @@
1
1
  % Created by `paperlint new`. A stub: replace all of it. The only thing `paperlint lint` needs is that
2
- % this file exists; the document class is yours. `paperlint build` compiles it to paper.pdf.
2
+ % this file exists. With `--venue`, the class below is the venue's (`tex/template` checks it);
3
+ % without, it is yours. `paperlint build` compiles it to paper.pdf.
3
4
  \documentclass{article}
4
5
  \title{Working title for {{name}}}
5
6
  \begin{document}
@@ -1,67 +0,0 @@
1
- // announce.mjs — a skill says out loud that it started, and the ledger remembers that it did.
2
- //
3
- // vigiles:local-by-design — takes a skill name as an argument and so names none itself, which reads
4
- // generic; it is not. It writes THIS repo's ledger row format, consumed by `status.mjs`'s gate table
5
- // and `ledger.mjs`'s verdict vocabulary, both of which are conventions of this knowledge base rather
6
- // than of any product. Moving it would export a schema nobody else has.
7
- //
8
- // WHY. Repository owner's request, 2026-08-07: "lack observability into pipeline status and whether
9
- // skills are actually being called — let's ensure each skill outputs something when activated".
10
- //
11
- // The failure behind that sentence is specific and it has happened three times in this repo. An
12
- // advisory check CANNOT BE SEEN FAILING: silence is both its error state and its normal state.
13
- // `paper-skills-nudge.sh` was dead for twelve days and was found only because it writes a throttle
14
- // stamp whose mtime had frozen. Two inline hooks were dead for their entire existence and nobody
15
- // noticed at all. And on 2026-08-06 a whole session of paper work ran with no skill invoked once.
16
- //
17
- // So: starting is an EVENT, and events get written down. A skill that announces leaves two traces —
18
- // one the human sees immediately, one the ledger keeps. Their disagreement is itself information:
19
- //
20
- // announced, never recorded → the skill started and died, or the author abandoned it midway
21
- // recorded, never announced → someone wrote a verdict without running the gate
22
- // neither → it did not run, whatever any status file claims
23
- //
24
- // This is the "no trace means it did not work" rule the project already applies to hooks, made
25
- // available to skills, which had no equivalent.
26
-
27
- import { record, skillHash, inputHash } from "./ledger.mjs";
28
- import { existsSync } from "node:fs";
29
- import { resolve, basename } from "node:path";
30
- import { isMain } from "./consumer.mjs";
31
-
32
- export function announce(skill, paperDir) {
33
- const dir = resolve(paperDir || ".");
34
- const has = existsSync(dir);
35
- const input = has ? inputHash(dir) : null;
36
-
37
- const lines = [
38
- `▶ ${skill} — ${has ? basename(dir) : "no paper dir"}`,
39
- ` paper ${input ? input.slice(0, 8) : "(none)"} · skill ${(skillHash(skill) || "????????").slice(0, 8)}`,
40
- ];
41
- // stderr, not stdout: the skill's own output is the product, and a banner that pollutes a pipe
42
- // is a banner someone will remove.
43
- console.error(lines.join("\n"));
44
-
45
- // An abstention with reason `started`, not an ERROR row. Before 2026-08-10 this wrote `ERROR`,
46
- // which meant "the check errored" — so every announce looked like a crash until its terminal row
47
- // landed, and `started` and `crashed` were the same word. Both are abstentions and neither is a
48
- // judgement about the paper; the reason is what tells them apart.
49
- record({
50
- skill,
51
- check: skill,
52
- paper: dir,
53
- kind: "ABSTAINED",
54
- reason: "started",
55
- note: "no verdict recorded yet",
56
- });
57
- return { skill, paper: basename(dir), input };
58
- }
59
-
60
- if (isMain(import.meta.url)) {
61
- const [skill, paperDir] = process.argv.slice(2);
62
- if (!skill) {
63
- console.error("usage: announce.mjs <skill-name> [paper-dir]");
64
- process.exit(2);
65
- }
66
- announce(skill, paperDir);
67
- }
@@ -1,102 +0,0 @@
1
- /**
2
- * announce.mjs — the first line of every wired skill's `## Run me` block: a banner on stderr naming
3
- * the paper and skill versions, and a `started` abstention in the ledger. Run as the skills run it,
4
- * against a temp consumer and a temp ledger.
5
- */
6
- import assert from "node:assert/strict";
7
- import { cpSync, readFileSync, rmSync } from "node:fs";
8
- import { dirname, join } from "node:path";
9
- import { fileURLToPath } from "node:url";
10
- import { afterEach, test } from "vitest";
11
- import { runNode, useTempDir, writeTree } from "../../../test/support.ts";
12
-
13
- const HERE = dirname(fileURLToPath(import.meta.url));
14
- const SCRIPT = join(HERE, "announce.mjs");
15
- const root = useTempDir("announce-");
16
- const consumer = join(root, "consumer");
17
- const LEDGER = join(root, "runs.jsonl");
18
- writeTree(root, { "consumer/papers/p/paper.md": "# P\n\ntext\n" });
19
- cpSync(
20
- join(HERE, "..", "..", "cold-read-diff"),
21
- join(consumer, ".claude", "skills", "cold-read-diff"),
22
- {
23
- recursive: true,
24
- verbatimSymlinks: true,
25
- },
26
- );
27
- const env = { CLAUDE_PROJECT_DIR: consumer, PIPELINE_LEDGER: LEDGER };
28
- process.env.CLAUDE_PROJECT_DIR = consumer;
29
- process.env.PIPELINE_LEDGER = LEDGER;
30
- const { inputHash, skillHash } = await import("./ledger.mjs");
31
-
32
- const rows = () =>
33
- readFileSync(LEDGER, "utf8")
34
- .trim()
35
- .split("\n")
36
- .map((l) => JSON.parse(l))
37
- .map(({ skill, check, paper, kind, reason, note }) => ({
38
- skill,
39
- check,
40
- paper,
41
- kind,
42
- reason,
43
- note,
44
- }));
45
- afterEach(() => rmSync(LEDGER, { force: true }));
46
-
47
- test("no skill name: the usage line, exit 2, nothing recorded", () => {
48
- assert.deepEqual(runNode(SCRIPT, [], { env }), {
49
- status: 2,
50
- stdout: "",
51
- stderr: "usage: announce.mjs <skill-name> [paper-dir]\n",
52
- });
53
- });
54
-
55
- test("a skill on a paper: the banner names both versions, and a `started` row is recorded", () => {
56
- const paper = join(consumer, "papers", "p");
57
- const r = runNode(SCRIPT, ["cold-read-diff", paper], { env });
58
- assert.deepEqual(
59
- { ...r, rows: rows() },
60
- {
61
- status: 0,
62
- stdout: "",
63
- stderr:
64
- "▶ cold-read-diff — p\n" +
65
- ` paper ${inputHash(paper).slice(0, 8)} · skill ${skillHash("cold-read-diff").slice(0, 8)}\n`,
66
- rows: [
67
- {
68
- skill: "cold-read-diff",
69
- check: "cold-read-diff",
70
- paper: "p",
71
- kind: "ABSTAINED",
72
- reason: "started",
73
- note: "no verdict recorded yet",
74
- },
75
- ],
76
- },
77
- );
78
- });
79
-
80
- test("no paper directory and an unknown skill still announce, with placeholders", () => {
81
- const r = runNode(SCRIPT, ["no-such-skill", join(root, "nowhere")], { env });
82
- assert.deepEqual(
83
- { status: r.status, stderr: r.stderr, kind: rows()[0].kind },
84
- {
85
- status: 0,
86
- stderr:
87
- "▶ no-such-skill — no paper dir\n paper (none) · skill ????????\n",
88
- kind: "ABSTAINED",
89
- },
90
- );
91
- });
92
-
93
- test("no paper argument means the current directory", () => {
94
- const r = runNode(SCRIPT, ["cold-read-diff"], {
95
- env,
96
- cwd: join(consumer, "papers", "p"),
97
- });
98
- assert.deepEqual(
99
- { status: r.status, firstLine: r.stderr.split("\n")[0] },
100
- { status: 0, firstLine: "▶ cold-read-diff — p" },
101
- );
102
- });
@@ -1,51 +0,0 @@
1
- # PIPELINE-STATUS — fixture: nothing to report
2
- Venue: Fixture Workshop · Deadline: 2027-12-31 · Blind: double · State: drafting
3
- Updated: 2026-08-03
4
-
5
- **Readiness verdict:** Submit-ready — every gate green, artifact reproduces clean, nothing blocking.
6
-
7
- This fixture deliberately carries NO paper source file, so `newestSourceDate` is null and the
8
- staleness comparison has nothing to compare against. That keeps the clean case deterministic
9
- regardless of when the repo was checked out — the staleness rule is exercised by `dirty/`.
10
-
11
- ### SETUP
12
- | id | Work | Skill | Status | Date | Result | Open |
13
- |----|------|-------|--------|------|--------|------|
14
- | idea | Validate idea | research-ideate | ☑ | 2026-08-01 | go. judge: opus, judge: author | — |
15
- | priorwork | Map competitors | map-prior-work | ☑ | 2026-08-01 | 6 siblings. judge: sonnet, judge: author | siblings/ |
16
- | venue | Pick venue | find-venue | ☑ | 2026-08-01 | fixture. judge: sonnet, judge: author | — |
17
- | venuebar | Venue bar / levers | study-accepted-papers | ☑ | 2026-08-01 | 34 measured. judge: sonnet, judge: author | — |
18
- | **access** | 🔴 **Can you physically submit?** | plan-paper-timeline | ☑ | 2026-08-01 | profile ACTIVE since 2026-08-01. judge: author, judge: author | — |
19
- | schedule | Schedule + calendar | plan-paper-timeline | ☑ | 2026-08-01 | —. judge: author, judge: author | — |
20
- | **frame** | **Claim stated before the runs** | argument-arc (frame) | ☑ | 2026-07-20 | one paragraph filed | — |
21
-
22
- ### LOOP
23
- | id | Work | Skill | Status | Date | Result | Open |
24
- |----|------|-------|--------|------|--------|------|
25
- | study | Study + artifact | build-benchmark | ☑ | 2026-08-01 | ran | — |
26
- | draft | Draft | draft-paper | ☑ | 2026-08-01 | — | — |
27
- | arc | Argument arc | argument-arc | ☑ | 2026-08-01 | holds. judge: opus, judge: author | — |
28
-
29
- ### CONTINUOUS
30
- | id | Trigger | Skill | Status | Date | Result | Open |
31
- |----|---------|-------|--------|------|--------|------|
32
- | cites | any cite added/moved | verify-citations | ☑ | 2026-08-02 | clean | — |
33
- | siblings | a sibling surfaces | analyze-sibling-paper | ☑ | 2026-08-02 | 6 read. judge: opus, judge: author | siblings/ |
34
- | priordelta | framing moved | map-prior-work (delta) | ☑ | 2026-08-02 | no new | — |
35
- | render | any source edit | render-paper + page count | ☑ | 2026-08-02 | 8 pages / limit 8 | — |
36
-
37
- ### GATES
38
- | id | Gate | Skill | Requires | Status | Date | Result | Open |
39
- |----|------|-------|----------|--------|------|--------|------|
40
- | structure | Structure | tighten-paper | render, arc | ☑ | 2026-08-02 | cut plan applied. judge: opus, judge: author | — |
41
- | writing | Writing craft | grade-paper-writing | draft, arc, structure | ☑ | 2026-08-02 | 44/60. judge: sonnet, judge: author | — |
42
- | panel | Review decision | pc-panel-review | structure, writing | ☑ | 2026-08-03 | accept; judge: opus, judge: author | — |
43
- | claims | Claim preservation | Fable diff | draft, writing | ☑ | 2026-08-03 | clean | — |
44
- | harden | Harden (all axes) | harden-paper | panel, structure, writing, cites, priordelta | ☑ | 2026-08-03 | all axes green ; judge: opus, judge: author | — |
45
- | submit | Submit | submit-paper | harden, access | ☑ | 2026-08-03 | uploaded | — |
46
-
47
- ### AFTER
48
- | id | Work | Skill | Status | Date | Result | Open |
49
- |----|------|-------|--------|------|--------|------|
50
- | cameraready | Camera-ready | camera-ready | n/a | — | pre-acceptance | — |
51
- | extend | Extend | extend-paper | n/a | — | post-acceptance | — |
@@ -1,52 +0,0 @@
1
- # PIPELINE-STATUS — fixture: every failure this checker exists for
2
- Venue: Fixture Workshop · Deadline: 2026-08-06 · Blind: double · State: drafting
3
- Updated: 2026-01-01
4
-
5
- **Readiness verdict:** 88% accept probability, looking good.
6
-
7
- The `structure` row is missing `arc` from its Requires cell **on purpose** — that is the planted
8
- `undeclared-input`, copied from a defect found live on a real paper in August 2026. Every
9
- other gate cell here is the canonical set from `../../pipeline-edges.mjs`, so exactly one edge is
10
- undeclared and the finding cannot come from somewhere else.
11
-
12
- ### SETUP
13
- | id | Work | Skill | Status | Date | Result | Open |
14
- |----|------|-------|--------|------|--------|------|
15
- | idea | Validate idea | research-ideate | ☑ | 2026-01-01 | go | — |
16
- | priorwork | Map competitors | map-prior-work | ☑ | 2026-01-01 | 6 siblings | siblings/ |
17
- | venue | Pick venue | find-venue | ☑ | 2026-01-01 | fixture | — |
18
- | venuebar | Venue bar / levers | study-accepted-papers | ☐ | — | — | — |
19
- | **access** | 🔴 **Can you physically submit?** | plan-paper-timeline | ☐ | — | profile pending moderation | — |
20
- | schedule | Schedule + calendar | plan-paper-timeline | ☑ | 2026-01-01 | — | — |
21
- | **frame** | **Claim stated before the runs** | argument-arc (frame) | ☐ | — | — | — |
22
-
23
- ### LOOP
24
- | id | Work | Skill | Status | Date | Result | Open |
25
- |----|------|-------|--------|------|--------|------|
26
- | study | Study + artifact | build-benchmark | ☑ | 2026-01-01 | ran | — |
27
- | draft | Draft | draft-paper | ☑ | 2026-01-01 | — | — |
28
- | arc | Argument arc | argument-arc | ☐ | — | — | — |
29
-
30
- ### CONTINUOUS
31
- | id | Trigger | Skill | Status | Date | Result | Open |
32
- |----|---------|-------|--------|------|--------|------|
33
- | cites | any cite added/moved | verify-citations | ☑ | 2026-01-01 | clean | — |
34
- | siblings | a sibling surfaces | analyze-sibling-paper | ☑ | — | done | siblings/ |
35
- | priordelta | framing moved | map-prior-work (delta) | ☐ | — | — | — |
36
- | render | any source edit | render-paper + page count | ☑ | 2026-01-01 | 8 pages / limit 8 | — |
37
-
38
- ### GATES
39
- | id | Gate | Skill | Requires | Status | Date | Result | Open |
40
- |----|------|-------|----------|--------|------|--------|------|
41
- | structure | Structure | tighten-paper | render | ☑ | 2026-01-01 | cut plan | — |
42
- | writing | Writing craft | grade-paper-writing | draft, arc, structure | ☐ | — | — | — |
43
- | panel | Review decision | pc-panel-review | structure, writing | ☑ | 2026-01-01 | borderline | — |
44
- | claims | Claim preservation | Fable diff | draft, writing | ☐ | — | — | — |
45
- | harden | Harden (all axes) | harden-paper | panel, structure, writing, cites, priordelta | ☐ | — | — | — |
46
- | submit | Submit | submit-paper | harden, access | ☐ | — | — | — |
47
-
48
- ### AFTER
49
- | id | Work | Skill | Status | Date | Result | Open |
50
- |----|------|-------|--------|------|--------|------|
51
- | cameraready | Camera-ready | camera-ready | n/a | — | pre-acceptance | — |
52
- | extend | Extend | extend-paper | n/a | — | post-acceptance | — |
@@ -1,6 +0,0 @@
1
- # Fixture paper source
2
-
3
- This file exists only so `newestSourceDate` finds something. Its mtime is whenever the
4
- repo was checked out, which is always later than the 2026-01-01 dates in the fixture's
5
- CONTINUOUS rows — so the `stale-continuous` check fires deterministically without the
6
- test having to touch mtimes.
@@ -1,100 +0,0 @@
1
- /**
2
- * A PIPELINE-STATUS.md fixture shared by the pipeline-check harness and its CLI test: the four
3
- * template sections, every row green and dated `today`, so a test plants exactly one defect.
4
- */
5
- export const FAR = "2027-12-31";
6
-
7
- /** What a test may replace in one row: any cell, and for a GATES row its `Requires` cell. */
8
- export interface RowOverride {
9
- readonly status?: string;
10
- readonly date?: string;
11
- readonly skill?: string;
12
- readonly result?: string;
13
- readonly requires?: string;
14
- }
15
-
16
- /** Per-row overrides by row id, plus the four free-text parts of the file. */
17
- export interface ScorecardOverrides {
18
- readonly verdict?: string;
19
- readonly deadline?: string;
20
- readonly tail?: string;
21
- readonly preamble?: string;
22
- readonly [id: string]: RowOverride | string | undefined;
23
- }
24
-
25
- interface Row extends RowOverride {
26
- readonly id: string;
27
- readonly status: string;
28
- readonly date: string;
29
- }
30
-
31
- /** A row's overrides; the free-text keys never name a row, so a string here is none. */
32
- const cellsOf = (v: RowOverride | string | undefined): RowOverride =>
33
- typeof v === "object" ? v : {};
34
-
35
- /**
36
- * A scorecard with the four sections the template requires, all green. `over` replaces individual
37
- * cells: `{ cites: { date: "2020-01-01" } }`, `{ verdict: "…" }`, `{ deadline: "2026-08-18" }`,
38
- * `{ tail: "…" }` for anything appended after the last section, `{ preamble: "…" }` for anything
39
- * inserted BEFORE the first heading (the only way to plant a row under no heading at all).
40
- */
41
- export function scorecard(
42
- over: ScorecardOverrides = {},
43
- today: string = new Date().toISOString().slice(0, 10),
44
- ): string {
45
- const r = (id: string, status = "☑", date = today): Row => ({
46
- id,
47
- status,
48
- date,
49
- ...cellsOf(over[id]),
50
- });
51
- const row = (o: Row, ...mid: string[]): string =>
52
- `| ${o.id} | work | ${o.skill ?? "skill"} | ${mid.length ? mid.join(" | ") + " | " : ""}${o.status} | ${o.date} | ${o.result ?? "result"} | — |`;
53
- // The GATES `Requires` cell, canonical by default so the baseline is silent — `{ structure: {
54
- // requires: "render" } }` plants a dropped edge. `harden` deliberately declares four of its five
55
- // canonical inputs: the fifth (`priordelta`) has no row in this fixture, and the clean case asserts that the checker stays
56
- // quiet about an edge there is nowhere to point at.
57
- const req = (id: string, dflt: string): string =>
58
- cellsOf(over[id]).requires ?? dflt;
59
- const verdict =
60
- over.verdict ??
61
- "Submit-ready — every gate green, artifact reproduces clean, nothing blocking.";
62
- return [
63
- "# PIPELINE-STATUS — harness fixture",
64
- `Venue: Fixture Workshop · Deadline: ${over.deadline ?? FAR} · Blind: double · State: drafting`,
65
- "",
66
- `**Readiness verdict:** ${verdict}`,
67
- "",
68
- over.preamble ?? "",
69
- "",
70
- "### SETUP",
71
- "| id | Work | Skill | Status | Date | Result | Open |",
72
- "|----|------|-------|--------|------|--------|------|",
73
- row(r("idea")),
74
- row(r("access")),
75
- row(r("frame")),
76
- "",
77
- "### LOOP",
78
- "| id | Work | Skill | Status | Date | Result | Open |",
79
- "|----|------|-------|--------|------|--------|------|",
80
- row(r("study")),
81
- row(r("draft")),
82
- row(r("arc")),
83
- "",
84
- "### CONTINUOUS",
85
- "| id | Trigger | Skill | Status | Date | Result | Open |",
86
- "|----|---------|-------|--------|------|--------|------|",
87
- row(r("cites")),
88
- row(r("render")),
89
- "",
90
- "### GATES",
91
- "| id | Gate | Skill | Requires | Status | Date | Result | Open |",
92
- "|----|------|-------|----------|--------|------|--------|------|",
93
- row(r("structure"), req("structure", "render, arc")),
94
- row(r("writing"), req("writing", "draft, arc, structure")),
95
- row(r("panel"), req("panel", "structure, writing")),
96
- row(r("harden"), req("harden", "panel, structure, writing, cites")),
97
- "",
98
- over.tail ?? "",
99
- ].join("\n");
100
- }