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.
- package/.github/dependabot.yml +72 -0
- package/.github/workflows/ci.yml +297 -0
- package/.github/workflows/dependabot-automerge.yml +70 -0
- package/.github/workflows/pr-title.yml +59 -0
- package/.github/workflows/release.yml +54 -0
- package/CLAUDE.md +598 -0
- package/CONTRIBUTING.md +159 -0
- package/LICENSE +21 -0
- package/README.md +240 -0
- package/action.harness.mjs +287 -0
- package/action.mutations.mjs +162 -0
- package/action.yml +138 -0
- package/bin/rpp.mjs +43 -0
- package/dist/action-ref.d.ts +12 -0
- package/dist/action-ref.d.ts.map +1 -0
- package/dist/action-ref.js +16 -0
- package/dist/action-ref.js.map +1 -0
- package/dist/adapters/banal/failure.d.ts +73 -0
- package/dist/adapters/banal/failure.d.ts.map +1 -0
- package/dist/adapters/banal/failure.js +58 -0
- package/dist/adapters/banal/failure.js.map +1 -0
- package/dist/adapters/banal/index.d.ts +17 -0
- package/dist/adapters/banal/index.d.ts.map +1 -0
- package/dist/adapters/banal/index.js +56 -0
- package/dist/adapters/banal/index.js.map +1 -0
- package/dist/adapters/banal/install.d.ts +26 -0
- package/dist/adapters/banal/install.d.ts.map +1 -0
- package/dist/adapters/banal/install.js +15 -0
- package/dist/adapters/banal/install.js.map +1 -0
- package/dist/adapters/banal/invocation.d.ts +48 -0
- package/dist/adapters/banal/invocation.d.ts.map +1 -0
- package/dist/adapters/banal/invocation.js +43 -0
- package/dist/adapters/banal/invocation.js.map +1 -0
- package/dist/adapters/banal/locate.d.ts +50 -0
- package/dist/adapters/banal/locate.d.ts.map +1 -0
- package/dist/adapters/banal/locate.js +34 -0
- package/dist/adapters/banal/locate.js.map +1 -0
- package/dist/adapters/banal/output.d.ts +27 -0
- package/dist/adapters/banal/output.d.ts.map +1 -0
- package/dist/adapters/banal/output.js +112 -0
- package/dist/adapters/banal/output.js.map +1 -0
- package/dist/adapters/banal/pin.d.ts +19 -0
- package/dist/adapters/banal/pin.d.ts.map +1 -0
- package/dist/adapters/banal/pin.js +15 -0
- package/dist/adapters/banal/pin.js.map +1 -0
- package/dist/adapters/banal/probe.d.ts +12 -0
- package/dist/adapters/banal/probe.d.ts.map +1 -0
- package/dist/adapters/banal/probe.js +27 -0
- package/dist/adapters/banal/probe.js.map +1 -0
- package/dist/adapters/banal/run.d.ts +89 -0
- package/dist/adapters/banal/run.d.ts.map +1 -0
- package/dist/adapters/banal/run.js +104 -0
- package/dist/adapters/banal/run.js.map +1 -0
- package/dist/adapters/banal/settings.d.ts +18 -0
- package/dist/adapters/banal/settings.d.ts.map +1 -0
- package/dist/adapters/banal/settings.js +29 -0
- package/dist/adapters/banal/settings.js.map +1 -0
- package/dist/adapters/banal/xml.d.ts +48 -0
- package/dist/adapters/banal/xml.d.ts.map +1 -0
- package/dist/adapters/banal/xml.js +67 -0
- package/dist/adapters/banal/xml.js.map +1 -0
- package/dist/adapters/curl/download.io.d.ts +14 -0
- package/dist/adapters/curl/download.io.d.ts.map +1 -0
- package/dist/adapters/curl/download.io.js +69 -0
- package/dist/adapters/curl/download.io.js.map +1 -0
- package/dist/adapters/curl/index.d.ts +6 -0
- package/dist/adapters/curl/index.d.ts.map +1 -0
- package/dist/adapters/curl/index.js +6 -0
- package/dist/adapters/curl/index.js.map +1 -0
- package/dist/adapters/memory/index.d.ts +43 -0
- package/dist/adapters/memory/index.d.ts.map +1 -0
- package/dist/adapters/memory/index.js +79 -0
- package/dist/adapters/memory/index.js.map +1 -0
- package/dist/adapters/node/files.io.d.ts +3 -0
- package/dist/adapters/node/files.io.d.ts.map +1 -0
- package/dist/adapters/node/files.io.js +31 -0
- package/dist/adapters/node/files.io.js.map +1 -0
- package/dist/adapters/node/host.io.d.ts +3 -0
- package/dist/adapters/node/host.io.d.ts.map +1 -0
- package/dist/adapters/node/host.io.js +14 -0
- package/dist/adapters/node/host.io.js.map +1 -0
- package/dist/adapters/node/index.d.ts +25 -0
- package/dist/adapters/node/index.d.ts.map +1 -0
- package/dist/adapters/node/index.js +14 -0
- package/dist/adapters/node/index.js.map +1 -0
- package/dist/adapters/node/process.io.d.ts +14 -0
- package/dist/adapters/node/process.io.d.ts.map +1 -0
- package/dist/adapters/node/process.io.js +41 -0
- package/dist/adapters/node/process.io.js.map +1 -0
- package/dist/adapters/node/workspace.io.d.ts +4 -0
- package/dist/adapters/node/workspace.io.d.ts.map +1 -0
- package/dist/adapters/node/workspace.io.js +33 -0
- package/dist/adapters/node/workspace.io.js.map +1 -0
- package/dist/adapters/pdfjs/fill.d.ts +42 -0
- package/dist/adapters/pdfjs/fill.d.ts.map +1 -0
- package/dist/adapters/pdfjs/fill.js +91 -0
- package/dist/adapters/pdfjs/fill.js.map +1 -0
- package/dist/build-engine.d.ts +48 -0
- package/dist/build-engine.d.ts.map +1 -0
- package/dist/build-engine.js +148 -0
- package/dist/build-engine.js.map +1 -0
- package/dist/build.d.ts +163 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +575 -0
- package/dist/build.js.map +1 -0
- package/dist/cli.d.ts +151 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +951 -0
- package/dist/cli.js.map +1 -0
- package/dist/doctor.d.ts +42 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +280 -0
- package/dist/doctor.js.map +1 -0
- package/dist/domain/geometry.d.ts +71 -0
- package/dist/domain/geometry.d.ts.map +1 -0
- package/dist/domain/geometry.js +35 -0
- package/dist/domain/geometry.js.map +1 -0
- package/dist/domain/host.d.ts +16 -0
- package/dist/domain/host.d.ts.map +1 -0
- package/dist/domain/host.js +8 -0
- package/dist/domain/host.js.map +1 -0
- package/dist/domain/page-layout.d.ts +34 -0
- package/dist/domain/page-layout.d.ts.map +1 -0
- package/dist/domain/page-layout.js +8 -0
- package/dist/domain/page-layout.js.map +1 -0
- package/dist/domain/paths.d.ts +5 -0
- package/dist/domain/paths.d.ts.map +1 -0
- package/dist/domain/paths.js +2 -0
- package/dist/domain/paths.js.map +1 -0
- package/dist/domain/result.d.ts +23 -0
- package/dist/domain/result.d.ts.map +1 -0
- package/dist/domain/result.js +10 -0
- package/dist/domain/result.js.map +1 -0
- package/dist/domain/sha256.d.ts +7 -0
- package/dist/domain/sha256.d.ts.map +1 -0
- package/dist/domain/sha256.js +14 -0
- package/dist/domain/sha256.js.map +1 -0
- package/dist/domain/text.d.ts +6 -0
- package/dist/domain/text.d.ts.map +1 -0
- package/dist/domain/text.js +7 -0
- package/dist/domain/text.js.map +1 -0
- package/dist/engine.d.ts +93 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +119 -0
- package/dist/engine.js.map +1 -0
- package/dist/exit-code.d.ts +22 -0
- package/dist/exit-code.d.ts.map +1 -0
- package/dist/exit-code.js +10 -0
- package/dist/exit-code.js.map +1 -0
- package/dist/facts-file.d.ts +96 -0
- package/dist/facts-file.d.ts.map +1 -0
- package/dist/facts-file.js +134 -0
- package/dist/facts-file.js.map +1 -0
- package/dist/hooks-settings.d.ts +141 -0
- package/dist/hooks-settings.d.ts.map +1 -0
- package/dist/hooks-settings.js +306 -0
- package/dist/hooks-settings.js.map +1 -0
- package/dist/init.d.ts +201 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +579 -0
- package/dist/init.js.map +1 -0
- package/dist/latex-log.d.ts +80 -0
- package/dist/latex-log.d.ts.map +1 -0
- package/dist/latex-log.js +187 -0
- package/dist/latex-log.js.map +1 -0
- package/dist/latex-loop.d.ts +129 -0
- package/dist/latex-loop.d.ts.map +1 -0
- package/dist/latex-loop.js +113 -0
- package/dist/latex-loop.js.map +1 -0
- package/dist/link-skills.d.ts +51 -0
- package/dist/link-skills.d.ts.map +1 -0
- package/dist/link-skills.js +199 -0
- package/dist/link-skills.js.map +1 -0
- package/dist/new-paper.d.ts +48 -0
- package/dist/new-paper.d.ts.map +1 -0
- package/dist/new-paper.js +110 -0
- package/dist/new-paper.js.map +1 -0
- package/dist/pdf-facts.d.ts +44 -0
- package/dist/pdf-facts.d.ts.map +1 -0
- package/dist/pdf-facts.js +239 -0
- package/dist/pdf-facts.js.map +1 -0
- package/dist/pdf-geometry.d.ts +170 -0
- package/dist/pdf-geometry.d.ts.map +1 -0
- package/dist/pdf-geometry.js +158 -0
- package/dist/pdf-geometry.js.map +1 -0
- package/dist/ports/download.d.ts +9 -0
- package/dist/ports/download.d.ts.map +1 -0
- package/dist/ports/download.js +2 -0
- package/dist/ports/download.js.map +1 -0
- package/dist/ports/files.d.ts +11 -0
- package/dist/ports/files.d.ts.map +1 -0
- package/dist/ports/files.js +2 -0
- package/dist/ports/files.js.map +1 -0
- package/dist/ports/measure-geometry.d.ts +8 -0
- package/dist/ports/measure-geometry.d.ts.map +1 -0
- package/dist/ports/measure-geometry.js +2 -0
- package/dist/ports/measure-geometry.js.map +1 -0
- package/dist/ports/process.d.ts +45 -0
- package/dist/ports/process.d.ts.map +1 -0
- package/dist/ports/process.js +2 -0
- package/dist/ports/process.js.map +1 -0
- package/dist/ports/tool-installer.d.ts +29 -0
- package/dist/ports/tool-installer.d.ts.map +1 -0
- package/dist/ports/tool-installer.js +2 -0
- package/dist/ports/tool-installer.js.map +1 -0
- package/dist/ports/workspace.d.ts +18 -0
- package/dist/ports/workspace.d.ts.map +1 -0
- package/dist/ports/workspace.js +2 -0
- package/dist/ports/workspace.js.map +1 -0
- package/dist/rules-config.d.ts +34 -0
- package/dist/rules-config.d.ts.map +1 -0
- package/dist/rules-config.js +132 -0
- package/dist/rules-config.js.map +1 -0
- package/dist/structure.d.ts +34 -0
- package/dist/structure.d.ts.map +1 -0
- package/dist/structure.js +149 -0
- package/dist/structure.js.map +1 -0
- package/dist/tex-requirements.d.ts +43 -0
- package/dist/tex-requirements.d.ts.map +1 -0
- package/dist/tex-requirements.js +127 -0
- package/dist/tex-requirements.js.map +1 -0
- package/dist/toolchain.d.ts +159 -0
- package/dist/toolchain.d.ts.map +1 -0
- package/dist/toolchain.js +542 -0
- package/dist/toolchain.js.map +1 -0
- package/dist/types.d.ts +110 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/configuration.md +235 -0
- package/docs/e2e.md +152 -0
- package/docs/incidents.md +59 -0
- package/docs/install.md +170 -0
- package/docs/optional-rules.md +107 -0
- package/docs/package-shape-options.md +262 -0
- package/docs/prior-art/README.md +76 -0
- package/docs/prior-art/blocking-vs-advisory.md +83 -0
- package/docs/prior-art/content-delivery.md +124 -0
- package/docs/prior-art/multi-mode-tools.md +106 -0
- package/docs/prior-art/nondeterministic-checks.md +99 -0
- package/docs/prior-art/package-location.md +422 -0
- package/docs/prior-art/paper-folder-scaffolding.md +538 -0
- package/docs/prior-art/readme-structure.md +69 -0
- package/docs/prior-art/repro/README.md +92 -0
- package/docs/prior-art/repro/claim1-allowedtools.mjs +66 -0
- package/docs/prior-art/repro/claim1-at2.mjs +40 -0
- package/docs/prior-art/repro/claim1-crosschannel.mjs +54 -0
- package/docs/prior-art/repro/claim1-frontmatter.mjs +76 -0
- package/docs/prior-art/repro/claim1-hook-payload-reporter.mjs +10 -0
- package/docs/prior-art/repro/claim1-plugin-frontmatter.mjs +27 -0
- package/docs/prior-art/repro/claim1-plugin-skill.mjs +52 -0
- package/docs/prior-art/repro/claim1-project-skill.mjs +81 -0
- package/docs/prior-art/repro/claim2-marketplace-flat-asclaimed.json +1 -0
- package/docs/prior-art/repro/claim2-marketplace-negative-control.json +1 -0
- package/docs/prior-art/repro/claim2-marketplace-nested-exact.json +9 -0
- package/docs/prior-art/repro/claim2-marketplace-nested-noversion.json +9 -0
- package/docs/prior-art/repro/claim2-marketplace-nested-range.json +1 -0
- package/docs/prior-art/repro/claim3-imports.mjs +50 -0
- package/docs/prior-art/repro/claim4-find-package-json.mjs +8 -0
- package/docs/prior-art/repro/claim4-package-dir.mjs +39 -0
- package/docs/prior-art/repro/claim4-parent-arg.mjs +17 -0
- package/docs/prior-art/repro/claim4-resolve-apis.mjs +21 -0
- package/docs/prior-art/repro/claim4-setup-consumers.mjs +45 -0
- package/docs/prior-art/repro/claim4-yarn-pnp.mjs +70 -0
- package/docs/prior-art/repro/claim5-bin-launch.mjs +39 -0
- package/docs/prior-art/repro/claim5-exports-mutation.mjs +57 -0
- package/docs/prior-art/repro/claim5-resolved-location-and-bin.mjs +33 -0
- package/docs/prior-art/repro/claim6-candidate-ambiguity.mjs +17 -0
- package/docs/prior-art/repro/claim6-doc-path-candidates.mjs +27 -0
- package/docs/prior-art/test-tooling.md +131 -0
- package/docs/rules.md +58 -0
- package/docs/texlive-install-decision.md +230 -0
- package/docs/toolchain.md +152 -0
- package/eslint-rules/doc-fields.harness.mjs +336 -0
- package/eslint-rules/doc-fields.mjs +186 -0
- package/eslint-rules/doc-fields.mutations.mjs +96 -0
- package/eslint-rules/install-path-literals.harness.mjs +121 -0
- package/eslint-rules/install-path-literals.mjs +108 -0
- package/eslint-rules/install-path-literals.mutations.mjs +62 -0
- package/eslint-rules/latex-language.harness.mjs +599 -0
- package/eslint-rules/latex-language.mjs +591 -0
- package/eslint-rules/latex-language.mutations.mjs +196 -0
- package/eslint-rules/paper-research-question.harness.mjs +146 -0
- package/eslint-rules/paper-research-question.mjs +180 -0
- package/eslint-rules/paper-research-question.mutations.mjs +127 -0
- package/eslint-rules/paper-stages.harness.mjs +356 -0
- package/eslint-rules/paper-stages.mjs +455 -0
- package/eslint-rules/paper-stages.mutations.mjs +157 -0
- package/eslint-rules/paper-typography.harness.mjs +291 -0
- package/eslint-rules/paper-typography.mjs +313 -0
- package/eslint-rules/paper-typography.mutations.mjs +131 -0
- package/eslint-rules/papers.harness.mjs +259 -0
- package/eslint-rules/papers.mjs +166 -0
- package/eslint-rules/papers.mutations.mjs +186 -0
- package/eslint-rules/pdf-last-page-balance.harness.mjs +206 -0
- package/eslint-rules/pdf-last-page-balance.mjs +208 -0
- package/eslint-rules/review-findings-cause.harness.mjs +228 -0
- package/eslint-rules/review-findings-cause.mjs +135 -0
- package/eslint-rules/review-findings-cause.mutations.mjs +72 -0
- package/eslint-rules/temp-root-realpath.harness.mjs +176 -0
- package/eslint-rules/temp-root-realpath.mjs +129 -0
- package/eslint-rules/temp-root-realpath.mutations.mjs +99 -0
- package/eslint-rules/tex-build.harness.mjs +753 -0
- package/eslint-rules/tex-build.mjs +322 -0
- package/eslint-rules/tex-build.mutations.mjs +258 -0
- package/eslint.config.mjs +521 -0
- package/fixtures/build-e2e/acmart/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/acmart/paper.tex +11 -0
- package/fixtures/build-e2e/acmart/venue.json +1 -0
- package/fixtures/build-e2e/broken/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/broken/paper.tex +7 -0
- package/fixtures/build-e2e/cite/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/cite/build.sh +5 -0
- package/fixtures/build-e2e/cite/paper.tex +10 -0
- package/fixtures/build-e2e/cite/refs.bib +9 -0
- package/fixtures/build-e2e/empty/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/empty/paper.tex +6 -0
- package/fixtures/build-e2e/fallback/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/fallback/paper.tex +11 -0
- package/fixtures/build-e2e/guards/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/guards/paper.tex +10 -0
- package/fixtures/build-e2e/no-source/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/unbalanced/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/unbalanced/paper.tex +28 -0
- package/fixtures/build-e2e/unbalanced/refs.bib +269 -0
- package/fixtures/install-path-literals/clean.fixture.mjs +3 -0
- package/fixtures/install-path-literals/clean.md +15 -0
- package/fixtures/install-path-literals/defect.fixture.mjs +3 -0
- package/fixtures/install-path-literals/defect.md +14 -0
- package/fixtures/latex-language/clean.tex +50 -0
- package/fixtures/latex-language/defect.tex +52 -0
- package/fixtures/paper-research-question/comment-only/PIPELINE-STATUS.md +9 -0
- package/fixtures/paper-research-question/comment-only/paper.tex +7 -0
- package/fixtures/paper-research-question/declared-not-in-paper/PIPELINE-STATUS.md +10 -0
- package/fixtures/paper-research-question/declared-not-in-paper/paper.tex +6 -0
- package/fixtures/paper-research-question/draft/PIPELINE-STATUS.md +6 -0
- package/fixtures/paper-research-question/draft/paper.tex +2 -0
- package/fixtures/paper-research-question/markdown-no-rq/PIPELINE-STATUS.md +9 -0
- package/fixtures/paper-research-question/markdown-no-rq/paper.md +4 -0
- package/fixtures/paper-research-question/shipped-no-rq/PIPELINE-STATUS.md +12 -0
- package/fixtures/paper-research-question/shipped-no-rq/paper.tex +3 -0
- package/fixtures/paper-research-question/shipped-with-rq/PIPELINE-STATUS.md +10 -0
- package/fixtures/paper-research-question/shipped-with-rq/paper.tex +2 -0
- package/fixtures/paper-stages/authors-ran/PIPELINE-STATUS.md +16 -0
- package/fixtures/paper-stages/marker-in-prose/PIPELINE-STATUS.md +17 -0
- package/fixtures/paper-stages/nofile/PIPELINE-STATUS.md +8 -0
- package/fixtures/paper-stages/noheader/PIPELINE-STATUS.md +1 -0
- package/fixtures/paper-stages/noheader/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-stages/nothing/PIPELINE-STATUS.md +3 -0
- package/fixtures/paper-stages/ok/PIPELINE-STATUS.md +9 -0
- package/fixtures/paper-stages/ok/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-stages/stale/PIPELINE-STATUS.md +1 -0
- package/fixtures/paper-stages/stale/versions/2026-07-22-submitted.STALE-WRONG-FILE.pdf +0 -0
- package/fixtures/paper-stages/twice/PIPELINE-STATUS.md +14 -0
- package/fixtures/paper-stages/twice/versions/2026-08-06-submitted.pdf +0 -0
- package/fixtures/paper-stages/twice/versions/2026-10-24-submitted.pdf +0 -0
- package/fixtures/paper-stages/undeclared/PIPELINE-STATUS.md +8 -0
- package/fixtures/paper-stages/undeclared/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-stages/undeclared/versions/2026-08-29-camera-ready.pdf +0 -0
- package/fixtures/paper-stages/wrongsize/PIPELINE-STATUS.md +8 -0
- package/fixtures/paper-stages/wrongsize/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-typography/clean-paper/paper.tex +29 -0
- package/fixtures/paper-typography/messy-paper/paper.tex +27 -0
- package/fixtures/pdf-facts/README.md +22 -0
- package/fixtures/pdf-facts/corrupt-font.pdf +0 -0
- package/fixtures/pdf-facts/encrypted.pdf +0 -0
- package/fixtures/pdf-facts/hidden-text.pdf +0 -0
- package/fixtures/pdf-facts/hidden-text.tex +28 -0
- package/fixtures/pdf-facts/t3-all.pdf +0 -0
- package/fixtures/pdf-facts/t3-all.tex +8 -0
- package/fixtures/pdf-facts/t3-mixed.pdf +0 -0
- package/fixtures/pdf-facts/t3-mixed.tex +9 -0
- package/fixtures/pdf-facts/ttf.pdf +2240 -1
- package/fixtures/pdf-facts/ttf.tex +6 -0
- package/fixtures/real-markdown-paper/baseline.json +24 -0
- package/fixtures/real-markdown-paper/baseline.mjs +48 -0
- package/fixtures/render-paper/build-clean.sh +25 -0
- package/fixtures/render-paper/build-defect.sh +15 -0
- package/fixtures/review-findings-cause/clean.md +17 -0
- package/fixtures/review-findings-cause/defect.md +14 -0
- package/fixtures/review-findings-cause/old-debt.md +14 -0
- package/fixtures/review-findings-cause/quiet-in-fence.md +16 -0
- package/fixtures/tex-build/clean.tex +21 -0
- package/fixtures/tex-build/defect.tex +24 -0
- package/fixtures/tex-build/frontmatter-clean.tex +25 -0
- package/fixtures/tex-build/frontmatter-defect.tex +23 -0
- package/fixtures/toolchain-mirror/catalog.txt +5 -0
- package/fixtures/toolchain-mirror/install-tl +27 -0
- package/fixtures/toolchain-mirror/release-texlive.txt +3 -0
- package/fixtures/toolchain-mirror/release-year +1 -0
- package/fixtures/toolchain-mirror/stub-kpsewhich +8 -0
- package/fixtures/toolchain-mirror/stub-pdflatex +3 -0
- package/fixtures/toolchain-mirror/stub-tlmgr +44 -0
- package/hooks/hooks.harness.mjs +713 -0
- package/hooks/hooks.mutations.mjs +337 -0
- package/hooks/paper-edit-guard.hook.d.mts +13 -0
- package/hooks/paper-edit-guard.hook.mjs +457 -0
- package/hooks/paper-skills-nudge.hook.mjs +136 -0
- package/hooks/paper-status-gates.hook.mjs +156 -0
- package/hooks/paper-status-gates.sh +91 -0
- package/lib/agent-cli-version.harness.mjs +165 -0
- package/lib/agent-cli-version.mjs +106 -0
- package/lib/agent-cli-version.mutations.mjs +109 -0
- package/lib/markdown.mjs +386 -0
- package/lib/mutation-driver.harness.mjs +227 -0
- package/lib/mutation-driver.mjs +397 -0
- package/lib/mutation-driver.mutations.mjs +68 -0
- package/lib/paper-config.d.mts +34 -0
- package/lib/paper-config.harness.mjs +286 -0
- package/lib/paper-config.mjs +142 -0
- package/lib/paper-config.mutations.mjs +143 -0
- package/lib/skill-checks.mjs +701 -0
- package/lib/skill-corpus.mjs +403 -0
- package/lib/skill-eval-fixture.mjs +63 -0
- package/lib/skill-eval-kit.mjs +257 -0
- package/lib/skill-trigger-cases.harness.mjs +170 -0
- package/lib/skill-trigger-cases.mjs +446 -0
- package/lib/skill-trigger-cases.mutations.mjs +65 -0
- package/lib/trigger-ledger.mjs +215 -0
- package/package.json +97 -0
- package/plugin/.claude-plugin/plugin.json +8 -0
- package/plugin/hooks/hooks.json +30 -0
- package/scripts/check.harness.mjs +177 -0
- package/scripts/check.mjs +239 -0
- package/scripts/check.mutations.mjs +110 -0
- package/scripts/eslint-report-guard.mjs +82 -0
- package/scripts/exclusive.mjs +138 -0
- package/scripts/harness-api.frozen.json +76 -0
- package/scripts/harness-api.test.ts +175 -0
- package/scripts/layer-legacy-frozen.d.mts +28 -0
- package/scripts/layer-legacy-frozen.mjs +152 -0
- package/scripts/layer-legacy-frozen.test.ts +115 -0
- package/scripts/layer-legacy.frozen.json +50 -0
- package/scripts/mutation-batteries-frozen.harness.mjs +204 -0
- package/scripts/mutation-batteries-frozen.mjs +238 -0
- package/scripts/mutation-batteries.frozen.json +117 -0
- package/scripts/release-config.test.ts +90 -0
- package/scripts/rules-are-content-only.harness.mjs +113 -0
- package/scripts/rules-are-content-only.mjs +138 -0
- package/scripts/rules-are-content-only.mutations.mjs +81 -0
- package/scripts/rules-see-files.harness.mjs +115 -0
- package/scripts/rules-see-files.mjs +99 -0
- package/scripts/rules-see-files.mutations.mjs +131 -0
- package/scripts/run-mutations.mjs +100 -0
- package/scripts/semantic-release-plugins.d.ts +16 -0
- package/skills/README.md +15 -0
- package/skills/analyze-sibling-paper/SKILL.md +170 -0
- package/skills/analyze-sibling-paper/SKILL.md.spec.ts +186 -0
- package/skills/analyze-sibling-paper/analyze-sibling-paper.eval.mjs +19 -0
- package/skills/analyze-sibling-paper/analyze-sibling-paper.harness.mjs +23 -0
- package/skills/argument-arc/SKILL.md +177 -0
- package/skills/argument-arc/SKILL.md.spec.ts +192 -0
- package/skills/argument-arc/argument-arc.eval.mjs +19 -0
- package/skills/argument-arc/argument-arc.harness.mjs +23 -0
- package/skills/build-benchmark/SKILL.md +213 -0
- package/skills/build-benchmark/SKILL.md.spec.ts +220 -0
- package/skills/build-benchmark/build-benchmark.eval.mjs +19 -0
- package/skills/build-benchmark/build-benchmark.harness.mjs +23 -0
- package/skills/build-benchmark/references/adversarial-cold-repro.md +68 -0
- package/skills/camera-ready/SKILL.md +148 -0
- package/skills/camera-ready/SKILL.md.spec.ts +164 -0
- package/skills/camera-ready/camera-ready.eval.mjs +19 -0
- package/skills/camera-ready/camera-ready.harness.mjs +23 -0
- package/skills/cold-read-diff/SKILL.md +160 -0
- package/skills/cold-read-diff/SKILL.md.spec.ts +166 -0
- package/skills/cold-read-diff/cold-read-diff.eval.mjs +19 -0
- package/skills/cold-read-diff/cold-read-diff.harness.mjs +23 -0
- package/skills/draft-paper/SKILL.md +152 -0
- package/skills/draft-paper/SKILL.md.spec.ts +169 -0
- package/skills/draft-paper/draft-paper.eval.mjs +19 -0
- package/skills/draft-paper/draft-paper.harness.mjs +23 -0
- package/skills/extend-paper/SKILL.md +99 -0
- package/skills/extend-paper/SKILL.md.spec.ts +116 -0
- package/skills/extend-paper/extend-paper.eval.mjs +19 -0
- package/skills/extend-paper/extend-paper.harness.mjs +23 -0
- package/skills/find-venue/SKILL.md +128 -0
- package/skills/find-venue/SKILL.md.spec.ts +145 -0
- package/skills/find-venue/find-venue.eval.mjs +19 -0
- package/skills/find-venue/find-venue.harness.mjs +23 -0
- package/skills/grade-paper-writing/SKILL.md +436 -0
- package/skills/grade-paper-writing/SKILL.md.spec.ts +453 -0
- package/skills/grade-paper-writing/fixtures/control_gopen.txt +1 -0
- package/skills/grade-paper-writing/fixtures/control_human_paper.txt +1 -0
- package/skills/grade-paper-writing/fixtures/rewrite.txt +1 -0
- package/skills/grade-paper-writing/fixtures/specimen.txt +1 -0
- package/skills/grade-paper-writing/fixtures/structure-checks.md +22 -0
- package/skills/grade-paper-writing/grade-paper-writing.eval.mjs +19 -0
- package/skills/grade-paper-writing/grade-paper-writing.harness.mjs +23 -0
- package/skills/grade-paper-writing/prose-lint.mjs +713 -0
- package/skills/harden-paper/SKILL.md +318 -0
- package/skills/harden-paper/SKILL.md.spec.ts +336 -0
- package/skills/harden-paper/check-numbers.sh +33 -0
- package/skills/harden-paper/check-release-claims.sh +35 -0
- package/skills/harden-paper/fixtures/uncited-assertions-sample.md +43 -0
- package/skills/harden-paper/fixtures/uncited-assertions-sample.tex +77 -0
- package/skills/harden-paper/harden-paper.eval.mjs +19 -0
- package/skills/harden-paper/harden-paper.harness.mjs +23 -0
- package/skills/map-prior-work/SKILL.md +211 -0
- package/skills/map-prior-work/SKILL.md.spec.ts +227 -0
- package/skills/map-prior-work/map-prior-work.eval.mjs +19 -0
- package/skills/map-prior-work/map-prior-work.harness.mjs +23 -0
- package/skills/osf-artifact-upload/SKILL.md +52 -0
- package/skills/osf-artifact-upload/SKILL.md.spec.ts +59 -0
- package/skills/osf-artifact-upload/osf-artifact-upload.eval.mjs +22 -0
- package/skills/osf-artifact-upload/osf-artifact-upload.harness.mjs +103 -0
- package/skills/paper-adversarial-review/SKILL.md +126 -0
- package/skills/paper-adversarial-review/SKILL.md.spec.ts +142 -0
- package/skills/paper-adversarial-review/paper-adversarial-review.eval.mjs +19 -0
- package/skills/paper-adversarial-review/paper-adversarial-review.harness.mjs +23 -0
- package/skills/paper-pipeline/PIPELINE-MAP.md +371 -0
- package/skills/paper-pipeline/SKILL.md +499 -0
- package/skills/paper-pipeline/SKILL.md.spec.ts +517 -0
- package/skills/paper-pipeline/description-language.eval.mjs +347 -0
- package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +891 -0
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +1254 -0
- package/skills/paper-pipeline/paper-pipeline.eval.mjs +22 -0
- package/skills/paper-pipeline/paper-pipeline.harness.mjs +143 -0
- package/skills/paper-pipeline/pipeline-firing.baseline.json +270 -0
- package/skills/paper-pipeline/pipeline-firing.eval.mjs +664 -0
- package/skills/paper-pipeline/pipeline-language.eval.mjs +672 -0
- package/skills/paper-pipeline/references/acceptance-gate.md +329 -0
- package/skills/paper-pipeline/references/acl-venue-rules.md +142 -0
- package/skills/paper-pipeline/references/anonymization.md +68 -0
- package/skills/paper-pipeline/references/artifact-checklist.md +93 -0
- package/skills/paper-pipeline/references/body-vs-appendix.md +97 -0
- package/skills/paper-pipeline/references/credit-criteria.md +69 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/README.md +35 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/run_retext.mjs +24 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/sentences.txt +11 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/test_sentences.py +25 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-prose-checkers.md +538 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-reproducible-tooling.md +431 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-staleness-and-orchestration.md +592 -0
- package/skills/paper-pipeline/references/pipeline-status-template.md +162 -0
- package/skills/paper-pipeline/references/review-ratchet.md +36 -0
- package/skills/paper-pipeline/references/sweep-2026-08-09-ideal-pipeline.md +585 -0
- package/skills/paper-pipeline/references/writing-craft.md +448 -0
- package/skills/paper-pipeline/repro/2026-08-07-description-language-control.log +63 -0
- package/skills/paper-pipeline/repro/2026-08-07-fork-check.log +52 -0
- package/skills/paper-pipeline/repro/2026-08-07-fork-check2.log +33 -0
- package/skills/paper-pipeline/repro/2026-08-07-language-eval-pilot.log +33 -0
- package/skills/paper-pipeline/repro/2026-08-07-language-eval-raw.log +166 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.json +338 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.log +118 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-raw.log +245 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary.json +776 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-oracle.log +53 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-raw.log +89 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.json +450 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.log +136 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-raw.log +242 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-setupdiff.log +59 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation.json +1032 -0
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.json +139 -0
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.log +98 -0
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication.mjs +92 -0
- package/skills/paper-pipeline/repro/README.md +129 -0
- package/skills/paper-pipeline/repro/analyze-language-eval.py +116 -0
- package/skills/paper-pipeline/scripts/README.md +344 -0
- package/skills/paper-pipeline/scripts/announce.mjs +67 -0
- package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +496 -0
- package/skills/paper-pipeline/scripts/artifact-coverage.mjs +397 -0
- package/skills/paper-pipeline/scripts/artifact-coverage.mutations.mjs +218 -0
- package/skills/paper-pipeline/scripts/check-provenance.mjs +184 -0
- package/skills/paper-pipeline/scripts/consumer.d.mts +32 -0
- package/skills/paper-pipeline/scripts/consumer.harness.mjs +562 -0
- package/skills/paper-pipeline/scripts/consumer.mjs +535 -0
- package/skills/paper-pipeline/scripts/consumer.mutations.mjs +190 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +457 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +656 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.mutations.mjs +54 -0
- package/skills/paper-pipeline/scripts/fixtures/clean/PIPELINE-STATUS.md +51 -0
- package/skills/paper-pipeline/scripts/fixtures/dirty/PIPELINE-STATUS.md +52 -0
- package/skills/paper-pipeline/scripts/fixtures/dirty/paper.md +6 -0
- package/skills/paper-pipeline/scripts/fixtures/real-bib/refs.bib +153 -0
- package/skills/paper-pipeline/scripts/generated-code.harness.mjs +466 -0
- package/skills/paper-pipeline/scripts/generated-code.mjs +338 -0
- package/skills/paper-pipeline/scripts/generated-code.mutations.mjs +254 -0
- package/skills/paper-pipeline/scripts/ledger.mjs +623 -0
- package/skills/paper-pipeline/scripts/ledger.selftest.mjs +286 -0
- package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +389 -0
- package/skills/paper-pipeline/scripts/pipeline-check.mjs +737 -0
- package/skills/paper-pipeline/scripts/pipeline-check.mutations.mjs +54 -0
- package/skills/paper-pipeline/scripts/pipeline-edges.mjs +169 -0
- package/skills/paper-pipeline/scripts/population-map.harness.mjs +178 -0
- package/skills/paper-pipeline/scripts/population-map.mjs +181 -0
- package/skills/paper-pipeline/scripts/population-map.mutations.mjs +65 -0
- package/skills/paper-pipeline/scripts/population-map.selftest.mjs +122 -0
- package/skills/paper-pipeline/scripts/provenance.harness.mjs +240 -0
- package/skills/paper-pipeline/scripts/provenance.mutations.mjs +59 -0
- package/skills/paper-pipeline/scripts/round-diff.harness.mjs +881 -0
- package/skills/paper-pipeline/scripts/round-diff.mjs +576 -0
- package/skills/paper-pipeline/scripts/round-diff.mutations.mjs +276 -0
- package/skills/paper-pipeline/scripts/run-mechanical.mjs +633 -0
- package/skills/paper-pipeline/scripts/status.mjs +295 -0
- package/skills/paper-status/SKILL.md +183 -0
- package/skills/paper-status/SKILL.md.spec.ts +190 -0
- package/skills/paper-status/paper-status.eval.mjs +22 -0
- package/skills/paper-status/paper-status.harness.mjs +25 -0
- package/skills/pc-panel-review/SKILL.md +263 -0
- package/skills/pc-panel-review/SKILL.md.spec.ts +280 -0
- package/skills/pc-panel-review/pc-panel-review.eval.mjs +19 -0
- package/skills/pc-panel-review/pc-panel-review.harness.mjs +23 -0
- package/skills/plan-paper-timeline/SKILL.md +182 -0
- package/skills/plan-paper-timeline/SKILL.md.spec.ts +200 -0
- package/skills/plan-paper-timeline/fixtures/fake-google-calendar.mjs +239 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +431 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.mutations.mjs +65 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.eval.mjs +19 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.harness.mjs +23 -0
- package/skills/render-paper/SKILL.md +159 -0
- package/skills/render-paper/SKILL.md.spec.ts +166 -0
- package/skills/render-paper/check-render.sh +419 -0
- package/skills/render-paper/checkers-requirements.txt +55 -0
- package/skills/render-paper/ensure-checkers.sh +69 -0
- package/skills/render-paper/extract-pdf-facts.harness.mjs +166 -0
- package/skills/render-paper/extract-pdf-facts.mjs +144 -0
- package/skills/render-paper/render-paper.eval.mjs +19 -0
- package/skills/render-paper/render-paper.harness.mjs +339 -0
- package/skills/research-ideate/SKILL.md +136 -0
- package/skills/research-ideate/SKILL.md.spec.ts +152 -0
- package/skills/research-ideate/research-ideate.eval.mjs +19 -0
- package/skills/research-ideate/research-ideate.harness.mjs +23 -0
- package/skills/skill-contract.mutations.mjs +179 -0
- package/skills/study-accepted-papers/SKILL.md +206 -0
- package/skills/study-accepted-papers/SKILL.md.spec.ts +223 -0
- package/skills/study-accepted-papers/study-accepted-papers.eval.mjs +19 -0
- package/skills/study-accepted-papers/study-accepted-papers.harness.mjs +23 -0
- package/skills/submit-paper/SKILL.md +182 -0
- package/skills/submit-paper/SKILL.md.spec.ts +199 -0
- package/skills/submit-paper/check-deanon.sh +149 -0
- package/skills/submit-paper/references/publishers/acm.md +92 -0
- package/skills/submit-paper/references/venues/agenticdev.jsonc +108 -0
- package/skills/submit-paper/references/venues/agenticdev.md +139 -0
- package/skills/submit-paper/references/venues/agenticdev.tex +19 -0
- package/skills/submit-paper/references/venues/aisec.jsonc +101 -0
- package/skills/submit-paper/references/venues/aisec.md +105 -0
- package/skills/submit-paper/references/venues/paper-guards.tex +41 -0
- package/skills/submit-paper/references/venues/realm.jsonc +81 -0
- package/skills/submit-paper/references/venues/realm.md +155 -0
- package/skills/submit-paper/references/venues/tex-base.jsonc +50 -0
- package/skills/submit-paper/references/venues/venue-profile.schema.json +74 -0
- package/skills/submit-paper/submit-paper.eval.mjs +19 -0
- package/skills/submit-paper/submit-paper.harness.mjs +23 -0
- package/skills/sweep-design-space/SKILL.md +269 -0
- package/skills/sweep-design-space/SKILL.md.spec.ts +285 -0
- package/skills/sweep-design-space/sweep-design-space.eval.mjs +19 -0
- package/skills/sweep-design-space/sweep-design-space.harness.mjs +23 -0
- package/skills/tighten-paper/SKILL.md +368 -0
- package/skills/tighten-paper/SKILL.md.spec.ts +384 -0
- package/skills/tighten-paper/structure.mjs +371 -0
- package/skills/tighten-paper/tighten-paper.eval.mjs +19 -0
- package/skills/tighten-paper/tighten-paper.harness.mjs +23 -0
- package/skills/verify-citations/SKILL.md +328 -0
- package/skills/verify-citations/SKILL.md.spec.ts +345 -0
- package/skills/verify-citations/scripts/bib-authors.mjs +479 -0
- package/skills/verify-citations/scripts/bib-authors.test.mjs +175 -0
- package/skills/verify-citations/scripts/verify-cites.mjs +1108 -0
- package/skills/verify-citations/scripts/verify-cites.test.mjs +735 -0
- package/skills/verify-citations/verify-citations.eval.mjs +19 -0
- package/skills/verify-citations/verify-citations.harness.mjs +23 -0
- package/src/CLAUDE.md +51 -0
- package/src/action-ref.test.ts +26 -0
- package/src/action-ref.ts +15 -0
- package/src/adapters/banal/failure.test.ts +63 -0
- package/src/adapters/banal/failure.ts +118 -0
- package/src/adapters/banal/index.test.ts +119 -0
- package/src/adapters/banal/index.ts +100 -0
- package/src/adapters/banal/install.test.ts +20 -0
- package/src/adapters/banal/install.ts +41 -0
- package/src/adapters/banal/invocation.test.ts +74 -0
- package/src/adapters/banal/invocation.ts +95 -0
- package/src/adapters/banal/locate.test.ts +52 -0
- package/src/adapters/banal/locate.ts +84 -0
- package/src/adapters/banal/output.test.ts +140 -0
- package/src/adapters/banal/output.ts +141 -0
- package/src/adapters/banal/pin.ts +30 -0
- package/src/adapters/banal/probe.ts +35 -0
- package/src/adapters/banal/run.test.ts +191 -0
- package/src/adapters/banal/run.ts +244 -0
- package/src/adapters/banal/settings.test.ts +31 -0
- package/src/adapters/banal/settings.ts +55 -0
- package/src/adapters/banal/xml.test.ts +111 -0
- package/src/adapters/banal/xml.ts +112 -0
- package/src/adapters/curl/download.io.ts +73 -0
- package/src/adapters/curl/download.test.ts +55 -0
- package/src/adapters/curl/index.ts +5 -0
- package/src/adapters/memory/index.ts +131 -0
- package/src/adapters/node/files.io.ts +39 -0
- package/src/adapters/node/files.test.ts +28 -0
- package/src/adapters/node/host.io.ts +15 -0
- package/src/adapters/node/index.ts +36 -0
- package/src/adapters/node/process.io.ts +49 -0
- package/src/adapters/node/process.test.ts +46 -0
- package/src/adapters/node/workspace.io.ts +40 -0
- package/src/adapters/node/workspace.test.ts +58 -0
- package/src/adapters/pdfjs/fill.test.ts +111 -0
- package/src/adapters/pdfjs/fill.ts +141 -0
- package/src/build-engine.harness.mjs +314 -0
- package/src/build-engine.ts +219 -0
- package/src/build.harness.mjs +631 -0
- package/src/build.mutations.mjs +195 -0
- package/src/build.ts +793 -0
- package/src/cli.harness.mjs +2007 -0
- package/src/cli.mutations.mjs +448 -0
- package/src/cli.ts +1189 -0
- package/src/doctor.harness.mjs +396 -0
- package/src/doctor.mutations.mjs +175 -0
- package/src/doctor.ts +356 -0
- package/src/domain/geometry.ts +108 -0
- package/src/domain/host.ts +23 -0
- package/src/domain/page-layout.ts +32 -0
- package/src/domain/paths.ts +5 -0
- package/src/domain/result.test.ts +26 -0
- package/src/domain/result.ts +29 -0
- package/src/domain/sha256.test.ts +12 -0
- package/src/domain/sha256.ts +21 -0
- package/src/domain/text.ts +11 -0
- package/src/engine.harness.mjs +252 -0
- package/src/engine.ts +176 -0
- package/src/exit-code.test.ts +21 -0
- package/src/exit-code.ts +38 -0
- package/src/facts-file.test.ts +240 -0
- package/src/facts-file.ts +241 -0
- package/src/hooks-settings.harness.mjs +386 -0
- package/src/hooks-settings.mutations.mjs +116 -0
- package/src/hooks-settings.ts +434 -0
- package/src/init.ts +900 -0
- package/src/latex-log.harness.mjs +226 -0
- package/src/latex-log.ts +234 -0
- package/src/latex-loop.harness.mjs +449 -0
- package/src/latex-loop.ts +211 -0
- package/src/link-skills.harness.mjs +273 -0
- package/src/link-skills.mutations.mjs +136 -0
- package/src/link-skills.ts +258 -0
- package/src/new-paper.harness.mjs +216 -0
- package/src/new-paper.mutations.mjs +79 -0
- package/src/new-paper.ts +158 -0
- package/src/pdf-facts.harness.mjs +188 -0
- package/src/pdf-facts.ts +327 -0
- package/src/pdf-geometry.harness.mjs +254 -0
- package/src/pdf-geometry.ts +300 -0
- package/src/ports/download.ts +10 -0
- package/src/ports/files.ts +11 -0
- package/src/ports/measure-geometry.ts +8 -0
- package/src/ports/process.ts +46 -0
- package/src/ports/tool-installer.ts +33 -0
- package/src/ports/workspace.ts +20 -0
- package/src/rules-config.harness.mjs +114 -0
- package/src/rules-config.ts +178 -0
- package/src/structure.harness.mjs +179 -0
- package/src/structure.mutations.mjs +83 -0
- package/src/structure.ts +166 -0
- package/src/tex-requirements.harness.mjs +238 -0
- package/src/tex-requirements.ts +181 -0
- package/src/toolchain.harness.mjs +651 -0
- package/src/toolchain.ts +755 -0
- package/src/types.ts +106 -0
- package/templates/paper/PIPELINE-STATUS.md +72 -0
- package/templates/paper/paper.md +4 -0
- package/templates/paper/paper.tex +8 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Where CI gets TeX Live from: three methods, measurements, what was chosen and why"
|
|
3
|
+
created: 2026-09-01
|
|
4
|
+
updated: 2026-09-24
|
|
5
|
+
tags: [ci, texlive, render-paper, resheno-i-otkloneno, zamery]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# What to install TeX with in CI — a measured breakdown
|
|
9
|
+
|
|
10
|
+
**Why this file exists.** Over one night on 2026-09-01 the method for installing TeX in the
|
|
11
|
+
`build` job changed **three times**, each change cost a CI run, and two of the three methods
|
|
12
|
+
failed for reasons invisible in the documentation. Without this record, the next attempt would
|
|
13
|
+
start from the same first try.
|
|
14
|
+
|
|
15
|
+
The corpus owner's question that prompted the file: _"write down why we chose one action over
|
|
16
|
+
another"_.
|
|
17
|
+
|
|
18
|
+
> **Where this ended up (2026-09-24).** Method 5 below won, and it is no longer a CI script: it is
|
|
19
|
+
> `paperlint toolchain` (`src/toolchain.ts`), the command users run and the `build-e2e` job runs on Linux
|
|
20
|
+
> and macOS. The package list and the file contract (`REQUIRED_FILES` in the deleted
|
|
21
|
+
> `ensure-toolchain.sh`) moved into the venue profiles — each `skills/submit-paper/references/venues/*.jsonc`
|
|
22
|
+
> declares a `tex` block of CTAN package → proof files, and `tex-base.jsonc` the set every paper
|
|
23
|
+
> gets. This file stays as the record of why tlmgr and not apt or a container; the scripts it names
|
|
24
|
+
> (`ensure-toolchain.sh`, `ci-install-texlive.sh`) were deleted in the same change.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Summary
|
|
29
|
+
|
|
30
|
+
| method | size | cold install | cacheable | verdict |
|
|
31
|
+
| ------------------------------------------- | ----------- | ------------------------- | --------- | ------------------- |
|
|
32
|
+
| `texlive/texlive:latest` container | **2700 MB** | 2 m 01 s **every** run | ❌ never | 🟡 works, expensive |
|
|
33
|
+
| `texlive/texlive:latest-medium` container | 940 MB | hangs 14 min | ❌ | 🔴 rejected |
|
|
34
|
+
| apt + `awalsh128/cache-apt-pkgs-action` | 2100 MB | installed **zero** | ✅ | 🔴 rejected |
|
|
35
|
+
| tlmgr + `teatimeguest/setup-texlive-action` | ~250 MB | — | ✅ | 🔴 **blocked** |
|
|
36
|
+
| tlmgr + `actions/cache` (own script) | ~250 MB | 77 s + follow-up installs | ✅ | 🟢 candidate |
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 1. `texlive/texlive:latest` container — what worked
|
|
41
|
+
|
|
42
|
+
**How:** `container: image: texlive/texlive:latest` on the job.
|
|
43
|
+
|
|
44
|
+
**Cost, measured 2026-09-01** (run `33455680908`): the "Initialize containers" step —
|
|
45
|
+
**2 minutes 01 seconds**, and that **on every run**. GitHub does **not cache the job container
|
|
46
|
+
image** under any circumstances; a homemade cache via `docker save`/`load` for a multi-gigabyte
|
|
47
|
+
image costs more than the pool time it would replace.
|
|
48
|
+
|
|
49
|
+
Of that 2.7 GB, our papers touch **47 MB of fonts** — the runtime subset of three families,
|
|
50
|
+
without `doc/` and `source/` (issue #37; this line said "71 MB" until 2026-09-24, which counted the
|
|
51
|
+
documentation and sources too):
|
|
52
|
+
|
|
53
|
+
| family | size, with doc/ and source/ | what it's for |
|
|
54
|
+
| ------------------- | --------------------------- | ---------------------- |
|
|
55
|
+
| libertine | 29 MB | acmart's main typeface |
|
|
56
|
+
| inconsolata (`zi4`) | 23 MB | monospace |
|
|
57
|
+
| newtx | 19 MB | math |
|
|
58
|
+
|
|
59
|
+
So overhead is ~98%. A conscious tradeoff, not an unnoticed one.
|
|
60
|
+
|
|
61
|
+
## 2. `latest-medium` — rejected 2026-09-01
|
|
62
|
+
|
|
63
|
+
**Idea:** a 940 MB image instead of 2700, with the gap filled in by `tlmgr`.
|
|
64
|
+
|
|
65
|
+
**What happened:** the follow-up class-install step hung for **14 minutes** (reaching out to
|
|
66
|
+
CTAN), got cut off by the job's bond, the job came back `cancelled`, and the gate never ran at
|
|
67
|
+
all. The trade of "save 2 minutes of pool time" cost fifteen minutes and an unrun gate.
|
|
68
|
+
|
|
69
|
+
**The lesson isn't about the image, it's about bonds:** any step that reaches the network needs
|
|
70
|
+
its **own** `timeout-minutes`. A job-level bond doesn't distinguish "the step is stuck" from "the
|
|
71
|
+
set is slow" and hands the whole job over to the hang.
|
|
72
|
+
|
|
73
|
+
## 3. apt + `awalsh128/cache-apt-pkgs-action` — rejected, and rejected QUIETLY
|
|
74
|
+
|
|
75
|
+
**Idea:** GitHub caches `.deb` files, unlike a container image. Install the nine packages from
|
|
76
|
+
`PACKAGES` in `ensure-toolchain.sh`.
|
|
77
|
+
|
|
78
|
+
**What happened** (run `33456559102`, job `99697684739`):
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
E: Failed to fetch mirror+file:/etc/apt/apt-mirrors.txt/pool/main/o/openjdk-21/
|
|
82
|
+
openjdk-21-jre-headless_21.0.11+10-1~24.04.2_amd64.deb 404 Not Found
|
|
83
|
+
E: Unable to fetch some archives
|
|
84
|
+
Caching 0 installed packages...
|
|
85
|
+
Skipped all manifest write. No packages to install.
|
|
86
|
+
##[end-action …;outcome=success;conclusion=success]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`texlive-latex-extra` pulls in JRE via its dependencies, its version in the runner's index had
|
|
90
|
+
gone stale, the mirror returned 404 — and apt installed **nothing**.
|
|
91
|
+
|
|
92
|
+
🔴 **The main point here isn't the 404, it's that the step was GREEN.** The action reported
|
|
93
|
+
`outcome=success`, cached 32 KB, and moved on. Only a separate `kpsewhich` check in the next step
|
|
94
|
+
caught it, printing all 14 required files as missing. This is the third instance in the knowledge
|
|
95
|
+
base of the rule **"`exit=0` and empty output is NOT 'clean'"**, and it's also the reason the
|
|
96
|
+
post-install check lives in its own separate step: **you cannot trust the installer**.
|
|
97
|
+
|
|
98
|
+
**The second reason for rejecting it, independent of the 404 — granularity.** apt's minimal unit
|
|
99
|
+
is the distribution package: for three font styles it pulls in the **whole** of
|
|
100
|
+
`texlive-fonts-extra`.
|
|
101
|
+
|
|
102
|
+
| | |
|
|
103
|
+
| ----------- | ------------------------------------ |
|
|
104
|
+
| needed | 47 MB (three families, runtime; #37) |
|
|
105
|
+
| installed | **1691 MB** (`texlive-fonts-extra`) |
|
|
106
|
+
| waste share | **97%** |
|
|
107
|
+
|
|
108
|
+
apt can't go finer than that — in Debian these three families aren't split into separate
|
|
109
|
+
packages.
|
|
110
|
+
|
|
111
|
+
⚠️ **A side effect: a stale note surfaced in our own file.** While justifying the move, I quoted a
|
|
112
|
+
comment from `ensure-toolchain.sh`: "172 MB fetched, 482 MB on disk." That measurement is dated
|
|
113
|
+
17.08, and `texlive-fonts-extra` was added to the list on **24.08** — meaning the comment predates
|
|
114
|
+
half the set. Remeasured: **2.1 GB**. A comment in our own file is not a verified measurement.
|
|
115
|
+
|
|
116
|
+
## 4. `teatimeguest/setup-texlive-action` — BLOCKED, not rejected
|
|
117
|
+
|
|
118
|
+
**Idea:** the right class of tool. Installs the **upstream** TeX Live from a list of CTAN
|
|
119
|
+
packages, cache built in. The LaTeX ecosystem's standard answer to "I need three fonts, not two
|
|
120
|
+
gigabytes."
|
|
121
|
+
|
|
122
|
+
**What happened** (run `33457399377`): the job died at the **"Set up job"** step in 2 seconds.
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
Prepare all required actions
|
|
126
|
+
Getting action download info
|
|
127
|
+
##[error]Repository access blocked
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The account has a **third-party GitHub Actions allowlist** turned on, and this action isn't on
|
|
131
|
+
it. Confirmed alongside it: `awalsh128/cache-apt-pkgs-action@v1` and `zernie/vigiles@v20.0.0` in
|
|
132
|
+
the same file start up normally.
|
|
133
|
+
|
|
134
|
+
🔴 **Fixed NOT by code:** Settings → Actions → General → Allow specified actions. Not reachable
|
|
135
|
+
from the session — checked, not assumed:
|
|
136
|
+
|
|
137
|
+
| channel | result |
|
|
138
|
+
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
139
|
+
| `GH_TOKEN` in the environment | present, and valid (`GET /user` → `zernie`) |
|
|
140
|
+
| bare `curl` to api.github.com | **403 on everything**, including `GET /repos/<owner>/<private-repo>`: `Access to this GitHub Actions path is not permitted through this proxy` |
|
|
141
|
+
| GitHub MCP | the only reachable channel, and it has **no** repository-settings tool |
|
|
142
|
+
|
|
143
|
+
**Status: not rejected, unreachable.** If the action ever gets allowlisted, it's the best option,
|
|
144
|
+
and the code for it sits in the history of the `claude/article-deadline-check-0unptg` branch
|
|
145
|
+
(commit `be6f33ec8`), restorable in one commit.
|
|
146
|
+
|
|
147
|
+
### Why comparing the actions was, in itself, the wrong move
|
|
148
|
+
|
|
149
|
+
I picked `cache-apt-pkgs-action` **first** because I searched for "how to cache an install," and a
|
|
150
|
+
generic tool for a generic query turned up immediately. The right question was different:
|
|
151
|
+
**"what does this ecosystem actually use to install TeX specifically."** LaTeX-specific actions
|
|
152
|
+
exist, and there are three of them:
|
|
153
|
+
|
|
154
|
+
| action | what it does | why not this one |
|
|
155
|
+
| --------------------------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
156
|
+
| **`teatimeguest/setup-texlive-action`** | upstream TL from a package list, cache built in | ✅ best on the merits, blocked by the allowlist |
|
|
157
|
+
| `xu-cheng/latex-action` | the most popular of all | wraps the same `texlive/texlive` **image** underneath — same pool cost, solves nothing |
|
|
158
|
+
| `zauguin/install-texlive` | same idea as the first | less maintained, no advantage |
|
|
159
|
+
|
|
160
|
+
**Lesson:** before reaching for a general-purpose tool, ask whether a specialized one exists for
|
|
161
|
+
this exact subject. The general one gives you its own domain's granularity (apt → distribution
|
|
162
|
+
package), the specialized one gives you its own (tlmgr → CTAN package), and here the difference
|
|
163
|
+
is **twelvefold** by weight.
|
|
164
|
+
|
|
165
|
+
## 5. tlmgr + `actions/cache` with our own script — candidate
|
|
166
|
+
|
|
167
|
+
**Idea:** bypass the allowlist entirely. `actions/cache` is first-party (`actions/*`), allowed
|
|
168
|
+
under any "Allow select actions" policy, and we **already** use it to cache TeXtidote's
|
|
169
|
+
224-megabyte jar.
|
|
170
|
+
|
|
171
|
+
**Measurement, run LOCALLY in the session container on 2026-09-01** (not on CI — that's the whole
|
|
172
|
+
point):
|
|
173
|
+
|
|
174
|
+
| step | result |
|
|
175
|
+
| ------------------------------------ | -------------------------- |
|
|
176
|
+
| `install-tl-unx.tar.gz` | 5.3 MB |
|
|
177
|
+
| `scheme-basic` per profile | **147 MB, 65 seconds** |
|
|
178
|
+
| `tlmgr install` 19 packages | +75 MB, 12 seconds |
|
|
179
|
+
| **total before the first build** | **222 MB, 77 seconds** |
|
|
180
|
+
| binary directory | `texlive/bin/x86_64-linux` |
|
|
181
|
+
| `kpsewhich` against `REQUIRED_FILES` | **14 of 14** ✅ |
|
|
182
|
+
|
|
183
|
+
⚠️ **`--cacert` is mandatory:** in this container `curl` to CTAN fails certificate verification
|
|
184
|
+
(the proxy), fixed with `CURL_CA_BUNDLE=/root/.ccr/ca-bundle.crt`. Not needed on a GitHub runner —
|
|
185
|
+
written down so the next spike doesn't lose ten minutes to it.
|
|
186
|
+
|
|
187
|
+
### 🔴 And this is where `kpsewhich` lied — and the build didn't
|
|
188
|
+
|
|
189
|
+
14 of 14 files resolved, and `pdflatex` failed on the real paper:
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
! LaTeX Error: File `xstring.sty' not found.
|
|
193
|
+
! ==> Fatal error occurred, no output PDF file produced!
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
`xstring` is pulled in by `acmart`, and it's in neither the package list nor `REQUIRED_FILES`.
|
|
197
|
+
What followed was the knowledge base's familiar **endless queue**: `xstring` → `everyshi` →
|
|
198
|
+
`hyperxmp` → `ncctools` → `cmap` → `float` → `comment` → `upquote` → `doclicense` → …
|
|
199
|
+
|
|
200
|
+
**This is exactly the case that `CLAUDE.md`'s "install by COLLECTION, not by name" rule is for.**
|
|
201
|
+
But that same rule also names the reason: _"names surface one at a time, and each one costs a full
|
|
202
|
+
CI run (~25 min of quota)"_.
|
|
203
|
+
|
|
204
|
+
🔑 **Here that reason disappears.** The queue is run **locally**, in a loop of "build → pull out
|
|
205
|
+
the missing file → `tlmgr search --global --file` → install → repeat," and it costs **zero
|
|
206
|
+
minutes of quota**. What changes is not the fact of the queue, but its cost — and the conclusion
|
|
207
|
+
"names can't be listed one at a time" rested precisely on that cost.
|
|
208
|
+
|
|
209
|
+
**The rule this implies, and it's broader than TeX:** before accepting "must list only in bulk,
|
|
210
|
+
never one at a time," check **whether the iteration itself can be made free.** An expensive
|
|
211
|
+
iteration is a property of where it's run, not a property of the task.
|
|
212
|
+
|
|
213
|
+
⚠️ **The honest cost of an exact list:** a new paper pulling in a new package will break CI with a
|
|
214
|
+
missing filename. That's acceptable, because the `REQUIRED_FILES` check prints the fix straight
|
|
215
|
+
into the log, and because the alternative — pulling in the whole of `collection-latexextra` —
|
|
216
|
+
means going back to the same hundreds of megabytes of waste this move was meant to escape.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## What to check on the next attempt
|
|
221
|
+
|
|
222
|
+
1. **Build a real paper, not just `kpsewhich`.** Filename resolution and a successful build are
|
|
223
|
+
**different claims**, and here the second turned out to be stricter than the first by nine
|
|
224
|
+
packages.
|
|
225
|
+
2. **Watch for `Class acmart Warning` in `paper.log`.** Missing libertine/zi4/newtxmath does
|
|
226
|
+
**not** crash acmart — it silently falls back to Computer Modern, the PDF builds and looks
|
|
227
|
+
normal, but is typeset in the wrong font, and a different metric gives a different pagination.
|
|
228
|
+
3. **After installing, check the result, not the installer's exit code.** See §3.
|
|
229
|
+
4. **Run iterations locally.** The spike above cost zero minutes of quota and caught three errors,
|
|
230
|
+
each of which would have cost a CI run.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# External programs the skills need
|
|
2
|
+
|
|
3
|
+
`paperlint lint` needs nothing but Node — it reads your files and reports. **The skills are a different
|
|
4
|
+
matter**: they build PDFs, read them back, and run external checkers, so they call programs this
|
|
5
|
+
package does not ship.
|
|
6
|
+
|
|
7
|
+
This page is the prerequisite list and how to satisfy it cheaply. It was moved out of the README
|
|
8
|
+
on 2026-09-19: it answers "how do I make the render stage work", which is a question you have
|
|
9
|
+
after deciding to use the tool, not before.
|
|
10
|
+
|
|
11
|
+
## The programs
|
|
12
|
+
|
|
13
|
+
| program | comes from | which skills call it | what happens without it |
|
|
14
|
+
| -------------------- | -------------------------------- | ---------------------------------------- | --------------------------------------------------------------------- |
|
|
15
|
+
| `pdflatex`, `bibtex` | TeX Live (`paperlint toolchain`) | render-paper, submit-paper, camera-ready | no PDF is produced — loud |
|
|
16
|
+
| `texcount` | TeX Live (`paperlint toolchain`) | render-paper, grade-paper-writing | the length checks cannot run |
|
|
17
|
+
| `checkcites` | TeX Live (`paperlint toolchain`) | render-paper | nothing asks whether a bibliography entry is uncited |
|
|
18
|
+
| `java` | any JRE (21 works) | render-paper | TeXtidote does not run, and **nothing else spell-checks the text** |
|
|
19
|
+
| `python3` | your system | the analysis and report scripts | those scripts do not start |
|
|
20
|
+
| `tlmgr` | TeX Live | the TeX installer itself | you cannot add a TeX package |
|
|
21
|
+
| `banal` | HotCRP (`paperlint toolchain`) | `paperlint build`, extract-pdf-facts | page size, columns and font sizes are `null` in the facts file |
|
|
22
|
+
| `perl` | your system (macOS has it) | banal | banal cannot run — the same `null`, and `paperlint toolchain` refuses |
|
|
23
|
+
|
|
24
|
+
🔴 **Most of these fail QUIETLY**, which is why they are listed rather than left to be discovered.
|
|
25
|
+
A missing checker and a passing checker look identical from outside, so every script here states in
|
|
26
|
+
its last line which checks actually ran — read that line, not the exit code.
|
|
27
|
+
|
|
28
|
+
## TeX Live and banal: `paperlint toolchain`
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
npx paperlint toolchain # install, or add what is missing; a second run does nothing
|
|
32
|
+
npx paperlint toolchain --check # report, change nothing; exit 1 when a declared package or banal is missing
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The command has two halves, TeX Live and banal (below). Both always run, so one failing does not
|
|
36
|
+
hide the other, and the exit code is 0 only when both are ready.
|
|
37
|
+
|
|
38
|
+
It downloads `install-tl` from a CTAN mirror (four in turn; each download has its own time limit,
|
|
39
|
+
TLS is always verified), installs `scheme-basic` into `~/.cache/rpp/texlive/<TeX Live year>`
|
|
40
|
+
(`$XDG_CACHE_HOME/rpp/texlive` when that is set, `$RPP_TEXLIVE_DIR` over both), and `tlmgr install`s
|
|
41
|
+
every package the venue profiles declare. Then it checks the RESULT: `kpsewhich` must find every
|
|
42
|
+
file each profile names, and each declared tool must be an executable in the bin directory. A gap
|
|
43
|
+
fails the command and names the package and its file. Linux and macOS; on Windows it refuses, and
|
|
44
|
+
`paperlint build` uses a TeX Live on PATH that has the packages. `RPP_CTAN_MIRROR` names one mirror
|
|
45
|
+
(a `…/systems/texlive/tlnet` URL) to use instead of the list.
|
|
46
|
+
|
|
47
|
+
Measured 2026-09-24 from an empty directory: **3 min 04 s, 269 MB** (du: 298 MB), TeX Live 2026, 47
|
|
48
|
+
packages verified (46 files, 2 tools). The second run: 0.55 s, "nothing to do".
|
|
49
|
+
|
|
50
|
+
**When CTAN moves to a new TeX Live year.** A complete tree is left alone: a new release on CTAN is
|
|
51
|
+
not by itself a reason to download 270 MB. It matters only when a venue declares a package the
|
|
52
|
+
tree lacks — tlmgr then refuses to install into an older year (`Local TeX Live (2026) is older
|
|
53
|
+
than remote repository (2027)`). `paperlint toolchain` reads the two years from that refusal, installs
|
|
54
|
+
a fresh tree for the new year into its own `<TeX Live year>` directory, installs every declared
|
|
55
|
+
package there and verifies it, and from then on that tree is used. The old tree is not deleted;
|
|
56
|
+
the run names it with its size, and removing it is your call. `paperlint build` uses the newest tree
|
|
57
|
+
that has every declared package, so a new-year install that was interrupted never replaces a
|
|
58
|
+
complete older one.
|
|
59
|
+
|
|
60
|
+
**Where the package list lives:** in the venue profiles, not here and not in a script.
|
|
61
|
+
`skills/submit-paper/references/venues/<venue>.jsonc` carries a `tex` block — CTAN package → the
|
|
62
|
+
files that prove it is installed — and `tex-base.jsonc` carries what every paper gets, and all a
|
|
63
|
+
paper with an unknown venue gets. The shape is `venue-profile.schema.json` in the same directory.
|
|
64
|
+
A venue that needs a new package gets one line there.
|
|
65
|
+
|
|
66
|
+
**`paperlint build` uses it without being asked to.** It looks for a TeX Live with every package the
|
|
67
|
+
paper's venue declares — paperlint's own first, then the `pdflatex` on PATH — and on a terminal offers to
|
|
68
|
+
install one. Without a terminal it stops with one line naming `npx paperlint toolchain`. The last line
|
|
69
|
+
`paperlint toolchain` prints is `bin: <dir>`: put that first on PATH to call `pdflatex`, `texcount` or
|
|
70
|
+
`checkcites` by hand, or from a skill's script.
|
|
71
|
+
|
|
72
|
+
Why upstream TeX Live by package name and not the distribution packages: `texlive-fonts-extra`
|
|
73
|
+
alone is **1691 MB**, and acmart papers use **47 MB** of it — apt cannot install less. The measured
|
|
74
|
+
comparison of every method tried (containers, apt, a GitHub Action, this) is
|
|
75
|
+
[`texlive-install-decision.md`](texlive-install-decision.md).
|
|
76
|
+
|
|
77
|
+
## Reading the PDF: nothing to install
|
|
78
|
+
|
|
79
|
+
`paperlint build` and `extract-pdf-facts.mjs` read the finished PDF — page count, the fonts it draws text
|
|
80
|
+
with, the last page's words — with **pdf.js**, which arrives with paperlint as the npm dependency
|
|
81
|
+
[`unpdf`](https://www.npmjs.com/package/unpdf). Until 2026-09-24 this took three poppler programs
|
|
82
|
+
(`pdfinfo`, `pdffonts`, `pdftotext`) from the system package manager; a measurement on 25 PDFs
|
|
83
|
+
found pdf.js equal on page counts and Type 3 fonts, and the last page's column heights within
|
|
84
|
+
0.2 pt except on an all-Type-3 page (7.2 pt). Poppler is no longer needed by anything paperlint runs.
|
|
85
|
+
|
|
86
|
+
## Page geometry: banal, without poppler
|
|
87
|
+
|
|
88
|
+
The page size, column count, body and reference font sizes and page types in the facts file come
|
|
89
|
+
from **banal**, the page-geometry script HotCRP's format checker runs
|
|
90
|
+
([`src/banal`](https://github.com/kohler/hotcrp/blob/master/src/banal), by Geoffrey M. Voelker and
|
|
91
|
+
Eddie Kohler). paperlint runs the real banal, unmodified, so the numbers are the ones HotCRP shows at
|
|
92
|
+
upload.
|
|
93
|
+
|
|
94
|
+
banal normally reads a PDF through poppler's `pdftohtml -xml`. paperlint does not: it writes that XML
|
|
95
|
+
itself from the same pdf.js read (`src/adapters/banal/xml.ts`) and hands banal the `.xml` file, which banal
|
|
96
|
+
accepts as input. Measured on 50 PDFs / 598 pages (2026-09-25): banal on paperlint's XML and banal on
|
|
97
|
+
real `pdftohtml` agree on every field the facts file keeps, with no venue verdict changed. They
|
|
98
|
+
agree only because paperlint leaves out rotated and invisible text (as `pdftohtml` does), writes each
|
|
99
|
+
text's colour so banal drops light text by its own rule, and writes sizes and coordinates at the
|
|
100
|
+
zoom and precision banal expects. `test/e2e/banal.mjs` checks each of those against the real banal
|
|
101
|
+
on the committed fixtures.
|
|
102
|
+
|
|
103
|
+
**How it is installed — and the licence boundary.** banal is **GPL-2.0-or-later**; paperlint is MIT. paperlint
|
|
104
|
+
therefore does not contain banal. `paperlint toolchain` downloads it from HotCRP at a pinned commit:
|
|
105
|
+
|
|
106
|
+
| | |
|
|
107
|
+
| ------- | ---------------------------------------------------------------------------------------------------- |
|
|
108
|
+
| URL | `https://raw.githubusercontent.com/kohler/hotcrp/f3e4352133f3184c7c42b0d5e6501124bead18e6/src/banal` |
|
|
109
|
+
| version | banal 1.2 |
|
|
110
|
+
| sha256 | `fd8cc4ae189b9da02460ae442a34f14434e5784210489fb668313ac671006911` |
|
|
111
|
+
|
|
112
|
+
It refuses a file with any other sha256, stores it in `~/.cache/rpp/banal/<commit>/banal`
|
|
113
|
+
(`$XDG_CACHE_HOME/rpp/banal` when set, `$RPP_BANAL_DIR` over both), and accepts it only after banal
|
|
114
|
+
has run on a one-page probe and measured it. paperlint then runs it as a separate program — `perl banal
|
|
115
|
+
-no-time -json <file>.xml` — and reads its JSON output. Nothing of banal is copied, linked or
|
|
116
|
+
translated into this package.
|
|
117
|
+
|
|
118
|
+
**perl is required** for that, and is checked: without it `paperlint toolchain` fails and says how to
|
|
119
|
+
install it (Debian/Ubuntu `apt-get install perl`; macOS ships it). banal asks `pdftohtml -v` before
|
|
120
|
+
it reads any input, so paperlint points banal's `$PDFTOHTML` at a small stub that answers with the
|
|
121
|
+
`pdftohtml` version whose XML paperlint writes. Poppler itself is not needed by anything paperlint runs.
|
|
122
|
+
|
|
123
|
+
**Which banal is used**, in order: `$BANAL` (a path you name), `vendor/banal` in the project (a copy
|
|
124
|
+
you vendor), then the one `paperlint toolchain` installed. Without any, `paperlint build` still succeeds with
|
|
125
|
+
the geometry fields `null` and says so with the command that fixes it; `extract-pdf-facts.mjs
|
|
126
|
+
--strict` fails.
|
|
127
|
+
|
|
128
|
+
pdf.js needs **Node 22.13 or later**, which paperlint requires anyway: on Node 20 it opens the same PDFs
|
|
129
|
+
and reports zero fonts without an error, and paperlint refuses such a read instead of reporting a
|
|
130
|
+
clean font list.
|
|
131
|
+
|
|
132
|
+
## The external checkers
|
|
133
|
+
|
|
134
|
+
`aclpubcheck` (the official ACL format checker), TeXtidote (spelling) and `rebiber` are not TeX
|
|
135
|
+
packages and not npm packages. One idempotent command installs them and then **proves each one
|
|
136
|
+
starts**:
|
|
137
|
+
|
|
138
|
+
```sh
|
|
139
|
+
bash node_modules/paperlint/skills/render-paper/ensure-checkers.sh
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
✅ aclpubcheck
|
|
144
|
+
✅ rebiber
|
|
145
|
+
✅ jinja2
|
|
146
|
+
✅ textidote (/opt/textidote/textidote.jar)
|
|
147
|
+
✅ all checkers are installed AND run
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
It checks that the tools RUN, not that pip exited zero — `aclpubcheck --help` prints usage and
|
|
151
|
+
exits zero on an interpreter where its own dependencies do not import, so "installed" and "works"
|
|
152
|
+
are separate questions here.
|