paperlint 7.2.0 → 9.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/CLAUDE.md +9 -25
- package/CONTRIBUTING.md +5 -4
- package/README.md +4 -3
- package/dist/adapters/bibtex/index.d.ts +3 -0
- package/dist/adapters/bibtex/index.d.ts.map +1 -0
- package/dist/adapters/bibtex/index.js +103 -0
- package/dist/adapters/bibtex/index.js.map +1 -0
- package/dist/adapters/latex/filecontents.d.ts +33 -0
- package/dist/adapters/latex/filecontents.d.ts.map +1 -0
- package/dist/adapters/latex/filecontents.js +65 -0
- package/dist/adapters/latex/filecontents.js.map +1 -0
- package/dist/adapters/latex/includes.d.ts +3 -3
- package/dist/adapters/latex/includes.d.ts.map +1 -1
- package/dist/adapters/latex/includes.js +18 -10
- package/dist/adapters/latex/includes.js.map +1 -1
- package/dist/adapters/latex/index.d.ts.map +1 -1
- package/dist/adapters/latex/index.js +2 -0
- package/dist/adapters/latex/index.js.map +1 -1
- package/dist/adapters/latex/nodes.d.ts +12 -0
- package/dist/adapters/latex/nodes.d.ts.map +1 -1
- package/dist/adapters/latex/nodes.js +39 -0
- package/dist/adapters/latex/nodes.js.map +1 -1
- package/dist/adapters/latex/parse.d.ts +7 -1
- package/dist/adapters/latex/parse.d.ts.map +1 -1
- package/dist/adapters/latex/parse.js +55 -6
- package/dist/adapters/latex/parse.js.map +1 -1
- package/dist/adapters/memory/index.d.ts +3 -0
- package/dist/adapters/memory/index.d.ts.map +1 -1
- package/dist/adapters/memory/index.js +9 -0
- package/dist/adapters/memory/index.js.map +1 -1
- package/dist/adapters/references/index.d.ts.map +1 -1
- package/dist/adapters/references/index.js +27 -24
- package/dist/adapters/references/index.js.map +1 -1
- package/dist/adapters/sources-record/index.d.ts +9 -0
- package/dist/adapters/sources-record/index.d.ts.map +1 -0
- package/dist/adapters/sources-record/index.js +68 -0
- package/dist/adapters/sources-record/index.js.map +1 -0
- package/dist/adapters/tex-output/blg.d.ts +9 -0
- package/dist/adapters/tex-output/blg.d.ts.map +1 -0
- package/dist/adapters/tex-output/blg.js +20 -0
- package/dist/adapters/tex-output/blg.js.map +1 -0
- package/dist/adapters/tex-output/fls.d.ts +8 -0
- package/dist/adapters/tex-output/fls.d.ts.map +1 -0
- package/dist/adapters/tex-output/fls.js +16 -0
- package/dist/adapters/tex-output/fls.js.map +1 -0
- package/dist/adapters/tex-output/index.d.ts +7 -0
- package/dist/adapters/tex-output/index.d.ts.map +1 -0
- package/dist/adapters/tex-output/index.js +7 -0
- package/dist/adapters/tex-output/index.js.map +1 -0
- package/dist/bib-rules.d.ts +47 -0
- package/dist/bib-rules.d.ts.map +1 -0
- package/dist/bib-rules.js +127 -0
- package/dist/bib-rules.js.map +1 -0
- package/dist/build.d.ts +29 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +41 -30
- package/dist/build.js.map +1 -1
- package/dist/claim-provenance.d.ts +2 -2
- package/dist/cli.d.ts +9 -5
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +43 -68
- package/dist/cli.js.map +1 -1
- package/dist/domain/paper-source.d.ts +1 -10
- package/dist/domain/paper-source.d.ts.map +1 -1
- package/dist/domain/paper-source.js +34 -24
- package/dist/domain/paper-source.js.map +1 -1
- package/dist/domain/paper-sources.d.ts +72 -0
- package/dist/domain/paper-sources.d.ts.map +1 -0
- package/dist/domain/paper-sources.js +18 -0
- package/dist/domain/paper-sources.js.map +1 -0
- package/dist/domain/sources-record.d.ts +102 -0
- package/dist/domain/sources-record.d.ts.map +1 -0
- package/dist/domain/sources-record.js +38 -0
- package/dist/domain/sources-record.js.map +1 -0
- package/dist/domain/tex-run.d.ts +25 -0
- package/dist/domain/tex-run.d.ts.map +1 -0
- package/dist/domain/tex-run.js +79 -0
- package/dist/domain/tex-run.js.map +1 -0
- package/dist/eslint-rules/latex-language.d.ts.map +1 -1
- package/dist/eslint-rules/latex-language.js +10 -13
- package/dist/eslint-rules/latex-language.js.map +1 -1
- package/dist/eslint-rules/paper-context.d.ts.map +1 -1
- package/dist/eslint-rules/paper-context.js +6 -5
- package/dist/eslint-rules/paper-context.js.map +1 -1
- package/dist/eslint-rules/paper-typography.d.ts +0 -9
- package/dist/eslint-rules/paper-typography.d.ts.map +1 -1
- package/dist/eslint-rules/paper-typography.js +34 -96
- package/dist/eslint-rules/paper-typography.js.map +1 -1
- package/dist/eslint-rules/papers.d.ts +1 -3
- package/dist/eslint-rules/papers.d.ts.map +1 -1
- package/dist/eslint-rules/papers.js +2 -4
- package/dist/eslint-rules/papers.js.map +1 -1
- package/dist/eslint-rules/tex-build.d.ts.map +1 -1
- package/dist/eslint-rules/tex-build.js +8 -25
- package/dist/eslint-rules/tex-build.js.map +1 -1
- package/dist/init.d.ts +1 -4
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +3 -9
- package/dist/init.js.map +1 -1
- package/dist/latex-log.d.ts +10 -0
- package/dist/latex-log.d.ts.map +1 -1
- package/dist/latex-log.js +10 -0
- package/dist/latex-log.js.map +1 -1
- package/dist/latex-loop.d.ts +2 -0
- package/dist/latex-loop.d.ts.map +1 -1
- package/dist/latex-loop.js.map +1 -1
- package/dist/lib/markdown.d.ts +5 -81
- package/dist/lib/markdown.d.ts.map +1 -1
- package/dist/lib/markdown.js +5 -116
- package/dist/lib/markdown.js.map +1 -1
- package/dist/lib/paper-config.d.ts.map +1 -1
- package/dist/lib/paper-config.js +0 -2
- package/dist/lib/paper-config.js.map +1 -1
- package/dist/lib/skill-eval-fixture.d.ts +6 -4
- package/dist/lib/skill-eval-fixture.d.ts.map +1 -1
- package/dist/lib/skill-eval-fixture.js +25 -18
- package/dist/lib/skill-eval-fixture.js.map +1 -1
- package/dist/new-paper.d.ts +5 -11
- package/dist/new-paper.d.ts.map +1 -1
- package/dist/new-paper.js +12 -23
- package/dist/new-paper.js.map +1 -1
- package/dist/package-dirs.d.ts.map +1 -1
- package/dist/package-dirs.js +3 -2
- package/dist/package-dirs.js.map +1 -1
- package/dist/paper-files.d.ts +4 -4
- package/dist/paper-includes.d.ts +4 -7
- package/dist/paper-includes.d.ts.map +1 -1
- package/dist/paper-includes.js +29 -21
- package/dist/paper-includes.js.map +1 -1
- package/dist/paper-prose.d.ts +17 -0
- package/dist/paper-prose.d.ts.map +1 -0
- package/dist/paper-prose.js +46 -0
- package/dist/paper-prose.js.map +1 -0
- package/dist/paper-record.d.ts +25 -0
- package/dist/paper-record.d.ts.map +1 -0
- package/dist/paper-record.js +39 -0
- package/dist/paper-record.js.map +1 -0
- package/dist/paper-sources.d.ts +66 -0
- package/dist/paper-sources.d.ts.map +1 -0
- package/dist/paper-sources.js +92 -0
- package/dist/paper-sources.js.map +1 -0
- package/dist/ports/bib-reader.d.ts +18 -0
- package/dist/ports/bib-reader.d.ts.map +1 -0
- package/dist/ports/bib-reader.js +2 -0
- package/dist/ports/bib-reader.js.map +1 -0
- package/dist/ports/check-references.d.ts +9 -4
- package/dist/ports/check-references.d.ts.map +1 -1
- package/dist/ports/latex.d.ts +15 -0
- package/dist/ports/latex.d.ts.map +1 -1
- package/dist/ports/sources-codec.d.ts +11 -0
- package/dist/ports/sources-codec.d.ts.map +1 -0
- package/dist/ports/sources-codec.js +2 -0
- package/dist/ports/sources-codec.js.map +1 -0
- package/dist/ports/tex-output.d.ts +17 -0
- package/dist/ports/tex-output.d.ts.map +1 -0
- package/dist/ports/tex-output.js +2 -0
- package/dist/ports/tex-output.js.map +1 -0
- package/dist/recorded-bibliography.d.ts +89 -0
- package/dist/recorded-bibliography.d.ts.map +1 -0
- package/dist/recorded-bibliography.js +136 -0
- package/dist/recorded-bibliography.js.map +1 -0
- package/dist/reference-rules.d.ts +5 -5
- package/dist/reference-rules.d.ts.map +1 -1
- package/dist/reference-rules.js +150 -77
- package/dist/reference-rules.js.map +1 -1
- package/dist/references.d.ts +71 -16
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +110 -26
- package/dist/references.js.map +1 -1
- package/dist/register-bands.d.ts +3 -3
- package/dist/register-bands.d.ts.map +1 -1
- package/dist/register-bands.js.map +1 -1
- package/dist/register.d.ts +2 -2
- package/dist/sources-record.d.ts +38 -0
- package/dist/sources-record.d.ts.map +1 -0
- package/dist/sources-record.js +123 -0
- package/dist/sources-record.js.map +1 -0
- package/dist/sources-rules.d.ts +44 -0
- package/dist/sources-rules.d.ts.map +1 -0
- package/dist/sources-rules.js +76 -0
- package/dist/sources-rules.js.map +1 -0
- package/dist/structure.d.ts.map +1 -1
- package/dist/structure.js +4 -3
- package/dist/structure.js.map +1 -1
- package/dist/tex-paper.d.ts +10 -15
- package/dist/tex-paper.d.ts.map +1 -1
- package/dist/tex-paper.js +24 -40
- package/dist/tex-paper.js.map +1 -1
- package/dist/tex-venue-rules.d.ts +3 -3
- package/dist/tex-venue-rules.d.ts.map +1 -1
- package/dist/tex-venue-rules.js.map +1 -1
- package/dist/types.d.ts +0 -2
- package/dist/types.d.ts.map +1 -1
- package/docs/configuration.md +18 -12
- package/docs/design/paper-sources.md +197 -0
- package/docs/e2e.md +4 -6
- package/docs/package-shape-options.md +1 -1
- package/docs/prior-art/paper-folder-scaffolding.md +24 -29
- package/docs/references.md +34 -8
- package/docs/rules/bib/reachable-entry.md +64 -0
- package/docs/rules/paper/sources-fresh.md +81 -0
- package/docs/rules/tex/claim-provenance.md +0 -1
- package/docs/rules.md +62 -61
- package/eslint-rules/latex-language.harness.mjs +3 -3
- package/eslint-rules/latex-language.ts +10 -13
- package/eslint-rules/paper-context.ts +6 -5
- package/eslint-rules/paper-typography.test.mjs +29 -157
- package/eslint-rules/paper-typography.ts +35 -104
- package/eslint-rules/papers.harness.mjs +1 -1
- package/eslint-rules/papers.ts +2 -4
- package/eslint-rules/tex-build.harness.mjs +2 -2
- package/eslint-rules/tex-build.ts +8 -25
- package/eslint.config.mjs +22 -1
- package/fixtures/build-e2e/aidc/paper.tex +1 -1
- package/fixtures/build-e2e/anon-ack/paper.tex +1 -1
- package/fixtures/build-e2e/anon-author/paper.tex +1 -1
- package/fixtures/build-e2e/anon-bib/paper.tex +1 -1
- package/fixtures/build-e2e/anon-href/paper.tex +1 -1
- package/fixtures/build-e2e/anon-macro/paper.tex +1 -1
- package/fixtures/build-e2e/anon-pdfauthor/paper.tex +1 -1
- package/fixtures/paper-sources/p1/paper.tex +30 -0
- package/fixtures/paper-sources/p1/refs.bib +6 -0
- package/fixtures/paper-sources/p1/sections/dead.tex +6 -0
- package/fixtures/paper-sources/p1/sections/intro.tex +7 -0
- package/fixtures/paper-sources/p1/sections/parked.tex +6 -0
- package/fixtures/paper-sources/p1/tex-truth.json +37 -0
- package/fixtures/paper-sources/p1/versions/2026-01-01-review.tex +4 -0
- package/fixtures/paper-sources/v1-stale/paper.tex +14 -0
- package/fixtures/paper-sources/v1-stale/refs.bib +6 -0
- package/fixtures/paper-sources/v1-stale/tex-truth.json +32 -0
- package/fixtures/paper-sources/v10-ifthenelse/anon.bib +1 -0
- package/fixtures/paper-sources/v10-ifthenelse/paper.tex +9 -0
- package/fixtures/paper-sources/v10-ifthenelse/real.bib +1 -0
- package/fixtures/paper-sources/v10-ifthenelse/tex-truth.json +28 -0
- package/fixtures/paper-sources/v11-two-blocks/paper.tex +12 -0
- package/fixtures/paper-sources/v11-two-blocks/tex-truth.json +30 -0
- package/fixtures/paper-sources/v13-percent-text/paper.tex +6 -0
- package/fixtures/paper-sources/v13-percent-text/refs.bib +3 -0
- package/fixtures/paper-sources/v13-percent-text/tex-truth.json +30 -0
- package/fixtures/paper-sources/v15-after-end/paper.tex +6 -0
- package/fixtures/paper-sources/v15-after-end/refs.bib +1 -0
- package/fixtures/paper-sources/v15-after-end/tex-truth.json +17 -0
- package/fixtures/paper-sources/v16-included-block/bibblock.tex +3 -0
- package/fixtures/paper-sources/v16-included-block/paper.tex +7 -0
- package/fixtures/paper-sources/v16-included-block/refs.bib +1 -0
- package/fixtures/paper-sources/v16-included-block/tex-truth.json +35 -0
- package/fixtures/paper-sources/v17-include-order/bibsetup.tex +1 -0
- package/fixtures/paper-sources/v17-include-order/first.bib +1 -0
- package/fixtures/paper-sources/v17-include-order/later.bib +1 -0
- package/fixtures/paper-sources/v17-include-order/paper.tex +7 -0
- package/fixtures/paper-sources/v17-include-order/tex-truth.json +40 -0
- package/fixtures/paper-sources/v2-overwrite/paper.tex +14 -0
- package/fixtures/paper-sources/v2-overwrite/refs.bib +6 -0
- package/fixtures/paper-sources/v2-overwrite/tex-truth.json +30 -0
- package/fixtures/paper-sources/v22-parked-thebibliography/paper.tex +7 -0
- package/fixtures/paper-sources/v22-parked-thebibliography/tex-truth.json +17 -0
- package/fixtures/paper-sources/v23-parked-include/old.bib +1 -0
- package/fixtures/paper-sources/v23-parked-include/paper.tex +7 -0
- package/fixtures/paper-sources/v23-parked-include/parked.tex +1 -0
- package/fixtures/paper-sources/v23-parked-include/refs.bib +1 -0
- package/fixtures/paper-sources/v23-parked-include/tex-truth.json +28 -0
- package/fixtures/paper-sources/v24-quoted-fields/paper.tex +6 -0
- package/fixtures/paper-sources/v24-quoted-fields/refs.bib +5 -0
- package/fixtures/paper-sources/v24-quoted-fields/tex-truth.json +32 -0
- package/fixtures/paper-sources/v25-string-differs/paper.tex +10 -0
- package/fixtures/paper-sources/v25-string-differs/refs.bib +2 -0
- package/fixtures/paper-sources/v25-string-differs/tex-truth.json +32 -0
- package/fixtures/paper-sources/v26-comment-in-names/one.bib +1 -0
- package/fixtures/paper-sources/v26-comment-in-names/paper.tex +7 -0
- package/fixtures/paper-sources/v26-comment-in-names/tex-truth.json +32 -0
- package/fixtures/paper-sources/v26-comment-in-names/two.bib +1 -0
- package/fixtures/paper-sources/v27-shared-string/abbreviations.bib +2 -0
- package/fixtures/paper-sources/v27-shared-string/paper.tex +6 -0
- package/fixtures/paper-sources/v27-shared-string/references.bib +6 -0
- package/fixtures/paper-sources/v27-shared-string/tex-truth.json +31 -0
- package/fixtures/paper-sources/v28-shared-string-block/abbreviations.bib +2 -0
- package/fixtures/paper-sources/v28-shared-string-block/paper.tex +14 -0
- package/fixtures/paper-sources/v28-shared-string-block/tex-truth.json +33 -0
- package/fixtures/paper-sources/v29-generated-input/paper.tex +9 -0
- package/fixtures/paper-sources/v29-generated-input/tex-truth.json +24 -0
- package/fixtures/paper-sources/v3-declared/paper.bib +6 -0
- package/fixtures/paper-sources/v3-declared/paper.tex +6 -0
- package/fixtures/paper-sources/v3-declared/tex-truth.json +28 -0
- package/fixtures/paper-sources/v30-name-next-line/paper.tex +14 -0
- package/fixtures/paper-sources/v30-name-next-line/tex-truth.json +30 -0
- package/fixtures/paper-sources/v4-commented/paper.tex +14 -0
- package/fixtures/paper-sources/v4-commented/refs.bib +6 -0
- package/fixtures/paper-sources/v4-commented/tex-truth.json +28 -0
- package/fixtures/paper-sources/v5-percent-entry/paper.tex +20 -0
- package/fixtures/paper-sources/v5-percent-entry/tex-truth.json +37 -0
- package/fixtures/paper-sources/v6-unclosed/paper.tex +6 -0
- package/fixtures/paper-sources/v6-unclosed/refs.bib +5 -0
- package/fixtures/paper-sources/v6-unclosed/tex-truth.json +37 -0
- package/fixtures/paper-sources/v8-jobname/paper.tex +9 -0
- package/fixtures/paper-sources/v8-jobname/tex-truth.json +30 -0
- package/fixtures/paper-sources/v9-two-bibliographies/one.bib +1 -0
- package/fixtures/paper-sources/v9-two-bibliographies/paper.tex +7 -0
- package/fixtures/paper-sources/v9-two-bibliographies/tex-truth.json +35 -0
- package/fixtures/paper-sources/v9-two-bibliographies/two.bib +1 -0
- package/fixtures/paper-typography/clean-paper/paper.tex +1 -1
- package/fixtures/paper-typography/messy-paper/paper.tex +1 -1
- package/hooks/hooks.harness.ts +15 -15
- package/hooks/paper-edit-guard.hook.mjs +5 -8
- package/hooks/paper-edit-guard.test.ts +12 -3
- package/hooks/paper-skills-nudge.hook.mjs +2 -3
- package/hooks/paper-skills-nudge.test.ts +13 -3
- package/hooks/paper-status-gates.hook.mjs +2 -2
- package/hooks/paper-status-gates.test.ts +14 -2
- package/lib/markdown.test.mjs +5 -60
- package/lib/markdown.ts +9 -136
- package/lib/paper-config.ts +0 -2
- package/lib/skill-checks.test.mjs +4 -1
- package/lib/skill-eval-fixture.ts +25 -18
- package/package.json +2 -12
- package/presets/realm.jsonc +1 -1
- package/scripts/check.test.ts +23 -1
- package/scripts/check.ts +7 -3
- package/scripts/coverage-config.test.ts +19 -10
- package/scripts/harness-api.frozen.json +0 -5
- package/scripts/layer-legacy-frozen.test.ts +41 -2
- package/scripts/layer-legacy-frozen.ts +31 -2
- package/skills/cold-read-diff/SKILL.md +3 -3
- package/skills/cold-read-diff/SKILL.md.spec.ts +2 -2
- package/skills/grade-paper-writing/SKILL.md +15 -22
- package/skills/grade-paper-writing/SKILL.md.spec.ts +14 -21
- package/skills/grade-paper-writing/__snapshots__/prose-lint.test.ts.snap +40 -26
- package/skills/grade-paper-writing/prose-lint.mjs +117 -278
- package/skills/grade-paper-writing/prose-lint.test.ts +230 -79
- package/skills/paper-adversarial-review/SKILL.md +2 -2
- package/skills/paper-adversarial-review/SKILL.md.spec.ts +1 -1
- package/skills/paper-pipeline/SKILL.md +9 -12
- package/skills/paper-pipeline/SKILL.md.spec.ts +8 -11
- package/skills/paper-pipeline/description-language.eval.mjs +25 -15
- package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +25 -15
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +25 -15
- package/skills/paper-pipeline/pipeline-firing.eval.mjs +27 -16
- package/skills/paper-pipeline/pipeline-language.eval.mjs +25 -15
- package/skills/paper-pipeline/references/acceptance-gate.md +16 -20
- package/skills/paper-pipeline/references/artifact-checklist.md +4 -5
- package/skills/paper-pipeline/references/pipeline-status-template.md +1 -1
- package/skills/paper-pipeline/scripts/README.md +0 -4
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +91 -105
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +85 -241
- package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +166 -153
- package/skills/paper-pipeline/scripts/generated-code.harness.mjs +3 -3
- package/skills/paper-status/SKILL.md +2 -2
- package/skills/paper-status/SKILL.md.spec.ts +1 -1
- package/skills/render-paper/SKILL.md +2 -2
- package/skills/render-paper/SKILL.md.spec.ts +1 -1
- package/skills/render-paper/extract-pdf-facts.mjs +2 -2
- package/skills/tighten-paper/SKILL.md +29 -42
- package/skills/tighten-paper/SKILL.md.spec.ts +28 -41
- package/skills/verify-citations/SKILL.md +7 -4
- package/skills/verify-citations/SKILL.md.spec.ts +6 -3
- package/skills/verify-citations/scripts/bib-authors.check.test.mjs +17 -32
- package/skills/verify-citations/scripts/bib-authors.cli.test.ts +138 -43
- package/skills/verify-citations/scripts/bib-authors.d.mts +22 -2
- package/skills/verify-citations/scripts/bib-authors.mjs +54 -193
- package/skills/verify-citations/scripts/bib-authors.test.mjs +1 -44
- package/skills/verify-citations/scripts/verify-cites.cli.test.ts +16 -0
- package/skills/verify-citations/scripts/verify-cites.d.mts +19 -3
- package/skills/verify-citations/scripts/verify-cites.mjs +42 -108
- package/skills/verify-citations/scripts/verify-cites.test.mjs +23 -36
- package/src/adapters/bibtex/index.test.ts +163 -0
- package/src/adapters/bibtex/index.ts +155 -0
- package/src/adapters/latex/filecontents.ts +114 -0
- package/src/adapters/latex/includes.ts +18 -10
- package/src/adapters/latex/index.test.ts +73 -0
- package/src/adapters/latex/index.ts +2 -0
- package/src/adapters/latex/nodes.ts +64 -0
- package/src/adapters/latex/parse.test.ts +38 -0
- package/src/adapters/latex/parse.ts +67 -6
- package/src/adapters/memory/index.ts +17 -0
- package/src/adapters/references/index.test.ts +75 -21
- package/src/adapters/references/index.ts +37 -32
- package/src/adapters/sources-record/index.test.ts +87 -0
- package/src/adapters/sources-record/index.ts +82 -0
- package/src/adapters/tex-output/blg.ts +27 -0
- package/src/adapters/tex-output/fls.ts +24 -0
- package/src/adapters/tex-output/index.test.ts +171 -0
- package/src/adapters/tex-output/index.ts +12 -0
- package/src/bib-rules.test.ts +356 -0
- package/src/bib-rules.ts +228 -0
- package/src/bibliography-at.test.ts +286 -0
- package/src/build.harness.mjs +2 -2
- package/src/build.test.ts +211 -1
- package/src/build.ts +101 -41
- package/src/claim-provenance.test.ts +33 -7
- package/src/claim-provenance.ts +2 -2
- package/src/cli.harness.mjs +46 -56
- package/src/cli.test.ts +44 -48
- package/src/cli.ts +51 -82
- package/src/domain/paper-source.test.ts +58 -20
- package/src/domain/paper-source.ts +38 -42
- package/src/domain/paper-sources.ts +94 -0
- package/src/domain/sources-record.test.ts +71 -0
- package/src/domain/sources-record.ts +136 -0
- package/src/domain/tex-run.test.ts +228 -0
- package/src/domain/tex-run.ts +139 -0
- package/src/folder-venue-rule.test.ts +1 -1
- package/src/heading-case.test.ts +24 -10
- package/src/init.test.ts +6 -7
- package/src/init.ts +3 -22
- package/src/latex-log.ts +20 -0
- package/src/latex-loop.harness.ts +1 -0
- package/src/latex-loop.ts +2 -0
- package/src/lint-fix.test.ts +10 -4
- package/src/linted-set.test.ts +9 -1
- package/src/new-paper.harness.mjs +12 -30
- package/src/new-paper.test.ts +15 -11
- package/src/new-paper.ts +11 -30
- package/src/package-dirs.ts +3 -2
- package/src/paper-files.test.ts +5 -4
- package/src/paper-files.ts +4 -4
- package/src/paper-includes.test.ts +157 -38
- package/src/paper-includes.ts +39 -35
- package/src/paper-prose.test.ts +78 -0
- package/src/paper-prose.ts +67 -0
- package/src/paper-record.test.ts +173 -0
- package/src/paper-record.ts +66 -0
- package/src/paper-settings-commands.test.ts +5 -2
- package/src/paper-sources.ts +151 -0
- package/src/parse-args.test.ts +0 -1
- package/src/ports/bib-reader.ts +27 -0
- package/src/ports/check-references.ts +7 -3
- package/src/ports/latex.ts +19 -0
- package/src/ports/sources-codec.ts +11 -0
- package/src/ports/tex-output.ts +17 -0
- package/src/recorded-bibliography.ts +297 -0
- package/src/reference-rules.ts +217 -104
- package/src/references.test.ts +520 -177
- package/src/references.ts +185 -37
- package/src/register-bands.test.ts +5 -1
- package/src/register-bands.ts +3 -5
- package/src/register.test.ts +6 -1
- package/src/register.ts +2 -2
- package/src/rule-cases.test.ts +68 -12
- package/src/rule-docs.test.ts +0 -1
- package/src/sources-record.test.ts +308 -0
- package/src/sources-record.ts +231 -0
- package/src/sources-rules.test.ts +152 -0
- package/src/sources-rules.ts +117 -0
- package/src/structure.harness.ts +5 -11
- package/src/structure.ts +4 -3
- package/src/tex-paper.test.ts +148 -78
- package/src/tex-paper.ts +31 -61
- package/src/tex-venue-rules.test.ts +36 -8
- package/src/tex-venue-rules.ts +3 -5
- package/src/types.ts +0 -2
- package/dist/eslint-rules/bib-reachable-entry.d.ts +0 -22
- package/dist/eslint-rules/bib-reachable-entry.d.ts.map +0 -1
- package/dist/eslint-rules/bib-reachable-entry.js +0 -75
- package/dist/eslint-rules/bib-reachable-entry.js.map +0 -1
- package/eslint-rules/bib-reachable-entry.ts +0 -76
- package/fixtures/real-markdown-paper/baseline.json +0 -22
- package/fixtures/real-markdown-paper/baseline.ts +0 -70
- package/skills/paper-pipeline/PIPELINE-MAP.md +0 -381
- package/skills/paper-pipeline/pipeline-firing.baseline.json +0 -270
- package/skills/paper-pipeline/references/sweep-2026-08-09-ideal-pipeline.md +0 -585
- package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +0 -492
- package/skills/paper-pipeline/scripts/artifact-coverage.mjs +0 -397
- package/skills/paper-pipeline/scripts/artifact-coverage.test.ts +0 -86
- package/skills/paper-pipeline/scripts/check-provenance.mjs +0 -184
- package/skills/paper-pipeline/scripts/population-map.harness.mjs +0 -135
- package/skills/paper-pipeline/scripts/population-map.mjs +0 -188
- package/skills/paper-pipeline/scripts/population-map.test.mjs +0 -154
- package/skills/paper-pipeline/scripts/provenance.harness.mjs +0 -266
- package/skills/paper-pipeline/scripts/round-diff.harness.ts +0 -907
- package/skills/paper-pipeline/scripts/round-diff.mjs +0 -583
- package/skills/paper-pipeline/scripts/round-diff.test.mjs +0 -159
- package/skills/tighten-paper/__snapshots__/structure.test.ts.snap +0 -133
- package/skills/tighten-paper/structure.mjs +0 -364
- package/skills/tighten-paper/structure.test.ts +0 -117
- package/templates/paper/paper.md +0 -4
|
@@ -40,31 +40,12 @@ counter, is not this skill; it is the damage this skill exists to undo.
|
|
|
40
40
|
If you are invoked while the author is still adding material, say so and decline the pass. The right
|
|
41
41
|
answer is "not yet", not a smaller cut.
|
|
42
42
|
|
|
43
|
-
## 🏗 START WITH THE
|
|
43
|
+
## 🏗 START WITH THE SHAPE — weigh every section before judging it
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
🔴 **THE SCRIPT NO LONGER FINDS ANYTHING — it is an INVENTORY (2026-08-26).** All sixteen of its
|
|
51
|
-
checks moved into ESLint rules (\`eslint-rules/paper-structure.mjs\`). The findings you used to read
|
|
52
|
-
here now come from:
|
|
53
|
-
|
|
54
|
-
\`\`\`
|
|
55
|
-
npx eslint --no-config-lookup --config eslint.config.mjs <paper.md>
|
|
56
|
-
\`\`\`
|
|
57
|
-
|
|
58
|
-
⚠️ Do NOT pass \`--flags-only\`. It is kept only because two harnesses assert on its silence, and it
|
|
59
|
-
prints nothing and exits 0 whatever the paper looks like. Measured 26.08 on \`the reference paper\`:
|
|
60
|
-
\`--flags-only\` printed 0 lines while ESLint on the same bytes reported **6 structural findings**
|
|
61
|
-
(subsection-size ×2 · section-lead · free-section-size ×2 · block-ungraded). A mode that cannot
|
|
62
|
-
speak is not a check, and reading its silence as "clean" is the defect this pipeline is named for.
|
|
63
|
-
|
|
64
|
-
What the inventory prints, and why it stays a script: the outline **in order** — every heading, its
|
|
65
|
-
own words, its total with subsections, its share of the body, and the \`carries:\`/\`score:\`/\`verdict:\`
|
|
66
|
-
note the section wrote about itself, printed beside its weight. That comparison is the whole point
|
|
67
|
-
and a linter cannot make it: a finding has nowhere to put a table.
|
|
45
|
+
Write the outline out **in order**: every heading, its own words before its first subsection, its
|
|
46
|
+
total with subsections, its share of the body — and beside each, the \`carries:\`/\`score:\`/\`verdict:\`
|
|
47
|
+
note the section wrote about itself. That comparison, a section's weight printed next to what it
|
|
48
|
+
claims about itself, is the whole point.
|
|
68
49
|
|
|
69
50
|
🔴 **Why this exists, and why word counts alone mislead.** Asked whether a paper short-changed its
|
|
70
51
|
contribution, per-section word counts said no — the contribution had 38% more words than related
|
|
@@ -73,7 +54,7 @@ owned **0, 28 and 38** words before their first subsection. Nothing in the toolc
|
|
|
73
54
|
question, because every metric was per-sentence or per-document and none was per-section. The author's
|
|
74
55
|
word for the result was a "salad" — a jumbled mix — and he was right while the numbers said fine.
|
|
75
56
|
|
|
76
|
-
**What the
|
|
57
|
+
**What the count does NOT decide, and you must:** the ORDER, the NAMES, and whether a section earns
|
|
77
58
|
its place. Those are judgement, and pretending otherwise would be the failure this whole pipeline
|
|
78
59
|
exists to prevent.
|
|
79
60
|
|
|
@@ -194,18 +175,24 @@ called it "fat for no reason" after a pass that cut almost nothing and left it 1
|
|
|
194
175
|
|
|
195
176
|
A verdict that lives in a chat log is gone by the next session, and the next session then re-derives
|
|
196
177
|
it or ignores it. **Every section and subsection gets a one-line verdict comment immediately above its
|
|
197
|
-
heading**, in
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
178
|
+
heading**, in \`paper.tex\`, as LaTeX \`%\` comments, stamped with the date and the skill that
|
|
179
|
+
produced it:
|
|
180
|
+
|
|
181
|
+
\`\`\`latex
|
|
182
|
+
% TIGHTEN 2026-08-06 · score 8/10 · verdict: KEEP · 190w
|
|
183
|
+
% carries: tools get close but none resolves a NAMED rule against a committed config
|
|
184
|
+
% because: it is the only place the tool-ecosystem gap is closed; a reviewer who skips it
|
|
185
|
+
% asks "hasn't this been done" and gets no answer
|
|
186
|
+
\\section{Related tools}
|
|
187
|
+
|
|
188
|
+
% TIGHTEN 2026-08-06 · score 5/10 · verdict: SHORTEN -89w · 210w
|
|
189
|
+
% carries: why a linter for the rules file cannot work
|
|
190
|
+
% because: the point lands in two sentences; the rest re-establishes what §2 already did
|
|
191
|
+
\\subsection{Why not a linter}
|
|
192
|
+
|
|
193
|
+
% TIGHTEN 2026-08-06 · score 2/10 · verdict: MOVE→artifact · 45w
|
|
194
|
+
% carries: nothing the reader must hold to judge the contribution
|
|
195
|
+
\\subsection{Implementation notes}
|
|
209
196
|
\`\`\`
|
|
210
197
|
|
|
211
198
|
🔴 **Before assigning any \`MOVE→\` verdict, read \`paper-pipeline/references/body-vs-appendix.md\`.**
|
|
@@ -230,7 +217,7 @@ written and **not one ever concluded "cut me"**. Author, 2026-08-06, reading the
|
|
|
230
217
|
value on every page and compared nothing.
|
|
231
218
|
|
|
232
219
|
The pairing is the mechanism: **a low score beside a KEEP verdict is a visible contradiction**, and
|
|
233
|
-
|
|
220
|
+
the outline above puts the two side by side. Prose cannot contradict itself; a number can.
|
|
234
221
|
|
|
235
222
|
**Calibration, so the scale does not drift to "everything is an 8":**
|
|
236
223
|
- **9–10** — remove it and a reviewer cannot assess the contribution. The claim, its evidence, its price.
|
|
@@ -285,10 +272,10 @@ Rules for these comments:
|
|
|
285
272
|
unpriced prose accumulates, because nothing there costs a page.
|
|
286
273
|
- **Stale beats absent, but not by much.** A comment whose date precedes the last substantive edit to
|
|
287
274
|
its section is stale; re-grade it rather than trusting it.
|
|
288
|
-
- **A verdict nobody executed is a note, not a decision.**
|
|
289
|
-
|
|
275
|
+
- **A verdict nobody executed is a note, not a decision.** List the blocks still carrying a
|
|
276
|
+
non-KEEP verdict — a paper full of unexecuted CUTs is worse than one never graded,
|
|
290
277
|
because it reads as decided.
|
|
291
|
-
- They are working notes:
|
|
278
|
+
- They are working notes: a \`%\` comment never reaches the PDF.
|
|
292
279
|
- The chat summary still gets written — but it is the *derivative*, and the paper is the record.
|
|
293
280
|
|
|
294
281
|
## 🧱 Enforce subsections — a section without them is a wall
|
|
@@ -4,7 +4,7 @@ description: Verify every citation is a real work with correct metadata, and tha
|
|
|
4
4
|
allowed-tools: [Read, Write, Edit, Grep, Glob, Bash, WebSearch, WebFetch, Agent]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
<!-- vigiles:sha256:
|
|
7
|
+
<!-- vigiles:sha256:fdb023ed7c613e1b compiled from skills/verify-citations/SKILL.md.spec.ts -->
|
|
8
8
|
|
|
9
9
|
# verify-citations — every cite real, the delta explicit
|
|
10
10
|
|
|
@@ -165,10 +165,13 @@ heavier than a typo, and it is exactly the class §"any regalia about third part
|
|
|
165
165
|
|
|
166
166
|
**How to run.**
|
|
167
167
|
```
|
|
168
|
-
node scripts/bib-authors.mjs <paper-dir> # or a .bib, or a .tex
|
|
168
|
+
node scripts/bib-authors.mjs <paper-dir> # the databases bibtex opened in the last build; or a .bib, or a .tex
|
|
169
169
|
node scripts/bib-authors.mjs <paper-dir> --json
|
|
170
170
|
```
|
|
171
|
-
Exits **1** on any author-set or author-order disagreement, **0** when clean, **2** on usage/IO error.
|
|
171
|
+
Exits **1** on any author-set or author-order disagreement, **0** when clean, **2** on usage/IO error. A paper
|
|
172
|
+
directory or a `.tex` is read through the build's record (`_build/sources.json`): a paper that was never built,
|
|
173
|
+
or changed since, is refused (exit 2) and told to run `npx paperlint build` first — the databases are TeX's answer,
|
|
174
|
+
not a reading of the source. A `.bib` named is read as itself.
|
|
172
175
|
|
|
173
176
|
**Source is DBLP, and that choice is the evidence.** DBLP indexes the proceedings record and the CoRR record
|
|
174
177
|
**separately**, so a single query returns both lists side by side — the cleanest proof available that the
|
|
@@ -244,7 +247,7 @@ It used to be one script, `verify-refs.mjs`; the measurement half stayed a scrip
|
|
|
244
247
|
became twelve ESLint rules over the facts it writes.
|
|
245
248
|
|
|
246
249
|
```
|
|
247
|
-
# 1. measure: parse the bibliography, ask CrossRef/arXiv, write _build/refs.facts.json
|
|
250
|
+
# 1. measure: parse the bibliography bibtex opened (build the paper first), ask CrossRef/arXiv, write _build/refs.facts.json
|
|
248
251
|
node .claude/skills/paper-pipeline/scripts/extract-ref-facts.mjs <paper-dir>
|
|
249
252
|
node .claude/skills/paper-pipeline/scripts/extract-ref-facts.mjs <paper-dir> --offline # cached responses only
|
|
250
253
|
|
|
@@ -181,10 +181,13 @@ heavier than a typo, and it is exactly the class §"any regalia about third part
|
|
|
181
181
|
|
|
182
182
|
**How to run.**
|
|
183
183
|
\`\`\`
|
|
184
|
-
node scripts/bib-authors.mjs <paper-dir> # or a .bib, or a .tex
|
|
184
|
+
node scripts/bib-authors.mjs <paper-dir> # the databases bibtex opened in the last build; or a .bib, or a .tex
|
|
185
185
|
node scripts/bib-authors.mjs <paper-dir> --json
|
|
186
186
|
\`\`\`
|
|
187
|
-
Exits **1** on any author-set or author-order disagreement, **0** when clean, **2** on usage/IO error.
|
|
187
|
+
Exits **1** on any author-set or author-order disagreement, **0** when clean, **2** on usage/IO error. A paper
|
|
188
|
+
directory or a \`.tex\` is read through the build's record (\`_build/sources.json\`): a paper that was never built,
|
|
189
|
+
or changed since, is refused (exit 2) and told to run \`npx paperlint build\` first — the databases are TeX's answer,
|
|
190
|
+
not a reading of the source. A \`.bib\` named is read as itself.
|
|
188
191
|
|
|
189
192
|
**Source is DBLP, and that choice is the evidence.** DBLP indexes the proceedings record and the CoRR record
|
|
190
193
|
**separately**, so a single query returns both lists side by side — the cleanest proof available that the
|
|
@@ -260,7 +263,7 @@ It used to be one script, \`verify-refs.mjs\`; the measurement half stayed a scr
|
|
|
260
263
|
became twelve ESLint rules over the facts it writes.
|
|
261
264
|
|
|
262
265
|
\`\`\`
|
|
263
|
-
# 1. measure: parse the bibliography, ask CrossRef/arXiv, write _build/refs.facts.json
|
|
266
|
+
# 1. measure: parse the bibliography bibtex opened (build the paper first), ask CrossRef/arXiv, write _build/refs.facts.json
|
|
264
267
|
node .claude/skills/paper-pipeline/scripts/extract-ref-facts.mjs <paper-dir>
|
|
265
268
|
node .claude/skills/paper-pipeline/scripts/extract-ref-facts.mjs <paper-dir> --offline # cached responses only
|
|
266
269
|
|
|
@@ -13,9 +13,15 @@ import {
|
|
|
13
13
|
checkAuthors,
|
|
14
14
|
claimsPublished,
|
|
15
15
|
dblpHits,
|
|
16
|
-
|
|
16
|
+
authorEntries,
|
|
17
17
|
surnames,
|
|
18
18
|
} from "./bib-authors.mjs";
|
|
19
|
+
import { bibReader } from "#src/adapters/bibtex/index";
|
|
20
|
+
import { absolutePath } from "#src/domain/paths";
|
|
21
|
+
|
|
22
|
+
/** This check's entries of a BibTeX text, read by the one bibtex reader. */
|
|
23
|
+
const entriesIn = (bib) =>
|
|
24
|
+
authorEntries(bibReader.readFile(absolutePath("/p/refs.bib"), bib).entries);
|
|
19
25
|
|
|
20
26
|
afterEach(() => vi.unstubAllGlobals());
|
|
21
27
|
|
|
@@ -50,15 +56,6 @@ test("checkAuthors sorts every kind of entry into its bucket", async () => {
|
|
|
50
56
|
entry("nomatch", "Ada Lovelace", "Nobody Has This"),
|
|
51
57
|
entry("onlycorr", "Ada Lovelace", "Only On CoRR"),
|
|
52
58
|
entry("down", "Ada Lovelace", "Service Down"),
|
|
53
|
-
{
|
|
54
|
-
type: "mdref",
|
|
55
|
-
key: "sw",
|
|
56
|
-
unparsed: true,
|
|
57
|
-
author: "",
|
|
58
|
-
title: "",
|
|
59
|
-
booktitle: "",
|
|
60
|
-
journal: "",
|
|
61
|
-
},
|
|
62
59
|
];
|
|
63
60
|
const answers = {
|
|
64
61
|
"Same Paper": [hit("Same Paper.", ["Ada Lovelace", "Alan Turing"])],
|
|
@@ -101,10 +98,6 @@ test("checkAuthors sorts every kind of entry into its bucket", async () => {
|
|
|
101
98
|
},
|
|
102
99
|
],
|
|
103
100
|
skipped: [
|
|
104
|
-
{
|
|
105
|
-
key: "sw",
|
|
106
|
-
why: "no author/title — a reference to software or a dataset, DBLP does not apply",
|
|
107
|
-
},
|
|
108
101
|
{
|
|
109
102
|
key: "etal",
|
|
110
103
|
why: "author list ends in `and others` — completeness not checkable",
|
|
@@ -228,35 +221,27 @@ test("dblpHits: no hits is an empty list; 429 is retryable; other errors are not
|
|
|
228
221
|
);
|
|
229
222
|
});
|
|
230
223
|
|
|
231
|
-
test("
|
|
224
|
+
test("authorEntries: names written family first (`and others` kept), a quoted value, a field that is absent", () => {
|
|
232
225
|
assert.deepEqual(
|
|
233
|
-
|
|
234
|
-
'@article{k, author = "Ada Lovelace", title = {
|
|
226
|
+
entriesIn(
|
|
227
|
+
'@article{k, author = "Ada Lovelace and van der Berg, Jan and others", title = {The {BERT} Model}, journal = {J}}\n@misc{untitled, author = {Hopper, Grace}}',
|
|
235
228
|
),
|
|
236
229
|
[
|
|
237
230
|
{
|
|
238
231
|
type: "article",
|
|
239
232
|
key: "k",
|
|
240
|
-
author: "Ada
|
|
241
|
-
title: "
|
|
233
|
+
author: "Lovelace, Ada and van der Berg, Jan and others",
|
|
234
|
+
title: "The BERT Model",
|
|
242
235
|
booktitle: "",
|
|
243
236
|
journal: "J",
|
|
244
237
|
},
|
|
245
|
-
],
|
|
246
|
-
);
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
test("parseBib: braces nested inside a value, and a bare last value with no comma after it", () => {
|
|
250
|
-
assert.deepEqual(
|
|
251
|
-
parseBib("@article{k, title = {The {BERT} Model}, journal = J}"),
|
|
252
|
-
[
|
|
253
238
|
{
|
|
254
|
-
type: "
|
|
255
|
-
key: "
|
|
256
|
-
author: "",
|
|
257
|
-
title: "
|
|
239
|
+
type: "misc",
|
|
240
|
+
key: "untitled",
|
|
241
|
+
author: "Hopper, Grace",
|
|
242
|
+
title: "",
|
|
258
243
|
booktitle: "",
|
|
259
|
-
journal: "
|
|
244
|
+
journal: "",
|
|
260
245
|
},
|
|
261
246
|
],
|
|
262
247
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* bib-authors.mjs as a process:
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* bib-authors.mjs as a process: the bibliography it reads (a .bib named, or what the last build's
|
|
3
|
+
* bibtex opened for a paper directory or a .tex — `_build/sources.json`, through `bibliographyAt`), the
|
|
4
|
+
* targets it refuses, and the report and exit code for each outcome. DBLP is a preload that answers
|
|
5
5
|
* by title from a table (the documented `result.hits.hit[].info` shape) — no network.
|
|
6
6
|
*/
|
|
7
7
|
import assert from "node:assert/strict";
|
|
@@ -15,6 +15,13 @@ import {
|
|
|
15
15
|
useTempDir,
|
|
16
16
|
writeTree,
|
|
17
17
|
} from "../../../test/support.ts";
|
|
18
|
+
import {
|
|
19
|
+
plantedOnDisk,
|
|
20
|
+
recordOnDisk,
|
|
21
|
+
type Shape,
|
|
22
|
+
} from "../../../test/recorded-fixture.ts";
|
|
23
|
+
import { absolutePath } from "#src/domain/paths";
|
|
24
|
+
import { bibliographyUnreadWhy } from "#src/paper-sources";
|
|
18
25
|
|
|
19
26
|
const SCRIPT = join(dirname(fileURLToPath(import.meta.url)), "bib-authors.mjs");
|
|
20
27
|
const root = useTempDir("bib-authors-cli-");
|
|
@@ -32,6 +39,10 @@ const bib = (
|
|
|
32
39
|
) =>
|
|
33
40
|
`@inproceedings{${key}, author = {${author}}, title = {${title}}, ${venue}}\n`;
|
|
34
41
|
|
|
42
|
+
/** A .tex that declares the database `name`, with `preamble` before its document. */
|
|
43
|
+
const declaring = (name: string, preamble = "") =>
|
|
44
|
+
`\\documentclass{article}\n${preamble}\\begin{document}x\\bibliography{${name}}\\end{document}\n`;
|
|
45
|
+
|
|
35
46
|
writeTree(root, {
|
|
36
47
|
// The retry backoff (1.5 s, then 3 s) and the 900 ms courtesy pause are real sleeps in the
|
|
37
48
|
// script. Under CPU load they made "DBLP down" take 11.7 s once and time out. The preload
|
|
@@ -46,22 +57,53 @@ writeTree(root, {
|
|
|
46
57
|
" const hit = (answers[q] ?? []).map((authors) => ({ info: { title: q, venue: 'X', year: '2024',\n" +
|
|
47
58
|
" type: 'Conference and Workshop Papers', authors: { author: authors.map((text) => ({ text })) } } }));\n" +
|
|
48
59
|
" return new Response(JSON.stringify({ result: { hits: { hit } } }), { status: 200 });\n};\n",
|
|
60
|
+
"findings/paper.tex": declaring("paper"),
|
|
49
61
|
"findings/paper.bib":
|
|
50
62
|
bib("dropped", "Ada Lovelace and Grace Hopper", "Dropped Author") +
|
|
51
63
|
bib("order", "Alan Turing and Ada Lovelace", "Swapped Order"),
|
|
52
64
|
"findings/other.bib": bib("never", "X", "Never Read"),
|
|
53
65
|
"twotex/b.tex": "\\documentclass{article}\n",
|
|
54
|
-
"twotex/
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
66
|
+
"twotex/paper.tex": declaring(
|
|
67
|
+
"refs",
|
|
68
|
+
"\\begin{filecontents*}[overwrite]{refs.bib}\n" +
|
|
69
|
+
bib("pre", "Ada Lovelace", "A Preprint", "journal = {arXiv preprint}") +
|
|
70
|
+
"\\end{filecontents*}\n",
|
|
71
|
+
),
|
|
72
|
+
// What TeX wrote from that block: the file bibtex opened.
|
|
73
|
+
"twotex/refs.bib": bib(
|
|
74
|
+
"pre",
|
|
75
|
+
"Ada Lovelace",
|
|
76
|
+
"A Preprint",
|
|
77
|
+
"journal = {arXiv preprint}",
|
|
78
|
+
),
|
|
79
|
+
"sibling/paper.tex": declaring("refs"),
|
|
59
80
|
"sibling/refs.bib": bib("good", "Ada Lovelace", "All Good"),
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
|
|
81
|
+
"etal/paper.tex": declaring("refs"),
|
|
82
|
+
"etal/notes.txt": "not a bibliography\n",
|
|
83
|
+
"etal/refs.bib":
|
|
84
|
+
bib("good", "Ada Lovelace", "All Good") +
|
|
85
|
+
bib("etal", "Ada Lovelace and others", "Truncated"),
|
|
86
|
+
"down/paper.tex": declaring("refs"),
|
|
63
87
|
"down/refs.bib": bib("down", "Ada Lovelace", "Service Down"),
|
|
64
|
-
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
/** The record of a paper whose bibliography is `db`, a file the author keeps. */
|
|
91
|
+
const keeps = (db: string): Shape => ({
|
|
92
|
+
inputs: [
|
|
93
|
+
["paper.tex", "body"],
|
|
94
|
+
[db, "preamble"],
|
|
95
|
+
],
|
|
96
|
+
databases: [db],
|
|
97
|
+
});
|
|
98
|
+
recordOnDisk(join(root, "findings"), keeps("paper.bib"));
|
|
99
|
+
recordOnDisk(join(root, "sibling"), keeps("refs.bib"));
|
|
100
|
+
recordOnDisk(join(root, "etal"), keeps("refs.bib"));
|
|
101
|
+
recordOnDisk(join(root, "down"), keeps("refs.bib"));
|
|
102
|
+
// A block TeX wrote into refs.bib: the file is no input, the block is.
|
|
103
|
+
recordOnDisk(join(root, "twotex"), {
|
|
104
|
+
inputs: [["paper.tex", "body"]],
|
|
105
|
+
written: ["refs.bib"],
|
|
106
|
+
databases: ["refs.bib"],
|
|
65
107
|
});
|
|
66
108
|
|
|
67
109
|
const run = (...args: string[]) => {
|
|
@@ -75,14 +117,16 @@ const run = (...args: string[]) => {
|
|
|
75
117
|
};
|
|
76
118
|
};
|
|
77
119
|
|
|
78
|
-
test("refusals: no argument
|
|
120
|
+
test("refusals: no argument; a path with no bibliography to read, in bibliographyAt's words", () => {
|
|
121
|
+
// The path → bibliography vocabulary is tested once, beside bibliographyAt (src/paper-sources.ts).
|
|
122
|
+
// Here: that the command speaks it, and exits 2.
|
|
123
|
+
const notes = join(root, "etal", "notes.txt");
|
|
79
124
|
assert.deepEqual(
|
|
80
|
-
[
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
].map(({ status, stdout, stderr }) => ({ status, stdout, stderr })),
|
|
125
|
+
[run(), run(notes)].map(({ status, stdout, stderr }) => ({
|
|
126
|
+
status,
|
|
127
|
+
stdout,
|
|
128
|
+
stderr,
|
|
129
|
+
})),
|
|
86
130
|
[
|
|
87
131
|
{
|
|
88
132
|
status: 2,
|
|
@@ -93,24 +137,13 @@ test("refusals: no argument, no such path, a directory with nothing to read, a .
|
|
|
93
137
|
{
|
|
94
138
|
status: 2,
|
|
95
139
|
stdout: "",
|
|
96
|
-
stderr:
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
status: 2,
|
|
100
|
-
stdout: "",
|
|
101
|
-
stderr: "bib-authors: no .bib, .tex or paper.md in <root>/empty\n",
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
status: 2,
|
|
105
|
-
stdout: "",
|
|
106
|
-
stderr:
|
|
107
|
-
"bib-authors: no bibliography found in <root>/nobib/paper.tex (no filecontents block, no refs.bib beside it)\n",
|
|
140
|
+
stderr: `bib-authors: ${bibliographyUnreadWhy({ kind: "not-bib-or-tex", path: absolutePath(notes) }).replaceAll(root, "<root>")}\n`,
|
|
108
141
|
},
|
|
109
142
|
],
|
|
110
143
|
);
|
|
111
144
|
});
|
|
112
145
|
|
|
113
|
-
test("a paper directory: the
|
|
146
|
+
test("a paper directory: the paper.bib its \\bibliography{paper} declares is read, and each difference is printed — FAIL, exit 1", () => {
|
|
114
147
|
assert.deepEqual(run(join(root, "findings")), {
|
|
115
148
|
status: 1,
|
|
116
149
|
stderr: "",
|
|
@@ -133,16 +166,16 @@ test("a paper directory: the canonical paper.bib is read, and each difference is
|
|
|
133
166
|
});
|
|
134
167
|
});
|
|
135
168
|
|
|
136
|
-
test("
|
|
137
|
-
const r = run(join(root, "twotex"), "--json");
|
|
169
|
+
test("a .tex named is its folder's paper: the database bibtex opened is read from the block that wrote it", () => {
|
|
170
|
+
const r = run(join(root, "twotex", "paper.tex"), "--json");
|
|
138
171
|
assert.deepEqual(
|
|
139
172
|
{ status: r.status, stderr: r.stderr, report: parseJson(r.stdout) },
|
|
140
173
|
{
|
|
141
174
|
status: 0,
|
|
142
|
-
stderr:
|
|
143
|
-
"⚠️ <root>/twotex holds several .tex files and no canonical paper.tex: a.tex, b.tex — took a.tex. If that is the wrong one, name the file explicitly.\n",
|
|
175
|
+
stderr: "",
|
|
144
176
|
report: {
|
|
145
|
-
file: "<root>/twotex/
|
|
177
|
+
file: "<root>/twotex/paper.tex",
|
|
178
|
+
files: ["<root>/twotex/paper.tex"],
|
|
146
179
|
entries: 1,
|
|
147
180
|
findings: [],
|
|
148
181
|
skipped: [],
|
|
@@ -152,7 +185,18 @@ test("two .tex files and no paper.tex: the first by name, said out loud; its fil
|
|
|
152
185
|
);
|
|
153
186
|
});
|
|
154
187
|
|
|
155
|
-
test("a .
|
|
188
|
+
test("a .bib named is read as itself", () => {
|
|
189
|
+
const r = run(join(root, "findings", "paper.bib"), "--json");
|
|
190
|
+
const report: unknown = parseJson(r.stdout);
|
|
191
|
+
assert.deepEqual(
|
|
192
|
+
report !== null && typeof report === "object" && "files" in report
|
|
193
|
+
? report.files
|
|
194
|
+
: report,
|
|
195
|
+
["<root>/findings/paper.bib"],
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("a .tex that declares refs and has no block reads refs.bib — PASS, exit 0", () => {
|
|
156
200
|
const r = run(join(root, "sibling", "paper.tex"));
|
|
157
201
|
assert.deepEqual(r, {
|
|
158
202
|
status: 0,
|
|
@@ -162,15 +206,15 @@ test("a .tex with no filecontents reads the refs.bib beside it — PASS, exit 0"
|
|
|
162
206
|
});
|
|
163
207
|
});
|
|
164
208
|
|
|
165
|
-
test("
|
|
166
|
-
const r = run(join(root, "
|
|
209
|
+
test("an `and others` entry is printed as not applicable, not dropped", () => {
|
|
210
|
+
const r = run(join(root, "etal"));
|
|
167
211
|
assert.deepEqual(r, {
|
|
168
212
|
status: 0,
|
|
169
213
|
stderr: "",
|
|
170
214
|
stdout:
|
|
171
|
-
"== bib-authors: <root>/
|
|
172
|
-
" · n/a
|
|
173
|
-
"--
|
|
215
|
+
"== bib-authors: <root>/etal/refs.bib ==\n" +
|
|
216
|
+
" · n/a etal — author list ends in `and others` — completeness not checkable\n\n" +
|
|
217
|
+
"-- 2 entries · 0 difference(s) · 1 not applicable · 0 NOT CHECKED\nPASS: no author-list disagreement.\n",
|
|
174
218
|
});
|
|
175
219
|
});
|
|
176
220
|
|
|
@@ -186,3 +230,54 @@ test("DBLP down: NOT CHECKED, PARTIAL, exit 2 — never a pass", () => {
|
|
|
186
230
|
"PARTIAL: no disagreement among the entries reached, but 1 could not be checked — this is NOT a pass.\n",
|
|
187
231
|
});
|
|
188
232
|
});
|
|
233
|
+
|
|
234
|
+
// The planted papers of fixtures/paper-sources, laid out as a build leaves them: the file read is the
|
|
235
|
+
// one bibtex opened (tex-truth.json), or the block that wrote it.
|
|
236
|
+
test.each([
|
|
237
|
+
["v1-stale", "refs.bib"],
|
|
238
|
+
["v2-overwrite", "paper.tex"],
|
|
239
|
+
["v3-declared", "paper.bib"],
|
|
240
|
+
["v4-commented", "refs.bib"],
|
|
241
|
+
])("%s: reads %s", (paper, file) => {
|
|
242
|
+
const dir = join(root, "planted", paper);
|
|
243
|
+
plantedOnDisk(dir, paper);
|
|
244
|
+
const r = runNode(SCRIPT, [dir, "--json"], {
|
|
245
|
+
nodeArgs: ["--import", join(root, "dblp.mjs")],
|
|
246
|
+
});
|
|
247
|
+
const report: unknown = parseJson(r.stdout);
|
|
248
|
+
assert.deepEqual(
|
|
249
|
+
report !== null && typeof report === "object" && "files" in report
|
|
250
|
+
? report.files
|
|
251
|
+
: report,
|
|
252
|
+
[join(dir, file)],
|
|
253
|
+
);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("🔴 a paper that was not built is refused — run `npx paperlint build` first — and so is one changed since", () => {
|
|
257
|
+
const unbuilt = join(root, "unbuilt");
|
|
258
|
+
writeTree(unbuilt, {
|
|
259
|
+
"paper.tex": declaring("refs"),
|
|
260
|
+
"refs.bib": bib("good", "Ada Lovelace", "All Good"),
|
|
261
|
+
});
|
|
262
|
+
assert.deepEqual(
|
|
263
|
+
[run(unbuilt), run(join(unbuilt, "paper.tex"))].map(
|
|
264
|
+
({ status, stdout, stderr }) => ({ status, stdout, stderr }),
|
|
265
|
+
),
|
|
266
|
+
Array.from({ length: 2 }, () => ({
|
|
267
|
+
status: 2,
|
|
268
|
+
stdout: "",
|
|
269
|
+
stderr:
|
|
270
|
+
"bib-authors: <root>/unbuilt has not been built — run `npx paperlint build` first, which records the databases bibtex reads\n",
|
|
271
|
+
})),
|
|
272
|
+
);
|
|
273
|
+
recordOnDisk(unbuilt, keeps("refs.bib"));
|
|
274
|
+
writeTree(unbuilt, {
|
|
275
|
+
"refs.bib": bib("good", "Ada Lovelace", "All Good Edited"),
|
|
276
|
+
});
|
|
277
|
+
assert.deepEqual(run(unbuilt), {
|
|
278
|
+
status: 2,
|
|
279
|
+
stdout: "",
|
|
280
|
+
stderr:
|
|
281
|
+
"bib-authors: <root>/unbuilt changed since the last build (refs.bib edited) — run `npx paperlint build` first\n",
|
|
282
|
+
});
|
|
283
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Types for bib-authors.mjs, for the exports `src/` imports — the same arrangement as
|
|
2
2
|
// `verify-cites.d.mts` beside it. Only what `src/` imports is declared.
|
|
3
3
|
|
|
4
|
-
/** One entry as `
|
|
4
|
+
/** One entry as `authorEntries` returns it; a field the entry lacks is "". */
|
|
5
5
|
export interface BibEntry {
|
|
6
6
|
readonly type: string;
|
|
7
7
|
readonly key: string;
|
|
@@ -11,7 +11,27 @@ export interface BibEntry {
|
|
|
11
11
|
readonly journal: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
/** A name of a name list, in its parts (`BibName`). */
|
|
15
|
+
export interface ReadName {
|
|
16
|
+
readonly lastName?: string;
|
|
17
|
+
readonly firstName?: string;
|
|
18
|
+
readonly prefix?: string;
|
|
19
|
+
readonly suffix?: string;
|
|
20
|
+
readonly name?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** An entry as the bibtex reader reads it (src/domain/paper-sources.ts), as far as this check uses it. */
|
|
24
|
+
export interface ReadEntry {
|
|
25
|
+
readonly type: string;
|
|
26
|
+
readonly key: string;
|
|
27
|
+
readonly fields: Readonly<Record<string, string>>;
|
|
28
|
+
readonly names: Readonly<Record<string, readonly ReadName[]>>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The entries this check reads, from the bibtex reader's. */
|
|
32
|
+
export declare function authorEntries(
|
|
33
|
+
entries: readonly ReadEntry[],
|
|
34
|
+
): BibEntry[];
|
|
15
35
|
|
|
16
36
|
/** True when the author list ends in `and others` — there is nothing complete to compare. */
|
|
17
37
|
export declare const truncated: (authorField: string) => boolean;
|