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,72 @@
|
|
|
1
|
+
# Dependency updates by bot, not by hand.
|
|
2
|
+
#
|
|
3
|
+
# WHY A BOT AT ALL, not runtime version checks: an update IS AN ACTION,
|
|
4
|
+
# and the bot creates a pull request you can press. A terminal line
|
|
5
|
+
# saying "something newer exists" is not an action; it gets forgotten in a day. The local gate
|
|
6
|
+
# `npm ls vigiles` in the consumer solves a DIFFERENT problem — "installed does not match manifest" —
|
|
7
|
+
# and knows nothing about freshness against the registry: measurement 2026-09-16 — manifest `^27.1.4`
|
|
8
|
+
# with 27.1.6 and 27.2.0 in the registry gives RC=0, the range is satisfied.
|
|
9
|
+
#
|
|
10
|
+
# WHY THIS IS ALLOWED HERE, though the bot is off in a neighboring repository: that one is
|
|
11
|
+
# PRIVATE, and its runs ate the shared Actions quota. This one is
|
|
12
|
+
# public, free-tier minutes are free, so that objection does not apply here. If it becomes private,
|
|
13
|
+
# the rule changes with it.
|
|
14
|
+
#
|
|
15
|
+
# 🔴 WHAT THIS FILE DOES NOT GOVERN, and we have tripped on this: it controls only
|
|
16
|
+
# version updates. SECURITY updates are a separate mechanism and a separate
|
|
17
|
+
# REPOSITORY SETTING (Settings → Advanced Security), their frequency and
|
|
18
|
+
# grouping cannot be changed from here. If single PRs for indirect
|
|
19
|
+
# dependencies start appearing — look for the lever there, not in this file.
|
|
20
|
+
version: 2
|
|
21
|
+
updates:
|
|
22
|
+
- package-ecosystem: "npm"
|
|
23
|
+
directory: "/"
|
|
24
|
+
# MONTHLY. It used to be daily so that new vigiles releases arrived fast, but we bump
|
|
25
|
+
# vigiles by hand in the same pass that merges a vigiles PR, so the bot was only ever
|
|
26
|
+
# catching up with what we had already done. Once a month is the pace someone reads.
|
|
27
|
+
#
|
|
28
|
+
# 🔴 SCHEDULE IS NOT THE ONLY DELAY, and the second one is easy to miss: version
|
|
29
|
+
# updates have a COOLDOWN, defaulting to 3 days — "a new version is not
|
|
30
|
+
# considered for a version update until 3 days after its release". It is NOT
|
|
31
|
+
# removed deliberately: three days is protection against a just-revoked release, and
|
|
32
|
+
# we learn about our own versions before the bot anyway (vigiles is our package, bumped
|
|
33
|
+
# in the same pass where we merge PRs). Net window ~3-4 days.
|
|
34
|
+
#
|
|
35
|
+
# ⚠️ Publishing to the registry does not wake the bot — it runs on schedule only.
|
|
36
|
+
# For immediate check: Insights -> Dependency graph -> Dependabot -> Check for updates.
|
|
37
|
+
schedule:
|
|
38
|
+
interval: "monthly"
|
|
39
|
+
time: "06:00"
|
|
40
|
+
timezone: "Asia/Almaty"
|
|
41
|
+
# 🔴 OUR OWN PACKAGE IS EXEMPT FROM COOLDOWN, answering a specific objection:
|
|
42
|
+
# we can release multiple vigiles versions in a day, and a three-day
|
|
43
|
+
# default wait would mean the bot offers yesterday's.
|
|
44
|
+
# The wait exists as protection against a just-revoked release — for
|
|
45
|
+
# SOMEONE ELSE'S package that is sound; for our own, it is pointless: we will revoke it ourselves.
|
|
46
|
+
cooldown:
|
|
47
|
+
default-days: 3
|
|
48
|
+
exclude: ["vigiles"]
|
|
49
|
+
|
|
50
|
+
# One PR for the whole dev tree instead of a PR per package. Without this, the first
|
|
51
|
+
# week yields four separate CI runs and four reviews for one piece of work.
|
|
52
|
+
groups:
|
|
53
|
+
dev-dependencies:
|
|
54
|
+
patterns: ["*"]
|
|
55
|
+
open-pull-requests-limit: 2
|
|
56
|
+
commit-message:
|
|
57
|
+
prefix: "deps"
|
|
58
|
+
labels: ["dependencies"]
|
|
59
|
+
|
|
60
|
+
# Versions of GitHub Actions themselves go stale silently and are only noticed when
|
|
61
|
+
# the runner drops a warning about old Node.
|
|
62
|
+
- package-ecosystem: "github-actions"
|
|
63
|
+
directory: "/"
|
|
64
|
+
schedule:
|
|
65
|
+
interval: "monthly"
|
|
66
|
+
groups:
|
|
67
|
+
actions:
|
|
68
|
+
patterns: ["*"]
|
|
69
|
+
open-pull-requests-limit: 1
|
|
70
|
+
commit-message:
|
|
71
|
+
prefix: "ci"
|
|
72
|
+
labels: ["dependencies"]
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
# CI for paperlint — ONE job, every gate, bonds set BY MEASUREMENT.
|
|
2
|
+
#
|
|
3
|
+
# ── WHY ONE JOB ─────────────────────────────────────────────────────────────
|
|
4
|
+
# Parallelism does not save billable minutes, it ADDS them: every job pays for its own
|
|
5
|
+
# setup + checkout + install (~20-30 s). Splitting is worth it for signal isolation on a big
|
|
6
|
+
# matrix; here the whole suite is 84 seconds, so a second job would cost more than it isolates.
|
|
7
|
+
#
|
|
8
|
+
# ⛽ THIS REPOSITORY IS PUBLIC, so its minutes are FREE — they do not touch the account's
|
|
9
|
+
# 3000/month quota (verified against the API 2026-09-12: `"private": false`). The header said
|
|
10
|
+
# PRIVATE until then, which was true when written and is the reason the gates were measured
|
|
11
|
+
# rather than guessed. Keep measuring them anyway: the bond exists to stop a HANG, and a hang
|
|
12
|
+
# costs wall-clock and a red check whether or not anyone is billed.
|
|
13
|
+
#
|
|
14
|
+
# ── THE MEASUREMENT THE BONDS COME FROM (local, 2026-09-12) ─────────────────
|
|
15
|
+
# npm test (12 harnesses) 11 s
|
|
16
|
+
# npm run lint (eslint over fixtures) 1 s
|
|
17
|
+
# rules-see-files.mjs 1 s
|
|
18
|
+
# vigiles lint 3 s ← added 2026-09-12 with the first ten skills
|
|
19
|
+
# run-mutations.mjs (12 batteries) 242 s ← 130 mutations, the expensive and load-bearing part
|
|
20
|
+
# ───────────────────────────────────────────
|
|
21
|
+
# total work ~255 s, plus checkout + install
|
|
22
|
+
#
|
|
23
|
+
# 🔴 THE PREVIOUS NUMBERS ARE KEPT NOWHERE BUT THIS SENTENCE, ON PURPOSE: they said ~84 s total
|
|
24
|
+
# with `timeout-minutes: 8`, i.e. a ~2.7× bond. The pipeline's checkers arrived on 2026-09-12 and
|
|
25
|
+
# tripled every count — 4 harnesses became 12, 45 mutations became 130 — which would have left an
|
|
26
|
+
# 8-minute bond at ~1.3× of a ~6-minute run. A bond that tight fails on a slow runner and reads
|
|
27
|
+
# as a broken suite rather than as a bond. Re-measure when the counts move again; do not cite the
|
|
28
|
+
# table above without re-running it.
|
|
29
|
+
#
|
|
30
|
+
# Expect ~6 minutes warm. `timeout-minutes: 20` is ~3.3×, and `npm ci` carries its OWN bond
|
|
31
|
+
# because a job-level bond cannot tell "the registry hung" from "the suite is slow" and so hands
|
|
32
|
+
# a hang the whole job.
|
|
33
|
+
name: ci
|
|
34
|
+
|
|
35
|
+
on:
|
|
36
|
+
push:
|
|
37
|
+
branches: [main]
|
|
38
|
+
# 🔴 `types:` FULLY ENUMERATED, INCLUDING DEFAULTS. As soon as this key is declared, the defaults
|
|
39
|
+
# (`opened`, `synchronize`, `reopened`) DISAPPEAR; forgetting them would disable CI on PR pushes,
|
|
40
|
+
# and the only way to notice would be by the absence of a run, i.e., silence.
|
|
41
|
+
# `ready_for_review` is not a default either, and without it the draft gate below would work only
|
|
42
|
+
# halfway: marking a draft ready is none of the defaults, so the PR would reach the merge button
|
|
43
|
+
# with zero checks. macOS is a cell of the build-e2e matrix below.
|
|
44
|
+
pull_request:
|
|
45
|
+
types: [opened, synchronize, reopened, ready_for_review]
|
|
46
|
+
|
|
47
|
+
# A newer push supersedes an older one on the same ref. Note this does NOT make the cancelled
|
|
48
|
+
# run free — cancellation arrives after the expensive steps have already run and been billed —
|
|
49
|
+
# it only stops the tail. (Until 2026-09-23 the key also split on `labeled` events, because a
|
|
50
|
+
# label click used to cancel the code run; with no `labeled` trigger that split has nothing to do.)
|
|
51
|
+
concurrency:
|
|
52
|
+
group: ci-${{ github.ref }}
|
|
53
|
+
cancel-in-progress: true
|
|
54
|
+
|
|
55
|
+
permissions:
|
|
56
|
+
contents: read
|
|
57
|
+
|
|
58
|
+
jobs:
|
|
59
|
+
gates:
|
|
60
|
+
# 🔴 A DRAFT PULL REQUEST PAYS NOTHING (same gate as vigiles and the knowledge base). Written as
|
|
61
|
+
# `event_name != 'pull_request' || draft == false`, not `draft == false` alone: on a push to main
|
|
62
|
+
# there is no pull_request object, and a bare `.draft` would be missing and kill every check there.
|
|
63
|
+
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
timeout-minutes: 20
|
|
66
|
+
steps:
|
|
67
|
+
- uses: actions/checkout@v7
|
|
68
|
+
|
|
69
|
+
- uses: actions/setup-node@v7
|
|
70
|
+
with:
|
|
71
|
+
# The floor: engines.node is >=22.13, so the gates run on 22. build-e2e runs 24.
|
|
72
|
+
node-version: 22
|
|
73
|
+
cache: npm
|
|
74
|
+
|
|
75
|
+
# 🔴 ITS OWN BOND. This is the only step that talks to the network, and a hung registry is
|
|
76
|
+
# indistinguishable from a slow suite at the job level. Measured in the consumer repo:
|
|
77
|
+
# a `npm ci` without its own bond once held a job for five minutes on a slow mirror.
|
|
78
|
+
- name: npm ci
|
|
79
|
+
run: npm ci
|
|
80
|
+
timeout-minutes: 5
|
|
81
|
+
|
|
82
|
+
# Harnesses. `--min=1` is inside the script: it refuses a run where NOTHING matched.
|
|
83
|
+
#
|
|
84
|
+
# 🔴 WHAT IT DOES NOT DO, contrary to what was written here before 17.09: it does NOT protect
|
|
85
|
+
# against a directory in the argument. Measurement: `npx vigiles test . --min=1` → `0 passed, 1 skipped`,
|
|
86
|
+
# **RC=0** — the directory exists, discovery treats it as a file, and it counts toward the
|
|
87
|
+
# minimum. The prior version called `--min=1` "load-bearing" precisely against this case,
|
|
88
|
+
# i.e., claimed a protection that does not exist. Protection against this case comes from `--no-skip` below.
|
|
89
|
+
# 🔴 THE `claude` CLI IS A TEST DEPENDENCY HERE, and its absence was NOT harmless.
|
|
90
|
+
# `plan-paper-timeline.effects.harness.mjs` spawns the real agent binary; without it the
|
|
91
|
+
# harness SKIPS, the skip exits 0 through `vigiles test`, and the mutation driver interprets
|
|
92
|
+
# that as "the mutation survived". Measured 2026-09-15 on run 34966606186: two survivors in CI,
|
|
93
|
+
# zero locally, the difference being this binary and nothing about the code.
|
|
94
|
+
#
|
|
95
|
+
# The driver now REFUSES a battery whose harness skips (lib/mutation-driver.mjs), so the
|
|
96
|
+
# wrong diagnosis is gone either way. This step is the other half: it restores the evidence
|
|
97
|
+
# instead of settling for an honest "no evidence". No API key — the tier drives a scripted
|
|
98
|
+
# stand-in, which is why it costs seconds and $0.
|
|
99
|
+
# 🔴 DELIBERATELY UNPINNED, and since issue #7 that is finally honest. The tier exists to
|
|
100
|
+
# notice upstream moving, so a pin would defeat it — but until the version was OBSERVED
|
|
101
|
+
# rather than probed for, an unpinned install meant the assertions characterized whatever
|
|
102
|
+
# shipped that morning while the docblock named 2.1.227 (measured drift: 2.1.273). The run
|
|
103
|
+
# now prints both numbers, so the log says which version the green belongs to.
|
|
104
|
+
- name: the agent binary the effects tier observes
|
|
105
|
+
run: npm i -g @anthropic-ai/claude-code
|
|
106
|
+
timeout-minutes: 3
|
|
107
|
+
|
|
108
|
+
# 🔴 `--no-skip` HERE, AND PRECISELY AFTER THE INSTALL ABOVE. Measurement 17.09: without it,
|
|
109
|
+
# skip is green, and `skip` status is applied not only to author-declared `skip()`, but also to a file
|
|
110
|
+
# that failed to LOAD. In other words, one flag closes both doors at once.
|
|
111
|
+
#
|
|
112
|
+
# ⚠️ The step order was incorrect and made the install above nearly pointless: `npm test`
|
|
113
|
+
# ran BEFORE it, so the effects tier in this job was always skipped, and the binary
|
|
114
|
+
# reached only the mutation batteries. The comment about "its absence was NOT harmless"
|
|
115
|
+
# described exactly this harm yet did not protect against it itself.
|
|
116
|
+
#
|
|
117
|
+
# ⚠️ And the flag belongs IN THE STEP, not in `package.json`: the macOS cell of build-e2e runs the same `npm test`
|
|
118
|
+
# WITHOUT installing the binary, where skips are legitimate, and a global flag would falsely color it.
|
|
119
|
+
- name: npm test
|
|
120
|
+
run: npm test -- --no-skip
|
|
121
|
+
|
|
122
|
+
# The rules themselves, over this repository's own fixtures. Two of the four fixtures are
|
|
123
|
+
# DEFECTIVE ON PURPOSE, so a healthy checkout reports warnings here and still exits 0 —
|
|
124
|
+
# the pass/fail signal for rule behaviour lives in `npm test`, not in the warning count.
|
|
125
|
+
- name: npm run lint
|
|
126
|
+
run: npm run lint
|
|
127
|
+
|
|
128
|
+
# Prettier defaults, the same config as vigiles; `.prettierignore` names what it must not
|
|
129
|
+
# reflow (compiled SKILL.md, fixture data, raw runs) and why.
|
|
130
|
+
- name: npm run fmt:check
|
|
131
|
+
run: npm run fmt:check
|
|
132
|
+
|
|
133
|
+
# 🔴 THE ONLY GATE THAT LOOKS AT THE SKILLS THEMSELVES — it existed as a package.json
|
|
134
|
+
# script (`lint:skills`, removed 2026-09-24) long before it was WIRED HERE. That cost
|
|
135
|
+
# nothing while the package shipped rules only; the first skill arrived 2026-09-12 and made it the one check
|
|
136
|
+
# covering frontmatter, `allowed-tools` contracts, dangling pointers and name collisions.
|
|
137
|
+
# `npm run lint` above is ESLint over .tex/.md fixtures and never opens a SKILL.md.
|
|
138
|
+
#
|
|
139
|
+
# A PLAIN GATE, NOT `continue-on-error` — and that is a measurement, not a preference. In
|
|
140
|
+
# the consumer repository this command CANNOT exit 0: a repo-wide "Markdown code block refs"
|
|
141
|
+
# pass walks prose that quotes OTHER projects' commands, so it is pinned under
|
|
142
|
+
# `continue-on-error` there and gates nothing. Measured here on a clean tree 2026-09-12:
|
|
143
|
+
# **28 findings, 0 error, exit 0, 3 seconds**. Nothing to hide, so nothing is hidden.
|
|
144
|
+
#
|
|
145
|
+
# ⚠️ Warnings are expected and do not fail the run (description budgets, missing
|
|
146
|
+
# `disallowed-tools:` fences). What fails it is severity `error` — a malformed frontmatter
|
|
147
|
+
# or a pointer to a file that does not exist. Bond is 3 × the measured 3 s, floored at the
|
|
148
|
+
# 1-minute granularity GitHub bills in.
|
|
149
|
+
#
|
|
150
|
+
# README.md is linted too, by name (see the same gate in scripts/check.mjs). Its
|
|
151
|
+
# `vigiles:symbol` marks name the functions behind `paperlint init` and `paperlint new`; if one is
|
|
152
|
+
# renamed or removed, this step fails and names README.md.
|
|
153
|
+
- name: vigiles lint (skills + README.md)
|
|
154
|
+
run: npx vigiles lint . README.md
|
|
155
|
+
timeout-minutes: 2
|
|
156
|
+
|
|
157
|
+
# 🔴 THE GREEN-ZERO GUARD. A rule whose glob matches no file is never invoked, and a run
|
|
158
|
+
# with no findings is byte-identical to a run that examined everything and passed. This
|
|
159
|
+
# asks ESLint for the effective config of every linted file and names any rule that saw
|
|
160
|
+
# none. In the source corpus it once found NINETEEN blind rules on a green run.
|
|
161
|
+
- name: every rule sees a file
|
|
162
|
+
run: node scripts/rules-see-files.mjs
|
|
163
|
+
|
|
164
|
+
# Hand-written mutation batteries are being removed (#52): the frozen list may only
|
|
165
|
+
# shrink — no new battery, no new case in an existing one.
|
|
166
|
+
- name: mutation batteries are frozen
|
|
167
|
+
run: node scripts/mutation-batteries-frozen.mjs
|
|
168
|
+
|
|
169
|
+
# The legacy exemptions from src/'s layer rules (#76) may only shrink: counted from ESLint's
|
|
170
|
+
# own suppressedMessages, so a new `-- legacy …` directive anywhere fails here.
|
|
171
|
+
- name: legacy layer exemptions are frozen
|
|
172
|
+
run: node scripts/layer-legacy-frozen.mjs
|
|
173
|
+
|
|
174
|
+
# The batteries that remain still run until #52 replaces or removes them. The runner
|
|
175
|
+
# DISCOVERS batteries rather than listing them and errors on finding zero — see
|
|
176
|
+
# scripts/run-mutations.mjs for why (the hand list it replaced had gone stale, silently).
|
|
177
|
+
- name: mutation batteries
|
|
178
|
+
run: node scripts/run-mutations.mjs
|
|
179
|
+
|
|
180
|
+
# pnpm is INSTALLED here rather than hoped for. The runner image may or may not carry it,
|
|
181
|
+
# and the npm/pnpm difference is the entire reason the install e2e exists — so leaving it
|
|
182
|
+
# to the image means the half that matters most is covered by luck. `corepack` ships with
|
|
183
|
+
# Node, so this costs a second and no network round-trip to a third-party action.
|
|
184
|
+
- name: enable pnpm
|
|
185
|
+
run: corepack enable pnpm
|
|
186
|
+
timeout-minutes: 2
|
|
187
|
+
|
|
188
|
+
# Called directly with --strict, the same as the build e2e below. --strict: a manager that does not launch is a declared skip on a
|
|
189
|
+
# contributor's clone and a FAILURE here, because in CI it means a broken environment.
|
|
190
|
+
- name: install e2e (--strict)
|
|
191
|
+
run: node test/e2e/install.mjs --strict
|
|
192
|
+
timeout-minutes: 10
|
|
193
|
+
|
|
194
|
+
# ── BUILDING THE PAPER WITH REAL pdflatex, ON LINUX AND macOS ──────────────────────────────────
|
|
195
|
+
#
|
|
196
|
+
# 🔴 ONE JOB, TWO OPERATING SYSTEMS. TeX Live comes from `paperlint toolchain` — the same command a user
|
|
197
|
+
# runs — so the job tests the install path users get, not a CI-only script. It used to be a bash
|
|
198
|
+
# installer here and a separate macOS workflow (platform.yml) there; both are gone. macOS matters
|
|
199
|
+
# twice: install-tl-unx has to work on it, and issue #9 (`/var` is a symlink to `/private/var`)
|
|
200
|
+
# exists only there, so the macOS cell also runs `npm test` and the temp-dir probe.
|
|
201
|
+
#
|
|
202
|
+
# 🔴 WHY A SEPARATE JOB, NOT A STEP IN `gates`. Its only dependency is TeX Live, and it alone costs
|
|
203
|
+
# minutes: ~270 MB, ~3 min cold (measured 2026-09-24 with `paperlint toolchain`). A separate job runs in
|
|
204
|
+
# parallel and caches itself.
|
|
205
|
+
#
|
|
206
|
+
# 🔴 WHY ON EVERY PUSH. It catches silent font substitution — a class that HAS ALREADY shipped a
|
|
207
|
+
# paper typeset in the wrong font. This repository is public, so the macOS minutes are free.
|
|
208
|
+
#
|
|
209
|
+
# `--strict`: absence of TeX locally is a legitimate skip; here it is an environment failure.
|
|
210
|
+
build-e2e:
|
|
211
|
+
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
|
|
212
|
+
strategy:
|
|
213
|
+
fail-fast: false
|
|
214
|
+
matrix:
|
|
215
|
+
os: [ubuntu-latest, macos-latest]
|
|
216
|
+
runs-on: ${{ matrix.os }}
|
|
217
|
+
timeout-minutes: 25
|
|
218
|
+
steps:
|
|
219
|
+
- uses: actions/checkout@v7
|
|
220
|
+
|
|
221
|
+
- uses: actions/setup-node@v7
|
|
222
|
+
with:
|
|
223
|
+
# The latest LTS; the gates job covers the 22 floor, so no job runs twice.
|
|
224
|
+
node-version: 24
|
|
225
|
+
cache: npm
|
|
226
|
+
|
|
227
|
+
- name: npm ci
|
|
228
|
+
run: npm ci
|
|
229
|
+
timeout-minutes: 8
|
|
230
|
+
|
|
231
|
+
# Issue #9's distinction: prints BOTH spellings of the temp root. If they match, this machine
|
|
232
|
+
# is not the platform the macOS cell exists for, and green below says nothing about it.
|
|
233
|
+
- name: two spellings of one directory — does this machine see them
|
|
234
|
+
if: runner.os == 'macOS'
|
|
235
|
+
run: |
|
|
236
|
+
node -e '
|
|
237
|
+
const { mkdtempSync, realpathSync } = require("node:fs");
|
|
238
|
+
const { tmpdir } = require("node:os");
|
|
239
|
+
const { join } = require("node:path");
|
|
240
|
+
const d = mkdtempSync(join(tmpdir(), "probe-"));
|
|
241
|
+
console.log("as typed :", d);
|
|
242
|
+
console.log("realpath :", realpathSync(d));
|
|
243
|
+
console.log(d === realpathSync(d) ? "MATCH — issue #9 traps do not exist on this machine"
|
|
244
|
+
: "DIVERGE — this is the platform this job exists for");
|
|
245
|
+
'
|
|
246
|
+
|
|
247
|
+
# Issue #9: `npm test` was red ONLY on macOS. Only `npm test`, not the mutation batteries:
|
|
248
|
+
# they would verify on macOS exactly what the gates job verifies on Linux.
|
|
249
|
+
- name: npm test (macOS)
|
|
250
|
+
if: runner.os == 'macOS'
|
|
251
|
+
run: npm test
|
|
252
|
+
timeout-minutes: 10
|
|
253
|
+
|
|
254
|
+
# The cache key is what the install depends on: the runner and the declared packages (every
|
|
255
|
+
# venue profile, the base set included). NOT the TeX Live year, and not the calendar year that
|
|
256
|
+
# used to stand in for it: the calendar says nothing about CTAN's release, and a restored tree
|
|
257
|
+
# from an older year is safe — `paperlint toolchain` verifies every declared file on it, leaves it
|
|
258
|
+
# alone when it is complete, and when a newly declared package makes tlmgr refuse it across
|
|
259
|
+
# releases, installs the new year beside it (src/toolchain.ts, `newRelease`).
|
|
260
|
+
# No `restore-keys`, deliberately: a partial restore would carry the old year's tree into the
|
|
261
|
+
# new entry, and the old tree is never deleted, so the cache would grow by a whole TeX Live.
|
|
262
|
+
- name: where TeX Live is cached
|
|
263
|
+
run: echo "RPP_TEXLIVE_DIR=$HOME/.cache/rpp-texlive" >> "$GITHUB_ENV"
|
|
264
|
+
|
|
265
|
+
- name: TeX Live from cache
|
|
266
|
+
uses: actions/cache@v6
|
|
267
|
+
with:
|
|
268
|
+
path: ~/.cache/rpp-texlive
|
|
269
|
+
key: texlive-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('skills/submit-paper/references/venues/*.jsonc') }}
|
|
270
|
+
|
|
271
|
+
# The same command a user runs. On a cache hit it verifies and says "nothing to do"; on a miss
|
|
272
|
+
# it installs from an empty directory and verifies every declared file with kpsewhich.
|
|
273
|
+
# `node bin/rpp.mjs`, not `npx paperlint`: inside this repository npx would look the name up on npm.
|
|
274
|
+
# It also downloads banal, the Perl script HotCRP runs to measure page geometry (~90 KB, pinned
|
|
275
|
+
# commit, sha256-checked, then run once on a test page). paperlint keeps banal and no longer needs
|
|
276
|
+
# poppler: paperlint now writes the XML banal reads, which poppler's pdftohtml used to write.
|
|
277
|
+
# Both runner images ship perl. The download is too small to be worth an Actions cache.
|
|
278
|
+
- name: paperlint toolchain
|
|
279
|
+
run: node bin/rpp.mjs toolchain
|
|
280
|
+
timeout-minutes: 15
|
|
281
|
+
|
|
282
|
+
# Proves banal works without poppler. This runner has no poppler, and the test also limits
|
|
283
|
+
# PATH to perl. paperlint turns each fixture PDF into XML with pdf.js, banal measures it, and the
|
|
284
|
+
# result must equal the numbers banal gave on the same PDFs through poppler's pdftohtml
|
|
285
|
+
# (recorded once, hardcoded in the test). Runs in seconds, hence the short timeout.
|
|
286
|
+
- name: banal e2e (--strict)
|
|
287
|
+
run: node test/e2e/banal.mjs --strict
|
|
288
|
+
timeout-minutes: 3
|
|
289
|
+
|
|
290
|
+
# Called directly with --strict (see the install e2e step in the `gates` job).
|
|
291
|
+
- name: build e2e (--strict)
|
|
292
|
+
run: node test/e2e/build.mjs --strict
|
|
293
|
+
timeout-minutes: 10
|
|
294
|
+
|
|
295
|
+
- name: toolchain e2e (--strict)
|
|
296
|
+
run: node test/e2e/toolchain.mjs --strict
|
|
297
|
+
timeout-minutes: 10
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# MERGE THE UPDATE UPON A GREEN RUN.
|
|
2
|
+
#
|
|
3
|
+
# 🔴 WHY NOT `gh pr merge --auto`, even though the owner enabled "Allow auto-merge" and branch protection.
|
|
4
|
+
# Standard auto-merge waits for precisely the checks declared REQUIRED in the branch protection. That
|
|
5
|
+
# is a setting in the interface, invisible from here, and it can drift from `ci.yml` on any job rename —
|
|
6
|
+
# and it WILL drift silently, because the PR will simply merge faster.
|
|
7
|
+
# Measurement 2026-09-16 showed exactly this cost: before protection, `main` had `"protected": false`, and
|
|
8
|
+
# `--auto` would have merged the PR IMMEDIATELY, looking like "merge on green" while doing the opposite.
|
|
9
|
+
#
|
|
10
|
+
# This workflow fires FROM THE COMPLETION EVENT of `ci` and reads its `conclusion`. The same reasoning
|
|
11
|
+
# as all checks in this repository: we ask for the THING (run verdict), not its shadow (a setting
|
|
12
|
+
# that promises the verdict will be considered).
|
|
13
|
+
#
|
|
14
|
+
# ⚠️ Branch protection is not redundant — it closes a DIFFERENT hole: a person or bot merging
|
|
15
|
+
# a PR outside CI manually. Two mechanisms, two concerns.
|
|
16
|
+
#
|
|
17
|
+
# 🔴 WHY WE MERGE ANY BUMP, INCLUDING MAJOR, not just patch/minor.
|
|
18
|
+
# Semver is a PUBLISHER'S CLAIM about their release; `ci` is a MEASUREMENT on our corpus (full harness suite,
|
|
19
|
+
# mutations, gates for "rules see their files" and "rules don't touch git"). A major bump that breaks us
|
|
20
|
+
# reds the run and does not merge; a minor one that breaks us does the same.
|
|
21
|
+
# A version-number filter adds a manual step where measurement already exists, and would miss
|
|
22
|
+
# exactly the class for which version filters are typically used.
|
|
23
|
+
#
|
|
24
|
+
# ⚠️ WHAT THIS DOES NOT GIVE, stated plainly: green `ci` means "our harnesses detected no difference,"
|
|
25
|
+
# not "no difference exists." Uncovered behavior passes silently — as vigiles schema 19 → 27 passed
|
|
26
|
+
# through a hook reading an empty field for eight releases in a row.
|
|
27
|
+
name: dependabot auto-merge
|
|
28
|
+
|
|
29
|
+
on:
|
|
30
|
+
workflow_run:
|
|
31
|
+
workflows: ["ci"]
|
|
32
|
+
types: [completed]
|
|
33
|
+
|
|
34
|
+
# Merge and delete branch — nothing else this workflow needs.
|
|
35
|
+
permissions:
|
|
36
|
+
contents: write
|
|
37
|
+
pull-requests: write
|
|
38
|
+
|
|
39
|
+
jobs:
|
|
40
|
+
merge:
|
|
41
|
+
# Three conditions, each blocking a case: the run was for a PR (not a push to main) ·
|
|
42
|
+
# the run is GREEN · the author is a bot, not a person. Remove any one and meaning changes.
|
|
43
|
+
if: >
|
|
44
|
+
github.event.workflow_run.event == 'pull_request' &&
|
|
45
|
+
github.event.workflow_run.conclusion == 'success' &&
|
|
46
|
+
github.event.workflow_run.actor.login == 'dependabot[bot]'
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
timeout-minutes: 5
|
|
49
|
+
steps:
|
|
50
|
+
- name: merge the PR that just turned green
|
|
51
|
+
env:
|
|
52
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
53
|
+
REPO: ${{ github.repository }}
|
|
54
|
+
RUN_ID: ${{ github.event.workflow_run.id }}
|
|
55
|
+
# 🔴 The number comes FROM THE EVENT, not from a search by branch name. A search would find the PR
|
|
56
|
+
# but would not prove the green run belongs to it.
|
|
57
|
+
PR: ${{ github.event.workflow_run.pull_requests[0].number }}
|
|
58
|
+
# 🔴 The commit the green run TESTED. Without it, a push landing between the run and this job
|
|
59
|
+
# would merge an untested head: `gh pr merge` merges whatever the PR points at NOW.
|
|
60
|
+
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
|
|
61
|
+
run: |
|
|
62
|
+
set -euo pipefail
|
|
63
|
+
# An empty number is NOT "nothing to do": fork PRs come with an empty array, and
|
|
64
|
+
# silent exit would look like a successful merge. Fail with a reason.
|
|
65
|
+
if [ -z "${PR}" ]; then
|
|
66
|
+
echo "::error::ci run did not yield a PR number (fork?) — nothing merged, resolve manually"
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
gh pr merge "${PR}" --repo "${REPO}" --squash --delete-branch --match-head-commit "${HEAD_SHA}"
|
|
70
|
+
echo "merged PR #${PR} on green ci run ${RUN_ID}"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# The PR title becomes the squash commit on main, and that commit is what semantic-release reads
|
|
2
|
+
# (release.yml). A title without a conventional prefix means a merged fix releases nothing, with
|
|
3
|
+
# every check green. Copied from zernie/vigiles.
|
|
4
|
+
name: PR Title
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
pull_request_target:
|
|
8
|
+
types: [opened, edited, synchronize]
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
pull-requests: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
validate:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
# Measured 5 s in vigiles.
|
|
17
|
+
timeout-minutes: 5
|
|
18
|
+
steps:
|
|
19
|
+
- uses: amannn/action-semantic-pull-request@v6
|
|
20
|
+
env:
|
|
21
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
22
|
+
with:
|
|
23
|
+
types: |
|
|
24
|
+
feat
|
|
25
|
+
fix
|
|
26
|
+
docs
|
|
27
|
+
chore
|
|
28
|
+
refactor
|
|
29
|
+
test
|
|
30
|
+
perf
|
|
31
|
+
ci
|
|
32
|
+
build
|
|
33
|
+
requireScope: false
|
|
34
|
+
subjectPattern: ^.+$
|
|
35
|
+
subjectPatternError: "PR title must not be empty after the type prefix."
|
|
36
|
+
|
|
37
|
+
# For a PR with exactly ONE commit, GitHub's squash merge takes the COMMIT's subject as the
|
|
38
|
+
# squash title, not the PR's. So a PR can pass the check above and still land a commit with
|
|
39
|
+
# no conventional prefix, and semantic-release then cuts no version. Measured in vigiles
|
|
40
|
+
# (#163, 2026-08-19): a `feat!:` PR title, check green, a plain commit on main, no release.
|
|
41
|
+
- name: The squash title, for a single-commit PR
|
|
42
|
+
if: github.event.pull_request.commits == 1
|
|
43
|
+
env:
|
|
44
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
45
|
+
PR: ${{ github.event.pull_request.number }}
|
|
46
|
+
REPO: ${{ github.repository }}
|
|
47
|
+
run: |
|
|
48
|
+
set -euo pipefail
|
|
49
|
+
subject=$(gh api "repos/$REPO/pulls/$PR/commits" --jq '.[0].commit.message' | head -1)
|
|
50
|
+
echo "commit subject: $subject"
|
|
51
|
+
if ! printf '%s' "$subject" | grep -qE '^(feat|fix|docs|chore|refactor|test|perf|ci|build)(\([^)]+\))?!?: .+'; then
|
|
52
|
+
echo "::error::This PR has ONE commit, so GitHub's squash default will use the COMMIT subject"
|
|
53
|
+
echo "::error::as the squash title — not the PR title, which already passed the check above."
|
|
54
|
+
echo "::error::That subject is: $subject"
|
|
55
|
+
echo "::error::Fix either side: amend the commit to carry the same conventional prefix, or"
|
|
56
|
+
echo "::error::set the squash title explicitly when merging. Otherwise semantic-release sees"
|
|
57
|
+
echo "::error::no releasable change and publishes nothing, with every check green."
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Release: every push to main runs semantic-release, which reads the commits since the last tag,
|
|
2
|
+
# and — only when one of them is `feat:`, `fix:` or breaking — bumps the version, publishes the
|
|
3
|
+
# package to npm as `paperlint`, tags `vX.Y.Z` and writes a GitHub release.
|
|
4
|
+
# Copied from zernie/vigiles, whose release setup this mirrors.
|
|
5
|
+
#
|
|
6
|
+
# The squash title of a merged PR IS the commit this reads, so a PR titled without a
|
|
7
|
+
# conventional prefix releases nothing, silently. pr-title.yml guards that.
|
|
8
|
+
name: Release
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
push:
|
|
12
|
+
branches: [main]
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: write
|
|
16
|
+
issues: write
|
|
17
|
+
pull-requests: write
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
release:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
# Not measured yet; npm ci + tsc + publish. Bounded generously, but bounded: an unbounded
|
|
23
|
+
# job that hangs runs to GitHub's six-hour ceiling instead of failing.
|
|
24
|
+
timeout-minutes: 20
|
|
25
|
+
if: "!contains(github.event.head_commit.message, '[skip ci]')"
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v7
|
|
28
|
+
with:
|
|
29
|
+
# semantic-release walks the history back to the last release tag.
|
|
30
|
+
fetch-depth: 0
|
|
31
|
+
|
|
32
|
+
- uses: actions/setup-node@v7
|
|
33
|
+
with:
|
|
34
|
+
# semantic-release 25 refuses to start below Node ^22.14.0.
|
|
35
|
+
node-version: "22"
|
|
36
|
+
cache: npm
|
|
37
|
+
|
|
38
|
+
# `npm ci` runs `prepare` (tsc), so dist/ exists before publishing; `npm publish` runs it
|
|
39
|
+
# again. dist/ is not committed.
|
|
40
|
+
- name: npm ci
|
|
41
|
+
run: npm ci
|
|
42
|
+
timeout-minutes: 5
|
|
43
|
+
|
|
44
|
+
- name: Release
|
|
45
|
+
env:
|
|
46
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
47
|
+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
48
|
+
# `--no-install`: run the version pinned in package.json, never fetch one. The preset is
|
|
49
|
+
# pinned with it (conventional-changelog-conventionalcommits 9.3.1): preset 10 renders only
|
|
50
|
+
# through conventional-changelog-writer 9, which @semantic-release/release-notes-generator
|
|
51
|
+
# 14 does not load, and vigiles shipped empty release notes for five weeks that way
|
|
52
|
+
# (zernie/vigiles#282). scripts/release-config.test.ts renders the notes with the installed
|
|
53
|
+
# toolchain.
|
|
54
|
+
run: npx --no-install semantic-release
|