paperlint 3.1.2 → 3.2.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/workflows/ci.yml +12 -7
- package/CLAUDE.md +3 -3
- package/CONTRIBUTING.md +34 -5
- package/dist/adapters/banal/index.d.ts.map +1 -1
- package/dist/adapters/banal/index.js +9 -5
- package/dist/adapters/banal/index.js.map +1 -1
- package/dist/adapters/banal/invocation.d.ts +1 -1
- package/dist/adapters/node/files.io.d.ts.map +1 -1
- package/dist/adapters/node/files.io.js +2 -1
- package/dist/adapters/node/files.io.js.map +1 -1
- package/dist/adapters/node/process.io.d.ts +0 -5
- package/dist/adapters/node/process.io.d.ts.map +1 -1
- package/dist/adapters/node/process.io.js +4 -3
- package/dist/adapters/node/process.io.js.map +1 -1
- package/dist/adapters/pdfjs/fill.d.ts.map +1 -1
- package/dist/adapters/pdfjs/fill.js +13 -7
- package/dist/adapters/pdfjs/fill.js.map +1 -1
- package/dist/adapters/references/index.d.ts.map +1 -1
- package/dist/adapters/references/index.js +4 -5
- package/dist/adapters/references/index.js.map +1 -1
- package/dist/adapters/references/pool.d.ts.map +1 -1
- package/dist/adapters/references/pool.js +4 -5
- package/dist/adapters/references/pool.js.map +1 -1
- package/dist/adapters/references/reach.io.d.ts.map +1 -1
- package/dist/adapters/references/reach.io.js +2 -1
- package/dist/adapters/references/reach.io.js.map +1 -1
- package/dist/build-engine.js +2 -2
- package/dist/build-engine.js.map +1 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +14 -11
- package/dist/build.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +31 -34
- package/dist/cli.js.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +3 -2
- package/dist/doctor.js.map +1 -1
- package/dist/domain/lookup-cache.d.ts.map +1 -1
- package/dist/domain/lookup-cache.js +10 -8
- package/dist/domain/lookup-cache.js.map +1 -1
- package/dist/domain/text.d.ts +12 -0
- package/dist/domain/text.d.ts.map +1 -1
- package/dist/domain/text.js +17 -0
- package/dist/domain/text.js.map +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +4 -3
- package/dist/engine.js.map +1 -1
- package/dist/eslint-rules/bib-reachable-entry.d.ts +22 -0
- package/dist/eslint-rules/bib-reachable-entry.d.ts.map +1 -0
- package/dist/eslint-rules/bib-reachable-entry.js +75 -0
- package/dist/eslint-rules/bib-reachable-entry.js.map +1 -0
- package/dist/eslint-rules/install-path-literals.d.ts +80 -0
- package/dist/eslint-rules/install-path-literals.d.ts.map +1 -0
- package/dist/eslint-rules/install-path-literals.js +105 -0
- package/dist/eslint-rules/install-path-literals.js.map +1 -0
- package/dist/eslint-rules/latex-language.d.ts +136 -0
- package/dist/eslint-rules/latex-language.d.ts.map +1 -0
- package/dist/eslint-rules/latex-language.js +639 -0
- package/dist/eslint-rules/latex-language.js.map +1 -0
- package/dist/eslint-rules/paper-research-question.d.ts +31 -0
- package/dist/eslint-rules/paper-research-question.d.ts.map +1 -0
- package/dist/eslint-rules/paper-research-question.js +188 -0
- package/dist/eslint-rules/paper-research-question.js.map +1 -0
- package/dist/eslint-rules/paper-stages.d.ts +83 -0
- package/dist/eslint-rules/paper-stages.d.ts.map +1 -0
- package/dist/eslint-rules/paper-stages.js +358 -0
- package/dist/eslint-rules/paper-stages.js.map +1 -0
- package/dist/eslint-rules/paper-typography.d.ts +74 -0
- package/dist/eslint-rules/paper-typography.d.ts.map +1 -0
- package/dist/eslint-rules/paper-typography.js +404 -0
- package/dist/eslint-rules/paper-typography.js.map +1 -0
- package/dist/eslint-rules/papers.d.ts +42 -0
- package/dist/eslint-rules/papers.d.ts.map +1 -0
- package/dist/eslint-rules/papers.js +145 -0
- package/dist/eslint-rules/papers.js.map +1 -0
- package/dist/eslint-rules/pdf-last-page-balance.d.ts +72 -0
- package/dist/eslint-rules/pdf-last-page-balance.d.ts.map +1 -0
- package/dist/eslint-rules/pdf-last-page-balance.js +237 -0
- package/dist/eslint-rules/pdf-last-page-balance.js.map +1 -0
- package/dist/eslint-rules/review-frontmatter.d.ts +57 -0
- package/dist/eslint-rules/review-frontmatter.d.ts.map +1 -0
- package/dist/eslint-rules/review-frontmatter.js +126 -0
- package/dist/eslint-rules/review-frontmatter.js.map +1 -0
- package/dist/eslint-rules/review-frontmatter.schema.json +36 -0
- package/dist/eslint-rules/rule-context.d.ts +55 -0
- package/dist/eslint-rules/rule-context.d.ts.map +1 -0
- package/dist/eslint-rules/rule-context.js +9 -0
- package/dist/eslint-rules/rule-context.js.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.d.ts +25 -0
- package/dist/eslint-rules/sibling-frontmatter.d.ts.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.js +16 -0
- package/dist/eslint-rules/sibling-frontmatter.js.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.schema.json +14 -0
- package/dist/eslint-rules/temp-root-realpath.d.ts +71 -0
- package/dist/eslint-rules/temp-root-realpath.d.ts.map +1 -0
- package/dist/eslint-rules/temp-root-realpath.js +139 -0
- package/dist/eslint-rules/temp-root-realpath.js.map +1 -0
- package/dist/eslint-rules/tex-build.d.ts +35 -0
- package/dist/eslint-rules/tex-build.d.ts.map +1 -0
- package/dist/eslint-rules/tex-build.js +313 -0
- package/dist/eslint-rules/tex-build.js.map +1 -0
- package/dist/facts-file.d.ts.map +1 -1
- package/dist/facts-file.js +2 -1
- package/dist/facts-file.js.map +1 -1
- package/dist/hooks-settings.d.ts +1 -1
- package/dist/hooks-settings.d.ts.map +1 -1
- package/dist/hooks-settings.js +14 -9
- package/dist/hooks-settings.js.map +1 -1
- package/dist/init.d.ts +5 -5
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +15 -7
- package/dist/init.js.map +1 -1
- package/dist/latex-loop.d.ts +2 -2
- package/dist/latex-loop.d.ts.map +1 -1
- package/dist/latex-loop.js +2 -2
- package/dist/latex-loop.js.map +1 -1
- package/dist/lib/agent-cli-version.d.ts +65 -0
- package/dist/lib/agent-cli-version.d.ts.map +1 -0
- package/dist/lib/agent-cli-version.js +77 -0
- package/dist/lib/agent-cli-version.js.map +1 -0
- package/dist/lib/check.d.ts +19 -0
- package/dist/lib/check.d.ts.map +1 -0
- package/dist/lib/check.js +52 -0
- package/dist/lib/check.js.map +1 -0
- package/dist/lib/markdown.d.ts +180 -0
- package/dist/lib/markdown.d.ts.map +1 -0
- package/dist/lib/markdown.js +302 -0
- package/dist/lib/markdown.js.map +1 -0
- package/dist/lib/paper-config.d.ts +84 -0
- package/dist/lib/paper-config.d.ts.map +1 -0
- package/dist/lib/paper-config.js +142 -0
- package/dist/lib/paper-config.js.map +1 -0
- package/dist/lib/skill-eval-fixture.d.ts +29 -0
- package/dist/lib/skill-eval-fixture.d.ts.map +1 -0
- package/dist/lib/skill-eval-fixture.js +62 -0
- package/dist/lib/skill-eval-fixture.js.map +1 -0
- package/dist/link-skills.d.ts.map +1 -1
- package/dist/link-skills.js +5 -4
- package/dist/link-skills.js.map +1 -1
- package/dist/new-paper.d.ts.map +1 -1
- package/dist/new-paper.js +2 -2
- package/dist/new-paper.js.map +1 -1
- package/dist/paper-settings.d.ts.map +1 -1
- package/dist/paper-settings.js +5 -4
- package/dist/paper-settings.js.map +1 -1
- package/dist/pdf-facts.d.ts.map +1 -1
- package/dist/pdf-facts.js +2 -1
- package/dist/pdf-facts.js.map +1 -1
- package/dist/pdf-geometry.d.ts.map +1 -1
- package/dist/pdf-geometry.js +1 -1
- package/dist/pdf-geometry.js.map +1 -1
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +3 -2
- package/dist/presets.js.map +1 -1
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +11 -6
- package/dist/references.js.map +1 -1
- package/dist/rules-config.d.ts.map +1 -1
- package/dist/rules-config.js +6 -3
- package/dist/rules-config.js.map +1 -1
- package/dist/structure.js +1 -1
- package/dist/structure.js.map +1 -1
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +8 -3
- package/dist/tex-requirements.js.map +1 -1
- package/dist/toolchain.d.ts.map +1 -1
- package/dist/toolchain.js +21 -16
- package/dist/toolchain.js.map +1 -1
- package/dist/venue-rules.js +1 -1
- package/dist/venue-rules.js.map +1 -1
- package/docs/e2e.md +7 -7
- package/docs/install.md +1 -1
- package/docs/prior-art/README.md +1 -1
- package/docs/prior-art/package-location.md +5 -5
- package/docs/prior-art/repro/README.md +1 -1
- package/docs/prior-art/test-tooling.md +2 -2
- package/docs/testing.md +11 -8
- package/docs/toolchain.md +1 -1
- package/eslint-rules/{bib-reachable-entry.mjs → bib-reachable-entry.ts} +4 -3
- package/eslint-rules/install-path-literals.harness.mjs +1 -1
- package/eslint-rules/{install-path-literals.mjs → install-path-literals.ts} +16 -8
- package/eslint-rules/latex-language.harness.mjs +2 -2
- package/eslint-rules/latex-language.test.mjs +7 -1
- package/eslint-rules/{latex-language.mjs → latex-language.ts} +297 -109
- package/eslint-rules/paper-research-question.harness.mjs +3 -3
- package/eslint-rules/{paper-research-question.mjs → paper-research-question.ts} +34 -15
- package/eslint-rules/paper-stages.harness.mjs +2 -2
- package/eslint-rules/paper-stages.test.mjs +8 -2
- package/eslint-rules/{paper-stages.mjs → paper-stages.ts} +74 -29
- package/eslint-rules/paper-typography.test.mjs +3 -3
- package/eslint-rules/{paper-typography.mjs → paper-typography.ts} +132 -54
- package/eslint-rules/papers.harness.mjs +2 -2
- package/eslint-rules/{papers.mjs → papers.ts} +7 -4
- package/eslint-rules/pdf-last-page-balance.harness.mjs +3 -3
- package/eslint-rules/{pdf-last-page-balance.mjs → pdf-last-page-balance.ts} +88 -31
- package/eslint-rules/review-frontmatter.test.mjs +2 -2
- package/eslint-rules/{review-frontmatter.mjs → review-frontmatter.ts} +37 -15
- package/eslint-rules/rule-context.ts +62 -0
- package/eslint-rules/{sibling-frontmatter.mjs → sibling-frontmatter.ts} +1 -1
- package/eslint-rules/temp-root-realpath.harness.mjs +1 -1
- package/eslint-rules/{temp-root-realpath.mjs → temp-root-realpath.ts} +19 -6
- package/eslint-rules/tex-build.harness.mjs +2 -2
- package/eslint-rules/{tex-build.mjs → tex-build.ts} +4 -3
- package/eslint.config.mjs +139 -30
- package/fixtures/pdf-facts/README.md +2 -2
- package/fixtures/real-markdown-paper/{baseline.mjs → baseline.ts} +31 -10
- package/hooks/{hooks.harness.mjs → hooks.harness.ts} +51 -34
- package/hooks/paper-edit-guard.hook.mjs +1 -1
- package/hooks/{paper-edit-guard.test.mjs → paper-edit-guard.test.ts} +3 -7
- package/hooks/paper-skills-nudge.hook.mjs +1 -1
- package/hooks/{paper-skills-nudge.test.mjs → paper-skills-nudge.test.ts} +2 -6
- package/hooks/{paper-status-gates.test.mjs → paper-status-gates.test.ts} +2 -6
- package/lib/agent-cli-version.harness.mjs +1 -1
- package/lib/{agent-cli-version.mjs → agent-cli-version.ts} +32 -18
- package/lib/check.test.ts +14 -9
- package/lib/{check.mjs → check.ts} +22 -9
- package/lib/markdown.test.mjs +7 -7
- package/lib/{markdown.mjs → markdown.ts} +68 -29
- package/lib/paper-config.harness.mjs +8 -8
- package/lib/{paper-config.mjs → paper-config.ts} +19 -14
- package/lib/skill-checks.mjs +1 -1
- package/lib/skill-checks.test.mjs +1 -1
- package/lib/skill-corpus.mjs +1 -1
- package/lib/skill-eval-kit.mjs +1 -1
- package/package.json +42 -10
- package/scripts/{check.harness.mjs → check.harness.ts} +111 -10
- package/scripts/{check.test.mjs → check.test.ts} +13 -10
- package/scripts/{check.mjs → check.ts} +116 -42
- package/scripts/coverage-config.test.ts +80 -37
- package/scripts/eslint-report-guard.test.mjs +1 -1
- package/scripts/harness-api.frozen.json +14 -15
- package/scripts/harness-api.test.ts +8 -5
- package/scripts/layer-legacy-frozen.test.ts +17 -13
- package/scripts/{layer-legacy-frozen.mjs → layer-legacy-frozen.ts} +128 -39
- package/scripts/release-config.test.ts +12 -4
- package/scripts/rules-see-files.test.mjs +1 -1
- package/scripts/test.test.ts +78 -0
- package/scripts/test.ts +87 -0
- package/skills/grade-paper-writing/prose-lint.mjs +1 -1
- package/skills/grade-paper-writing/{prose-lint.test.mjs → prose-lint.test.ts} +2 -2
- package/skills/paper-pipeline/description-language.eval.mjs +1 -1
- package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +1 -1
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +1 -1
- package/skills/paper-pipeline/pipeline-firing.eval.mjs +1 -1
- package/skills/paper-pipeline/pipeline-language.eval.mjs +1 -1
- package/skills/paper-pipeline/scripts/README.md +1 -1
- package/skills/paper-pipeline/scripts/announce.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/artifact-coverage.mjs +1 -1
- package/skills/paper-pipeline/scripts/{artifact-coverage.test.mjs → artifact-coverage.test.ts} +2 -2
- package/skills/paper-pipeline/scripts/consumer.mjs +1 -5
- package/skills/paper-pipeline/scripts/consumer.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +1 -1
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +1 -4
- package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/fixtures/{scorecard.mjs → scorecard.ts} +36 -7
- package/skills/paper-pipeline/scripts/{generated-code.test.mjs → generated-code.test.ts} +3 -3
- package/skills/paper-pipeline/scripts/ledger.cli.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/pipeline-check.cli.test.mjs +2 -2
- package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +2 -2
- package/skills/paper-pipeline/scripts/pipeline-check.mjs +1 -4
- package/skills/paper-pipeline/scripts/population-map.mjs +1 -4
- package/skills/paper-pipeline/scripts/population-map.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/{round-diff.harness.mjs → round-diff.harness.ts} +45 -18
- package/skills/paper-pipeline/scripts/round-diff.mjs +1 -1
- package/skills/paper-pipeline/scripts/round-diff.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/run-mechanical.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/status.test.mjs +1 -1
- package/skills/plan-paper-timeline/fixtures/{fake-google-calendar.mjs → fake-google-calendar.ts} +101 -70
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +3 -3
- package/skills/render-paper/{extract-pdf-facts.harness.mjs → extract-pdf-facts.harness.ts} +34 -9
- package/skills/render-paper/render-paper.harness.mjs +2 -2
- package/skills/tighten-paper/structure.mjs +1 -1
- package/skills/tighten-paper/{structure.test.mjs → structure.test.ts} +4 -3
- package/skills/verify-citations/scripts/{bib-authors.cli.test.mjs → bib-authors.cli.test.ts} +14 -4
- package/skills/verify-citations/scripts/bib-authors.d.mts +63 -0
- package/skills/verify-citations/scripts/bib-authors.mjs +1 -4
- package/skills/verify-citations/scripts/{verify-cites.cli.test.mjs → verify-cites.cli.test.ts} +32 -9
- package/skills/verify-citations/scripts/verify-cites.d.mts +58 -0
- package/skills/verify-citations/scripts/verify-cites.mjs +11 -1
- package/skills/verify-citations/scripts/verify-cites.net.test.mjs +35 -0
- package/skills/verify-citations/scripts/verify-cites.test.mjs +14 -0
- package/src/CLAUDE.md +1 -1
- package/src/adapters/banal/index.test.ts +3 -6
- package/src/adapters/banal/index.ts +7 -4
- package/src/adapters/banal/invocation.test.ts +3 -1
- package/src/adapters/banal/invocation.ts +1 -1
- package/src/adapters/banal/output.test.ts +1 -1
- package/src/adapters/banal/run.test.ts +3 -3
- package/src/adapters/banal/xml.test.ts +1 -1
- package/src/adapters/curl/download.test.ts +4 -2
- package/src/adapters/node/files.io.ts +2 -2
- package/src/adapters/node/files.test.ts +3 -1
- package/src/adapters/node/process.io.ts +4 -3
- package/src/adapters/node/process.test.ts +1 -1
- package/src/adapters/node/workspace.test.ts +3 -1
- package/src/adapters/pdfjs/fill.test.ts +2 -2
- package/src/adapters/pdfjs/fill.ts +14 -7
- package/src/adapters/references/index.test.ts +31 -38
- package/src/adapters/references/index.ts +26 -58
- package/src/adapters/references/pool.ts +3 -5
- package/src/adapters/references/reach.io.ts +2 -1
- package/src/build-engine.harness.mjs +1 -1
- package/src/build-engine.test.ts +2 -2
- package/src/build-engine.ts +2 -2
- package/src/build.harness.mjs +2 -2
- package/src/build.test.ts +15 -14
- package/src/build.ts +28 -19
- package/src/cli.harness.mjs +6 -6
- package/src/cli.test.ts +13 -6
- package/src/cli.ts +39 -37
- package/src/doctor.harness.mjs +3 -3
- package/src/doctor.ts +3 -2
- package/src/domain/lookup-cache.ts +21 -11
- package/src/domain/result.test.ts +2 -2
- package/src/domain/text.test.ts +32 -0
- package/src/domain/text.ts +23 -0
- package/src/engine.harness.mjs +2 -2
- package/src/engine.ts +4 -3
- package/src/facts-file.test.ts +9 -7
- package/src/facts-file.ts +2 -1
- package/src/{hooks-settings.harness.mjs → hooks-settings.harness.ts} +39 -20
- package/src/hooks-settings.ts +20 -9
- package/src/init.test.ts +12 -9
- package/src/init.ts +22 -12
- package/src/{latex-log.harness.mjs → latex-log.harness.ts} +14 -14
- package/src/{latex-loop.harness.mjs → latex-loop.harness.ts} +64 -36
- package/src/latex-loop.ts +3 -3
- package/src/{link-skills.harness.mjs → link-skills.harness.ts} +19 -19
- package/src/link-skills.test.ts +3 -3
- package/src/link-skills.ts +5 -4
- package/src/lint-fix.test.ts +4 -6
- package/src/linted-set.test.ts +2 -2
- package/src/new-paper.harness.mjs +2 -2
- package/src/new-paper.test.ts +8 -10
- package/src/new-paper.ts +2 -2
- package/src/paper-files.test.ts +3 -5
- package/src/paper-settings-commands.test.ts +5 -4
- package/src/paper-settings.test.ts +5 -5
- package/src/paper-settings.ts +8 -9
- package/src/{pdf-facts.harness.mjs → pdf-facts.harness.ts} +9 -8
- package/src/pdf-facts.test.ts +10 -11
- package/src/pdf-facts.ts +2 -1
- package/src/{pdf-geometry.harness.mjs → pdf-geometry.harness.ts} +19 -12
- package/src/pdf-geometry.ts +3 -1
- package/src/presets.ts +3 -2
- package/src/references.test.ts +51 -43
- package/src/references.ts +14 -7
- package/src/{rules-config.harness.mjs → rules-config.harness.ts} +7 -13
- package/src/rules-config.ts +7 -4
- package/src/rules-off.test.ts +6 -4
- package/src/{structure.harness.mjs → structure.harness.ts} +26 -15
- package/src/structure.ts +1 -1
- package/src/{tex-requirements.harness.mjs → tex-requirements.harness.ts} +50 -22
- package/src/tex-requirements.ts +8 -5
- package/src/toolchain.harness.mjs +2 -2
- package/src/toolchain.test.ts +1 -1
- package/src/toolchain.ts +31 -17
- package/src/venue-rules.test.ts +62 -64
- package/src/venue-rules.ts +1 -1
- package/tsconfig.json +6 -3
- package/tsconfig.pkg.json +10 -0
- package/lib/check.d.mts +0 -17
- package/lib/paper-config.d.mts +0 -31
- package/scripts/layer-legacy-frozen.d.mts +0 -36
- package/scripts/rules-are-content-only.harness.mjs +0 -105
- package/scripts/rules-are-content-only.mjs +0 -148
- package/scripts/rules-are-content-only.test.mjs +0 -61
- package/scripts/test.mjs +0 -44
- package/scripts/test.test.mjs +0 -52
- /package/bin/{paperlint.test.mjs → paperlint.test.ts} +0 -0
- /package/lib/{skill-eval-fixture.mjs → skill-eval-fixture.ts} +0 -0
- /package/skills/grade-paper-writing/__snapshots__/{prose-lint.test.mjs.snap → prose-lint.test.ts.snap} +0 -0
- /package/skills/osf-artifact-upload/{osf-artifact-upload.harness.mjs → osf-artifact-upload.harness.ts} +0 -0
- /package/skills/tighten-paper/__snapshots__/{structure.test.mjs.snap → structure.test.ts.snap} +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
rules: {
|
|
3
|
+
frontmatter: {
|
|
4
|
+
meta: {
|
|
5
|
+
type: string;
|
|
6
|
+
docs: {
|
|
7
|
+
description: string;
|
|
8
|
+
};
|
|
9
|
+
schema: never[];
|
|
10
|
+
messages: {
|
|
11
|
+
malformed: string;
|
|
12
|
+
invalid: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
create(context: import("./rule-context.ts").RuleContext): {
|
|
16
|
+
yaml(node: {
|
|
17
|
+
readonly value?: string;
|
|
18
|
+
}): void;
|
|
19
|
+
"root:exit"(root: object): void;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
export default _default;
|
|
25
|
+
//# sourceMappingURL=sibling-frontmatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sibling-frontmatter.d.ts","sourceRoot":"","sources":["../../eslint-rules/sibling-frontmatter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;kCAmBgmI,CAAC;;;;;;;AARjmI,wBAOE"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sibling/frontmatter` — a sibling card (`<paper>/siblings/*.md`, written by the
|
|
3
|
+
* `analyze-sibling-paper` skill) says in its frontmatter how much of the competing paper was READ:
|
|
4
|
+
* `read: full | abstract | none`. A verdict like "not a scoop" means something different when only
|
|
5
|
+
* the abstract was read, and a card that does not say is a finding.
|
|
6
|
+
*
|
|
7
|
+
* The package writes the cards, so it ships the schema: `sibling-frontmatter.schema.json`. Same
|
|
8
|
+
* mechanism as `review/frontmatter`.
|
|
9
|
+
*/
|
|
10
|
+
import { frontmatterRule } from "./review-frontmatter.js";
|
|
11
|
+
export default {
|
|
12
|
+
rules: {
|
|
13
|
+
frontmatter: frontmatterRule(new URL("./sibling-frontmatter.schema.json", import.meta.url), "a sibling card's frontmatter records how much of the competing paper was read (`read: full | abstract | none`)"),
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=sibling-frontmatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sibling-frontmatter.js","sourceRoot":"","sources":["../../eslint-rules/sibling-frontmatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE1D,eAAe;IACb,KAAK,EAAE;QACL,WAAW,EAAE,eAAe,CAC1B,IAAI,GAAG,CAAC,mCAAmC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAC7D,gHAAgH,CACjH;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "urn:paperlint:sibling-frontmatter",
|
|
4
|
+
"title": "paperlint sibling-card frontmatter",
|
|
5
|
+
"description": "The YAML frontmatter of a sibling card under <paper>/siblings/ (written by the analyze-sibling-paper skill): how much of the competing paper was actually read. A verdict about a paper read only at the abstract is a different claim from one read in full, and the card must say which.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["read"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"read": {
|
|
10
|
+
"description": "full: the whole paper was read. abstract: only the abstract (and maybe the figures). none: the card is from citations or a summary alone.",
|
|
11
|
+
"enum": ["full", "abstract", "none"]
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `local/temp-root-realpath` — a temp root taken from `tmpdir()` that is not resolved to its
|
|
3
|
+
* realpath.
|
|
4
|
+
*
|
|
5
|
+
* ── THE DEFECT THIS RULE WAS WRITTEN FOR (measured from issue #9) ──────────────────
|
|
6
|
+
* On macOS `os.tmpdir()` returns a path under `/var/folders/…`, and `/var` is itself a symlink
|
|
7
|
+
* to `/private/var`. Node resolves a module's `import.meta.url` to the REAL path, while
|
|
8
|
+
* `process.argv[1]` and any path a test composes from the root "as typed" keep the old
|
|
9
|
+
* spelling. The same directory gets two names, and every path comparison starts lying:
|
|
10
|
+
*
|
|
11
|
+
* meta: "file:///private/var/folders/…/probe.mjs"
|
|
12
|
+
* argv: "/var/folders/…/probe.mjs"
|
|
13
|
+
*
|
|
14
|
+
* On `ubuntu-latest` `/tmp` is a real directory, the two spellings coincide, and the same
|
|
15
|
+
* asserts pass. Three harnesses failed on macOS on a CLEAN checkout, while CI only ran Linux,
|
|
16
|
+
* so green there was a statement about Linux and nothing else.
|
|
17
|
+
*
|
|
18
|
+
* 🔴 WHY A RULE, NOT THREE FIXES. The issue's author fixed the three files they named and
|
|
19
|
+
* honestly wrote that they had not audited the rest. FIFTEEN files in this repository set up a
|
|
20
|
+
* root; every next harness sets up a sixteenth. A fix patches today's list, a rule fixes the
|
|
21
|
+
* shape — and, crucially, it is RED ON LINUX, where the defect itself cannot be reproduced.
|
|
22
|
+
* Without it the only guard is a Mac in someone's hands.
|
|
23
|
+
*
|
|
24
|
+
* ── THE PREDICATE, AND WHY IT IS EXACTLY THIS ──────────────────────────────────────
|
|
25
|
+
* Finding: a call to `mkdtempSync` whose ARGUMENTS contain a call to `tmpdir()`, and which is
|
|
26
|
+
* not itself the sole argument of `realpathSync(…)`.
|
|
27
|
+
*
|
|
28
|
+
* ⚠️ `tmpdir()` in the arguments is the load-bearing part, not decoration. A nested root of the
|
|
29
|
+
* shape `mkdtempSync(join(TMP, "repo-"))` is NOT flagged: it inherits its spelling from `TMP`,
|
|
30
|
+
* and `TMP` is caught at its own point of creation. This turns the issue's "resolve each temp
|
|
31
|
+
* root ONCE at creation" into something checkable character by character rather than by
|
|
32
|
+
* judgment: the rule does not try to guess whether a variable is resolved — it demands that
|
|
33
|
+
* exactly the spot where a platform path enters the program is resolved.
|
|
34
|
+
*
|
|
35
|
+
* ⚠️ WHAT THE RULE DOES NOT CATCH, named out loud so it is not mistaken for more. A root
|
|
36
|
+
* assembled around it (`const t = tmpdir(); const d = mkdtempSync(t + "/x")`) will pass: there
|
|
37
|
+
* is no call to `tmpdir()` in `mkdtempSync`'s arguments. On this corpus the defect has exactly
|
|
38
|
+
* one shape — `mkdtempSync(join(tmpdir(), …))` — and a wider predicate would have to be built
|
|
39
|
+
* on interprocedural analysis, whose cost outweighs the benefit across fifteen sites.
|
|
40
|
+
*
|
|
41
|
+
* 🔴 AND WHAT THE RULE DELIBERATELY LEAVES ALONE: `symlinkSync`. A symlink created BY A TEST
|
|
42
|
+
* inside an already-resolved root is exactly the subject three harnesses test, and resolving
|
|
43
|
+
* the root does not cancel that, it makes it the only symlink in the frame. Before the fix
|
|
44
|
+
* there were two: the one the test placed and the one the platform planted — and the test's
|
|
45
|
+
* assertion was about their sum.
|
|
46
|
+
*/
|
|
47
|
+
import type { CallExpression, Node } from "estree";
|
|
48
|
+
import type { RuleContext } from "./rule-context.ts";
|
|
49
|
+
declare const _default: {
|
|
50
|
+
rules: {
|
|
51
|
+
"temp-root-realpath": {
|
|
52
|
+
meta: {
|
|
53
|
+
type: string;
|
|
54
|
+
docs: {
|
|
55
|
+
description: string;
|
|
56
|
+
};
|
|
57
|
+
schema: never[];
|
|
58
|
+
messages: {
|
|
59
|
+
unresolved: string;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
create(context: RuleContext): {
|
|
63
|
+
CallExpression(node: CallExpression & {
|
|
64
|
+
readonly parent?: Node;
|
|
65
|
+
}): void;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
export default _default;
|
|
71
|
+
//# sourceMappingURL=temp-root-realpath.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temp-root-realpath.d.ts","sourceRoot":"","sources":["../../eslint-rules/temp-root-realpath.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;;;;;;;;;;;;;;4BAgF/B,WAAW;qCAEF,cAAc,GAAG;oBAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,CAAA;iBAAE;;;;;AApB1E,wBA+BE"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `local/temp-root-realpath` — a temp root taken from `tmpdir()` that is not resolved to its
|
|
3
|
+
* realpath.
|
|
4
|
+
*
|
|
5
|
+
* ── THE DEFECT THIS RULE WAS WRITTEN FOR (measured from issue #9) ──────────────────
|
|
6
|
+
* On macOS `os.tmpdir()` returns a path under `/var/folders/…`, and `/var` is itself a symlink
|
|
7
|
+
* to `/private/var`. Node resolves a module's `import.meta.url` to the REAL path, while
|
|
8
|
+
* `process.argv[1]` and any path a test composes from the root "as typed" keep the old
|
|
9
|
+
* spelling. The same directory gets two names, and every path comparison starts lying:
|
|
10
|
+
*
|
|
11
|
+
* meta: "file:///private/var/folders/…/probe.mjs"
|
|
12
|
+
* argv: "/var/folders/…/probe.mjs"
|
|
13
|
+
*
|
|
14
|
+
* On `ubuntu-latest` `/tmp` is a real directory, the two spellings coincide, and the same
|
|
15
|
+
* asserts pass. Three harnesses failed on macOS on a CLEAN checkout, while CI only ran Linux,
|
|
16
|
+
* so green there was a statement about Linux and nothing else.
|
|
17
|
+
*
|
|
18
|
+
* 🔴 WHY A RULE, NOT THREE FIXES. The issue's author fixed the three files they named and
|
|
19
|
+
* honestly wrote that they had not audited the rest. FIFTEEN files in this repository set up a
|
|
20
|
+
* root; every next harness sets up a sixteenth. A fix patches today's list, a rule fixes the
|
|
21
|
+
* shape — and, crucially, it is RED ON LINUX, where the defect itself cannot be reproduced.
|
|
22
|
+
* Without it the only guard is a Mac in someone's hands.
|
|
23
|
+
*
|
|
24
|
+
* ── THE PREDICATE, AND WHY IT IS EXACTLY THIS ──────────────────────────────────────
|
|
25
|
+
* Finding: a call to `mkdtempSync` whose ARGUMENTS contain a call to `tmpdir()`, and which is
|
|
26
|
+
* not itself the sole argument of `realpathSync(…)`.
|
|
27
|
+
*
|
|
28
|
+
* ⚠️ `tmpdir()` in the arguments is the load-bearing part, not decoration. A nested root of the
|
|
29
|
+
* shape `mkdtempSync(join(TMP, "repo-"))` is NOT flagged: it inherits its spelling from `TMP`,
|
|
30
|
+
* and `TMP` is caught at its own point of creation. This turns the issue's "resolve each temp
|
|
31
|
+
* root ONCE at creation" into something checkable character by character rather than by
|
|
32
|
+
* judgment: the rule does not try to guess whether a variable is resolved — it demands that
|
|
33
|
+
* exactly the spot where a platform path enters the program is resolved.
|
|
34
|
+
*
|
|
35
|
+
* ⚠️ WHAT THE RULE DOES NOT CATCH, named out loud so it is not mistaken for more. A root
|
|
36
|
+
* assembled around it (`const t = tmpdir(); const d = mkdtempSync(t + "/x")`) will pass: there
|
|
37
|
+
* is no call to `tmpdir()` in `mkdtempSync`'s arguments. On this corpus the defect has exactly
|
|
38
|
+
* one shape — `mkdtempSync(join(tmpdir(), …))` — and a wider predicate would have to be built
|
|
39
|
+
* on interprocedural analysis, whose cost outweighs the benefit across fifteen sites.
|
|
40
|
+
*
|
|
41
|
+
* 🔴 AND WHAT THE RULE DELIBERATELY LEAVES ALONE: `symlinkSync`. A symlink created BY A TEST
|
|
42
|
+
* inside an already-resolved root is exactly the subject three harnesses test, and resolving
|
|
43
|
+
* the root does not cancel that, it makes it the only symlink in the frame. Before the fix
|
|
44
|
+
* there were two: the one the test placed and the one the platform planted — and the test's
|
|
45
|
+
* assertion was about their sum.
|
|
46
|
+
*/
|
|
47
|
+
/**
|
|
48
|
+
* Whether `value` is an ESTree node: an object whose `type` is a string. The walk below reaches
|
|
49
|
+
* every property of a node, and most of them (`regex`, `value`, arrays of flags) are not nodes.
|
|
50
|
+
*/
|
|
51
|
+
const isNode = (value) => "type" in value && typeof value.type === "string";
|
|
52
|
+
/** The called function's name: `f()` and `mod.f()` count as the same name for our purposes. */
|
|
53
|
+
const calleeName = (node) => {
|
|
54
|
+
if (node?.type !== "CallExpression")
|
|
55
|
+
return "";
|
|
56
|
+
const c = node.callee;
|
|
57
|
+
if (c.type === "Identifier")
|
|
58
|
+
return c.name;
|
|
59
|
+
if (c.type === "MemberExpression" &&
|
|
60
|
+
!c.computed &&
|
|
61
|
+
c.property.type === "Identifier")
|
|
62
|
+
return c.property.name;
|
|
63
|
+
return "";
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Whether the subtree contains a call to `tmpdir()`. A walk over nodes, not a substring
|
|
67
|
+
* search: the word `tmpdir` in a comment or a string literal nearby is not a call, and a
|
|
68
|
+
* text-based guard would be forced to exclude itself — exactly the class of check string-based
|
|
69
|
+
* checks are banned for in this repository.
|
|
70
|
+
*/
|
|
71
|
+
const mentionsTmpdir = (node) => {
|
|
72
|
+
if (node === null || typeof node !== "object")
|
|
73
|
+
return false;
|
|
74
|
+
if (Array.isArray(node))
|
|
75
|
+
return node.some(mentionsTmpdir);
|
|
76
|
+
if (!isNode(node))
|
|
77
|
+
return false;
|
|
78
|
+
if (calleeName(node) === "tmpdir")
|
|
79
|
+
return true;
|
|
80
|
+
for (const [key, value] of Object.entries(node)) {
|
|
81
|
+
if (key === "parent" || key === "loc" || key === "range")
|
|
82
|
+
continue;
|
|
83
|
+
if (value !== null && typeof value === "object" && mentionsTmpdir(value))
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
};
|
|
88
|
+
/** `realpathSync(x)` and `realpathSync.native(x)` — both resolve the path, both count. */
|
|
89
|
+
const isRealpathCall = (node) => {
|
|
90
|
+
if (node?.type !== "CallExpression")
|
|
91
|
+
return false;
|
|
92
|
+
const name = calleeName(node);
|
|
93
|
+
if (name === "realpathSync")
|
|
94
|
+
return true;
|
|
95
|
+
// `realpathSync.native(…)`: the call's name is `native`, and the object is `realpathSync` itself.
|
|
96
|
+
if (name !== "native")
|
|
97
|
+
return false;
|
|
98
|
+
const obj = node.callee.type === "MemberExpression" ? node.callee.object : null;
|
|
99
|
+
if (obj?.type === "Identifier")
|
|
100
|
+
return obj.name === "realpathSync";
|
|
101
|
+
return (obj?.type === "MemberExpression" &&
|
|
102
|
+
!obj.computed &&
|
|
103
|
+
"name" in obj.property &&
|
|
104
|
+
obj.property.name === "realpathSync");
|
|
105
|
+
};
|
|
106
|
+
export default {
|
|
107
|
+
rules: {
|
|
108
|
+
"temp-root-realpath": {
|
|
109
|
+
meta: {
|
|
110
|
+
type: "problem",
|
|
111
|
+
docs: {
|
|
112
|
+
description: "a temp root taken from tmpdir() is resolved to its realpath at the moment it is created",
|
|
113
|
+
},
|
|
114
|
+
schema: [],
|
|
115
|
+
messages: {
|
|
116
|
+
unresolved: "temp root from tmpdir() is not resolved: wrap it as `realpathSync(mkdtempSync(join(tmpdir(), …)))`. " +
|
|
117
|
+
"On macOS /var is a symlink to /private/var, so Node resolves import.meta.url to the realpath " +
|
|
118
|
+
"while process.argv[1] and any path this file composes keep the spelling as typed — the same " +
|
|
119
|
+
"directory under two names, and every path comparison built on it silently compares the two.",
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
create(context) {
|
|
123
|
+
return {
|
|
124
|
+
CallExpression(node) {
|
|
125
|
+
if (calleeName(node) !== "mkdtempSync")
|
|
126
|
+
return;
|
|
127
|
+
if (!mentionsTmpdir(node.arguments))
|
|
128
|
+
return;
|
|
129
|
+
const parent = node.parent;
|
|
130
|
+
if (isRealpathCall(parent) && parent.arguments[0] === node)
|
|
131
|
+
return;
|
|
132
|
+
context.report({ node, messageId: "unresolved" });
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=temp-root-realpath.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temp-root-realpath.js","sourceRoot":"","sources":["../../eslint-rules/temp-root-realpath.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { RuleContext } from "./rule-context.ts";
|
|
2
|
+
declare const _default: {
|
|
3
|
+
"future-promise": {
|
|
4
|
+
meta: {
|
|
5
|
+
type: string;
|
|
6
|
+
docs: {
|
|
7
|
+
description: string;
|
|
8
|
+
};
|
|
9
|
+
schema: never[];
|
|
10
|
+
messages: {
|
|
11
|
+
promise: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
create(context: RuleContext): {
|
|
15
|
+
root(): void;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
"acm-frontmatter-override": {
|
|
19
|
+
meta: {
|
|
20
|
+
type: string;
|
|
21
|
+
docs: {
|
|
22
|
+
description: string;
|
|
23
|
+
};
|
|
24
|
+
schema: never[];
|
|
25
|
+
messages: {
|
|
26
|
+
override: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
create(context: RuleContext): {
|
|
30
|
+
root(): void;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export default _default;
|
|
35
|
+
//# sourceMappingURL=tex-build.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tex-build.d.ts","sourceRoot":"","sources":["../../eslint-rules/tex-build.ts"],"names":[],"mappings":"AAoDA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;;;;;;;;;;;;;wBAuGnC,WAAW;;;;;;;;;;;;;;;wBA2GX,WAAW;;;;;AAyD7B,wBAGE"}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tex-build — rules about the BUILT LaTeX paper: about what the preamble declares, not about
|
|
3
|
+
* how the prose is written.
|
|
4
|
+
*
|
|
5
|
+
* ── WHY ITS OWN NAMESPACE, AND NOT `paper/*` ────────────────────────────────
|
|
6
|
+
* In the source corpus the `paper/*` family is declared on BOTH `paper.md` and `paper.tex`,
|
|
7
|
+
* and that parity is pinned by an assertion: "a rule added for markdown must not silently
|
|
8
|
+
* fail to reach LaTeX (or the other way round)". The subject here is the BUILD MODE
|
|
9
|
+
* (`\documentclass[review]`, `printacmref=false`), and in markdown it does not exist: a draft
|
|
10
|
+
* has no mechanical sign saying "this build is the final one". Writing
|
|
11
|
+
* `paper/future-promise: "off"` on markdown would be an exemption justified by "there is no
|
|
12
|
+
* subject", and that same parity assertion requires the rule to be ALIVE on the other side —
|
|
13
|
+
* i.e. the machinery exists but is built for the opposite direction (off on `.tex`, alive on
|
|
14
|
+
* `.md`).
|
|
15
|
+
*
|
|
16
|
+
* Hence a namespace of its own, the same way an external word-counter gets one: it has a word
|
|
17
|
+
* count markdown does not have, we have a preamble markdown does not have. Where the config
|
|
18
|
+
* enumerates non-`paper/*` rules on `.tex`, the list is CLOSED and `tex/future-promise` is
|
|
19
|
+
* named explicitly, so that a new namespace arrives by decision rather than as a side effect
|
|
20
|
+
* of editing a config.
|
|
21
|
+
*
|
|
22
|
+
* ⚠️ THE MEASUREMENT THIS DECISION RESTS ON (source corpus, 2026-09-06 → 2026-09-07): had the
|
|
23
|
+
* rule been declared on `paper.md` it would produce ZERO there TODAY — not one match of
|
|
24
|
+
* `FUTURE_PROMISE_RE` in the markdown draft of the one paper that has one. So the choice is
|
|
25
|
+
* about semantics, not noise: markdown has no input distinguishing a draft from a shipped
|
|
26
|
+
* build, and the rule would fire on a draft's honest promise.
|
|
27
|
+
*
|
|
28
|
+
* ── THE RULE READS THE FILE FROM DISK, NOT THE PROJECTION. This is load-bearing ──
|
|
29
|
+
* The LaTeX language in `latex-language.mjs` hands rules a PROJECTION of `.tex` into a
|
|
30
|
+
* markdown-like text, and the first thing that projection does is blank the whole preamble
|
|
31
|
+
* (`blank(0, preEnd)`) plus `\documentclass` and `\settopmatter` as OPAQUE macros. Both signs
|
|
32
|
+
* of the build mode are therefore ERASED in the projection, and this check is inexpressible
|
|
33
|
+
* over `sourceCode.getText()`. `readFileSync(context.filename)` is the same thing sibling
|
|
34
|
+
* rules in the source corpus do when they read a registry file next to the paper; the only
|
|
35
|
+
* difference is that here the file being read is the linted file itself. The one side effect
|
|
36
|
+
* is named: in an editor with an unsaved buffer the rule judges the saved version. On a CLI
|
|
37
|
+
* run (and in CI) there is no such difference.
|
|
38
|
+
*
|
|
39
|
+
* ── WHAT THE MOVE TO A LINT RULE FIXED BESIDES THE FORM (measured 2026-09-07) ──
|
|
40
|
+
* 🔴 The previous implementation, a script, picked its input like this: look for `paper.md` /
|
|
41
|
+
* `draft.md`, and failing that take `readdirSync(dir).find((f) => f.endsWith(".tex"))` — the
|
|
42
|
+
* FIRST `.tex` in directory order. Measured across the three papers of the source corpus:
|
|
43
|
+
* paper A → a 536-BYTE file holding nine `\def`s with venue numbers. The check, added
|
|
44
|
+
* specifically BECAUSE of paper A, was not reading paper A;
|
|
45
|
+
* paper B → a four-month-old draft, one of SEVEN `.tex` files in that directory (the real
|
|
46
|
+
* `paper.tex` happened to get the same verdict — "review mode" — so the set of
|
|
47
|
+
* findings matched by accident, not by construction);
|
|
48
|
+
* paper C → `paper.tex`, the only `.tex` there; it matched.
|
|
49
|
+
* An ESLint rule runs over the glob declared in the config, so the address stopped depending
|
|
50
|
+
* on `readdirSync` order and on how many drafts happen to lie next to the paper.
|
|
51
|
+
*/
|
|
52
|
+
import { readFileSync } from "node:fs";
|
|
53
|
+
/**
|
|
54
|
+
* A copy rather than an import, for the same reason the prose lexicons are copies: a rule may
|
|
55
|
+
* not depend on the script it is replacing, or removing the check from that script becomes
|
|
56
|
+
* impossible.
|
|
57
|
+
*/
|
|
58
|
+
const FUTURE_PROMISE_RE = /\b(?:at (?:the )?camera[-\s]?ready|upon acceptance|on acceptance|will be (?:released|published|made (?:publicly )?available|public|open[-\s]?sourced))\b/i;
|
|
59
|
+
/** The same two signs a render script uses to tell a review build from a camera-ready one. */
|
|
60
|
+
const REVIEW_MODE_RE = /^[^%\n]*(?:\\documentclass\[[^\]]*\breview\b|printacmref=false)/m;
|
|
61
|
+
/** Is this an `acmart` build at all? Outside acmart none of the macros below mean anything. */
|
|
62
|
+
const ACMART_RE = /\\documentclass(?:\s*\[[^\]]*\])?\s*\{acmart\}/;
|
|
63
|
+
/**
|
|
64
|
+
* acmart's OWN escape hatch for "this is not going to an ACM venue": in `nonacm` mode the
|
|
65
|
+
* class disables the ACM Reference Format itself (`acmart.cls:110-121`, "in 'nonacm' mode we
|
|
66
|
+
* disable the \"ACM Reference Format\""). A build that declares it is not an ACM submission
|
|
67
|
+
* is exempt — that is the supported way to say so, and it is a class option rather than a
|
|
68
|
+
* hand-rolled blanking of the front matter.
|
|
69
|
+
*/
|
|
70
|
+
const NONACM_RE = /\\documentclass\s*\[[^\]]*\bnonacm\b/;
|
|
71
|
+
/**
|
|
72
|
+
* The overrides whose ENTIRE effect is to remove furniture the template puts on page 1.
|
|
73
|
+
* Each entry names what disappears, because a message that only says "do not do this" gets
|
|
74
|
+
* read as style advice.
|
|
75
|
+
*/
|
|
76
|
+
const FRONTMATTER_OVERRIDES = [
|
|
77
|
+
{
|
|
78
|
+
re: /\\setcopyright\s*\{\s*none\s*\}/g,
|
|
79
|
+
removes: "the copyright statement (acmart.cls:1857, copyright mode 0)",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
re: /\\renewcommand\s*\*?\s*\{?\s*\\footnotetextcopyrightpermission/g,
|
|
83
|
+
removes: "the permission/copyright footnote block on page 1 (acmart.cls:2180)",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
re: /\\(?:this)?pagestyle\s*\{\s*(?:plain|empty)\s*\}/g,
|
|
87
|
+
removes: "acmart's own page style, i.e. the running heads and folios it restores at " +
|
|
88
|
+
"\\begin{document} (acmart.cls:2876-2881)",
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
92
|
+
// future-promise — a shipped build promises what it has already handed over
|
|
93
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
94
|
+
// 🔴 Added 2026-08-24, because it would have caught a real blocker hours earlier. An accepted
|
|
95
|
+
// paper printed "(full harness at camera-ready)" in BOTH of its most-read positions — the end
|
|
96
|
+
// of the abstract and the end of the conclusion — while its own Availability paragraph three
|
|
97
|
+
// lines below said the harness was already public. The document contradicted itself two
|
|
98
|
+
// against one, and the losing side was the abstract: exactly the part a digital library
|
|
99
|
+
// indexes and the only part most people read.
|
|
100
|
+
//
|
|
101
|
+
// Worse than a typo: a reviewer had listed it as a weakness — "The experimental harness is not
|
|
102
|
+
// yet available (it is expected for the camera-ready version)" — so the printed camera-ready
|
|
103
|
+
// kept asserting the very thing the reviewer complained about.
|
|
104
|
+
//
|
|
105
|
+
// ⚠️ Here is why it survived, and it generalises. The promise WAS swept out the same day — out
|
|
106
|
+
// of the ARTIFACT (`README:34`, `reproduce.py:166`). Nobody made the same pass over
|
|
107
|
+
// `paper.tex`. One class of defect, two carriers, one of them cleaned. A human pass goes where
|
|
108
|
+
// the human is looking; this check goes to both places, because it does not know where anyone
|
|
109
|
+
// is looking.
|
|
110
|
+
//
|
|
111
|
+
// The scope is DELIBERATELY NARROW, because a noisy gate is a silenced gate:
|
|
112
|
+
// - `.tex` only (see the file header: markdown has no sign of "this build is final");
|
|
113
|
+
// - a review-mode build is EXEMPT, and that is the whole point: during peer review
|
|
114
|
+
// "at camera-ready" is a true and ordinary promise. The defect is only a promise that
|
|
115
|
+
// outlived its own delivery;
|
|
116
|
+
// - LaTeX comments are blanked FIRST: author notes of the form `% camera-ready blocker`
|
|
117
|
+
// legitimately live in the source, and a finding on those would be precisely the false
|
|
118
|
+
// positive that kills a check.
|
|
119
|
+
//
|
|
120
|
+
// SEVERITY is `warn`, and that is an analysis rather than caution. The finding is NOT binary:
|
|
121
|
+
// a promise in a camera-ready is sometimes honest (something genuinely not released yet), and
|
|
122
|
+
// the verdict "does it contradict the Availability paragraph" is made by a human — which is
|
|
123
|
+
// what the message text says. There is also a demonstrable class of false positives: "their
|
|
124
|
+
// replication will be published in 2027" is a sentence about SOMEONE ELSE's work. An `error`
|
|
125
|
+
// that fails on a correct input gets switched off the same day, and after that the binary
|
|
126
|
+
// checks stop being read too.
|
|
127
|
+
const futurePromise = {
|
|
128
|
+
meta: {
|
|
129
|
+
type: "problem",
|
|
130
|
+
docs: {
|
|
131
|
+
description: "the build is not in review mode, yet the text promises a future release of something already handed over",
|
|
132
|
+
},
|
|
133
|
+
schema: [],
|
|
134
|
+
messages: {
|
|
135
|
+
promise: "this build is NOT in review mode, but the text promises a future release: " +
|
|
136
|
+
"«{{text}}». Check it against Availability: if the thing is already published, the " +
|
|
137
|
+
"paper contradicts itself, and the abstract is the side that loses — the abstract is " +
|
|
138
|
+
"what the digital library indexes. That is exactly how «(full harness at " +
|
|
139
|
+
"camera-ready)» survived a sweep that went through the artifact and never went " +
|
|
140
|
+
"through paper.tex",
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
create(context) {
|
|
144
|
+
return {
|
|
145
|
+
root() {
|
|
146
|
+
// 🔴 ONE GUARD, NOT THREE. The first draft had `if (!file || !existsSync(file))`
|
|
147
|
+
// BEFORE the read — removing that line on 2026-09-07 showed it is caught by NOTHING:
|
|
148
|
+
// the `try/catch` around the read covers exactly the same cases (no path, a `<text>` run
|
|
149
|
+
// through stdin, no permission). Two guards doing one job is a guard the harness can
|
|
150
|
+
// never fail on, i.e. a dead assertion dressed as robustness.
|
|
151
|
+
// What is left is the read inside `try`: stay quiet rather than judge content we never
|
|
152
|
+
// saw.
|
|
153
|
+
let text;
|
|
154
|
+
try {
|
|
155
|
+
text = readFileSync(context.filename, "utf8");
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (REVIEW_MODE_RE.test(text))
|
|
161
|
+
return;
|
|
162
|
+
// Comments are blanked by CUTTING THE TAIL of the line, exactly as the previous
|
|
163
|
+
// implementation did. Columns do not shift: a match is only possible to the LEFT of
|
|
164
|
+
// `%`, and the left part of the line stays in place character for character. An
|
|
165
|
+
// escaped `\%` is not a comment.
|
|
166
|
+
const lines = text
|
|
167
|
+
.split("\n")
|
|
168
|
+
.map((l) => l.replace(/(^|[^\\])%.*$/, "$1"));
|
|
169
|
+
lines.forEach((line, i) => {
|
|
170
|
+
const m = FUTURE_PROMISE_RE.exec(line);
|
|
171
|
+
if (!m)
|
|
172
|
+
return;
|
|
173
|
+
const column = m.index + 1;
|
|
174
|
+
context.report({
|
|
175
|
+
loc: {
|
|
176
|
+
start: { line: i + 1, column },
|
|
177
|
+
end: { line: i + 1, column: column + m[0].length },
|
|
178
|
+
},
|
|
179
|
+
messageId: "promise",
|
|
180
|
+
data: { text: m[0] },
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
188
|
+
// acm-frontmatter-override — the build blanks template furniture off page 1
|
|
189
|
+
// ══════════════════════════════════════════════════════════════════════════════
|
|
190
|
+
// 🔴 Added 2026-09-11, from a DESK REJECT. A submission was rejected before any content
|
|
191
|
+
// review with the reason "substantially deviating from the specified template… front page
|
|
192
|
+
// lacks elements included in the template". Its preamble carried three lines whose entire
|
|
193
|
+
// effect is to take elements off the front page:
|
|
194
|
+
//
|
|
195
|
+
// \setcopyright{none}
|
|
196
|
+
// \renewcommand\footnotetextcopyrightpermission[1]{}
|
|
197
|
+
// \pagestyle{plain}
|
|
198
|
+
//
|
|
199
|
+
// The same three lines sit COMMENTED OUT in a sibling paper of the same corpus — the one that
|
|
200
|
+
// was accepted. The safer pattern was already known in the same directory and was not carried
|
|
201
|
+
// over. That is the shape this check exists for: a defect that is invisible in the source,
|
|
202
|
+
// visible only on the rendered first page, and fatal before anyone reads a word.
|
|
203
|
+
//
|
|
204
|
+
// ⚠️ THE RULE IS NOT CONDITIONED ON REVIEW MODE, AND THAT IS A MEASUREMENT, NOT A CHOICE.
|
|
205
|
+
// The obvious framing — "flag these when `\documentclass[review]`, because review mode already
|
|
206
|
+
// handles the front matter" — does not survive reading `acmart.cls`. The `review` option does
|
|
207
|
+
// exactly two things (`acmart.cls:93-99`): it turns on line numbers and sets
|
|
208
|
+
// `\@ACM@printfoliostrue`. It does not touch the copyright statement, the permission footnote
|
|
209
|
+
// or the page style. So the overrides are not fighting review mode; they strip the same
|
|
210
|
+
// furniture in EVERY mode — and stripping it in the camera-ready is worse, because that is
|
|
211
|
+
// the version the digital library keeps.
|
|
212
|
+
//
|
|
213
|
+
// The guards are therefore about the CLASS, not the stage:
|
|
214
|
+
// - `acmart` only. Outside it `\pagestyle{plain}` is an ordinary, correct line;
|
|
215
|
+
// - `nonacm` exempts, because that is acmart's own supported way to say "not an ACM venue",
|
|
216
|
+
// and in that mode the class disables the ACM Reference Format itself (`acmart.cls:110`).
|
|
217
|
+
//
|
|
218
|
+
// 🔴 `printacmref=false` IS DELIBERATELY NOT IN THE SET, though it was in the rejected preamble
|
|
219
|
+
// and it too removes a block from page 1. Two reasons, and the first is structural: the
|
|
220
|
+
// sibling rule in this very file reads `printacmref=false` as a SIGN OF REVIEW MODE
|
|
221
|
+
// (`REVIEW_MODE_RE`). A module where one rule treats a token as a legitimate marker and
|
|
222
|
+
// another calls the same token a defect is a module that contradicts itself, and the reader
|
|
223
|
+
// cannot tell which half to believe. The second is that ACM sanctions it for preprints. If it
|
|
224
|
+
// ever needs catching, it needs catching together with a decision about `REVIEW_MODE_RE` —
|
|
225
|
+
// not as a fourth line in this array.
|
|
226
|
+
//
|
|
227
|
+
// SEVERITY is for the CONSUMER to set, and the two configs differ on purpose: this repository
|
|
228
|
+
// lints fixtures that are broken by construction, a consumer lints a real paper. The finding
|
|
229
|
+
// is binary — the macro is present or it is not — it has a named exemption, and the cost of a
|
|
230
|
+
// miss is a rejection with no content review. In a consumer that is `error`.
|
|
231
|
+
const acmFrontmatterOverride = {
|
|
232
|
+
meta: {
|
|
233
|
+
type: "problem",
|
|
234
|
+
docs: {
|
|
235
|
+
description: "an acmart build overrides ACM's front-matter commands, removing template elements from page 1",
|
|
236
|
+
},
|
|
237
|
+
schema: [],
|
|
238
|
+
messages: {
|
|
239
|
+
override: "«{{text}}» removes {{removes}} from the front page of an `acmart` build. A submission " +
|
|
240
|
+
"was desk-rejected for exactly this — «substantially deviating from the specified " +
|
|
241
|
+
"template… front page lacks elements included in the template» — before any content " +
|
|
242
|
+
"review happened. The `review` option does not suppress this furniture itself " +
|
|
243
|
+
"(acmart.cls:93-99 turns on line numbers and folios, nothing else), so the line is not " +
|
|
244
|
+
"compensating for the class: it is fighting the template. Delete it. If this paper " +
|
|
245
|
+
"genuinely is not going to an ACM venue, say so the supported way — " +
|
|
246
|
+
"`\\documentclass[…,nonacm]{acmart}` — and the class drops the ACM Reference Format " +
|
|
247
|
+
"on its own",
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
create(context) {
|
|
251
|
+
return {
|
|
252
|
+
root() {
|
|
253
|
+
// Same single guard as the sibling rule, and for the same measured reason: a separate
|
|
254
|
+
// `existsSync` before the read is covered by this `catch` in every case it could
|
|
255
|
+
// fire, i.e. it is an assertion the harness can never fail on.
|
|
256
|
+
let text;
|
|
257
|
+
try {
|
|
258
|
+
text = readFileSync(context.filename, "utf8");
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
// Comments are blanked BEFORE the guards, not only before the search — unlike the
|
|
264
|
+
// sibling rule, which carries the `^[^%\n]*` prefix inside each pattern instead.
|
|
265
|
+
// It matters here: the accepted sibling paper of the source corpus keeps this exact
|
|
266
|
+
// block commented out as a record of what it used to do, and a `\documentclass` line
|
|
267
|
+
// quoted inside a comment must not arm the rule any more than a commented
|
|
268
|
+
// `\setcopyright{none}` must trip it. Columns do not shift: a match is only possible
|
|
269
|
+
// to the LEFT of `%`, and the left part of the line survives character for character.
|
|
270
|
+
// An escaped `\%` is not a comment.
|
|
271
|
+
const lines = text
|
|
272
|
+
.split("\n")
|
|
273
|
+
.map((l) => l.replace(/(^|[^\\])%.*$/, "$1"));
|
|
274
|
+
const code = lines.join("\n");
|
|
275
|
+
if (!ACMART_RE.test(code))
|
|
276
|
+
return;
|
|
277
|
+
if (NONACM_RE.test(code))
|
|
278
|
+
return;
|
|
279
|
+
lines.forEach((line, i) => {
|
|
280
|
+
for (const { re, removes } of FRONTMATTER_OVERRIDES) {
|
|
281
|
+
// 🔴 NO `re.lastIndex = 0` HERE, AND THAT IS A MEASUREMENT. These regexes are
|
|
282
|
+
// module-level and `/g`, so they carry `lastIndex` across files, and the obvious
|
|
283
|
+
// defensive line is to reset it. Removing that line on 2026-09-11 left the harness
|
|
284
|
+
// GREEN — because the loop below always drains the regex, and a `/g` `exec` that
|
|
285
|
+
// returns `null` resets `lastIndex` to 0 by itself. The reset was a guard the
|
|
286
|
+
// harness could never fail on, i.e. the same dead assertion dressed as robustness
|
|
287
|
+
// that the sibling rule's single-`try` comment describes. What keeps the property
|
|
288
|
+
// honest instead is an ASSERTION, not a line of code: the harness lints the same
|
|
289
|
+
// input twice in one process and requires the two runs to agree. Add a `break` to
|
|
290
|
+
// this loop and that assertion is what will fail.
|
|
291
|
+
let m;
|
|
292
|
+
while ((m = re.exec(line)) !== null) {
|
|
293
|
+
const column = m.index + 1;
|
|
294
|
+
context.report({
|
|
295
|
+
loc: {
|
|
296
|
+
start: { line: i + 1, column },
|
|
297
|
+
end: { line: i + 1, column: column + m[0].length },
|
|
298
|
+
},
|
|
299
|
+
messageId: "override",
|
|
300
|
+
data: { text: m[0], removes },
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
export default {
|
|
310
|
+
"future-promise": futurePromise,
|
|
311
|
+
"acm-frontmatter-override": acmFrontmatterOverride,
|
|
312
|
+
};
|
|
313
|
+
//# sourceMappingURL=tex-build.js.map
|