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,83 @@
|
|
|
1
|
+
# Who decides to fail the run — the rule, or the runner?
|
|
2
|
+
|
|
3
|
+
**Question this file answers:** when a check finds something, what decides whether the build
|
|
4
|
+
goes red: a property of the check, or a decision of whatever invoked it?
|
|
5
|
+
|
|
6
|
+
**Verdict:** in every tool examined, severity is **data attached to the finding or the rule**,
|
|
7
|
+
and the decision to fail is made by the **runner**. No tool examined lets a check decide, in its
|
|
8
|
+
own body, that the process must stop.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Danger JS — the closest analogue, because it lints the PROCESS
|
|
13
|
+
|
|
14
|
+
Danger exists to check facts about a change rather than about code: did you update the
|
|
15
|
+
changelog, is this PR too large, does it touch a file that requires a second reviewer. That is
|
|
16
|
+
the same family as "a paper declares a submitted stage but no frozen PDF is on disk".
|
|
17
|
+
|
|
18
|
+
Its levels are not a severity field — they are **four separate functions**:
|
|
19
|
+
|
|
20
|
+
| function | effect |
|
|
21
|
+
| ------------ | ---------------------------- |
|
|
22
|
+
| `fail()` | declares a CI-blocking error |
|
|
23
|
+
| `warn()` | reports, does not block |
|
|
24
|
+
| `message()` | adds a row to the table |
|
|
25
|
+
| `markdown()` | free-form output |
|
|
26
|
+
|
|
27
|
+
So the author of a check chooses the level **at the call site**, and the level is structural
|
|
28
|
+
rather than a string somebody parses later.
|
|
29
|
+
|
|
30
|
+
Source: <https://danger.systems/js/reference.html>
|
|
31
|
+
|
|
32
|
+
## reviewdog — non-blocking by default, failing is the runner's flag
|
|
33
|
+
|
|
34
|
+
reviewdog ingests diagnostics from any linter and posts them as review comments.
|
|
35
|
+
|
|
36
|
+
> By default Reviewdog will return 0 as exit code once all actions done (linter report
|
|
37
|
+
> analyzed, comments posted and so on). Once `fail-on-error` flag passed — Reviewdog will
|
|
38
|
+
> return 1 as exit code if at least one violation was found/reported.
|
|
39
|
+
|
|
40
|
+
The important part is **where the flag lives**: on the invocation, not on the rule. The same
|
|
41
|
+
rule set is advisory in one pipeline and blocking in another without editing a rule.
|
|
42
|
+
|
|
43
|
+
A recurring complaint is worth recording because it is a design warning, not a bug report:
|
|
44
|
+
when `--fail-on-error` is enabled, reviewdog fails on **any** result regardless of the
|
|
45
|
+
diagnostic level — people did not want an `info` diagnostic to turn the build red. In other
|
|
46
|
+
words: a single global "fail on anything" switch is too coarse once levels exist at all.
|
|
47
|
+
|
|
48
|
+
Sources: <https://github.com/reviewdog/reviewdog> ·
|
|
49
|
+
<https://github.com/reviewdog/reviewdog/issues/856>
|
|
50
|
+
|
|
51
|
+
## ESLint — severity is a value in the config, keyed by rule id
|
|
52
|
+
|
|
53
|
+
The model this package already consumes. `off` / `warn` / `error` sit in the config against a
|
|
54
|
+
rule **identifier**; the rule body never decides. `error` is what makes the process exit
|
|
55
|
+
non-zero. The severity of a rule can be changed without touching the rule.
|
|
56
|
+
|
|
57
|
+
## Clippy — four levels, per lint, overridable
|
|
58
|
+
|
|
59
|
+
`allow` / `warn` / `deny` / `forbid`, set per lint and overridable on the command line
|
|
60
|
+
(`-D warnings` being the familiar CI incantation). Again: the lint declares a default, the
|
|
61
|
+
invoker decides.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## What this means for this package
|
|
66
|
+
|
|
67
|
+
1. **A finding must be a record, not a rendered string.** `{ rule, severity, where, message }`.
|
|
68
|
+
Anything that reads severity out of the message text — an emoji, a prefix, a keyword — is
|
|
69
|
+
the same defect as parsing prose for a fact: it works until someone rewrites the sentence.
|
|
70
|
+
|
|
71
|
+
2. **The blocking decision belongs to the entry point.** `rpp lint` in CI may exit non-zero on
|
|
72
|
+
`error`; an editor hook running the same rules should not block at all. Same rules, two
|
|
73
|
+
policies, no rule edited.
|
|
74
|
+
|
|
75
|
+
3. **A single "fail on anything" switch is not enough** once more than one level exists — the
|
|
76
|
+
reviewdog complaint above is exactly that lesson, reported by its users.
|
|
77
|
+
|
|
78
|
+
4. **Default to not blocking.** This is also the house position for a separate, measured
|
|
79
|
+
reason: a gate that fails on taste gets routed around, and once people route around a gate
|
|
80
|
+
they stop reading the binary findings it also carried.
|
|
81
|
+
|
|
82
|
+
See also: [`nondeterministic-checks.md`](nondeterministic-checks.md) — a check whose verdict can
|
|
83
|
+
change without the file changing is a check that must not block, whatever its severity.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# How tools ship installable CONTENT, not just code
|
|
2
|
+
|
|
3
|
+
**Question this file answers:** this package carries 26 skills (2.9 MB of markdown an agent
|
|
4
|
+
reads) alongside its rules and its CLI. How do comparable tools deliver content that is neither
|
|
5
|
+
code nor configuration — styles, extensions, presets — and what do they refuse to do?
|
|
6
|
+
|
|
7
|
+
**Verdict:** the tools that do this well **declare the content in the config and fetch it with
|
|
8
|
+
an explicit command**. None of them silently bundle a large content tree into the tool itself,
|
|
9
|
+
and none of them rely on the user's file layout matching a path the content hard-codes.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Vale — the closest analogue, because its content IS the product
|
|
14
|
+
|
|
15
|
+
Vale is a prose linter whose rules are not compiled in: a _style_ is a folder of YAML rules, and
|
|
16
|
+
published styles (Google, Microsoft, Red Hat) are installed rather than vendored.
|
|
17
|
+
|
|
18
|
+
The declaration lives in `.vale.ini`:
|
|
19
|
+
|
|
20
|
+
```ini
|
|
21
|
+
StylesPath = styles
|
|
22
|
+
MinAlertLevel = suggestion
|
|
23
|
+
|
|
24
|
+
Packages = Google, write-good
|
|
25
|
+
|
|
26
|
+
[*.md]
|
|
27
|
+
BasedOnStyles = Vale, Google
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
and one command fetches what the config asked for:
|
|
31
|
+
|
|
32
|
+
```console
|
|
33
|
+
$ vale sync
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Three properties worth stealing:
|
|
37
|
+
|
|
38
|
+
- **The config names the content; the command fetches it.** There is no step where the user
|
|
39
|
+
copies a folder, and no step where the tool guesses.
|
|
40
|
+
- **`StylesPath` is declared by the consumer**, so the content does not need to know where it
|
|
41
|
+
will land.
|
|
42
|
+
- **A style is versioned and shareable independently of the binary.** Updating a style is not a
|
|
43
|
+
release of Vale.
|
|
44
|
+
|
|
45
|
+
Sources: <https://vale.sh/> · <https://github.com/vale-cli/vale>
|
|
46
|
+
|
|
47
|
+
## Quarto — extensions as a first-class verb pair
|
|
48
|
+
|
|
49
|
+
Quarto is the closest _domain_ analogue (scientific publishing: `render`, `preview`, `check`,
|
|
50
|
+
`publish`, `create`). For content it uses two verbs:
|
|
51
|
+
|
|
52
|
+
- `quarto add <extension>` — install an extension into the project
|
|
53
|
+
- `quarto use <template>` — start from a template that an extension provides
|
|
54
|
+
|
|
55
|
+
Project configuration lives in one place, `_quarto.yml`. The extension mechanism covers
|
|
56
|
+
filters, output formats and templates — that is, all three of "transform my document",
|
|
57
|
+
"produce this venue's format" and "start me from this skeleton" go through the same door.
|
|
58
|
+
|
|
59
|
+
Source: <https://quarto.org/docs/reference/>
|
|
60
|
+
|
|
61
|
+
## ESLint — shareable configs as ordinary packages
|
|
62
|
+
|
|
63
|
+
The pattern this package already lives inside. A shareable config is a normal npm package that
|
|
64
|
+
the consumer installs and names in their config; nothing is copied into the consumer's tree and
|
|
65
|
+
no path inside the config assumes where the consumer put it. Resolution is the module system's
|
|
66
|
+
job, which is precisely why it survives pnpm, yarn and npm layouts.
|
|
67
|
+
|
|
68
|
+
## pre-commit — hooks declared, fetched, pinned
|
|
69
|
+
|
|
70
|
+
```yaml
|
|
71
|
+
repos:
|
|
72
|
+
- repo: https://github.com/psf/black
|
|
73
|
+
rev: 24.4.2
|
|
74
|
+
hooks:
|
|
75
|
+
- id: black
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The consumer declares a repository, a **revision**, and which hooks to enable; the tool clones
|
|
79
|
+
and caches it. The lesson is the `rev:` field — content delivered from elsewhere is **pinned**,
|
|
80
|
+
so an upstream change cannot alter today's run.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## What this means for this package
|
|
85
|
+
|
|
86
|
+
The measured problem this file exists for: the plugin channel carries **zero skills**.
|
|
87
|
+
`marketplace.json` points at `plugin/`, which contains `plugin.json` (with no `skills` key) and
|
|
88
|
+
`hooks/hooks.json` — while `README.md` claims the install brings "all 24 skills". Separately,
|
|
89
|
+
**113 literal paths** of the form `.claude/skills/paper-pipeline/scripts/<x>.mjs` are written
|
|
90
|
+
inside the skills' own prose.
|
|
91
|
+
|
|
92
|
+
Read against the four tools above, those are one problem, not two:
|
|
93
|
+
|
|
94
|
+
1. **The content hard-codes where it expects to live.** No tool above does this. Vale's styles
|
|
95
|
+
do not know `StylesPath`; an ESLint shareable config does not know the consumer's directory.
|
|
96
|
+
A path inside the content is a bet on one installation channel, and there are two.
|
|
97
|
+
|
|
98
|
+
2. **There is no declared-and-fetched step.** Today the content arrives by being _inside_ the
|
|
99
|
+
package (npm) or by not arriving at all (plugin). Neither is `vale sync`: a line in the
|
|
100
|
+
config plus one command.
|
|
101
|
+
|
|
102
|
+
3. **Nothing pins the content** the way `pre-commit`'s `rev:` does.
|
|
103
|
+
|
|
104
|
+
➕ **Correction, 2026-09-19.** Point 1 above stands; the framing around it does not. The
|
|
105
|
+
`README.md` sentence this note quoted — "that installs all 24 skills" — has been fixed, and the
|
|
106
|
+
plugin carrying no skills turns out to be **deliberate and forced**, not an oversight: a plugin
|
|
107
|
+
fetched from npm gets no `node_modules`, so a plugin that shipped the skills would ship scripts it
|
|
108
|
+
could not run. The npm door delivers the skills; the plugin door delivers the hook wiring. What
|
|
109
|
+
remains a real problem is the paths written inside the skills.
|
|
110
|
+
|
|
111
|
+
Three directions follow, and they are genuinely different bets rather than one idea:
|
|
112
|
+
|
|
113
|
+
- **One channel.** Skills ship only through npm; the plugin carries hooks and says so. Cheapest;
|
|
114
|
+
gives up the plugin's discovery story.
|
|
115
|
+
- **Content is fetched, not bundled.** `rpp` declares which skill pack a project wants and a
|
|
116
|
+
command materialises it into a path the consumer declared. Closest to Vale; costs a fetcher
|
|
117
|
+
and a cache.
|
|
118
|
+
- **Content is addressed, never pathed.** Skills stop naming filesystem locations and call the
|
|
119
|
+
CLI instead (`rpp <verb>`), so the same prose works in both channels. Attacks the root cause;
|
|
120
|
+
costs a pass over 113 sites and a rule that keeps them from coming back.
|
|
121
|
+
|
|
122
|
+
See also: [`multi-mode-tools.md`](multi-mode-tools.md) for how these verbs sit beside `lint` and
|
|
123
|
+
`build`, and `CONTRIBUTING.md` § "Why not one of the existing academic skill suites" — that
|
|
124
|
+
section answers a different question (should this exist), not this one (what shape it takes).
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# When one tool is a linter AND a builder AND an extension host
|
|
2
|
+
|
|
3
|
+
**Question this file answers:** this package is four things at once — a linter over papers, a
|
|
4
|
+
build front-end, a host for agent hooks, and a carrier of 26 skills. Tools that carry a
|
|
5
|
+
comparable mix exist. How do they keep the command surface small, and where do they draw the
|
|
6
|
+
line between a verb and a flag?
|
|
7
|
+
|
|
8
|
+
**Verdict:** the tools that feel small do not have fewer capabilities — they have **fewer
|
|
9
|
+
NOUNS**. Capability is reached by a verb over a project that the config already describes; what
|
|
10
|
+
would be a second noun becomes a flag or disappears into the config. The tools that feel large
|
|
11
|
+
are the ones where the user must name, on the command line, a thing the project already knows.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Biome — the "one tool, one config, one command" position, stated by its authors
|
|
16
|
+
|
|
17
|
+
> Biome unifies functionalities that have previously been separate tools. Building upon a shared
|
|
18
|
+
> base allows it to provide a cohesive experience for processing code, displaying errors,
|
|
19
|
+
> parallelize work, caching, and configuration.
|
|
20
|
+
|
|
21
|
+
The user-facing consequence is a single default verb:
|
|
22
|
+
|
|
23
|
+
```console
|
|
24
|
+
$ biome check . # format + lint + organize imports
|
|
25
|
+
$ biome check --write . # and fix what is fixable
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Formatting and linting remain available separately, but the advertised path is one command.
|
|
29
|
+
Note what this costs: `check` is doing three jobs, so its _output_ has to be legible enough to
|
|
30
|
+
tell them apart. Merging verbs pushes the burden onto the report.
|
|
31
|
+
|
|
32
|
+
Sources: <https://biomejs.dev/> · <https://github.com/biomejs/biome>
|
|
33
|
+
|
|
34
|
+
## Quarto — the domain analogue, and its verbs are all about the PROJECT
|
|
35
|
+
|
|
36
|
+
```console
|
|
37
|
+
$ quarto render # produce outputs
|
|
38
|
+
$ quarto preview # live preview
|
|
39
|
+
$ quarto check # validate the installation and its dependencies
|
|
40
|
+
$ quarto create # start a project or document
|
|
41
|
+
$ quarto publish # deploy
|
|
42
|
+
$ quarto add <ext> # install an extension
|
|
43
|
+
$ quarto use <template> # start from an extension's template
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Two things to steal, and one to notice:
|
|
47
|
+
|
|
48
|
+
- **`check` means "is my toolchain sane", not "is my document good".** That is a different
|
|
49
|
+
meaning from `biome check` and from this package's current `doctor`. Whichever meaning is
|
|
50
|
+
chosen, only one can be in play.
|
|
51
|
+
- **Extensions get their own verb pair** (`add` / `use`) rather than being wired by hand.
|
|
52
|
+
- Configuration lives in one file, `_quarto.yml`, and the verbs above almost never take a path —
|
|
53
|
+
the project says what it contains.
|
|
54
|
+
|
|
55
|
+
Source: <https://quarto.org/docs/reference/>
|
|
56
|
+
|
|
57
|
+
## What the small-surface tools have in common
|
|
58
|
+
|
|
59
|
+
| tool | verbs a user types weekly | where the nouns went |
|
|
60
|
+
| ------ | --------------------------- | ------------------------------------- |
|
|
61
|
+
| Biome | `check` (and `--write`) | config declares includes/excludes |
|
|
62
|
+
| Quarto | `render`, `preview` | `_quarto.yml` declares the project |
|
|
63
|
+
| Vale | `vale <paths>`, `vale sync` | `.vale.ini` declares styles and scope |
|
|
64
|
+
|
|
65
|
+
And the recurring shape: **one verb for the routine loop, one verb for "make my environment
|
|
66
|
+
match the declaration", and everything else rare.** Vale's `sync`, Quarto's `add`, and a
|
|
67
|
+
hypothetical `rpp install` are the same slot.
|
|
68
|
+
|
|
69
|
+
## This package today, measured 2026-09-19
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
npx rpp init [dir] set the project up
|
|
73
|
+
npx rpp lint [paths…] run every rule over your papers
|
|
74
|
+
npx rpp build <paper> | --all build a paper with its own build script
|
|
75
|
+
npx rpp doctor say what is actually wired — and what only LOOKS wired
|
|
76
|
+
npx rpp hook <name> run an editor hook (the plugin wiring calls this)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Read against the table above:
|
|
80
|
+
|
|
81
|
+
- `lint` takes `[paths…]` **and** the config declares `papers`. Two ways to say the same thing;
|
|
82
|
+
the help text already has to explain which wins.
|
|
83
|
+
- `build` takes `<paper>` or `--all` — a noun the project could supply, exactly the shape the
|
|
84
|
+
small-surface tools removed.
|
|
85
|
+
- `hook <name>` is **not a user verb at all**: it exists because the plugin wiring calls it.
|
|
86
|
+
Nobody types it. It is an entry point wearing a command's clothes.
|
|
87
|
+
- `doctor` and a possible `check` would collide in meaning unless one of them is renamed — see
|
|
88
|
+
the Quarto note above.
|
|
89
|
+
|
|
90
|
+
So the honest count of verbs a researcher types is **two** (`lint`, `build`), one is setup
|
|
91
|
+
(`init`), one is diagnostics (`doctor`), and one is machine-facing (`hook`). That is not a large
|
|
92
|
+
surface — but it is presented as five equals, which makes it read larger than it is.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Open questions this file does not settle
|
|
97
|
+
|
|
98
|
+
- Whether `lint` and `build` should merge into one routine verb the way `biome check` did, and
|
|
99
|
+
what that costs the report.
|
|
100
|
+
- Whether the machine-facing entry point should be hidden from `--help` entirely, or moved
|
|
101
|
+
behind a subcommand namespace.
|
|
102
|
+
- What `check` should mean here if it is introduced — toolchain sanity (Quarto) or the routine
|
|
103
|
+
pass (Biome). Both are taken; picking either forfeits the other.
|
|
104
|
+
|
|
105
|
+
See also: [`content-delivery.md`](content-delivery.md) for the `add` / `sync` slot, and
|
|
106
|
+
[`blocking-vs-advisory.md`](blocking-vs-advisory.md) for what each verb should exit with.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Checks whose verdict can change without the file changing
|
|
2
|
+
|
|
3
|
+
**Question this file answers:** a linter is normally a pure function of a file's bytes. Some
|
|
4
|
+
useful checks are not — they read the clock, compare modification times, or need a before/after
|
|
5
|
+
pair. Do real tools ship such checks, and how do they contain the damage?
|
|
6
|
+
|
|
7
|
+
**Verdict:** yes, they exist and they are respectable — but the tools that ship them
|
|
8
|
+
(a) make the non-deterministic condition **opt-in**, (b) **suppress it** in the context where it
|
|
9
|
+
would block someone who cannot fix it, and (c) **prefer a condition over recorded data** to a
|
|
10
|
+
condition over the clock wherever both are possible. Two of the three shapes below dissolve
|
|
11
|
+
entirely once the fact is recorded rather than inferred.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Shape 1: the clock — `unicorn/expiring-todo-comments`
|
|
16
|
+
|
|
17
|
+
The canonical time-dependent lint rule. A comment carries an expiry and starts being reported
|
|
18
|
+
once it passes:
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
// TODO [2018-05-01]: Do this thing
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Two defaults are the whole lesson:
|
|
25
|
+
|
|
26
|
+
- **`checkDates` defaults to `false`.** The date condition — the only non-deterministic one —
|
|
27
|
+
ships switched off.
|
|
28
|
+
- **`checkDatesOnPullRequests` also defaults to `false`.** Even with dates enabled, the rule
|
|
29
|
+
does not fire on pull requests, so the person blocked is the maintainer who wrote the TODO,
|
|
30
|
+
not a contributor who merely touched the file.
|
|
31
|
+
|
|
32
|
+
And the shape of the rest of the rule is the real argument. Of its six conditions, **five are
|
|
33
|
+
over declared data** and only one is over the clock:
|
|
34
|
+
|
|
35
|
+
| condition | example | deterministic? |
|
|
36
|
+
| --------------------------- | --------------------------- | -------------------------- |
|
|
37
|
+
| package version | `[>1]` | yes — reads `package.json` |
|
|
38
|
+
| engine version | `[engine:node@>8]` | yes |
|
|
39
|
+
| dependency present / absent | `[+package]` · `[-package]` | yes |
|
|
40
|
+
| dependency version | `[package@>1]` | yes |
|
|
41
|
+
| peer dependency version | `[peer:eslint@>9]` | yes |
|
|
42
|
+
| **expiry date** | `[2018-05-01]` | **no** |
|
|
43
|
+
|
|
44
|
+
There is a standing issue titled "failing the lint on a date may be inconvenient", which is the
|
|
45
|
+
community arriving at the same discomfort from the other direction.
|
|
46
|
+
|
|
47
|
+
Sources:
|
|
48
|
+
<https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/expiring-todo-comments.md>
|
|
49
|
+
· <https://github.com/sindresorhus/eslint-plugin-unicorn/issues/1541>
|
|
50
|
+
|
|
51
|
+
## Shape 2: the before/after pair — Semgrep `--baseline-commit`
|
|
52
|
+
|
|
53
|
+
A check that wants to see what a change _did_ seems to need an event. It does not.
|
|
54
|
+
|
|
55
|
+
> A diff-aware scan runs on your code before and after some "baseline" and only reports findings
|
|
56
|
+
> that are newly introduced in the commits after that baseline.
|
|
57
|
+
|
|
58
|
+
The same deterministic analysis is run on two revisions and the results are subtracted. Nothing
|
|
59
|
+
in the rule knows about events; the _runner_ supplies two inputs.
|
|
60
|
+
|
|
61
|
+
The cost is that the second input comes from git history, which brings its own constraint: a CI
|
|
62
|
+
checkout that fetches one commit cannot produce a baseline. That is a property of the pipeline,
|
|
63
|
+
not of the rule, and it has to be arranged deliberately.
|
|
64
|
+
|
|
65
|
+
Sources: <https://semgrep.dev/docs/semgrep-ci/findings-ci> · <https://semgrep.dev/docs/cli-reference>
|
|
66
|
+
|
|
67
|
+
## Shape 3: modification times — and this one is not a lint at all
|
|
68
|
+
|
|
69
|
+
Comparing the mtime of a built artefact against the mtime of its source is what a **build
|
|
70
|
+
system** does (`make`, `ninja`). It is non-deterministic in the way that matters here: a fresh
|
|
71
|
+
clone gives every file the same checkout timestamp, so the verdict changes without any content
|
|
72
|
+
changing.
|
|
73
|
+
|
|
74
|
+
The fix is not to accept the non-determinism but to **record the fact**: store a content hash of
|
|
75
|
+
the source alongside the artefact, and the question "is this build stale?" becomes
|
|
76
|
+
`sha256(source) !== recorded`, which is pure, file-only, and expressible as an ordinary rule.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## What this means for this package
|
|
81
|
+
|
|
82
|
+
1. **Prefer a recorded fact to an inferred one.** Wherever a check currently reasons about
|
|
83
|
+
_when_ something happened, ask whether the paper can simply declare _what_ it was built from.
|
|
84
|
+
A stage that records `source_sha256` turns two separate staleness checks into byte
|
|
85
|
+
comparisons.
|
|
86
|
+
|
|
87
|
+
2. **A hash of CONTENT, not a git object.** A commit id is a pointer that history rewriting,
|
|
88
|
+
garbage collection and shallow clones can all invalidate; the bytes cannot. If the check is
|
|
89
|
+
about the file, the recorded fact must be about the file.
|
|
90
|
+
|
|
91
|
+
3. **If a check must stay non-deterministic, it must not block, and it should be opt-in.**
|
|
92
|
+
That is what `checkDates: false` encodes, and the reasoning transfers unchanged.
|
|
93
|
+
|
|
94
|
+
4. **A diff-shaped check has two legitimate homes** — a runner that supplies two revisions
|
|
95
|
+
(Semgrep's model), or an editor/agent hook, which is handed the before and after of a single
|
|
96
|
+
edit for free and needs no git at all. The second is not a workaround for a missing
|
|
97
|
+
mechanism; it is using the event source that exists.
|
|
98
|
+
|
|
99
|
+
See also: [`blocking-vs-advisory.md`](blocking-vs-advisory.md).
|