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,419 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# check-render.sh — mechanical render-quality gate for a LaTeX paper.
|
|
3
|
+
#
|
|
4
|
+
# WHY: eyeballing page 1-2 misses layout defects on later pages. LaTeX already
|
|
5
|
+
# reports them in the .log ("Overfull \hbox (12.3pt too wide) at lines X--Y").
|
|
6
|
+
# This compiles the paper and FAILS on visible layout defects, so "renders fine"
|
|
7
|
+
# is a checked fact, not a hope. Spirit: prose isn't policy — compile the check.
|
|
8
|
+
#
|
|
9
|
+
# Usage: check-render.sh <paper-dir> [texbasename=paper] [threshold_pt=5]
|
|
10
|
+
# NO_COMPILE=1 check-render.sh <dir> ... # parse an existing .log, don't recompile
|
|
11
|
+
#
|
|
12
|
+
# Exit: 0 = clean; 1 = layout/ref defects found; 2 = usage/compile/log error.
|
|
13
|
+
# Threshold: hbox overfulls >= threshold_pt are "visible → must fix"; smaller ones
|
|
14
|
+
# are counted but not fatal (sub-point overfulls are usually invisible).
|
|
15
|
+
|
|
16
|
+
set -uo pipefail
|
|
17
|
+
|
|
18
|
+
# ── WHERE `paper-guards.tex` LIVES AND WHAT HAPPENS IF IT IS NOT FOUND ────────
|
|
19
|
+
# Papers pull in the reference guard with one line, `\input{paper-guards}`. The path is NOT
|
|
20
|
+
# spelled out there — LaTeX looks the file up via `TEXINPUTS`, and the caller must set that variable.
|
|
21
|
+
#
|
|
22
|
+
# 🔴 THE FAILURE MODE HERE IS SILENT, AND THE WHOLE FUNCTION EXISTS FOR THAT. An `\input` that is
|
|
23
|
+
# not found does NOT fail the build: LaTeX writes `File \`paper-guards.tex' not found` into the log
|
|
24
|
+
# and carries on. The PDF comes out, looks fine, and simply no longer contains the dangling-reference
|
|
25
|
+
# check — that is, the defect the guard was set up for comes back together with a green report.
|
|
26
|
+
# A broken `import` in JS fails loudly; here we have to fail on our own.
|
|
27
|
+
#
|
|
28
|
+
# A ladder of THREE candidates, because the directory MOVED (12.09): `venues/` is now carried by the
|
|
29
|
+
# `paperlint` package, and at the consumer's old location there is a symlink into it.
|
|
30
|
+
# The order "the declared package → a sibling inside the package → the consumer's directory" is
|
|
31
|
+
# deliberate: the package must win silently. It only gets loud when there is NOT A SINGLE ONE.
|
|
32
|
+
# 🔴 THE SCRIPT'S OWN DIRECTORY — TAKEN ABSOLUTE AND BEFORE ANY `cd` (12.09.2026, evening).
|
|
33
|
+
# Found by a RED HARNESS, `gates.harness.mjs`, not by proofreading, and it is my own regression from
|
|
34
|
+
# the same day: below, the script does `cd "$DIR"` into the paper directory, and the venues resolve
|
|
35
|
+
# stood AFTER that `cd`. So `git rev-parse --show-toplevel` was already running in the fixture's
|
|
36
|
+
# temporary directory, the root was not found, and the build honestly stopped — on a test that had
|
|
37
|
+
# been passing until then.
|
|
38
|
+
#
|
|
39
|
+
# ⚠️ The previous version was wrong THE SAME WAY, but silently: it did not check the result and built
|
|
40
|
+
# the PDF without the reference guard. That is, going red is not a new breakage but a manifestation
|
|
41
|
+
# of an old one; still, a test that was green yesterday and is red today is mine to fix.
|
|
42
|
+
#
|
|
43
|
+
# The neighbouring `build.sh` already carries this same lesson in a comment ("The script directory is
|
|
44
|
+
# taken ONCE and ABSOLUTE, before cd") — CI failed on it there on 30.08. A second instance of the same
|
|
45
|
+
# class in a file right next door.
|
|
46
|
+
SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
47
|
+
|
|
48
|
+
resolve_venues() {
|
|
49
|
+
local root pkg
|
|
50
|
+
# Three sources for the root, from the most explicit to the most reliable. The last one is the
|
|
51
|
+
# SCRIPT's location (`.claude/skills/render-paper/` ⇒ three levels up), and it is the only one that
|
|
52
|
+
# depends neither on the current directory nor on whether the caller sits inside a git tree.
|
|
53
|
+
# ⚠️ THE PARENTHESES ARE MANDATORY, and the harness caught this a minute after I wrote the line
|
|
54
|
+
# without them. In bash `A || B && C` parses as `(A || B) && C`, so on a SUCCESSFUL `git rev-parse`
|
|
55
|
+
# the `pwd` ran as well, and the substitution returned TWO lines — the repository root plus the
|
|
56
|
+
# current directory. By hand from a directory outside git this did not reproduce: there git failed,
|
|
57
|
+
# and only one branch ran.
|
|
58
|
+
# ⚠️ THE LAST RUNG SEARCHES, IT DOES NOT COUNT LEVELS. `cd "$SELF_DIR/../../.."` used to stand here
|
|
59
|
+
# — correct for `.claude/skills/render-paper/` (three levels to the root) and silently wrong after
|
|
60
|
+
# the skill moved into the package, where it lies in `skills/render-paper/` (two): the climb
|
|
61
|
+
# overshot ABOVE the repository, and rung 2 looked for venues in someone else's directory. Climbing
|
|
62
|
+
# up to the directory that holds `.claude` does not depend on the depth.
|
|
63
|
+
local probe="$SELF_DIR" fallback=""
|
|
64
|
+
while [ "$probe" != "/" ]; do
|
|
65
|
+
if [ -d "$probe/.claude" ]; then fallback="$probe"; break; fi
|
|
66
|
+
probe=$(dirname "$probe")
|
|
67
|
+
done
|
|
68
|
+
root="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || printf '%s' "$fallback")}"
|
|
69
|
+
|
|
70
|
+
# 1) the package, if it already carries venues/ (after step 5)
|
|
71
|
+
pkg=$(node -e 'try{process.stdout.write(require.resolve("paperlint/venues/paper-guards.tex"))}catch{}' 2>/dev/null || true)
|
|
72
|
+
if [ -n "$pkg" ] && [ -f "$pkg" ]; then
|
|
73
|
+
dirname "$pkg"
|
|
74
|
+
return 0
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# 2) THE SIBLING SKILL, from THIS script's directory. Added 12.09 together with the move of
|
|
78
|
+
# `submit-paper` into the package, and it is the only one of the three that works in BOTH worlds at
|
|
79
|
+
# once:
|
|
80
|
+
# • package checkout `skills/render-paper/..` → `skills/submit-paper/references/venues`
|
|
81
|
+
# • consumer `.claude/skills/render-paper/..` → `.claude/skills/submit-paper/...`
|
|
82
|
+
# (both directories are symlinks into the package, `-f` goes through them, and `cd`+`pwd`
|
|
83
|
+
# leaves the logical path)
|
|
84
|
+
# Rung 1 depends on cwd (`node -e` resolves from the current directory), rung 3 depends on the
|
|
85
|
+
# consumer's root having been found at all. This one depends on neither — exactly the anchor whose
|
|
86
|
+
# absence had `gates.harness.mjs` red on 12.09.
|
|
87
|
+
local sibling="$SELF_DIR/../submit-paper/references/venues"
|
|
88
|
+
if [ -f "$sibling/paper-guards.tex" ]; then
|
|
89
|
+
(cd "$sibling" && pwd)
|
|
90
|
+
return 0
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# 3) the skill directory in the consumer — for a consumer that mounted the skills differently
|
|
94
|
+
local local_dir="$root/.claude/skills/submit-paper/references/venues"
|
|
95
|
+
if [ -f "$local_dir/paper-guards.tex" ]; then
|
|
96
|
+
printf '%s\n' "$local_dir"
|
|
97
|
+
return 0
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
return 1
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# A separate mode so that the resolve can be CHECKED and not merely executed: without it the only
|
|
104
|
+
# way to learn where the script looks is to build the whole paper.
|
|
105
|
+
if [ "${1:-}" = "--print-venues" ]; then
|
|
106
|
+
if VD=$(resolve_venues); then
|
|
107
|
+
printf '%s\n' "$VD"
|
|
108
|
+
exit 0
|
|
109
|
+
fi
|
|
110
|
+
echo "✗ paper-guards.tex not found: not under the package name paperlint, not beside the script" >&2
|
|
111
|
+
echo " (skills/submit-paper/references/venues), not at the consumer (.claude/skills/submit-paper/references/venues)." >&2
|
|
112
|
+
echo " Building the paper without it is not allowed: \\input{paper-guards} will not fail, it will vanish silently," >&2
|
|
113
|
+
echo " and the PDF will be built WITHOUT the check for dangling \\ref and \\cite." >&2
|
|
114
|
+
exit 2
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
DIR="${1:?usage: check-render.sh <paper-dir> [texbasename=paper] [threshold_pt=5]}"
|
|
118
|
+
BASE="${2:-paper}"
|
|
119
|
+
THRESH="${3:-5}"
|
|
120
|
+
|
|
121
|
+
cd "$DIR" || { echo "FAIL: cannot cd $DIR"; exit 2; }
|
|
122
|
+
LOG="$BASE.log"
|
|
123
|
+
|
|
124
|
+
# 🔴 THE CONDITION `-f "$BASE.tex"` WAS ADDED ON 26.08 — without it the branch "no log → exit 2"
|
|
125
|
+
# became UNREACHABLE, and that is my own regression from the same day. The final pass is invoked as
|
|
126
|
+
# `pdflatex -jobname="$BASE" '\input{...}'`, and that form creates `$BASE.log` EVEN when there is no
|
|
127
|
+
# source: the script found a log, reached the common `exit 1` and reported "FAIL: fix the findings"
|
|
128
|
+
# instead of the honest "no log, the build was never run".
|
|
129
|
+
#
|
|
130
|
+
# The measurement that isolates the cause (a directory without `paper.tex`):
|
|
131
|
+
# version before 22642552 → exit 2, only texput.log on disk
|
|
132
|
+
# version after → exit 1, paper.log AND texput.log on disk
|
|
133
|
+
# The difference is exactly `paper.log`, which my third pass writes.
|
|
134
|
+
if [ "${NO_COMPILE:-0}" != "1" ] && [ -f "$BASE.tex" ]; then
|
|
135
|
+
# Two passes: overfull boxes surface on every pass; a second pass settles refs
|
|
136
|
+
# so Reference/Citation-undefined warnings are accurate.
|
|
137
|
+
# 🔴 TEXINPUTS is mandatory, and that was found by a crash on 26.08. Papers pull in their venue's
|
|
138
|
+
# numbers via `\input{<venue>}`, and the file lies in `.claude/skills/submit-paper/references/venues/`.
|
|
139
|
+
# Without that path the build fails with `File \`agenticdev.tex' not found` — and the `|| true`
|
|
140
|
+
# below swallows it, after which the script reads a log of UNKNOWN origin and reports on it. That
|
|
141
|
+
# is, compiling this paper here silently did not work, and it only became visible once a pass
|
|
142
|
+
# WITHOUT `|| true` appeared.
|
|
143
|
+
if ! VENUES_DIR=$(resolve_venues); then
|
|
144
|
+
echo "✗ paper-guards.tex not found — the build is stopped BEFORE pdflatex." >&2
|
|
145
|
+
echo " Otherwise \\input{paper-guards} vanishes silently and the PDF comes out without the reference check." >&2
|
|
146
|
+
exit 2
|
|
147
|
+
fi
|
|
148
|
+
export TEXINPUTS="${VENUES_DIR}:${TEXINPUTS:-}"
|
|
149
|
+
# The second half: ask TeX itself whether it sees the file by that path. `resolve_venues` checked
|
|
150
|
+
# that the file is present on disk; `kpsewhich` checks that it is visible EXACTLY THE WAY `\input`
|
|
151
|
+
# will look for it — that is, taking TEXINPUTS, the ls-R cache and permissions into account.
|
|
152
|
+
if ! kpsewhich paper-guards.tex >/dev/null 2>&1; then
|
|
153
|
+
echo "✗ the directory was found ($VENUES_DIR), but kpsewhich does not see paper-guards.tex — TEXINPUTS did not take effect." >&2
|
|
154
|
+
exit 2
|
|
155
|
+
fi
|
|
156
|
+
pdflatex -interaction=nonstopmode "$BASE.tex" >/dev/null 2>&1 || true
|
|
157
|
+
pdflatex -interaction=nonstopmode "$BASE.tex" >/dev/null 2>&1 || true
|
|
158
|
+
# 🔴 THE FINAL pass with `\finalpass` — it activates the preamble guards, which need a `.aux` that
|
|
159
|
+
# has CONVERGED. Today that means "no dangling references and citations": LaTeX already knows this
|
|
160
|
+
# fact, and asking it is strictly stronger than grepping its diary afterwards (see the undefined
|
|
161
|
+
# block below — it stays as insurance for papers without a guard in the preamble).
|
|
162
|
+
#
|
|
163
|
+
# Why a separate pass and not a flag on all three: on the first pass the `.aux` is empty, every
|
|
164
|
+
# reference is undefined BY CONSTRUCTION. Measured 26.08 with the unconditional form: pass 1 fails →
|
|
165
|
+
# the `.aux` is not written → pass 2 fails on the same thing → the cycle NEVER CONVERGES.
|
|
166
|
+
#
|
|
167
|
+
# There is deliberately NO `|| true` here: if the guard fired, that is a finding, not noise.
|
|
168
|
+
pdflatex -interaction=nonstopmode -jobname="$BASE" '\def\finalpass{}\input{'"$BASE"'}' >/dev/null 2>&1
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
[ -f "$LOG" ] || { echo "FAIL: no $DIR/$LOG (compile failed — run render-paper toolchain first)"; exit 2; }
|
|
172
|
+
|
|
173
|
+
fail=0
|
|
174
|
+
big=0
|
|
175
|
+
small=0
|
|
176
|
+
|
|
177
|
+
echo "== check-render: $DIR/$BASE (threshold ${THRESH}pt) =="
|
|
178
|
+
|
|
179
|
+
# --- Overfull \hbox (text/tables bleeding past the column/margin) ---
|
|
180
|
+
while IFS= read -r line; do
|
|
181
|
+
[ -z "$line" ] && continue
|
|
182
|
+
pt=""
|
|
183
|
+
if [[ $line =~ \(([0-9]+\.?[0-9]*)pt\ too\ wide\) ]]; then
|
|
184
|
+
pt="${BASH_REMATCH[1]}"
|
|
185
|
+
fi
|
|
186
|
+
if [ -n "$pt" ] && awk -v a="$pt" -v b="$THRESH" 'BEGIN{exit !(a>=b)}'; then
|
|
187
|
+
echo " OVERFULL hbox ${pt}pt ${line#*) }"
|
|
188
|
+
big=$((big+1))
|
|
189
|
+
else
|
|
190
|
+
small=$((small+1))
|
|
191
|
+
fi
|
|
192
|
+
done < <(grep -F 'Overfull \hbox' "$LOG" || true)
|
|
193
|
+
|
|
194
|
+
# --- Overfull \vbox (content past the bottom of the text block) ---
|
|
195
|
+
# Apply the same pt threshold as hbox: a sub-threshold vbox (< a few pt) is an
|
|
196
|
+
# invisible fraction of a line, not a visible defect.
|
|
197
|
+
while IFS= read -r line; do
|
|
198
|
+
[ -z "$line" ] && continue
|
|
199
|
+
pt=""
|
|
200
|
+
if [[ $line =~ \(([0-9]+\.?[0-9]*)pt\ too\ high\) ]]; then
|
|
201
|
+
pt="${BASH_REMATCH[1]}"
|
|
202
|
+
fi
|
|
203
|
+
if [ -z "$pt" ] || awk -v a="$pt" -v b="$THRESH" 'BEGIN{exit !(a>=b)}'; then
|
|
204
|
+
echo " OVERFULL vbox ${line}"
|
|
205
|
+
big=$((big+1))
|
|
206
|
+
else
|
|
207
|
+
small=$((small+1))
|
|
208
|
+
fi
|
|
209
|
+
done < <(grep -F 'Overfull \vbox' "$LOG" || true)
|
|
210
|
+
|
|
211
|
+
# --- Reference / citation / control-sequence problems ---
|
|
212
|
+
undef=$(grep -icE "Undefined control sequence|Citation .* undefined|Reference .* undefined|LaTeX Warning: There were undefined references" "$LOG" || true)
|
|
213
|
+
fatal=$(grep -icE "^!|Fatal error|Emergency stop" "$LOG" || true)
|
|
214
|
+
|
|
215
|
+
# --- BibTeX's OWN log (.blg) — a SECOND log nobody was reading ---
|
|
216
|
+
# 🔴 ADDED 2026-08-24, same evening as the class-warning check, after the same shape of failure
|
|
217
|
+
# bit twice. BibTeX does not abort the build: on a malformed entry it prints
|
|
218
|
+
# "You're missing a field name---line N of file refs.bib"
|
|
219
|
+
# "I'm skipping whatever remains of this entry"
|
|
220
|
+
# and CARRIES ON. pdflatex then produces a perfectly clean PDF with those references SILENTLY
|
|
221
|
+
# ABSENT from the bibliography. Measured: two entries vanished and the LaTeX log stayed at
|
|
222
|
+
# undefined=0, because the stale .aux still resolved the keys from the previous run.
|
|
223
|
+
# The trigger that day: a `%` comment placed INSIDE a @inproceedings entry. `%` is a LaTeX
|
|
224
|
+
# comment, NOT a BibTeX one — inside an entry it is a parse error that eats the whole record.
|
|
225
|
+
blgerr=0
|
|
226
|
+
BLG="$BASE.blg"
|
|
227
|
+
if [ -f "$BLG" ]; then
|
|
228
|
+
blgerr=$(grep -cE "I'm skipping whatever remains|Error may have been|repeated entry|I couldn't open" "$BLG" || true)
|
|
229
|
+
if [ "$blgerr" -gt 0 ]; then
|
|
230
|
+
echo " BIBTEX errors ($blgerr) — entries are DROPPED from the bibliography, build stays green:"
|
|
231
|
+
grep -E "^(You're|I'm skipping|Repeated entry|I couldn't open)" "$BLG" | sed 's/^/ /' | head -12
|
|
232
|
+
fi
|
|
233
|
+
# A cited key that never made it into the .bbl is the same failure seen from the other side.
|
|
234
|
+
if [ -f "$BASE.aux" ] && [ -f "$BASE.bbl" ]; then
|
|
235
|
+
missing=0
|
|
236
|
+
while read -r key; do
|
|
237
|
+
[ -z "$key" ] && continue
|
|
238
|
+
grep -qF "{$key}" "$BASE.bbl" || { echo " cited but NOT in the bibliography: $key"; missing=$((missing+1)); }
|
|
239
|
+
done < <(grep -oE '\\citation\{[^}]*\}' "$BASE.aux" | sed 's/.*{//;s/}//' | tr ',' '\n' | sort -u)
|
|
240
|
+
[ "$missing" -gt 0 ] && { echo " CITED-BUT-ABSENT references: $missing"; blgerr=$((blgerr+missing)); }
|
|
241
|
+
fi
|
|
242
|
+
fi
|
|
243
|
+
|
|
244
|
+
# --- Which PUBLISHER is this paper? The gate was publisher-blind until 2026-08-24 ---
|
|
245
|
+
# Measured that day: this repo holds THREE shapes — acmart (agenticdev, aisec), ACL's acl.sty
|
|
246
|
+
# (compile-rules, built via repro/build-submission.sh), and a bare `article` (scored). The
|
|
247
|
+
# checks above are publisher-independent and stay that way. What is NOT publisher-independent
|
|
248
|
+
# is which EXTERNAL checker exists, and naming it is the whole point: the author-side tool for
|
|
249
|
+
# an ACL paper (aclpubcheck) is not the one for an ACM paper, and for ACM there is no offline
|
|
250
|
+
# community checker at all — the publisher checks server-side. Saying so here stops the next
|
|
251
|
+
# person re-deriving it, and stops them running the ACL checker on an ACM paper.
|
|
252
|
+
publisher="unknown"
|
|
253
|
+
if [ -f "$BASE.tex" ]; then
|
|
254
|
+
if grep -qE '^[^%]*\\documentclass[^{]*\{acmart\}' "$BASE.tex"; then publisher="ACM"
|
|
255
|
+
elif grep -qE '^[^%]*\\usepackage\{acl\}|acl_natbib' "$BASE.tex"; then publisher="ACL"
|
|
256
|
+
elif grep -qE '^[^%]*\\documentclass[^{]*\{IEEEtran\}' "$BASE.tex"; then publisher="IEEE"
|
|
257
|
+
fi
|
|
258
|
+
fi
|
|
259
|
+
echo " publisher: $publisher"
|
|
260
|
+
case "$publisher" in
|
|
261
|
+
ACM)
|
|
262
|
+
# No offline community checker exists for ACM by design — HotCRP's format checker and ACM
|
|
263
|
+
# TAPS validate on upload. The author-side mechanism IS acmart's own warnings, checked below.
|
|
264
|
+
;;
|
|
265
|
+
ACL)
|
|
266
|
+
echo " NOTE: ACL paper — the official author-side checker is aclpubcheck, and it is NOT run here."
|
|
267
|
+
echo " Run it on the CAMERA-READY build (a line-numbered review PDF yields thousands of"
|
|
268
|
+
echo " spurious margin errors — measured 2026-08-24: 2192 errors, ALL of category Margin):"
|
|
269
|
+
echo " python3 -m venv env && env/bin/pip install 'setuptools<60' wheel"
|
|
270
|
+
echo " env/bin/pip install 'aclpubcheck @ git+https://github.com/acl-org/aclpubcheck'"
|
|
271
|
+
echo " env/bin/aclpubcheck --paper_type long <camera-ready.pdf>"
|
|
272
|
+
echo " (the setuptools pin is load-bearing: this container's setuptools breaks every"
|
|
273
|
+
echo " legacy setup.py package with 'AttributeError: install_layout')"
|
|
274
|
+
;;
|
|
275
|
+
IEEE)
|
|
276
|
+
echo " NOTE: IEEE paper — validate with IEEE PDF eXpress / LaTeX Analyzer before upload."
|
|
277
|
+
;;
|
|
278
|
+
esac
|
|
279
|
+
|
|
280
|
+
# --- The document CLASS's own warnings (acmart / IEEEtran / llncs) ---
|
|
281
|
+
# 🔴 ADDED 2026-08-24 after three camera-ready blockers shipped past this gate in one
|
|
282
|
+
# evening — every one of them ANNOUNCED IN THIS LOG, none of them read:
|
|
283
|
+
# "You do not have the libertine package installed" -> acmart does NOT fail; it falls
|
|
284
|
+
# back to Computer Modern, so the PDF compiles clean and is set in the WRONG FONTS.
|
|
285
|
+
# "A possible image without description" -> ACM requires alt text on figures.
|
|
286
|
+
# "CCS concepts are mandatory for papers over two pages" -> a hard venue requirement.
|
|
287
|
+
# None of these is an Overfull, an undefined ref, or a fatal error, so the gate passed all
|
|
288
|
+
# three. The class is the venue's own voice about venue compliance; not reading it was the
|
|
289
|
+
# whole defect. Blocking, not advisory: each of the three would have been shipped.
|
|
290
|
+
# 🔴 STAGE-AWARENESS, added 2026-08-24 after running this gate on the HELD-OUT submitted
|
|
291
|
+
# version (commit eac95937). It fired 4 warnings there and TWO were false: at submission time
|
|
292
|
+
# `printacmref=false` and a missing CCS block are DELIBERATE and correct — the venue requires
|
|
293
|
+
# neither until camera-ready. A gate that cries on a legitimate submission build gets muted,
|
|
294
|
+
# so camera-ready-only requirements are suppressed while the document is still in review mode.
|
|
295
|
+
# Detection is self-contained (the document says what it is), not a flag someone must remember.
|
|
296
|
+
review_mode=0
|
|
297
|
+
if [ -f "$BASE.tex" ]; then
|
|
298
|
+
grep -qE '^[^%]*\\documentclass\[[^]]*\breview\b' "$BASE.tex" && review_mode=1
|
|
299
|
+
grep -qE '^[^%]*printacmref=false' "$BASE.tex" && review_mode=1
|
|
300
|
+
fi
|
|
301
|
+
CAMERA_ONLY='ACM reference format is mandatory|CCS concepts are mandatory'
|
|
302
|
+
CLASS_RE="^(Class|Package) (acmart|IEEEtran|llncs|refcheck) Warning"
|
|
303
|
+
|
|
304
|
+
# 🔴 ADVISORY class warnings: printed, but NOT blocking (2026-09-02).
|
|
305
|
+
#
|
|
306
|
+
# The same argument as the paragraph above about CAMERA_ONLY, only from the other side: a gate that
|
|
307
|
+
# fails a paper over something the author cannot influence gets muted entirely — together with the
|
|
308
|
+
# real findings. The measurement that produced the list is the very first run of the fixed
|
|
309
|
+
# render-gate over the corpus:
|
|
310
|
+
#
|
|
311
|
+
# agenticdev-2026 1 warning, `\vspace`. It is NOT in paper.tex, NOT in the paper's .sty — the
|
|
312
|
+
# package generates it. The publisher accepted this PDF on the second submission
|
|
313
|
+
# with ZERO findings, that is, its own check had no objections.
|
|
314
|
+
# aisec-2026 3, TWO of them about images with no descriptions — that one IS an ACM
|
|
315
|
+
# requirement, it is fixed by the author, and it stays blocking.
|
|
316
|
+
#
|
|
317
|
+
# That is, one bucket held a finding that must be fixed and noise that cannot be fixed at all.
|
|
318
|
+
# Splitting them is not a weakening: `\vspace` is still visible in the output on its own line.
|
|
319
|
+
#
|
|
320
|
+
# ⚠️ Keep the list NARROW and extend it only by measurement: every line here is a class of findings
|
|
321
|
+
# that stopped failing the build. Extending it "just in case" turns the gate back into a printer.
|
|
322
|
+
CLASS_ADVISORY='\\vspace should only be used'
|
|
323
|
+
if [ "$review_mode" = 1 ]; then
|
|
324
|
+
echo " stage: SUBMISSION (review mode) — camera-ready-only requirements not gated"
|
|
325
|
+
class_hits=$(grep -E "$CLASS_RE" "$LOG" | grep -vE "$CAMERA_ONLY" || true)
|
|
326
|
+
else
|
|
327
|
+
echo " stage: CAMERA-READY"
|
|
328
|
+
class_hits=$(grep -E "$CLASS_RE" "$LOG" || true)
|
|
329
|
+
fi
|
|
330
|
+
# One selection, two buckets: blocking and advisory. Counting them as one number WAS the defect.
|
|
331
|
+
class_block=$(printf '%s\n' "$class_hits" | grep -vE '^$' | grep -vE "$CLASS_ADVISORY" || true)
|
|
332
|
+
class_advis=$(printf '%s\n' "$class_hits" | grep -vE '^$' | grep -E "$CLASS_ADVISORY" || true)
|
|
333
|
+
classwarn=$(printf '%s\n' "$class_block" | grep -cE '^.' || true)
|
|
334
|
+
classadv=$(printf '%s\n' "$class_advis" | grep -cE '^.' || true)
|
|
335
|
+
if [ "$classwarn" -gt 0 ]; then
|
|
336
|
+
echo " DOCUMENT-CLASS warnings ($classwarn) — venue compliance, not cosmetics:"
|
|
337
|
+
printf '%s\n' "$class_block" | sed 's/^/ /' | head -20
|
|
338
|
+
fi
|
|
339
|
+
if [ "$classadv" -gt 0 ]; then
|
|
340
|
+
echo " document-class advisory ($classadv) — printed, NOT gated (see CLASS_ADVISORY):"
|
|
341
|
+
printf '%s\n' "$class_advis" | sed 's/^/ /' | head -20
|
|
342
|
+
fi
|
|
343
|
+
|
|
344
|
+
# ⌫ ACM fonts: the check was REMOVED 2026-08-26 — it moved into the `pdf/fonts` rule.
|
|
345
|
+
# (deliberately NOT a `# ---` block: the ratchet counts blocks, and a tombstone is not a check)
|
|
346
|
+
# The block lived here from 25.08 and was the first to catch "acmart silently fell back to Computer
|
|
347
|
+
# Modern". The same thing is now done by `eslint-rules/pdf-facts.mjs` over `_build/paper.facts.json`,
|
|
348
|
+
# and it does it WIDER: the block here skipped review mode, the rule judges the artifact always. Two
|
|
349
|
+
# sources of truth about one fact drift apart — so the duplicate was deleted, not kept "just in case".
|
|
350
|
+
#
|
|
351
|
+
# 🔴 The condition under which this becomes a loss: the rule looks at papers that declared their venue
|
|
352
|
+
# in `venue.json`. Should a paper appear with a build but without `venue.json`, nobody will check its
|
|
353
|
+
# fonts. Today there are none (all three real papers are declared).
|
|
354
|
+
|
|
355
|
+
# --- chktex, if installed: LaTeX-source typography the log cannot see ---
|
|
356
|
+
# Adopted 2026-08-24 instead of writing our own. Measured first: out of the box it produced 28
|
|
357
|
+
# findings on a clean camera-ready and exactly ONE was real (`95\% CI. Averages` — a period after
|
|
358
|
+
# capitals that LaTeX renders with a NARROW space; the fix is `CI\@.`). The five noisy rules are
|
|
359
|
+
# disabled BY NAME in $REPO/.chktexrc with the measurement recorded there; ~30 rules stay armed.
|
|
360
|
+
# Advisory, not blocking: the two survivors of rule 13 on our own corpus are false, and a gate
|
|
361
|
+
# that fails on known-false findings is a gate someone disables.
|
|
362
|
+
if command -v chktex >/dev/null 2>&1 && [ -f "$BASE.tex" ]; then
|
|
363
|
+
# 🔴 Walk UP from the paper dir first. The three candidates below all failed on 2026-08-24 in a
|
|
364
|
+
# plain `bash check-render.sh <dir>` call: $CLAUDE_PROJECT_DIR is unset outside the harness, and
|
|
365
|
+
# $OLDPWD is whatever the caller sat in before this script's own `cd` — here the paper dir, which
|
|
366
|
+
# holds no rc. Result: 32 advisory findings printed with the five noisy rules ARMED, i.e. the
|
|
367
|
+
# config we measured and wrote that morning was silently not in effect. The tell is the absence
|
|
368
|
+
# of `rc=` in the header line; nothing else says so. Same class as every other config-not-applied
|
|
369
|
+
# defect in this repo, so the lookup no longer depends on an env var or on the caller's cwd.
|
|
370
|
+
RC=""
|
|
371
|
+
d=$(cd "$(dirname "$BASE.tex")" && pwd)
|
|
372
|
+
while [ -n "$d" ] && [ "$d" != "/" ]; do
|
|
373
|
+
[ -f "$d/.chktexrc" ] && { RC="$d/.chktexrc"; break; }
|
|
374
|
+
d=$(dirname "$d")
|
|
375
|
+
done
|
|
376
|
+
[ -z "$RC" ] && for cand in "${CLAUDE_PROJECT_DIR:-}/.chktexrc" "$OLDPWD/.chktexrc" "$HOME/.chktexrc"; do
|
|
377
|
+
[ -n "$cand" ] && [ -f "$cand" ] && { RC="$cand"; break; }
|
|
378
|
+
done
|
|
379
|
+
ck=$(chktex -q ${RC:+-l "$RC"} "$BASE.tex" 2>/dev/null | grep -cE "^(Warning|Error)" || true)
|
|
380
|
+
if [ "$ck" -gt 0 ]; then
|
|
381
|
+
echo " chktex ($ck, advisory${RC:+, rc=$(basename "$RC")}):"
|
|
382
|
+
chktex -q ${RC:+-l "$RC"} "$BASE.tex" 2>/dev/null | grep -E "^(Warning|Error)" | sed 's/^/ /' | head -10
|
|
383
|
+
fi
|
|
384
|
+
fi
|
|
385
|
+
|
|
386
|
+
# --- refcheck: unreferenced labels. NOT our code — the package ships with TeX Live (2013) and
|
|
387
|
+
# writes `Package refcheck Warning: Unused label 'tab:orphan'` into the same .log, which the
|
|
388
|
+
# CLASS_RE above already reads. Turn it on for a CHECK build only:
|
|
389
|
+
# \usepackage{refcheck}
|
|
390
|
+
# ⚠️ never in the shipped source — by default it prints label names in the margins. Verified
|
|
391
|
+
# 2026-08-24 on a fixture: it names both an orphan table and an orphan figure. Our own paper had
|
|
392
|
+
# exactly this defect the same day (`tab:tasks`, a table added AT THE REVIEWERS' REQUEST and never
|
|
393
|
+
# referenced), found by hand because nothing was looking.
|
|
394
|
+
|
|
395
|
+
# --- Advisory: literal space inside \texttt{...} (a multi-token command can wrap
|
|
396
|
+
# at that space with NO Overfull warning — e.g. rm -rf breaking as rm | -rf). Tie
|
|
397
|
+
# command tokens with ~ (\texttt{rm~-rf}). Advisory only: many \texttt spaces are fine.
|
|
398
|
+
if [ -f "$BASE.tex" ]; then
|
|
399
|
+
ttspace=$(grep -oE '\\texttt\{[^{}]* [^{}]*\}' "$BASE.tex" 2>/dev/null | sort -u | head -20 || true)
|
|
400
|
+
if [ -n "$ttspace" ]; then
|
|
401
|
+
ttn=$(printf '%s\n' "$ttspace" | wc -l | tr -d ' ')
|
|
402
|
+
echo " NOTE ($ttn): \\texttt{} block(s) with a literal space (can wrap mid-command — tie tokens with ~ if it's a shell command):"
|
|
403
|
+
printf '%s\n' "$ttspace" | sed 's/^/ /'
|
|
404
|
+
fi
|
|
405
|
+
fi
|
|
406
|
+
|
|
407
|
+
[ "$big" -gt 0 ] && fail=1
|
|
408
|
+
[ "$undef" -gt 0 ] && { echo " UNDEFINED refs/citations/control-seq: $undef line(s)"; fail=1; }
|
|
409
|
+
[ "$fatal" -gt 0 ] && { echo " FATAL/error lines: $fatal"; fail=1; }
|
|
410
|
+
[ "$classwarn" -gt 0 ] && fail=1
|
|
411
|
+
[ "$blgerr" -gt 0 ] && fail=1
|
|
412
|
+
|
|
413
|
+
echo "-- summary: ${big} visible overfull(s) >=${THRESH}pt, ${small} sub-threshold, undef=${undef}, fatal=${fatal}, class-warnings=${classwarn}, class-advisory=${classadv}, bibtex=${blgerr}"
|
|
414
|
+
if [ "$fail" -eq 0 ]; then
|
|
415
|
+
echo "PASS: no visible layout defects, no undefined refs."
|
|
416
|
+
else
|
|
417
|
+
echo "FAIL: fix the items above before declaring the render clean."
|
|
418
|
+
fi
|
|
419
|
+
exit $fail
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Python dependencies for the paper pipeline. Installed with ONE command, no manual steps:
|
|
2
|
+
#
|
|
3
|
+
# pip install -r node_modules/paperlint/skills/render-paper/checkers-requirements.txt
|
|
4
|
+
#
|
|
5
|
+
# Tools that need something other than Python (the TeXtidote jar) are installed by
|
|
6
|
+
# ensure-checkers.sh nearby — which also VERIFIES that each checker actually runs, not just that
|
|
7
|
+
# it's "installed."
|
|
8
|
+
|
|
9
|
+
jinja2>=3.0 # {{macro}} substitution in paper_numbers.py
|
|
10
|
+
|
|
11
|
+
# 🔴 THIS PIN IS LOAD-BEARING, not cosmetic. aclpubcheck pulls in bibtexparser; the 2.x branch is
|
|
12
|
+
# beta (2.0.0b9) with a different API, and the 1.4.x branch's sdist does not build under
|
|
13
|
+
# setuptools 68+. 1.3.0 has a prebuilt WHEEL (bibtexparser-1.3.0-py3-none-any.whl), so `<2`
|
|
14
|
+
# resolves to it and there's nothing to build. Verified 2026-09-04 in a clean venv: install exits
|
|
15
|
+
# 0, `python -m aclpubcheck.formatchecker --help` runs.
|
|
16
|
+
bibtexparser<2
|
|
17
|
+
|
|
18
|
+
# 🔴 aclpubcheck IS INSTALLED SEPARATELY, WITH `--no-deps`, AND IT IS DELIBERATELY NOT LISTED HERE.
|
|
19
|
+
# Measured 2026-09-17: it declares `tsv>=1.2,<2.0`, which has NO PREBUILT WHEEL AT ALL
|
|
20
|
+
# (`pip download --only-binary :all: tsv` → "from versions: none"), and its source does not build
|
|
21
|
+
# under a modern setuptools:
|
|
22
|
+
# AttributeError: install_layout. Did you mean: 'install_platlib'?
|
|
23
|
+
# That one line alone made installing the WHOLE file fail, and that's why the paper went a month
|
|
24
|
+
# without an official format check.
|
|
25
|
+
# Verified, not assumed: `tsv` is imported NOWHERE in aclpubcheck's source (grep for
|
|
26
|
+
# `import tsv|from tsv` — 0 hits), and without it
|
|
27
|
+
# `python3 -m aclpubcheck.formatchecker --help` prints usage and exits zero.
|
|
28
|
+
# So what follows is aclpubcheck's OWN dependencies, taken verbatim from the package's metadata,
|
|
29
|
+
# minus tsv. A list, rather than `aclpubcheck` on one line, so the next unrepresentable dependency
|
|
30
|
+
# reads as a missing line rather than an unverified assumption.
|
|
31
|
+
# 🔴 THE UPPER BOUNDS ARE REMOVED, AND THAT IS A MEASUREMENT, NOT CARELESSNESS. The first edition
|
|
32
|
+
# of this list copied aclpubcheck's bounds verbatim — and turned red on CI, where Python is newer:
|
|
33
|
+
# ERROR: Could not find a version that satisfies the requirement pandas<2.0.0,>=1.5.0
|
|
34
|
+
# (from versions: 2.1.1, 2.1.2, … 3.0.5)
|
|
35
|
+
# In other words, the package's upper bound had gone stale: pandas 1.x is not published for that
|
|
36
|
+
# interpreter at all. Verified by running against a REAL PDF (aisec-2026/paper.pdf) on numpy 2.4.6
|
|
37
|
+
# + pandas 3.0.5: the checker ran and reported 9 format findings (A4, font). The lower bounds stay
|
|
38
|
+
# — they rule out things that are known to be too old; there are no upper bounds because none of
|
|
39
|
+
# them has been verified, and an unverified bound breaks the install exactly when the environment
|
|
40
|
+
# gets updated.
|
|
41
|
+
# ⚠️ `--help` is NOT proof of anything: it prints usage and exits zero even when pandas cannot be
|
|
42
|
+
# imported in that same interpreter. Verified 09-17, caught the hard way.
|
|
43
|
+
Unidecode>=1.3.6
|
|
44
|
+
pandas>=1.5.0
|
|
45
|
+
pdfplumber>=0.7.5
|
|
46
|
+
pybtex>=0.24.0
|
|
47
|
+
pylatexenc>=2.10
|
|
48
|
+
termcolor>=2.0.1
|
|
49
|
+
tqdm>=4.64.1
|
|
50
|
+
|
|
51
|
+
rebiber>=1.1.3 # an aclpubcheck dependency AND our own tool
|
|
52
|
+
|
|
53
|
+
# ⚠️ Call ONLY as `python3 -m aclpubcheck.formatchecker`. The package has no __main__.py, and the
|
|
54
|
+
# form `python3 -m aclpubcheck` fails with "cannot be directly executed" — that's exactly how this
|
|
55
|
+
# step once lived its whole life without ever running.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ensure-checkers.sh — installs ALL the external paper checkers with one command and PROVES that
|
|
3
|
+
# each one runs. Idempotent: running it again breaks nothing.
|
|
4
|
+
#
|
|
5
|
+
# bash node_modules/paperlint/skills/render-paper/ensure-checkers.sh
|
|
6
|
+
#
|
|
7
|
+
# WHY IT EXISTS (2026-09-04). The install used to live as a COMMENT in requirements.txt:
|
|
8
|
+
# five commands, `cp -r … site-packages/` among them. That block was wrong along three axes at
|
|
9
|
+
# once, and all three showed up on the first attempt to use it:
|
|
10
|
+
# 1. NOT NEEDED — bibtexparser 1.3.0 has a prebuilt wheel, the pin `<2` is enough;
|
|
11
|
+
# 2. INCOMPLETE — it said nothing about cffi and unidecode, both needed, both surfaced by a crash;
|
|
12
|
+
# 3. HARMFUL — copying into site-packages leaves pip's metadata lying
|
|
13
|
+
# (`pip list` showed 2.0.0b9 on top of 1.4.4 files).
|
|
14
|
+
# Prose is not executed and therefore not checked. A script is executed.
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
18
|
+
JAR_DIR="${TEXTIDOTE_DIR:-/opt/textidote}"
|
|
19
|
+
JAR="$JAR_DIR/textidote.jar"
|
|
20
|
+
JAR_URL="https://github.com/sylvainhalle/textidote/releases/download/v0.9/textidote.jar"
|
|
21
|
+
JAR_SHA="3ad1aaa0922709f8" # sha256 prefix, checked 2026-09-04
|
|
22
|
+
|
|
23
|
+
echo "== python dependencies =="
|
|
24
|
+
# `--only-binary :all:` IS LOAD-BEARING: it turns "a dependency with no prebuilt wheel (.whl)" from
|
|
25
|
+
# an inscrutable build failure into a refusal that names the package. That is exactly how `tsv` was
|
|
26
|
+
# found on 17.09 — before that the install failed with a stack from the depths of setuptools, and
|
|
27
|
+
# bibtexparser looked like the guilty one.
|
|
28
|
+
pip install -q --only-binary :all: -r "$HERE/checkers-requirements.txt"
|
|
29
|
+
# The checker itself — WITHOUT its dependencies: they are listed in requirements.txt by name, minus
|
|
30
|
+
# the unbuildable `tsv`, which it does not import. The reason is in the header of requirements.txt.
|
|
31
|
+
pip install -q --no-deps aclpubcheck
|
|
32
|
+
|
|
33
|
+
echo "== TeXtidote (a jar, not a pip package) =="
|
|
34
|
+
if [ -f "$JAR" ]; then
|
|
35
|
+
echo " already in place: $JAR"
|
|
36
|
+
else
|
|
37
|
+
mkdir -p "$JAR_DIR"
|
|
38
|
+
curl -sSL --max-time 300 -o "$JAR" "$JAR_URL"
|
|
39
|
+
fi
|
|
40
|
+
got="$(sha256sum "$JAR" | cut -c1-16)"
|
|
41
|
+
if [ "$got" != "$JAR_SHA" ]; then
|
|
42
|
+
echo "🔴 sha256 does not match: expected $JAR_SHA…, got $got…" >&2
|
|
43
|
+
echo " The file is NOT the one that was checked. Do not use it, find out why." >&2
|
|
44
|
+
exit 1
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# 🔴 THE MAIN PART. The installer cannot be trusted — the only thing that can be trusted is that
|
|
48
|
+
# the tool RUNS. "Installed" and "works" are different, and this repo already records a separate
|
|
49
|
+
# failure on that difference: a handoff recorded "aclpubcheck: All Clear" for a check that was
|
|
50
|
+
# never executed once. A missing checker and a passing one look the same.
|
|
51
|
+
echo "== checking that every checker WORKS =="
|
|
52
|
+
fail=0
|
|
53
|
+
# 🔴 IMPORT THE MODULE, NOT `--help`. Measured 17.09: `--help` prints usage and exits ZERO on an
|
|
54
|
+
# interpreter where pandas does not import at all (numpy/pandas built against each other:
|
|
55
|
+
# "numpy.dtype size changed"). That is, the acceptance check that stood here let through exactly
|
|
56
|
+
# the failure it was written for. `import aclpubcheck.formatchecker` pulls in numpy and pandas on
|
|
57
|
+
# lines 15 and 20 of the checker itself and therefore fails honestly.
|
|
58
|
+
python3 -c "import aclpubcheck.formatchecker" >/dev/null 2>&1 \
|
|
59
|
+
&& echo " ✅ aclpubcheck" || { echo " ❌ aclpubcheck is installed, but does not run"; fail=1; }
|
|
60
|
+
python3 -c "import rebiber" >/dev/null 2>&1 \
|
|
61
|
+
&& echo " ✅ rebiber" || { echo " ❌ rebiber does not import"; fail=1; }
|
|
62
|
+
python3 -c "import jinja2" >/dev/null 2>&1 \
|
|
63
|
+
&& echo " ✅ jinja2" || { echo " ❌ jinja2 does not import"; fail=1; }
|
|
64
|
+
java -jar "$JAR" --version >/dev/null 2>&1 \
|
|
65
|
+
&& echo " ✅ textidote ($JAR)" || { echo " ❌ textidote does not run (a JRE is needed; java 21 works)"; fail=1; }
|
|
66
|
+
|
|
67
|
+
[ "$fail" -eq 0 ] || { echo; echo "🔴 not everything works — see above" >&2; exit 1; }
|
|
68
|
+
echo
|
|
69
|
+
echo "✅ all checkers are installed AND run"
|