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,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading what pdflatex and bibtex leave behind — PURE functions, text in, data out.
|
|
3
|
+
*
|
|
4
|
+
* `latex-loop.ts` decides what to run next; this module turns the files a pass wrote into the
|
|
5
|
+
* facts that decision needs. Nothing here touches the disk: the caller reads the file and hands
|
|
6
|
+
* over its text, so every function below is testable on a string.
|
|
7
|
+
*
|
|
8
|
+
* 🔴 WHY LINE MATCHING IS LEGITIMATE HERE. A `.log` has no grammar a parser could give us: it is
|
|
9
|
+
* the terminal transcript of a TeX run. What it does have is a small set of DOCUMENTED marker
|
|
10
|
+
* lines that LaTeX and its packages print word for word, and that every build tool (latexmk,
|
|
11
|
+
* arara, texlab) keys on. Those markers are the interface. The `.aux` is similar: TeX writes one
|
|
12
|
+
* command per line, and the three we read (`\citation`, `\bibdata`, `\bibstyle`) always start a
|
|
13
|
+
* line. Neither file is prose-with-structure that a parser would read better.
|
|
14
|
+
*
|
|
15
|
+
* 🔴 THE 79-COLUMN WRAP. TeX breaks every line it writes to the log at `max_print_line` (79 in
|
|
16
|
+
* TeX Live), counting BYTES. A marker that happens to straddle the break — "Rerun to get
|
|
17
|
+
* cross-references right." is the usual victim, since it closes a long warning — is not on any
|
|
18
|
+
* one physical line. `unwrapLog` rejoins a line of exactly 79 bytes with the next one. The caller
|
|
19
|
+
* must therefore read the log as `latin1`, so one character is one byte and `length` counts what
|
|
20
|
+
* TeX counted.
|
|
21
|
+
*/
|
|
22
|
+
/** TeX Live's `max_print_line`. A physical log line of exactly this many bytes was broken by TeX. */
|
|
23
|
+
export declare const MAX_PRINT_LINE = 79;
|
|
24
|
+
/**
|
|
25
|
+
* Log lines that ask for another pass, or report on references. Each is a documented message,
|
|
26
|
+
* not a guess about wording:
|
|
27
|
+
* - `rerun-requested` — any "Rerun to get …" (LaTeX core, natbib, hyperref's outlines, …)
|
|
28
|
+
* - `labels-changed` — "LaTeX Warning: Label(s) may have changed."
|
|
29
|
+
* - `rerunfilecheck` — "Package rerunfilecheck Warning: File `…' has changed."
|
|
30
|
+
* - `undefined-references` / `undefined-citations` — the end-of-run summaries. These do NOT
|
|
31
|
+
* request a rerun by themselves: on a converged document they mean a genuinely undefined key.
|
|
32
|
+
*/
|
|
33
|
+
export type LogMarker = "rerun-requested" | "labels-changed" | "rerunfilecheck" | "undefined-references" | "undefined-citations";
|
|
34
|
+
/** Markers that mean "the output of this pass is not final yet". */
|
|
35
|
+
export declare const RERUN_MARKERS: readonly LogMarker[];
|
|
36
|
+
/**
|
|
37
|
+
* Rejoin the lines TeX broke at column 79. A line of exactly 79 bytes continues on the next one.
|
|
38
|
+
*
|
|
39
|
+
* ⚠️ A line that is 79 bytes long BY ITSELF gets joined too — TeX gives no way to tell the two
|
|
40
|
+
* apart. The cost is one false join, which can only merge two lines, never split a marker; for
|
|
41
|
+
* the substring checks below that is harmless.
|
|
42
|
+
*/
|
|
43
|
+
export declare function unwrapLog(text: string): string[];
|
|
44
|
+
/** Which markers a log carries, in a fixed order, each at most once. */
|
|
45
|
+
export declare function logMarkers(lines: readonly string[]): LogMarker[];
|
|
46
|
+
/**
|
|
47
|
+
* Is this the line a TeX error starts on? Two spellings exist and `-file-line-error` produces
|
|
48
|
+
* both: most errors become `./paper.tex:4: Undefined control sequence.`, while some keep the
|
|
49
|
+
* classic `! LaTeX Error: File `x.sty' not found.` (measured on TeX Live 2023). The path part must
|
|
50
|
+
* contain a dot or a slash, so an ordinary warning ending in a number and a colon cannot pass.
|
|
51
|
+
*/
|
|
52
|
+
export declare function isErrorLine(line: string): boolean;
|
|
53
|
+
/** The `l.NNN …` line TeX prints to show where in the source it stopped. */
|
|
54
|
+
export declare const isContextLine: (line: string) => boolean;
|
|
55
|
+
/** At most this many lines are quoted from a log: enough for the error and its context. */
|
|
56
|
+
export declare const MAX_EXCERPT = 12;
|
|
57
|
+
/**
|
|
58
|
+
* The first error of a pass and the source context after it: the error line, the lines up to
|
|
59
|
+
* and including `l.NNN`, and the one line after it (TeX splits the source line at the point of
|
|
60
|
+
* the error, so the rest of it sits on the next line). Blank lines are dropped. Empty when the
|
|
61
|
+
* log carries no error line — the caller then says so rather than printing nothing.
|
|
62
|
+
*/
|
|
63
|
+
export declare function errorExcerpt(lines: readonly string[]): string[];
|
|
64
|
+
/** What bibtex said beyond its banner — the explanation of a failed run. */
|
|
65
|
+
export declare function bibtexExcerpt(stdout: string): string[];
|
|
66
|
+
/** What the `.aux` tells bibtex: the cited keys, the databases and the style. */
|
|
67
|
+
export interface AuxBib {
|
|
68
|
+
/** Sorted, unique. `*` (`\nocite{*}`) is a key like any other. */
|
|
69
|
+
readonly citations: readonly string[];
|
|
70
|
+
/** The names in `\bibdata{…}`, as written — without `.bib`. */
|
|
71
|
+
readonly databases: readonly string[];
|
|
72
|
+
readonly style: string | null;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Read `\citation`, `\bibdata` and `\bibstyle` out of an `.aux`, following `\@input{sub.aux}` —
|
|
76
|
+
* `\include` writes each chapter's citations into its own aux file, and bibtex follows them too.
|
|
77
|
+
* `readInput` returns the text of a nested aux, or null when it does not exist.
|
|
78
|
+
*/
|
|
79
|
+
export declare function auxBib(text: string, readInput?: (name: string) => string | null, seen?: Set<string>): AuxBib;
|
|
80
|
+
//# sourceMappingURL=latex-log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"latex-log.d.ts","sourceRoot":"","sources":["../src/latex-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,qGAAqG;AACrG,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC;;;;;;;;GAQG;AACH,MAAM,MAAM,SAAS,GACjB,iBAAiB,GACjB,gBAAgB,GAChB,gBAAgB,GAChB,sBAAsB,GACtB,qBAAqB,CAAC;AAE1B,oEAAoE;AACpE,eAAO,MAAM,aAAa,EAAE,SAAS,SAAS,EAI7C,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAchD;AAED,wEAAwE;AACxE,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EAAE,CAqBhE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIjD;AAED,4EAA4E;AAC5E,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,KAAG,OAA+B,CAAC;AAE7E,2FAA2F;AAC3F,eAAO,MAAM,WAAW,KAAK,CAAC;AAE9B;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAe/D;AAWD,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAOtD;AAED,iFAAiF;AACjF,MAAM,WAAW,MAAM;IACrB,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAqDD;;;;GAIG;AACH,wBAAgB,MAAM,CACpB,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAiB,EACvD,IAAI,GAAE,GAAG,CAAC,MAAM,CAAa,GAC5B,MAAM,CASR"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading what pdflatex and bibtex leave behind — PURE functions, text in, data out.
|
|
3
|
+
*
|
|
4
|
+
* `latex-loop.ts` decides what to run next; this module turns the files a pass wrote into the
|
|
5
|
+
* facts that decision needs. Nothing here touches the disk: the caller reads the file and hands
|
|
6
|
+
* over its text, so every function below is testable on a string.
|
|
7
|
+
*
|
|
8
|
+
* 🔴 WHY LINE MATCHING IS LEGITIMATE HERE. A `.log` has no grammar a parser could give us: it is
|
|
9
|
+
* the terminal transcript of a TeX run. What it does have is a small set of DOCUMENTED marker
|
|
10
|
+
* lines that LaTeX and its packages print word for word, and that every build tool (latexmk,
|
|
11
|
+
* arara, texlab) keys on. Those markers are the interface. The `.aux` is similar: TeX writes one
|
|
12
|
+
* command per line, and the three we read (`\citation`, `\bibdata`, `\bibstyle`) always start a
|
|
13
|
+
* line. Neither file is prose-with-structure that a parser would read better.
|
|
14
|
+
*
|
|
15
|
+
* 🔴 THE 79-COLUMN WRAP. TeX breaks every line it writes to the log at `max_print_line` (79 in
|
|
16
|
+
* TeX Live), counting BYTES. A marker that happens to straddle the break — "Rerun to get
|
|
17
|
+
* cross-references right." is the usual victim, since it closes a long warning — is not on any
|
|
18
|
+
* one physical line. `unwrapLog` rejoins a line of exactly 79 bytes with the next one. The caller
|
|
19
|
+
* must therefore read the log as `latin1`, so one character is one byte and `length` counts what
|
|
20
|
+
* TeX counted.
|
|
21
|
+
*/
|
|
22
|
+
/** TeX Live's `max_print_line`. A physical log line of exactly this many bytes was broken by TeX. */
|
|
23
|
+
export const MAX_PRINT_LINE = 79;
|
|
24
|
+
/** Markers that mean "the output of this pass is not final yet". */
|
|
25
|
+
export const RERUN_MARKERS = [
|
|
26
|
+
"rerun-requested",
|
|
27
|
+
"labels-changed",
|
|
28
|
+
"rerunfilecheck",
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* Rejoin the lines TeX broke at column 79. A line of exactly 79 bytes continues on the next one.
|
|
32
|
+
*
|
|
33
|
+
* ⚠️ A line that is 79 bytes long BY ITSELF gets joined too — TeX gives no way to tell the two
|
|
34
|
+
* apart. The cost is one false join, which can only merge two lines, never split a marker; for
|
|
35
|
+
* the substring checks below that is harmless.
|
|
36
|
+
*/
|
|
37
|
+
export function unwrapLog(text) {
|
|
38
|
+
const physical = text.split(/\r?\n/);
|
|
39
|
+
const out = [];
|
|
40
|
+
let carry = "";
|
|
41
|
+
for (const line of physical) {
|
|
42
|
+
if (line.length === MAX_PRINT_LINE) {
|
|
43
|
+
carry += line;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
out.push(carry + line);
|
|
47
|
+
carry = "";
|
|
48
|
+
}
|
|
49
|
+
if (carry)
|
|
50
|
+
out.push(carry);
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
/** Which markers a log carries, in a fixed order, each at most once. */
|
|
54
|
+
export function logMarkers(lines) {
|
|
55
|
+
const found = new Set();
|
|
56
|
+
for (const line of lines) {
|
|
57
|
+
if (line.includes("Rerun to get"))
|
|
58
|
+
found.add("rerun-requested");
|
|
59
|
+
if (line.startsWith("LaTeX Warning: Label(s) may have changed"))
|
|
60
|
+
found.add("labels-changed");
|
|
61
|
+
if (line.startsWith("Package rerunfilecheck Warning:"))
|
|
62
|
+
found.add("rerunfilecheck");
|
|
63
|
+
if (line.includes("There were undefined references"))
|
|
64
|
+
found.add("undefined-references");
|
|
65
|
+
if (line.includes("There were undefined citations"))
|
|
66
|
+
found.add("undefined-citations");
|
|
67
|
+
}
|
|
68
|
+
const order = [
|
|
69
|
+
"rerun-requested",
|
|
70
|
+
"labels-changed",
|
|
71
|
+
"rerunfilecheck",
|
|
72
|
+
"undefined-references",
|
|
73
|
+
"undefined-citations",
|
|
74
|
+
];
|
|
75
|
+
return order.filter((m) => found.has(m));
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Is this the line a TeX error starts on? Two spellings exist and `-file-line-error` produces
|
|
79
|
+
* both: most errors become `./paper.tex:4: Undefined control sequence.`, while some keep the
|
|
80
|
+
* classic `! LaTeX Error: File `x.sty' not found.` (measured on TeX Live 2023). The path part must
|
|
81
|
+
* contain a dot or a slash, so an ordinary warning ending in a number and a colon cannot pass.
|
|
82
|
+
*/
|
|
83
|
+
export function isErrorLine(line) {
|
|
84
|
+
if (line.startsWith("!"))
|
|
85
|
+
return true;
|
|
86
|
+
const m = /^([^\s:]+):(\d+): /.exec(line);
|
|
87
|
+
return m !== null && /[./]/.test(m[1] ?? "");
|
|
88
|
+
}
|
|
89
|
+
/** The `l.NNN …` line TeX prints to show where in the source it stopped. */
|
|
90
|
+
export const isContextLine = (line) => /^l\.\d+/.test(line);
|
|
91
|
+
/** At most this many lines are quoted from a log: enough for the error and its context. */
|
|
92
|
+
export const MAX_EXCERPT = 12;
|
|
93
|
+
/**
|
|
94
|
+
* The first error of a pass and the source context after it: the error line, the lines up to
|
|
95
|
+
* and including `l.NNN`, and the one line after it (TeX splits the source line at the point of
|
|
96
|
+
* the error, so the rest of it sits on the next line). Blank lines are dropped. Empty when the
|
|
97
|
+
* log carries no error line — the caller then says so rather than printing nothing.
|
|
98
|
+
*/
|
|
99
|
+
export function errorExcerpt(lines) {
|
|
100
|
+
const start = lines.findIndex(isErrorLine);
|
|
101
|
+
if (start < 0)
|
|
102
|
+
return [];
|
|
103
|
+
const out = [];
|
|
104
|
+
for (let i = start; i < lines.length && out.length < MAX_EXCERPT; i++) {
|
|
105
|
+
const line = lines[i] ?? "";
|
|
106
|
+
if (line.trim() === "")
|
|
107
|
+
continue;
|
|
108
|
+
out.push(line);
|
|
109
|
+
if (isContextLine(line)) {
|
|
110
|
+
const next = lines[i + 1];
|
|
111
|
+
if (next !== undefined && next.trim() !== "")
|
|
112
|
+
out.push(next);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
/** The lines bibtex prints before it has anything to say; never an explanation of a failure. */
|
|
119
|
+
const BIBTEX_PREAMBLE = [
|
|
120
|
+
"This is BibTeX",
|
|
121
|
+
"Capacity:",
|
|
122
|
+
"The top-level auxiliary file:",
|
|
123
|
+
"The style file:",
|
|
124
|
+
"Database file #",
|
|
125
|
+
];
|
|
126
|
+
/** What bibtex said beyond its banner — the explanation of a failed run. */
|
|
127
|
+
export function bibtexExcerpt(stdout) {
|
|
128
|
+
return stdout
|
|
129
|
+
.split(/\r?\n/)
|
|
130
|
+
.filter((l) => l.trim() !== "" && !BIBTEX_PREAMBLE.some((p) => l.startsWith(p)))
|
|
131
|
+
.slice(0, MAX_EXCERPT);
|
|
132
|
+
}
|
|
133
|
+
/** The argument of `\name{…}` when a line is exactly that command, else null. */
|
|
134
|
+
function argOf(line, name) {
|
|
135
|
+
const open = `\\${name}{`;
|
|
136
|
+
if (!line.startsWith(open) || !line.endsWith("}"))
|
|
137
|
+
return null;
|
|
138
|
+
return line.slice(open.length, -1);
|
|
139
|
+
}
|
|
140
|
+
const list = (arg) => arg
|
|
141
|
+
.split(",")
|
|
142
|
+
.map((s) => s.trim())
|
|
143
|
+
.filter(Boolean);
|
|
144
|
+
/**
|
|
145
|
+
* Fold one aux line into the walk. Order is load-bearing: a nested aux's databases land where
|
|
146
|
+
* its `\@input` stands, and its style counts only while no `\bibstyle` has been read yet — a
|
|
147
|
+
* later one in the parent still overrides it.
|
|
148
|
+
*/
|
|
149
|
+
function readAuxLine(line, acc, walk) {
|
|
150
|
+
for (const k of list(argOf(line, "citation") ?? ""))
|
|
151
|
+
acc.citations.add(k);
|
|
152
|
+
acc.databases.push(...list(argOf(line, "bibdata") ?? ""));
|
|
153
|
+
const st = argOf(line, "bibstyle");
|
|
154
|
+
if (st !== null)
|
|
155
|
+
acc.style = st.trim();
|
|
156
|
+
followInput(argOf(line, "@input"), acc, walk);
|
|
157
|
+
}
|
|
158
|
+
/** Merge an `\@input` aux into the walk — once per name, and only when it exists. */
|
|
159
|
+
function followInput(input, acc, walk) {
|
|
160
|
+
if (input === null || walk.seen.has(input))
|
|
161
|
+
return;
|
|
162
|
+
walk.seen.add(input);
|
|
163
|
+
const nested = walk.readInput(input);
|
|
164
|
+
if (nested === null)
|
|
165
|
+
return;
|
|
166
|
+
const sub = auxBib(nested, walk.readInput, walk.seen);
|
|
167
|
+
for (const k of sub.citations)
|
|
168
|
+
acc.citations.add(k);
|
|
169
|
+
acc.databases.push(...sub.databases);
|
|
170
|
+
acc.style ??= sub.style;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Read `\citation`, `\bibdata` and `\bibstyle` out of an `.aux`, following `\@input{sub.aux}` —
|
|
174
|
+
* `\include` writes each chapter's citations into its own aux file, and bibtex follows them too.
|
|
175
|
+
* `readInput` returns the text of a nested aux, or null when it does not exist.
|
|
176
|
+
*/
|
|
177
|
+
export function auxBib(text, readInput = () => null, seen = new Set()) {
|
|
178
|
+
const acc = { citations: new Set(), databases: [], style: null };
|
|
179
|
+
for (const raw of text.split(/\r?\n/))
|
|
180
|
+
readAuxLine(raw.trim(), acc, { readInput, seen });
|
|
181
|
+
return {
|
|
182
|
+
citations: [...acc.citations].sort(),
|
|
183
|
+
databases: acc.databases,
|
|
184
|
+
style: acc.style,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=latex-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"latex-log.js","sourceRoot":"","sources":["../src/latex-log.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE DECISION of the LaTeX build loop: given what the runs so far observed, what runs next.
|
|
3
|
+
*
|
|
4
|
+
* PURE. No disk, no process, no clock. The shell in `build.ts` runs a step, records what it saw
|
|
5
|
+
* as an `Observation`, appends it to the history and asks `nextStep(summarize(history))` again.
|
|
6
|
+
* Both halves are table tests (`latex-loop.harness.mjs`), not scenarios that need TeX.
|
|
7
|
+
*
|
|
8
|
+
* ── THE LOOP: `summarize` reduces the history to a `State`, and `nextStep` asks four questions ──
|
|
9
|
+
* 1. did the last program fail? → fail, with its error lines
|
|
10
|
+
* 2. did the citations, a .bib or the style change since
|
|
11
|
+
* bibtex last ran? → bibtex
|
|
12
|
+
* 3. did the last pass change a tracked file, or did the
|
|
13
|
+
* log ask for a rerun? → latex (fail after MAX_PASSES)
|
|
14
|
+
* 4. otherwise → one FINAL latex pass, then done
|
|
15
|
+
*
|
|
16
|
+
* 🔴 WHY A FINAL PASS AFTER CONVERGENCE. `paper-guards.tex`, shipped by this package, turns an
|
|
17
|
+
* undefined `\ref` or `\cite` into a BUILD FAILURE — but only on a pass that defines
|
|
18
|
+
* `\finalpass`, because on the first pass every reference is undefined by construction. Measured
|
|
19
|
+
* on 2026-08-26 with the unconditional form: pass 1 fails, the `.aux` is never written, pass 2
|
|
20
|
+
* fails the same way and no PDF ever appears. So the guards need to know which pass is the last,
|
|
21
|
+
* and only the loop knows it. The price is one extra pdflatex run per build, paid for turning
|
|
22
|
+
* "?? in the PDF" into a red build.
|
|
23
|
+
*
|
|
24
|
+
* 🔴 WHY A CAP. A document can keep changing its own aux on every pass — the textbook case is a
|
|
25
|
+
* page reference whose own wording moves its target to the other page, and back — and the aux never
|
|
26
|
+
* settles. Without a cap the loop runs forever; with a silent cap it stops and ships an
|
|
27
|
+
* unconverged PDF with wrong cross-references. It stops, and it FAILS, naming the file that kept
|
|
28
|
+
* changing.
|
|
29
|
+
*
|
|
30
|
+
* ⚠️ `filecontents` with `[overwrite]` is NOT such a case, though this comment said so until
|
|
31
|
+
* 2026-09-24. The environment body is verbatim, so every pass writes the same bytes, and the loop
|
|
32
|
+
* compares CONTENT digests, not modification times. Measured with `paperlint build` on TeX Live 2026: a
|
|
33
|
+
* real acmart paper embedding its `refs.bib` that way converged in 3 pdflatex passes, and an
|
|
34
|
+
* `article` with an overwritten `\input` file carrying `\label`/`\ref`, a `\tableofcontents` and
|
|
35
|
+
* an overwritten `.bib` converged in 4.
|
|
36
|
+
*/
|
|
37
|
+
/** Files a pdflatex pass writes and the next pass reads. A change means the output is stale. */
|
|
38
|
+
export type Tracked = "aux" | "toc" | "out" | "bbl";
|
|
39
|
+
export declare const TRACKED: readonly Tracked[];
|
|
40
|
+
/** Content digests of the tracked files; `null` = the file does not exist. */
|
|
41
|
+
export type Hashes = {
|
|
42
|
+
readonly [K in Tracked]: string | null;
|
|
43
|
+
};
|
|
44
|
+
/** Log markers, as `latex-log.ts` reads them. Re-declared as a type so this module has no imports. */
|
|
45
|
+
export type LogMarker = "rerun-requested" | "labels-changed" | "rerunfilecheck" | "undefined-references" | "undefined-citations";
|
|
46
|
+
/**
|
|
47
|
+
* What bibtex would be run on. `none` — the aux names no `\bibdata`, so there is nothing for
|
|
48
|
+
* bibtex to do. Two `needed` states are the same input exactly when every field is equal: the
|
|
49
|
+
* citation set moved, a database was added, or a `.bib` file's content changed ⇒ bibtex again.
|
|
50
|
+
*/
|
|
51
|
+
export type BibInput = {
|
|
52
|
+
readonly kind: "none";
|
|
53
|
+
} | {
|
|
54
|
+
readonly kind: "needed";
|
|
55
|
+
/** Sorted, unique cited keys. */
|
|
56
|
+
readonly citations: readonly string[];
|
|
57
|
+
readonly databases: readonly string[];
|
|
58
|
+
readonly style: string | null;
|
|
59
|
+
/** A digest over the content of every database file that exists; null when none was found. */
|
|
60
|
+
readonly bibHash: string | null;
|
|
61
|
+
};
|
|
62
|
+
export type Observation = {
|
|
63
|
+
readonly step: "latex";
|
|
64
|
+
/** Whether this pass defined `\finalpass`. */
|
|
65
|
+
readonly final: boolean;
|
|
66
|
+
readonly exitCode: number;
|
|
67
|
+
readonly before: Hashes;
|
|
68
|
+
readonly after: Hashes;
|
|
69
|
+
readonly markers: readonly LogMarker[];
|
|
70
|
+
/** The bibliography input as the aux stood after this pass. */
|
|
71
|
+
readonly bib: BibInput;
|
|
72
|
+
/** The first error and its source context, from the log. Empty on success. */
|
|
73
|
+
readonly errorLines: readonly string[];
|
|
74
|
+
} | {
|
|
75
|
+
readonly step: "bibtex";
|
|
76
|
+
readonly exitCode: number;
|
|
77
|
+
readonly before: Hashes;
|
|
78
|
+
readonly after: Hashes;
|
|
79
|
+
/** The input bibtex was run on. */
|
|
80
|
+
readonly bib: BibInput;
|
|
81
|
+
readonly errorLines: readonly string[];
|
|
82
|
+
};
|
|
83
|
+
export type FailCause = {
|
|
84
|
+
readonly kind: "exit";
|
|
85
|
+
readonly code: number;
|
|
86
|
+
} | {
|
|
87
|
+
readonly kind: "no-convergence";
|
|
88
|
+
readonly unsettled: readonly string[];
|
|
89
|
+
};
|
|
90
|
+
export type Step = {
|
|
91
|
+
readonly kind: "latex";
|
|
92
|
+
readonly final: boolean;
|
|
93
|
+
} | {
|
|
94
|
+
readonly kind: "bibtex";
|
|
95
|
+
} | {
|
|
96
|
+
readonly kind: "done";
|
|
97
|
+
/** Undefined-reference summaries still in the final log — a converged doc with a bad key. */
|
|
98
|
+
readonly warnings: readonly LogMarker[];
|
|
99
|
+
} | {
|
|
100
|
+
readonly kind: "fail";
|
|
101
|
+
readonly step: "latex" | "bibtex";
|
|
102
|
+
readonly cause: FailCause;
|
|
103
|
+
/** What to show the human: the log excerpt, or the non-convergence explanation. */
|
|
104
|
+
readonly lines: readonly string[];
|
|
105
|
+
};
|
|
106
|
+
/** Non-final pdflatex passes allowed before the build is declared non-converging. */
|
|
107
|
+
export declare const MAX_PASSES = 5;
|
|
108
|
+
/** The history, reduced to the four questions `nextStep` asks. */
|
|
109
|
+
export type State = {
|
|
110
|
+
readonly failed: {
|
|
111
|
+
readonly step: "latex" | "bibtex";
|
|
112
|
+
readonly cause: FailCause;
|
|
113
|
+
readonly lines: readonly string[];
|
|
114
|
+
} | null;
|
|
115
|
+
/** Citations, databases, style or `.bib` content changed since bibtex last ran. */
|
|
116
|
+
readonly bibOutdated: boolean;
|
|
117
|
+
/** Tracked files the last pass changed (`paper.aux`), or the rerun markers its log printed. */
|
|
118
|
+
readonly unsettled: readonly string[];
|
|
119
|
+
/** Non-final pdflatex passes so far. */
|
|
120
|
+
readonly latexPasses: number;
|
|
121
|
+
readonly finalDone: boolean;
|
|
122
|
+
/** Undefined-reference summaries in the log of the final pass. */
|
|
123
|
+
readonly warnings: readonly LogMarker[];
|
|
124
|
+
};
|
|
125
|
+
export declare function nextStep(s: State): Step;
|
|
126
|
+
/** The tracked files whose digest differs between two snapshots. */
|
|
127
|
+
export declare function changedFiles(before: Hashes, after: Hashes): Tracked[];
|
|
128
|
+
export declare function summarize(history: readonly Observation[]): State;
|
|
129
|
+
//# sourceMappingURL=latex-loop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"latex-loop.d.ts","sourceRoot":"","sources":["../src/latex-loop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE DECISION of the LaTeX build loop: given what the runs so far observed, what runs next.
|
|
3
|
+
*
|
|
4
|
+
* PURE. No disk, no process, no clock. The shell in `build.ts` runs a step, records what it saw
|
|
5
|
+
* as an `Observation`, appends it to the history and asks `nextStep(summarize(history))` again.
|
|
6
|
+
* Both halves are table tests (`latex-loop.harness.mjs`), not scenarios that need TeX.
|
|
7
|
+
*
|
|
8
|
+
* ── THE LOOP: `summarize` reduces the history to a `State`, and `nextStep` asks four questions ──
|
|
9
|
+
* 1. did the last program fail? → fail, with its error lines
|
|
10
|
+
* 2. did the citations, a .bib or the style change since
|
|
11
|
+
* bibtex last ran? → bibtex
|
|
12
|
+
* 3. did the last pass change a tracked file, or did the
|
|
13
|
+
* log ask for a rerun? → latex (fail after MAX_PASSES)
|
|
14
|
+
* 4. otherwise → one FINAL latex pass, then done
|
|
15
|
+
*
|
|
16
|
+
* 🔴 WHY A FINAL PASS AFTER CONVERGENCE. `paper-guards.tex`, shipped by this package, turns an
|
|
17
|
+
* undefined `\ref` or `\cite` into a BUILD FAILURE — but only on a pass that defines
|
|
18
|
+
* `\finalpass`, because on the first pass every reference is undefined by construction. Measured
|
|
19
|
+
* on 2026-08-26 with the unconditional form: pass 1 fails, the `.aux` is never written, pass 2
|
|
20
|
+
* fails the same way and no PDF ever appears. So the guards need to know which pass is the last,
|
|
21
|
+
* and only the loop knows it. The price is one extra pdflatex run per build, paid for turning
|
|
22
|
+
* "?? in the PDF" into a red build.
|
|
23
|
+
*
|
|
24
|
+
* 🔴 WHY A CAP. A document can keep changing its own aux on every pass — the textbook case is a
|
|
25
|
+
* page reference whose own wording moves its target to the other page, and back — and the aux never
|
|
26
|
+
* settles. Without a cap the loop runs forever; with a silent cap it stops and ships an
|
|
27
|
+
* unconverged PDF with wrong cross-references. It stops, and it FAILS, naming the file that kept
|
|
28
|
+
* changing.
|
|
29
|
+
*
|
|
30
|
+
* ⚠️ `filecontents` with `[overwrite]` is NOT such a case, though this comment said so until
|
|
31
|
+
* 2026-09-24. The environment body is verbatim, so every pass writes the same bytes, and the loop
|
|
32
|
+
* compares CONTENT digests, not modification times. Measured with `paperlint build` on TeX Live 2026: a
|
|
33
|
+
* real acmart paper embedding its `refs.bib` that way converged in 3 pdflatex passes, and an
|
|
34
|
+
* `article` with an overwritten `\input` file carrying `\label`/`\ref`, a `\tableofcontents` and
|
|
35
|
+
* an overwritten `.bib` converged in 4.
|
|
36
|
+
*/
|
|
37
|
+
export const TRACKED = ["aux", "toc", "out", "bbl"];
|
|
38
|
+
const RERUN = [
|
|
39
|
+
"rerun-requested",
|
|
40
|
+
"labels-changed",
|
|
41
|
+
"rerunfilecheck",
|
|
42
|
+
];
|
|
43
|
+
/** Non-final pdflatex passes allowed before the build is declared non-converging. */
|
|
44
|
+
export const MAX_PASSES = 5;
|
|
45
|
+
export function nextStep(s) {
|
|
46
|
+
if (s.failed)
|
|
47
|
+
return { kind: "fail", ...s.failed };
|
|
48
|
+
if (s.finalDone)
|
|
49
|
+
return { kind: "done", warnings: s.warnings };
|
|
50
|
+
if (s.bibOutdated)
|
|
51
|
+
return { kind: "bibtex" };
|
|
52
|
+
if (s.unsettled.length === 0)
|
|
53
|
+
return { kind: "latex", final: true };
|
|
54
|
+
if (s.latexPasses < MAX_PASSES)
|
|
55
|
+
return { kind: "latex", final: false };
|
|
56
|
+
return noConvergence(s.unsettled);
|
|
57
|
+
}
|
|
58
|
+
const noConvergence = (unsettled) => ({
|
|
59
|
+
kind: "fail",
|
|
60
|
+
step: "latex",
|
|
61
|
+
cause: { kind: "no-convergence", unsettled },
|
|
62
|
+
lines: [
|
|
63
|
+
`the build does not converge: after ${MAX_PASSES} pdflatex passes, the last one still changed or asked to rerun: ${unsettled.join(", ")}.`,
|
|
64
|
+
`A document whose aux keeps changing on every pass never settles; stopping here instead of`,
|
|
65
|
+
`shipping a PDF with stale cross-references.`,
|
|
66
|
+
],
|
|
67
|
+
});
|
|
68
|
+
/** The tracked files whose digest differs between two snapshots. */
|
|
69
|
+
export function changedFiles(before, after) {
|
|
70
|
+
return TRACKED.filter((f) => before[f] !== after[f]);
|
|
71
|
+
}
|
|
72
|
+
const sameBib = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
73
|
+
/**
|
|
74
|
+
* What the last run left unsettled. Walking back: a run that changed a tracked file answers with
|
|
75
|
+
* those files; a latex pass that changed none answers with the rerun markers in its log. A bibtex
|
|
76
|
+
* run that left the `.bbl` byte-identical changes nothing, so the pass before it answers.
|
|
77
|
+
*/
|
|
78
|
+
function unsettledBy(history) {
|
|
79
|
+
if (history.length === 0)
|
|
80
|
+
return ["paper.aux"]; // no pass has written it yet
|
|
81
|
+
for (const o of history.toReversed()) {
|
|
82
|
+
const files = changedFiles(o.before, o.after);
|
|
83
|
+
if (files.length > 0)
|
|
84
|
+
return files.map((f) => `paper.${f}`);
|
|
85
|
+
if (o.step === "latex")
|
|
86
|
+
return o.markers.filter((m) => RERUN.includes(m));
|
|
87
|
+
}
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
export function summarize(history) {
|
|
91
|
+
const last = history.at(-1);
|
|
92
|
+
const latexRuns = history.filter((o) => o.step === "latex");
|
|
93
|
+
const lastLatex = latexRuns.at(-1);
|
|
94
|
+
const lastBibtex = history.filter((o) => o.step === "bibtex").at(-1);
|
|
95
|
+
return {
|
|
96
|
+
failed: last && last.exitCode !== 0
|
|
97
|
+
? {
|
|
98
|
+
step: last.step,
|
|
99
|
+
cause: { kind: "exit", code: last.exitCode },
|
|
100
|
+
lines: last.errorLines,
|
|
101
|
+
}
|
|
102
|
+
: null,
|
|
103
|
+
bibOutdated: lastLatex?.bib.kind === "needed" &&
|
|
104
|
+
!(lastBibtex && sameBib(lastBibtex.bib, lastLatex.bib)),
|
|
105
|
+
unsettled: unsettledBy(history),
|
|
106
|
+
latexPasses: latexRuns.filter((o) => !o.final).length,
|
|
107
|
+
finalDone: last?.step === "latex" && last.final,
|
|
108
|
+
warnings: last?.step === "latex"
|
|
109
|
+
? last.markers.filter((m) => m === "undefined-references" || m === "undefined-citations")
|
|
110
|
+
: [],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=latex-loop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"latex-loop.js","sourceRoot":"","sources":["../src/latex-loop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { PACKAGE_NAME } from "../skills/paper-pipeline/scripts/consumer.mjs";
|
|
2
|
+
export { PACKAGE_NAME };
|
|
3
|
+
/** Where Claude Code looks for project skills, relative to the project root. */
|
|
4
|
+
export declare const SKILLS_HOME: string;
|
|
5
|
+
export type Located = {
|
|
6
|
+
readonly dir: string;
|
|
7
|
+
readonly spelled: string;
|
|
8
|
+
} | {
|
|
9
|
+
readonly error: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* The installed package, asked of Node from the PROJECT, not from wherever this code runs. A copy
|
|
13
|
+
* run out of the npx cache is not the project's install, and a link into a cache is a link that
|
|
14
|
+
* disappears with it — so "does not resolve from here" is an answer, not something to paper over.
|
|
15
|
+
*
|
|
16
|
+
* @returns `dir` — the real directory; `spelled` — the same directory as the project names it.
|
|
17
|
+
*/
|
|
18
|
+
export declare function locatePackage(project: string): Located;
|
|
19
|
+
/** The skills the package ships: every skill under its `SHIPPED_SKILLS_DIR`. */
|
|
20
|
+
export declare function shippedSkills(pkgDir: string): {
|
|
21
|
+
readonly skillsDir: string;
|
|
22
|
+
readonly names: readonly string[];
|
|
23
|
+
} | {
|
|
24
|
+
readonly error: string;
|
|
25
|
+
};
|
|
26
|
+
export type LinkStatus = "created" | "replaced" | "present" | "missing" | "foreign";
|
|
27
|
+
export interface SkillLink {
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly status: LinkStatus;
|
|
30
|
+
/** For `foreign`: what is there instead, in words. */
|
|
31
|
+
readonly reason?: string;
|
|
32
|
+
}
|
|
33
|
+
export type LinkReport = {
|
|
34
|
+
readonly ok: true;
|
|
35
|
+
readonly home: string;
|
|
36
|
+
/** The link target of the first skill, e.g. `../../node_modules/paperlint/skills/x`. */
|
|
37
|
+
readonly example: string | null;
|
|
38
|
+
readonly links: readonly SkillLink[];
|
|
39
|
+
} | {
|
|
40
|
+
readonly ok: false;
|
|
41
|
+
readonly error: string;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Link every shipped skill into `<project>/.claude/skills/`, or — with `write: false` — only
|
|
45
|
+
* report which ones are missing (that is what `paperlint doctor` asks).
|
|
46
|
+
*/
|
|
47
|
+
export declare function linkSkills(project: string, { write, locate, }?: {
|
|
48
|
+
write?: boolean;
|
|
49
|
+
locate?: (p: string) => Located;
|
|
50
|
+
}): LinkReport;
|
|
51
|
+
//# sourceMappingURL=link-skills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-skills.d.ts","sourceRoot":"","sources":["../src/link-skills.ts"],"names":[],"mappings":"AA0CA,OAAO,EAGL,YAAY,EAEb,MAAM,+CAA+C,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,CAAC;AACxB,gFAAgF;AAChF,eAAO,MAAM,WAAW,QAA4B,CAAC;AAErD,MAAM,MAAM,OAAO,GACf;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/B;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAyBtD;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,GAEZ;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GACjE;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAa7B;AAED,MAAM,MAAM,UAAU,GACpB,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAE7D,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,UAAU,GAClB;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wFAAwF;IACxF,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;CACtC,GACD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AA+EnD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,EACE,KAAY,EACZ,MAAsB,GACvB,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAA;CAAO,GAC3D,UAAU,CAuCZ"}
|