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,521 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint configuration for this repository's OWN fixtures.
|
|
3
|
+
*
|
|
4
|
+
* It is not the configuration a consumer will use — a consumer points the same block at the
|
|
5
|
+
* path of its own paper (`papers/<name>/paper.tex` or wherever the source actually lives).
|
|
6
|
+
* What is reusable here is the SHAPE: one plugin object carrying both the language and the
|
|
7
|
+
* rules that read LaTeX source, one `language:` line, and an explicit severity per rule.
|
|
8
|
+
*
|
|
9
|
+
* 🔴 The glob matters more than it looks. A rule whose glob matches nothing is not "clean" —
|
|
10
|
+
* it is never invoked, and the run reports exactly the same green as a rule that passed. That
|
|
11
|
+
* is why `scripts/rules-see-files.mjs` exists and why it runs as part of the test suite:
|
|
12
|
+
* every rule declared below must be enabled for at least one file that is actually on disk.
|
|
13
|
+
*/
|
|
14
|
+
import { texLanguage } from "./eslint-rules/latex-language.mjs";
|
|
15
|
+
import texBuild from "./eslint-rules/tex-build.mjs";
|
|
16
|
+
import markdown from "@eslint/markdown";
|
|
17
|
+
import reviewRules from "./eslint-rules/review-findings-cause.mjs";
|
|
18
|
+
import localRules from "./eslint-rules/temp-root-realpath.mjs";
|
|
19
|
+
import portRules from "./eslint-rules/install-path-literals.mjs";
|
|
20
|
+
import n from "eslint-plugin-n";
|
|
21
|
+
import tseslint from "typescript-eslint";
|
|
22
|
+
import boundaries from "eslint-plugin-boundaries";
|
|
23
|
+
import { dirname } from "node:path";
|
|
24
|
+
import { fileURLToPath } from "node:url";
|
|
25
|
+
|
|
26
|
+
// The complexity set, shared by the TypeScript block and the ratchet below. Every function
|
|
27
|
+
// measured over these limits on 2026-09-24 was either refactored under them (the #59 build code)
|
|
28
|
+
// or pinned at its current maximum in RATCHET.
|
|
29
|
+
const MAX_LINES = { max: 60, skipComments: true, skipBlankLines: true };
|
|
30
|
+
|
|
31
|
+
// 🔴 A CEILING, NOT A PERMISSION: each number is the file's measured maximum on 2026-09-24, so a
|
|
32
|
+
// function in these files can get simpler and cannot get worse. Lower a number when a refactor
|
|
33
|
+
// lowers the maximum; never raise one. Paying the debt down is issue #72. A file leaves this table
|
|
34
|
+
// when it passes the shared limits.
|
|
35
|
+
const RATCHET = {
|
|
36
|
+
"src/init.ts": { complexity: 59, "max-lines-per-function": 219 },
|
|
37
|
+
"src/cli.ts": { complexity: 43, "max-lines-per-function": 152 },
|
|
38
|
+
"src/doctor.ts": { complexity: 38, "max-lines-per-function": 125 },
|
|
39
|
+
"src/hooks-settings.ts": { complexity: 12, "max-depth": 4 },
|
|
40
|
+
"src/structure.ts": { complexity: 12, "max-depth": 4 },
|
|
41
|
+
"src/new-paper.ts": { complexity: 11 },
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// ── Hexagonal layers (src/CLAUDE.md, issue #76) ─────────────────────────────────────────
|
|
45
|
+
// TWO INDEPENDENT AXES, and a file has a position on each.
|
|
46
|
+
//
|
|
47
|
+
// A. KNOWLEDGE — whose vocabulary does the file speak, the paper's or one external program's?
|
|
48
|
+
// domain src/domain/ values and decisions in the paper's vocabulary
|
|
49
|
+
// port src/ports/ interfaces shaped by what the inside needs, never by what a tool does
|
|
50
|
+
// app src/*.ts use cases over ports (the flat files; most are ratcheted, #76)
|
|
51
|
+
// adapter src/adapters/<x>/ everything that exists because program/format <x> exists — pure
|
|
52
|
+
// parsers included. One element per folder, used through index.ts.
|
|
53
|
+
// root src/cli.ts reads process.*, builds adapters, wires ports
|
|
54
|
+
// B. PURITY — can anything here fail for a reason not in its arguments? Effects (disk, process,
|
|
55
|
+
// network, env) only in src/adapters/<x>/<y>.io.ts and the root.
|
|
56
|
+
//
|
|
57
|
+
// "Core" is deliberately not a layer name: the plugin's own `origin: "core"` means NODE BUILT-INS.
|
|
58
|
+
//
|
|
59
|
+
// Folders are ELEMENTS, single files are CATEGORIES (`boundaries/files`): that is v7's model — an
|
|
60
|
+
// element pattern is matched as a folder, and a file-shaped one is refused with a warning. So the
|
|
61
|
+
// root and the flat app files are categories, and the policies below select them with `file:`.
|
|
62
|
+
|
|
63
|
+
/** Modules that ARE effects. A file importing one must be an `io` file or the root. */
|
|
64
|
+
export const IO_MODULES = [
|
|
65
|
+
"fs",
|
|
66
|
+
"fs/promises",
|
|
67
|
+
"child_process",
|
|
68
|
+
"os",
|
|
69
|
+
"net",
|
|
70
|
+
"http",
|
|
71
|
+
"https",
|
|
72
|
+
"worker_threads",
|
|
73
|
+
"readline",
|
|
74
|
+
"readline/promises",
|
|
75
|
+
].flatMap((m) => [m, `node:${m}`]);
|
|
76
|
+
|
|
77
|
+
/** What the inside may import from outside the repository: types, pure path arithmetic, hashing. */
|
|
78
|
+
export const INSIDE_EXTERNALS = [
|
|
79
|
+
"ts-essentials",
|
|
80
|
+
"path",
|
|
81
|
+
"node:path",
|
|
82
|
+
"crypto",
|
|
83
|
+
"node:crypto",
|
|
84
|
+
];
|
|
85
|
+
/** What the flat app files may import besides: Node's pure helpers, measured on the first v7 run. */
|
|
86
|
+
export const APP_EXTERNALS = [
|
|
87
|
+
...INSIDE_EXTERNALS,
|
|
88
|
+
"util",
|
|
89
|
+
"node:util",
|
|
90
|
+
"url",
|
|
91
|
+
"node:url",
|
|
92
|
+
"module",
|
|
93
|
+
"node:module",
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
const ORIGINS = ["external", "core"];
|
|
97
|
+
const modules = (source) => ({ to: { module: { origin: ORIGINS, source } } });
|
|
98
|
+
const elements = (...types) => ({ element: { types: { anyOf: types } } });
|
|
99
|
+
const APP = { file: { categories: "app" } };
|
|
100
|
+
const ROOT = { file: { categories: "root" } };
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Both axes in one plugin: axis A by element and category, axis B by the file category `io`.
|
|
104
|
+
*
|
|
105
|
+
* 🔴 `boundaries/root-path` IS LOAD-BEARING. Without it the plugin matches its patterns against
|
|
106
|
+
* `process.cwd()` — not ESLint's `cwd` — so lint started from any other directory classifies no file
|
|
107
|
+
* and the rule passes silently. Measured 2026-09-25: a core → adapter import produced zero findings
|
|
108
|
+
* until the root was pinned. `root` is a parameter only so `test/eslint-layers.test.ts` can re-root
|
|
109
|
+
* it at its fixture tree.
|
|
110
|
+
*
|
|
111
|
+
* 🔴 `checkAllOrigins: true` IS LOAD-BEARING TOO. The default checks local imports only, so every
|
|
112
|
+
* `module:` policy below — all of axis B and the inside's library ban — would match nothing and
|
|
113
|
+
* report nothing. The fixture `src/adapters/one/disk.ts` is what makes that loud.
|
|
114
|
+
*
|
|
115
|
+
* Policies are evaluated in order and the LAST one that matches decides, so the narrowing ones
|
|
116
|
+
* (encapsulation, then axis B) come after the allows they narrow. A dependency no policy allows gets
|
|
117
|
+
* the plugin's own message, which names both ends ("no policy allowing dependencies from file of
|
|
118
|
+
* category "app" to elements of type "adapter""); custom messages sit only on the disallows.
|
|
119
|
+
*/
|
|
120
|
+
export const layerBoundaries = (root) => ({
|
|
121
|
+
files: ["src/**/*.ts"],
|
|
122
|
+
plugins: { boundaries },
|
|
123
|
+
settings: {
|
|
124
|
+
"boundaries/root-path": root,
|
|
125
|
+
"boundaries/elements": [
|
|
126
|
+
{ type: "port", pattern: "src/ports", partialMatch: false },
|
|
127
|
+
{ type: "domain", pattern: "src/domain", partialMatch: false },
|
|
128
|
+
// One element PER ADAPTER FOLDER, so adapter → other adapter is a cross-element import.
|
|
129
|
+
{
|
|
130
|
+
type: "adapter",
|
|
131
|
+
pattern: "src/adapters/*",
|
|
132
|
+
partialMatch: false,
|
|
133
|
+
capture: ["name"],
|
|
134
|
+
},
|
|
135
|
+
// The package's plain-JS modules. Named so an import of one is classified, not unknown.
|
|
136
|
+
{
|
|
137
|
+
type: "js-module",
|
|
138
|
+
pattern: "(eslint-rules|hooks|lib|skills)",
|
|
139
|
+
partialMatch: false,
|
|
140
|
+
capture: ["kind"],
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
"boundaries/files": [
|
|
144
|
+
// `exclusive`: the root also matches the app pattern below, and is only the root.
|
|
145
|
+
{ category: "root", pattern: "src/cli.ts", exclusive: true },
|
|
146
|
+
// Every other file at the top of src/ is app. A new one gets the full app rules.
|
|
147
|
+
{ category: "app", pattern: "src/*.ts" },
|
|
148
|
+
{ category: "test", pattern: "src/**/*.test.ts" },
|
|
149
|
+
// AXIS B: purity is a category, orthogonal to the element.
|
|
150
|
+
{ category: "io", pattern: "src/adapters/*/*.io.ts" },
|
|
151
|
+
],
|
|
152
|
+
},
|
|
153
|
+
rules: {
|
|
154
|
+
// 🔴 An unclassified file is invisible to the rules below: a new `src/lib/x.ts` could import an
|
|
155
|
+
// adapter from anywhere and nothing would fire. So every linted file must be a declared element
|
|
156
|
+
// or category, and every import must resolve to one.
|
|
157
|
+
"boundaries/no-unknown-files": "error",
|
|
158
|
+
"boundaries/no-unknown-dependencies": "error",
|
|
159
|
+
"boundaries/dependencies": [
|
|
160
|
+
"error",
|
|
161
|
+
{
|
|
162
|
+
default: "disallow",
|
|
163
|
+
checkAllOrigins: true,
|
|
164
|
+
policies: [
|
|
165
|
+
// ── AXIS A: who may know whom (jMolecules: Application → Port, never → Adapter) ──
|
|
166
|
+
{ from: elements("domain"), allow: { to: elements("domain") } },
|
|
167
|
+
{ from: elements("port"), allow: { to: elements("domain", "port") } },
|
|
168
|
+
{ from: APP, allow: { to: [elements("domain", "port"), APP] } },
|
|
169
|
+
// An adapter knows the domain and the ports (jMolecules#306's allowance), never the app,
|
|
170
|
+
// never another adapter.
|
|
171
|
+
{
|
|
172
|
+
from: elements("adapter"),
|
|
173
|
+
allow: { to: elements("domain", "port") },
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
from: ROOT,
|
|
177
|
+
allow: { to: [{ element: { type: "*" } }, APP] },
|
|
178
|
+
},
|
|
179
|
+
// The package's `.mjs` half is read by the outside layers and the flat app files, never
|
|
180
|
+
// by the domain or a port.
|
|
181
|
+
{
|
|
182
|
+
from: [elements("adapter"), APP, ROOT],
|
|
183
|
+
allow: { to: elements("js-module") },
|
|
184
|
+
},
|
|
185
|
+
// ── libraries and built-ins (the plugin calls Node built-ins origin "core") ──
|
|
186
|
+
{
|
|
187
|
+
from: elements("domain", "port"),
|
|
188
|
+
allow: modules(INSIDE_EXTERNALS),
|
|
189
|
+
},
|
|
190
|
+
{ from: APP, allow: modules(APP_EXTERNALS) },
|
|
191
|
+
{
|
|
192
|
+
from: [elements("adapter"), ROOT],
|
|
193
|
+
allow: { to: { module: { origin: ORIGINS } } },
|
|
194
|
+
},
|
|
195
|
+
// Tests may import anything, fakes included …
|
|
196
|
+
{
|
|
197
|
+
from: { file: { categories: "test" } },
|
|
198
|
+
allow: {
|
|
199
|
+
to: [
|
|
200
|
+
{ element: { type: "*" } },
|
|
201
|
+
APP,
|
|
202
|
+
ROOT,
|
|
203
|
+
{ module: { origin: ORIGINS } },
|
|
204
|
+
],
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
// … but everyone, tests too, reaches an adapter through its index.ts. (Imports inside one
|
|
208
|
+
// adapter are internal to its element and not checked.)
|
|
209
|
+
{
|
|
210
|
+
disallow: {
|
|
211
|
+
to: {
|
|
212
|
+
element: { type: "adapter", fileInternalPath: "!index.ts" },
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
message:
|
|
216
|
+
"{{ dependency.source }} is inside an adapter: import its index.ts (src/CLAUDE.md, layers).",
|
|
217
|
+
},
|
|
218
|
+
// ── AXIS B: effects only in io files, tests and the root ──
|
|
219
|
+
// Written as "nobody, then these three" rather than "everyone but these three": a file
|
|
220
|
+
// with no category has `categories: null`, and a `noneOf` query never matches null — so the
|
|
221
|
+
// negative form silently exempts every plain file. Measured on `adapters/one/disk.ts`.
|
|
222
|
+
{
|
|
223
|
+
disallow: {
|
|
224
|
+
to: { module: { origin: "core", source: IO_MODULES } },
|
|
225
|
+
},
|
|
226
|
+
message:
|
|
227
|
+
"I/O ({{ dependency.source }}) in a file that is not *.io.ts. Effects live in src/adapters/<tool>/<x>.io.ts or src/cli.ts; this file takes a port (src/CLAUDE.md, purity). Legacy sites carry `-- legacy I/O, moves behind a port in #76`.",
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
from: { file: { categories: ["io", "test", "root"] } },
|
|
231
|
+
allow: { to: { module: { origin: "core", source: IO_MODULES } } },
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* The globals the plugin cannot see: `process` and `fetch` are not imports. Same scope as axis B.
|
|
241
|
+
* Files that did I/O before the layers existed carry an `eslint-disable-next-line` naming #76 above
|
|
242
|
+
* each such import or use; `reportUnusedDisableDirectives: "error"` turns a disable that suppresses
|
|
243
|
+
* nothing into a finding, so an exemption leaves the moment its I/O does, and
|
|
244
|
+
* `scripts/layer-legacy-frozen.mjs` freezes how many each file may carry. A NEW module that needs
|
|
245
|
+
* the outside world is a new `*.io.ts` in the adapter of the program it talks to — not a disable.
|
|
246
|
+
*/
|
|
247
|
+
export const IO_GLOBALS = {
|
|
248
|
+
files: ["src/**/*.ts"],
|
|
249
|
+
ignores: ["src/cli.ts", "src/adapters/*/*.io.ts", "src/**/*.test.ts"],
|
|
250
|
+
linterOptions: { reportUnusedDisableDirectives: "error" },
|
|
251
|
+
rules: {
|
|
252
|
+
"no-restricted-globals": [
|
|
253
|
+
"error",
|
|
254
|
+
{
|
|
255
|
+
name: "process",
|
|
256
|
+
message:
|
|
257
|
+
"The environment is an input: the root reads it and passes a value in (src/CLAUDE.md, purity).",
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
name: "fetch",
|
|
261
|
+
message: "Network access is an adapter's *.io.ts file.",
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const ceiling = (rule, n) =>
|
|
268
|
+
rule === "max-lines-per-function"
|
|
269
|
+
? ["error", { ...MAX_LINES, max: n }]
|
|
270
|
+
: ["error", n];
|
|
271
|
+
|
|
272
|
+
export default [
|
|
273
|
+
// 🔴 TRANSIENT DIRECTORIES ARE NOT THE CORPUS, and leaving them in is a RACE, not sloppiness.
|
|
274
|
+
// `paper-stages.harness.mjs` creates a temp tree under fixtures and removes it when done,
|
|
275
|
+
// while `latex-language.harness.mjs` lints the whole repository to prove its glob matches
|
|
276
|
+
// real files. Run in parallel, ESLint enumerates a path and then reads it, and the file can
|
|
277
|
+
// be gone in between: ENOENT, in a harness that has nothing to do with either.
|
|
278
|
+
//
|
|
279
|
+
// Measured 2026-09-17: the race had been latent and surfaced the moment a 54th harness
|
|
280
|
+
// shifted the scheduling. Nothing about the new harness was wrong — which is the point.
|
|
281
|
+
// `docs/prior-art/repro/` is EVIDENCE, not source: those scripts are kept exactly as they were
|
|
282
|
+
// run, so that a verdict in the design notes can be re-measured rather than argued with. Linting
|
|
283
|
+
// them invites the next reader to tidy an unused import — and then the file on disk is no longer
|
|
284
|
+
// the file that produced the number it backs.
|
|
285
|
+
// `dist/` is tsc's OUTPUT (gitignored): its declaration files are generated from `src/`, so
|
|
286
|
+
// linting them would report every finding twice and blame a file nobody edits.
|
|
287
|
+
{
|
|
288
|
+
ignores: [
|
|
289
|
+
".tmp-stages-src-*/",
|
|
290
|
+
"fixtures/.tmp-*/",
|
|
291
|
+
"docs/prior-art/repro/",
|
|
292
|
+
"dist/",
|
|
293
|
+
],
|
|
294
|
+
},
|
|
295
|
+
// 🔴 THE PACKAGE'S TYPESCRIPT, and before 2026-09-24 no block matched it — the same silent
|
|
296
|
+
// ignore the `.mjs` block below was written against, recurring for `.ts` (#49). `src/` is the
|
|
297
|
+
// CLI and the build; the skill specs compile into the SKILL.md files the package ships; the
|
|
298
|
+
// three hand-written `.d.mts` type the `.mjs` modules `src/` imports. All tracked TypeScript.
|
|
299
|
+
//
|
|
300
|
+
// Measured on the first run (41727cd): 44 findings in 9 of 39 files, all in `src/`, plus one
|
|
301
|
+
// `no-useless-escape` in a spec that had dropped a backslash from its compiled SKILL.md. The
|
|
302
|
+
// #59 build files were refactored clean; the older CLI files sit in RATCHET, one block below.
|
|
303
|
+
//
|
|
304
|
+
// NOT TYPE-AWARE, deliberately: no rule here needs type information, and `npm run build`
|
|
305
|
+
// (tsc, strict) already type-checks `src/` as the first gate of `npm run check`.
|
|
306
|
+
//
|
|
307
|
+
// NOT HERE: `port/js-install-path` (43 findings, 41 of them the specs' `.claude/skills/`
|
|
308
|
+
// literals — issue #19's debt, `warn` for `.mjs` for the same reason; 2 in hooks-settings.ts,
|
|
309
|
+
// which writes the consumer's settings and must name the install) and `n/no-missing-import`
|
|
310
|
+
// (tsc already fails on an unresolved import in `src/`).
|
|
311
|
+
{
|
|
312
|
+
files: ["**/*.ts", "**/*.mts"],
|
|
313
|
+
languageOptions: { parser: tseslint.parser },
|
|
314
|
+
plugins: { "@typescript-eslint": tseslint.plugin, local: localRules },
|
|
315
|
+
rules: {
|
|
316
|
+
// The same macOS-only defect as in the `.mjs` block; clean here, so it opens at `error`.
|
|
317
|
+
"local/temp-root-realpath": "error",
|
|
318
|
+
// A dead import is a sign of an incomplete edit. The TypeScript variant, because the core
|
|
319
|
+
// rule does not understand type-only positions.
|
|
320
|
+
"@typescript-eslint/no-unused-vars": "error",
|
|
321
|
+
// `any` switches the checker off for everything it flows into — the gate that makes `src/`
|
|
322
|
+
// TypeScript at all. Five older sites are pinned inline and named in #49.
|
|
323
|
+
"@typescript-eslint/no-explicit-any": "error",
|
|
324
|
+
// The behaviour rules of the `.mjs` block, for the same reasons: each changes what the code
|
|
325
|
+
// DOES, not how it looks, and a regex that escapes the wrong thing searches for the wrong thing.
|
|
326
|
+
"no-empty": "error",
|
|
327
|
+
"no-constant-condition": "error",
|
|
328
|
+
"no-dupe-keys": "error",
|
|
329
|
+
"no-unreachable": "error",
|
|
330
|
+
"no-fallthrough": "error",
|
|
331
|
+
"no-useless-escape": "error",
|
|
332
|
+
"no-control-regex": "error",
|
|
333
|
+
"no-misleading-character-class": "error",
|
|
334
|
+
"no-prototype-builtins": "error",
|
|
335
|
+
// Branches per function. Ten is the rule's long-standing default; the worst function
|
|
336
|
+
// measured was 59, and one that size cannot be read, only re-run.
|
|
337
|
+
complexity: ["error", 10],
|
|
338
|
+
// `x as unknown as T` tells the checker to look away: two spellings of one port were once
|
|
339
|
+
// reconciled that way (#76). A real conversion is a function; a real subset needs no cast.
|
|
340
|
+
"no-restricted-syntax": [
|
|
341
|
+
"error",
|
|
342
|
+
{
|
|
343
|
+
selector:
|
|
344
|
+
"TSAsExpression > TSAsExpression[typeAnnotation.type='TSUnknownKeyword']",
|
|
345
|
+
message:
|
|
346
|
+
"`as unknown as` switches the type checker off. Convert with a function, or fix the type.",
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
// Nesting beyond three blocks is where a step belongs in its own named function.
|
|
350
|
+
"max-depth": ["error", 3],
|
|
351
|
+
// Five positional parameters are a record without field names; pass an object instead.
|
|
352
|
+
"max-params": ["error", 4],
|
|
353
|
+
// A function longer than a screen is read in pieces, and its pieces then want names.
|
|
354
|
+
"max-lines-per-function": ["error", MAX_LINES],
|
|
355
|
+
// Three levels of callbacks is the ceiling before a promise chain or a named step is due.
|
|
356
|
+
"max-nested-callbacks": ["error", 3],
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
// Hexagonal layers, both axes (src/CLAUDE.md): knowledge by element, purity by file category.
|
|
360
|
+
IO_GLOBALS,
|
|
361
|
+
layerBoundaries(dirname(fileURLToPath(import.meta.url))),
|
|
362
|
+
// The ratchet: per-file ceilings for the files written before the limits existed. See RATCHET.
|
|
363
|
+
...Object.entries(RATCHET).map(([file, max]) => ({
|
|
364
|
+
files: [file],
|
|
365
|
+
rules: Object.fromEntries(
|
|
366
|
+
Object.entries(max).map(([rule, n]) => [rule, ceiling(rule, n)]),
|
|
367
|
+
),
|
|
368
|
+
})),
|
|
369
|
+
/**
|
|
370
|
+
* 🔴 THIS BLOCK COVERS THE PACKAGE ITSELF, and before 2026-09-15 it was not here: the config held only
|
|
371
|
+
* one block for `.tex` (I don't quote the glob inside this comment: the sequence
|
|
372
|
+
* "star-slash" would close the comment itself — which is where I tripped), and a file with no block gets simply IGNORED by ESLint 9.
|
|
373
|
+
* Meaning: 133 own `.mjs` — mutation engine, three hooks, scripts for 24 skills — went unchecked,
|
|
374
|
+
* with green `npm run lint`. A tool that checks others' papers and not itself.
|
|
375
|
+
*
|
|
376
|
+
* Measurement on the first run: 15 files with dead imports (`resolve`, `pathToFileURL`) and
|
|
377
|
+
* orphaned constants — remnants of a move from `mine`, where those names were needed. All
|
|
378
|
+
* cleaned to zero in the same commit, so the rule opens as a GATE on a clean corpus,
|
|
379
|
+
* not as debt to be silenced.
|
|
380
|
+
*
|
|
381
|
+
* ⚠️ RULES ARE ENUMERATED, NOT TAKEN AS A SET, for two reasons. First: `@eslint/js` is not
|
|
382
|
+
* a dependency, and pulling it for a preset costs more than it saves with six devDeps.
|
|
383
|
+
* Second, more important: enumeration makes each inclusion a DECISION, just like in the .tex
|
|
384
|
+
* block below, where each line has a reason.
|
|
385
|
+
*
|
|
386
|
+
* 🔴 WHAT IS DELIBERATELY NOT HERE: `require-atomic-updates`. It finds three issues in
|
|
387
|
+
* `verify-cites.mjs` on a classic memoizer (read `cache[ck]` → `await` →
|
|
388
|
+
* write). Read from the code: citation walk is sequential, and a race in the worst case
|
|
389
|
+
* is a repeat network call with an equivalent result. So on THIS corpus
|
|
390
|
+
* it is a false positive, and for an `error`-level rule a false positive is worse than a miss:
|
|
391
|
+
* they turn it off the same day, and the rest stop being read with it.
|
|
392
|
+
* In `eslint:recommended`, by the way, it is also not there.
|
|
393
|
+
*/
|
|
394
|
+
{
|
|
395
|
+
files: ["**/*.mjs"],
|
|
396
|
+
languageOptions: { ecmaVersion: 2024, sourceType: "module" },
|
|
397
|
+
plugins: { local: localRules, port: portRules },
|
|
398
|
+
rules: {
|
|
399
|
+
// Rule 10's mechanical half, code side. `warn` and not `error`, unlike its neighbour
|
|
400
|
+
// above: this one does NOT open on a clean corpus. Two shipped modules print a command
|
|
401
|
+
// for a human that names an install-specific path, and neither is fixable by the
|
|
402
|
+
// answer the skills get — a printed command has to be resolved through the port at
|
|
403
|
+
// runtime. Nothing freezes that number: a ratchet would say "at least not worse" on the
|
|
404
|
+
// same day the rule was written, which is how a removal turns into a decision to keep.
|
|
405
|
+
// The severity only keeps `npx eslint .` from being red on a healthy clone, which is how
|
|
406
|
+
// a rule gets switched off and its binary neighbours ignored with it.
|
|
407
|
+
"port/js-install-path": "warn",
|
|
408
|
+
// 🔴 `error`, AND THIS IS A DECISION, NOT A DEFAULT. The rule is opened by a GATE on a
|
|
409
|
+
// clean corpus: all fifteen venues are allowed in the same commit, so there's no debt that
|
|
410
|
+
// would have to be muted. Stricter than that: the defect it catches is REPRODUCED ONLY ON
|
|
411
|
+
// macOS, and CI here is just one — `ubuntu-latest`. That is, on Linux this is the only
|
|
412
|
+
// guard that could ever turn red, and `warn` would mean that no one would ever see it:
|
|
413
|
+
// `eslint .` exits zero on warnings.
|
|
414
|
+
"local/temp-root-realpath": "error",
|
|
415
|
+
// A dead import is not style, it is a sign of an incomplete edit: it says a file once
|
|
416
|
+
// did something else. Fifteen such surfaced from the move.
|
|
417
|
+
"no-unused-vars": "error",
|
|
418
|
+
// Below are rules that silently change BEHAVIOR, not appearance: empty `catch {}` block (swallowed
|
|
419
|
+
// error — a documented failure mode of this repository), constant condition, duplicate key,
|
|
420
|
+
// unreachable code, fallthrough in `case`.
|
|
421
|
+
"no-empty": "error",
|
|
422
|
+
"no-constant-condition": "error",
|
|
423
|
+
"no-dupe-keys": "error",
|
|
424
|
+
"no-unreachable": "error",
|
|
425
|
+
"no-fallthrough": "error",
|
|
426
|
+
// Regexes: unnecessary escaping slash and control character in class — both findings about
|
|
427
|
+
// a pattern searching for NOT what the author wrote. The subject of this repository is checks,
|
|
428
|
+
// and a check searching for the wrong thing is green by construction.
|
|
429
|
+
"no-useless-escape": "error",
|
|
430
|
+
"no-control-regex": "error",
|
|
431
|
+
"no-misleading-character-class": "error",
|
|
432
|
+
"no-prototype-builtins": "error",
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
/**
|
|
436
|
+
* 🔴 AN IMPORT THAT RESOLVES TO NOTHING IS NOT A STYLE FINDING — it is a module that throws on
|
|
437
|
+
* load. Nothing here noticed one for a week (#67): a fixture imported `lib/consumer.mjs`, a file
|
|
438
|
+
* that has never existed in any commit, and no harness ever loads that fixture — the harness
|
|
439
|
+
* only LINTS it, and the port rule reads string literals, not whether they resolve. The same
|
|
440
|
+
* run found a second, older one: `lib/skill-eval-kit.mjs` dynamically imported
|
|
441
|
+
* `vigiles/testing`, a subpath vigiles stopped exporting in v16, on a branch no caller had
|
|
442
|
+
* taken yet.
|
|
443
|
+
*
|
|
444
|
+
* `n/no-missing-import` is the standard rule for this and covers relative AND package
|
|
445
|
+
* specifiers, static and dynamic, against the real resolver. Its own block so the one
|
|
446
|
+
* exception below does not leak into the other rules: the occupancy probe is EVIDENCE, kept
|
|
447
|
+
* exactly as it was run in a scratch environment where `retext-*` was installed — the same
|
|
448
|
+
* reason `docs/prior-art/repro/` is ignored globally above.
|
|
449
|
+
*/
|
|
450
|
+
{
|
|
451
|
+
files: ["**/*.mjs"],
|
|
452
|
+
ignores: ["skills/paper-pipeline/references/occupancy-2026-08-06-probe/**"],
|
|
453
|
+
plugins: { n },
|
|
454
|
+
rules: { "n/no-missing-import": "error" },
|
|
455
|
+
},
|
|
456
|
+
/**
|
|
457
|
+
* The package's first markdown rule — unit 1 of step 9 (moved from the consumer).
|
|
458
|
+
* The block targets OWN fixtures: in the consumer the same plugin is applied to its
|
|
459
|
+
* review report directory. `warn` for the same reason as .tex below: fixtures
|
|
460
|
+
* are DELIBERATELY defective, and `error` would mean `npx eslint .` reds on a healthy
|
|
461
|
+
* checkout. The signal lives in `npm test`, not in the warning count.
|
|
462
|
+
*/
|
|
463
|
+
{
|
|
464
|
+
files: ["fixtures/review-findings-cause/**/*.md"],
|
|
465
|
+
plugins: { markdown, review: reviewRules },
|
|
466
|
+
language: "markdown/gfm",
|
|
467
|
+
languageOptions: { frontmatter: "yaml" },
|
|
468
|
+
rules: { "review/findings-cause": "warn" },
|
|
469
|
+
},
|
|
470
|
+
/**
|
|
471
|
+
* Rule 10's mechanical half, prose side — and this is where the debt actually is: 76
|
|
472
|
+
* findings across 29 skills on a healthy checkout, every one of them a command that
|
|
473
|
+
* resolves in one delivery channel and is absent in another.
|
|
474
|
+
*
|
|
475
|
+
* The severity is a statement about the CORPUS, exactly as in the .tex block below: the
|
|
476
|
+
* finding itself is binary, but the corpus carries known debt that cannot be paid in the
|
|
477
|
+
* commit that introduces the rule. That debt is an ISSUE with an owner, deliberately not a
|
|
478
|
+
* frozen constant in a test — see the harness for why. `warn` keeps a clean clone from being
|
|
479
|
+
* red until the paths are gone; when they are, this line becomes `error`.
|
|
480
|
+
*/
|
|
481
|
+
{
|
|
482
|
+
files: ["skills/**/*.md"],
|
|
483
|
+
plugins: { markdown, port: portRules },
|
|
484
|
+
language: "markdown/gfm",
|
|
485
|
+
languageOptions: { frontmatter: "yaml" },
|
|
486
|
+
rules: { "port/md-install-path": "warn" },
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
files: ["**/*.tex"],
|
|
490
|
+
plugins: {
|
|
491
|
+
// One plugin object carries BOTH the language and the rules: `tex/latex` is the
|
|
492
|
+
// language, `tex/*` are the rules about the LaTeX source itself. ESLint permits this,
|
|
493
|
+
// and a second plugin name would buy nothing.
|
|
494
|
+
tex: { languages: { latex: texLanguage }, rules: texBuild },
|
|
495
|
+
},
|
|
496
|
+
language: "tex/latex",
|
|
497
|
+
rules: {
|
|
498
|
+
// `warn`, and that is an analysis rather than caution. The finding is NOT binary: a
|
|
499
|
+
// promise in a shipped build is sometimes honest (something genuinely not released
|
|
500
|
+
// yet), and the verdict "does this contradict the Availability paragraph" is a human
|
|
501
|
+
// one — which is what the message says. There is also a demonstrable class of false
|
|
502
|
+
// positives: "their replication will be published in 2027" is a sentence about SOMEONE
|
|
503
|
+
// ELSE's work. An `error` that fails on a correct input gets switched off the same day,
|
|
504
|
+
// and then the binary checks stop being read too.
|
|
505
|
+
"tex/future-promise": "warn",
|
|
506
|
+
// 🔴 `warn` HERE AND `error` IN A CONSUMER, on purpose. The finding itself is binary —
|
|
507
|
+
// the macro is present or it is not — and it has a named exemption (`nonacm`), so in a
|
|
508
|
+
// repository that lints a REAL paper it belongs at `error`: the cost of a miss is desk
|
|
509
|
+
// rejection with no review, and that asymmetry is the whole argument. This
|
|
510
|
+
// repository lints fixtures broken by construction, where the same severity
|
|
511
|
+
// would only mean `npx eslint .` reds on a healthy checkout. Severity is
|
|
512
|
+
// a statement about the CORPUS being linted, not the rule's certainty.
|
|
513
|
+
"tex/acm-frontmatter-override": "warn",
|
|
514
|
+
},
|
|
515
|
+
// ⚠️ `npx eslint .` therefore reports six warnings on a healthy checkout: the four defect
|
|
516
|
+
// fixtures are DEFECTIVE ON PURPOSE, and that is what makes the fire half of every harness
|
|
517
|
+
// real. Do not silence them by adding an ignore — a rule that lints only clean inputs is
|
|
518
|
+
// one whose firing path nothing exercises, which is rule 4 wearing a different hat. The
|
|
519
|
+
// pass/fail signal lives in `npm test`, not in the warning count of `npm run lint`.
|
|
520
|
+
},
|
|
521
|
+
];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
% A minimal acmart paper — exactly what ACM venues build.
|
|
2
|
+
% The full affiliation is not decoration: without \country the class ABORTS the build, and
|
|
3
|
+
% without it the fixture would fail for a reason unrelated to the subject of the test.
|
|
4
|
+
\documentclass[sigconf,nonacm]{acmart}
|
|
5
|
+
\begin{document}
|
|
6
|
+
\title{A fixture that must come out in the class's own fonts}
|
|
7
|
+
\author{A. Author}
|
|
8
|
+
\affiliation{\institution{X University}\city{Almaty}\country{Kazakhstan}}
|
|
9
|
+
\maketitle
|
|
10
|
+
The body exists so the run produces a page with text on it.
|
|
11
|
+
\end{document}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "venue": "agenticdev", "kind": "short" }
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
% A paper that does not compile: an undefined control sequence on line 6.
|
|
2
|
+
% `paperlint build` must name pdflatex, quote the error line and its `l.6` context from the log, and
|
|
3
|
+
% DELETE any paper.pdf left from an earlier build, so a red build cannot leave a PDF that looks current.
|
|
4
|
+
\documentclass{article}
|
|
5
|
+
\begin{document}
|
|
6
|
+
Text before, then \undefinedmacro{} after.
|
|
7
|
+
\end{document}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
% The bibtex path: a \cite needs pdflatex, bibtex, pdflatex before the citation resolves, and a
|
|
2
|
+
% \ref needs one more pass. The PDF must carry "[1]", not "[?]".
|
|
3
|
+
% The build.sh next to this file must NOT run: it would leave RAN behind and exit 1.
|
|
4
|
+
\documentclass{article}
|
|
5
|
+
\begin{document}
|
|
6
|
+
\section{Intro}\label{sec:intro}
|
|
7
|
+
Section~\ref{sec:intro} cites \cite{knuth84}.
|
|
8
|
+
\bibliographystyle{plain}
|
|
9
|
+
\bibliography{refs}
|
|
10
|
+
\end{document}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
% A document with no pages. pdflatex prints "No pages of output." and exits 0, writing NO PDF.
|
|
2
|
+
% `paperlint build` must report that as a failure — never a green paper.pdf — and must not let a
|
|
3
|
+
% paper.pdf from an earlier build stand in for the one this run did not write.
|
|
4
|
+
\documentclass{article}
|
|
5
|
+
\begin{document}
|
|
6
|
+
\end{document}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
% STANDS IN FOR THE CASE «acmart silently fell back to Computer Modern».
|
|
2
|
+
%
|
|
3
|
+
% The real substitution mechanism cannot be reproduced here: the class checks for
|
|
4
|
+
% `libertine.sty`/`zi4.sty`/`newtxmath.sty` through \IfFileExists, and hiding an installed
|
|
5
|
+
% package from kpathsea by adding a file is impossible — TEXINPUTS can append paths, not remove them.
|
|
6
|
+
% So the fixture produces the same OBSERVABLE result — a PDF typeset in Computer Modern —
|
|
7
|
+
% and the font check must reject it. What is being checked is the check itself.
|
|
8
|
+
\documentclass{article}
|
|
9
|
+
\begin{document}
|
|
10
|
+
The body exists so the run produces a page with text on it.
|
|
11
|
+
\end{document}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
% `\input{paper-guards}` resolves with NO configuration: rpp puts its own venues directory on
|
|
2
|
+
% TEXINPUTS. The guards fire only on the final pass (\finalpass), so a clean paper with a
|
|
3
|
+
% forward \ref must still build.
|
|
4
|
+
\documentclass{article}
|
|
5
|
+
\input{paper-guards}
|
|
6
|
+
\begin{document}
|
|
7
|
+
See Section~\ref{sec:later}.
|
|
8
|
+
\section{Later}\label{sec:later}
|
|
9
|
+
Text.
|
|
10
|
+
\end{document}
|