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
package/src/build.ts
CHANGED
|
@@ -66,7 +66,19 @@ import type { Runner } from "./engine.ts";
|
|
|
66
66
|
import type { Files } from "./ports/files.ts";
|
|
67
67
|
import type { MeasureGeometry } from "./ports/measure-geometry.ts";
|
|
68
68
|
import type { CheckReferences } from "./ports/check-references.ts";
|
|
69
|
-
import {
|
|
69
|
+
import {
|
|
70
|
+
notWiredRecord,
|
|
71
|
+
recordingNote,
|
|
72
|
+
type RecordSources,
|
|
73
|
+
type TexRun,
|
|
74
|
+
} from "./sources-record.ts";
|
|
75
|
+
import { JOBNAME } from "./domain/sources-record.ts";
|
|
76
|
+
import {
|
|
77
|
+
notWiredBibliography,
|
|
78
|
+
recordReferences,
|
|
79
|
+
REFERENCES_FILE,
|
|
80
|
+
type ReadBibliography,
|
|
81
|
+
} from "./references.ts";
|
|
70
82
|
import {
|
|
71
83
|
auxBib,
|
|
72
84
|
bibtexExcerpt,
|
|
@@ -89,16 +101,11 @@ import { messageOf, printed } from "./domain/text.ts";
|
|
|
89
101
|
const PIPED: ["ignore", "pipe", "pipe"] = ["ignore", "pipe", "pipe"];
|
|
90
102
|
|
|
91
103
|
/** A directory counts as a paper by the same markers as `structure.ts` — one shared dictionary. */
|
|
92
|
-
export const PAPER_MARKERS = [
|
|
93
|
-
"PIPELINE-STATUS.md",
|
|
94
|
-
"paper.tex",
|
|
95
|
-
"paper.md",
|
|
96
|
-
CONFIG_FILE,
|
|
97
|
-
];
|
|
104
|
+
export const PAPER_MARKERS = ["PIPELINE-STATUS.md", "paper.tex", CONFIG_FILE];
|
|
98
105
|
|
|
99
106
|
/** The source paperlint compiles, and the job name every output file carries. */
|
|
100
107
|
export const MAIN = "paper.tex";
|
|
101
|
-
export const JOB =
|
|
108
|
+
export const JOB = JOBNAME;
|
|
102
109
|
|
|
103
110
|
/**
|
|
104
111
|
* Scripts that `paperlint build` USED to run. Their presence is reported and nothing more: running a
|
|
@@ -106,11 +113,16 @@ export const JOB = "paper";
|
|
|
106
113
|
*/
|
|
107
114
|
export const IGNORED_SCRIPTS = ["build.sh", "repro/build-submission.sh"];
|
|
108
115
|
|
|
109
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* The pdflatex flags: never stop for input, stop at the first error, say file:line, and write
|
|
118
|
+
* `paper.fls` — every file the pass opened, which is how the build learns what the paper is made of
|
|
119
|
+
* (`sources-record.ts`) without reading its TeX.
|
|
120
|
+
*/
|
|
110
121
|
export const PDFLATEX_FLAGS = [
|
|
111
122
|
"-interaction=nonstopmode",
|
|
112
123
|
"-halt-on-error",
|
|
113
124
|
"-file-line-error",
|
|
125
|
+
"-recorder",
|
|
114
126
|
];
|
|
115
127
|
|
|
116
128
|
/** Everything the steps may know about a paper — parsed from it, not configured. */
|
|
@@ -143,6 +155,16 @@ export interface BuildContext {
|
|
|
143
155
|
readonly files: Files;
|
|
144
156
|
/** The online reference checks; the CLI wires the real ones, a test passes a function. */
|
|
145
157
|
readonly checkReferences: CheckReferences;
|
|
158
|
+
/**
|
|
159
|
+
* The databases the build's bibtex opened (`readBibliography`, from `_build/sources.json`); the CLI
|
|
160
|
+
* wires the real reader.
|
|
161
|
+
*/
|
|
162
|
+
readonly readBibliography: ReadBibliography;
|
|
163
|
+
/**
|
|
164
|
+
* Writes `_build/sources.json` from TeX's own files after a compile; the CLI wires the real one.
|
|
165
|
+
* `buildPapers` hands the step `notWiredRecord` when it is given none: it records nothing and says so.
|
|
166
|
+
*/
|
|
167
|
+
readonly recordSources: RecordSources;
|
|
146
168
|
}
|
|
147
169
|
|
|
148
170
|
export type StepOutcome =
|
|
@@ -270,7 +292,6 @@ export const inputsStep: BuildStep = {
|
|
|
270
292
|
yes: true,
|
|
271
293
|
why: `TEXINPUTS += the paper's directory, then ${texInputsDir()}`,
|
|
272
294
|
}),
|
|
273
|
-
// The same search path the lint rules read includes through: one answer to which file is meant.
|
|
274
295
|
// A pass runs IN the paper's directory, so that directory is `.` here. TeX names a file it finds
|
|
275
296
|
// through an absolute entry by that absolute path: with the directory itself on TEXINPUTS, the
|
|
276
297
|
// `broken` build fixture's log said `/tmp/…/paper.tex:6:` instead of `./paper.tex:6:` (measured).
|
|
@@ -379,6 +400,7 @@ function latexPass(
|
|
|
379
400
|
after: hashes(ctx.paperDir),
|
|
380
401
|
markers: logMarkers(log),
|
|
381
402
|
bib: bibInput(ctx.paperDir),
|
|
403
|
+
fls: readOr(join(ctx.paperDir, `${JOB}.fls`), "utf8"),
|
|
382
404
|
errorLines:
|
|
383
405
|
exitCode === 0
|
|
384
406
|
? []
|
|
@@ -425,28 +447,47 @@ function bibtexPass(ctx: BuildContext, opts: SpawnOptions): Observation | null {
|
|
|
425
447
|
};
|
|
426
448
|
}
|
|
427
449
|
|
|
450
|
+
/** What the loop's passes left for the record of the build's sources (`sources-record.ts`). */
|
|
451
|
+
function runOf(history: readonly Observation[]): TexRun {
|
|
452
|
+
const bibtexRuns = history.flatMap((o) =>
|
|
453
|
+
o.step === "bibtex" ? [o.exitCode] : [],
|
|
454
|
+
);
|
|
455
|
+
return {
|
|
456
|
+
fls: history.flatMap((o) =>
|
|
457
|
+
o.step === "latex" && o.fls !== null ? [o.fls] : [],
|
|
458
|
+
),
|
|
459
|
+
bibtexExit: bibtexRuns.at(-1) ?? null,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
428
463
|
/** Run the loop until `nextStep` says done or fail. */
|
|
429
464
|
export function compile(ctx: BuildContext): {
|
|
430
465
|
end: Terminal;
|
|
431
466
|
latex: number;
|
|
432
467
|
bibtex: number;
|
|
468
|
+
run: TexRun;
|
|
433
469
|
} {
|
|
434
470
|
const history: Observation[] = [];
|
|
435
471
|
const opts = spawnOptions(ctx);
|
|
436
|
-
// Every
|
|
437
|
-
// paper.log would otherwise have an earlier build's error quoted as this one's
|
|
438
|
-
|
|
472
|
+
// Every file this loop reads is one its own pdflatex wrote. A pdflatex that dies before opening
|
|
473
|
+
// paper.log would otherwise have an earlier build's error quoted as this one's, and one that wrote
|
|
474
|
+
// no paper.fls would hand the record the files an earlier build read.
|
|
475
|
+
const remove = (ext: string): void => {
|
|
476
|
+
rmSync(join(ctx.paperDir, `${JOB}.${ext}`), { force: true });
|
|
477
|
+
};
|
|
478
|
+
remove("log");
|
|
479
|
+
remove("fls");
|
|
439
480
|
const runs = { latex: 0, bibtex: 0 };
|
|
481
|
+
const finish = (end: Terminal) => ({ end, ...runs, run: runOf(history) });
|
|
440
482
|
for (;;) {
|
|
441
483
|
const step = nextStep(summarize(history));
|
|
442
|
-
if (step.kind === "done" || step.kind === "fail")
|
|
443
|
-
return { end: step, ...runs };
|
|
484
|
+
if (step.kind === "done" || step.kind === "fail") return finish(step);
|
|
444
485
|
runs[step.kind]++;
|
|
445
486
|
const seen =
|
|
446
487
|
step.kind === "latex"
|
|
447
488
|
? latexPass(ctx, step.final, opts)
|
|
448
489
|
: bibtexPass(ctx, opts);
|
|
449
|
-
if (seen === null) return
|
|
490
|
+
if (seen === null) return finish(cannotStart(step.kind));
|
|
450
491
|
history.push(seen);
|
|
451
492
|
}
|
|
452
493
|
}
|
|
@@ -473,7 +514,7 @@ export const compileStep: BuildStep = {
|
|
|
473
514
|
return { yes: true, why: `${facts.main} (${cls}${venue})` };
|
|
474
515
|
},
|
|
475
516
|
run: (ctx) => {
|
|
476
|
-
const { end, latex, bibtex } = compile(ctx);
|
|
517
|
+
const { end, latex, bibtex, run } = compile(ctx);
|
|
477
518
|
if (end.kind === "fail") {
|
|
478
519
|
const bin = end.step === "latex" ? "pdflatex" : "bibtex";
|
|
479
520
|
const where =
|
|
@@ -509,7 +550,7 @@ export const compileStep: BuildStep = {
|
|
|
509
550
|
: "";
|
|
510
551
|
return {
|
|
511
552
|
ok: true,
|
|
512
|
-
note: `${plural(latex, "pdflatex pass", "pdflatex passes")}, ${plural(bibtex, "bibtex run", "bibtex runs")}${warn}`,
|
|
553
|
+
note: `${plural(latex, "pdflatex pass", "pdflatex passes")}, ${plural(bibtex, "bibtex run", "bibtex runs")}${warn}; ${recordingNote(ctx.recordSources(ctx.paperDir, run))}`,
|
|
513
554
|
};
|
|
514
555
|
},
|
|
515
556
|
};
|
|
@@ -590,7 +631,12 @@ export const referencesStep: BuildStep = {
|
|
|
590
631
|
: { yes: false, why: "nothing is compiled" },
|
|
591
632
|
run: async (ctx) => ({
|
|
592
633
|
ok: true,
|
|
593
|
-
note: await recordReferences(
|
|
634
|
+
note: await recordReferences(
|
|
635
|
+
ctx.files,
|
|
636
|
+
ctx.paperDir,
|
|
637
|
+
ctx.readBibliography(ctx.paperDir),
|
|
638
|
+
ctx.checkReferences,
|
|
639
|
+
),
|
|
594
640
|
}),
|
|
595
641
|
};
|
|
596
642
|
|
|
@@ -653,6 +699,17 @@ export interface BuildOptions {
|
|
|
653
699
|
* warns. The CLI passes the real ones (`adapters/references`).
|
|
654
700
|
*/
|
|
655
701
|
checkReferences?: CheckReferences;
|
|
702
|
+
/**
|
|
703
|
+
* The databases the build's bibtex opened, for the references step. Default: none — the step says
|
|
704
|
+
* it was not wired and records nothing, and lint warns. The CLI passes `bibliographyReader` over the
|
|
705
|
+
* real ports.
|
|
706
|
+
*/
|
|
707
|
+
readBibliography?: ReadBibliography;
|
|
708
|
+
/**
|
|
709
|
+
* Writes `_build/sources.json` after a compile. Default: none — the build says it recorded nothing,
|
|
710
|
+
* and lint reports the paper as not built. The CLI passes `recordSources` over the real ports.
|
|
711
|
+
*/
|
|
712
|
+
recordSources?: RecordSources;
|
|
656
713
|
}
|
|
657
714
|
|
|
658
715
|
/**
|
|
@@ -674,7 +731,14 @@ function baseDefaults({
|
|
|
674
731
|
readPdf = pdfjsReader,
|
|
675
732
|
projectRoot = env["CLAUDE_PROJECT_DIR"] || cwd,
|
|
676
733
|
}: BuildOptions): Required<
|
|
677
|
-
Omit<
|
|
734
|
+
Omit<
|
|
735
|
+
BuildOptions,
|
|
736
|
+
| "measure"
|
|
737
|
+
| "files"
|
|
738
|
+
| "checkReferences"
|
|
739
|
+
| "readBibliography"
|
|
740
|
+
| "recordSources"
|
|
741
|
+
>
|
|
678
742
|
> {
|
|
679
743
|
return { run, cwd, env, steps, log, dryRun, readPdf, projectRoot };
|
|
680
744
|
}
|
|
@@ -691,7 +755,16 @@ const notWired: CheckReferences = (_bib, cache) =>
|
|
|
691
755
|
|
|
692
756
|
/** banal as the measurer, wired from the build's environment: the one piece of root work left here (#76). */
|
|
693
757
|
function defaultMeasurer(
|
|
694
|
-
b: Required<
|
|
758
|
+
b: Required<
|
|
759
|
+
Omit<
|
|
760
|
+
BuildOptions,
|
|
761
|
+
| "measure"
|
|
762
|
+
| "files"
|
|
763
|
+
| "checkReferences"
|
|
764
|
+
| "readBibliography"
|
|
765
|
+
| "recordSources"
|
|
766
|
+
>
|
|
767
|
+
>,
|
|
695
768
|
): MeasureGeometry {
|
|
696
769
|
const dirs = hostDirs({ cwd: b.cwd });
|
|
697
770
|
return banalMeasurer(
|
|
@@ -710,6 +783,8 @@ function withDefaults(o: BuildOptions): Required<BuildOptions> {
|
|
|
710
783
|
measure,
|
|
711
784
|
files: o.files ?? nodeFiles,
|
|
712
785
|
checkReferences: o.checkReferences ?? notWired,
|
|
786
|
+
readBibliography: o.readBibliography ?? notWiredBibliography,
|
|
787
|
+
recordSources: o.recordSources ?? notWiredRecord,
|
|
713
788
|
};
|
|
714
789
|
}
|
|
715
790
|
|
|
@@ -718,29 +793,14 @@ async function runSteps(
|
|
|
718
793
|
paperDir: string,
|
|
719
794
|
dir: string,
|
|
720
795
|
plan: PlanLine[],
|
|
721
|
-
|
|
722
|
-
run,
|
|
723
|
-
env,
|
|
724
|
-
steps,
|
|
725
|
-
readPdf,
|
|
726
|
-
measure,
|
|
727
|
-
files,
|
|
728
|
-
checkReferences,
|
|
729
|
-
}: Required<BuildOptions>,
|
|
796
|
+
o: Required<BuildOptions>,
|
|
730
797
|
): Promise<BuildResult> {
|
|
731
|
-
let stepEnv = env;
|
|
798
|
+
let stepEnv = o.env;
|
|
732
799
|
const notes: string[] = [];
|
|
733
|
-
for (const [i, step] of steps.entries()) {
|
|
800
|
+
for (const [i, step] of o.steps.entries()) {
|
|
734
801
|
if (!plan[i]?.applies) continue;
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
env: stepEnv,
|
|
738
|
-
run,
|
|
739
|
-
readPdf,
|
|
740
|
-
measure,
|
|
741
|
-
files,
|
|
742
|
-
checkReferences,
|
|
743
|
-
});
|
|
802
|
+
// Every port the options hold, the paper, and the environment so far.
|
|
803
|
+
const out = await step.run({ ...o, paperDir, env: stepEnv });
|
|
744
804
|
if (!out.ok) {
|
|
745
805
|
// The PDF THIS run wrote and the step then rejected (a partial pass).
|
|
746
806
|
// A PDF from an earlier run is already gone — `buildPapers` removed it before anything ran.
|
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
import { describe, expect, it } from "vitest";
|
|
8
8
|
import { latexReader } from "./adapters/latex/index.ts";
|
|
9
9
|
import { memoryFiles } from "./adapters/memory/index.ts";
|
|
10
|
+
import { sourcesCodec } from "./adapters/sources-record/index.ts";
|
|
10
11
|
import {
|
|
11
12
|
claimProvenanceRule,
|
|
12
13
|
judgeClaimProvenance,
|
|
13
14
|
} from "./claim-provenance.ts";
|
|
15
|
+
import { builtPaper } from "../test/recorded-fixture.ts";
|
|
14
16
|
|
|
15
17
|
const doc = (body: string): string =>
|
|
16
18
|
`\\documentclass{article}\n\\begin{document}\n${body}\n\\end{document}\n`;
|
|
@@ -317,7 +319,11 @@ function run(
|
|
|
317
319
|
}
|
|
318
320
|
|
|
319
321
|
const onDisk = (files: Record<string, string> = {}) =>
|
|
320
|
-
claimProvenanceRule({
|
|
322
|
+
claimProvenanceRule({
|
|
323
|
+
files: memoryFiles(files),
|
|
324
|
+
latex: latexReader,
|
|
325
|
+
codec: sourcesCodec,
|
|
326
|
+
});
|
|
321
327
|
|
|
322
328
|
describe("the rule, as ESLint runs it", () => {
|
|
323
329
|
it("reports each finding at its lines, with the message naming the owners that would do", () => {
|
|
@@ -337,16 +343,36 @@ describe("the rule, as ESLint runs it", () => {
|
|
|
337
343
|
]);
|
|
338
344
|
});
|
|
339
345
|
|
|
340
|
-
it("🔴 reads the files the
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
+
it("🔴 reads the files the build recorded TeX reading: a finding there is reported at the \\input, naming its file and line", () => {
|
|
347
|
+
const main = doc("Intro.\n\\input{sections/results}");
|
|
348
|
+
const rule = onDisk(
|
|
349
|
+
builtPaper(
|
|
350
|
+
"/p",
|
|
351
|
+
{
|
|
352
|
+
"paper.tex": main,
|
|
353
|
+
"sections/results.tex":
|
|
354
|
+
"\\section{Results}\nIt holds.\nAgents ignore 42\\% of rules.\n",
|
|
355
|
+
},
|
|
356
|
+
[
|
|
357
|
+
{ path: "paper.tex", role: "body" },
|
|
358
|
+
{ path: "sections/results.tex", role: "body" },
|
|
359
|
+
],
|
|
360
|
+
),
|
|
361
|
+
);
|
|
362
|
+
expect(run(rule, main)).toEqual([
|
|
346
363
|
{
|
|
347
364
|
line: 4,
|
|
348
365
|
message: `sections/results.tex:3:1: ${String(rule.meta.messages["noOwner"]).replace("{{number}}", "42%")}`,
|
|
349
366
|
},
|
|
350
367
|
]);
|
|
351
368
|
});
|
|
369
|
+
|
|
370
|
+
it("🔴 without a record the paper is paper.tex: the file it \\inputs is not read", () => {
|
|
371
|
+
const main = doc("Intro.\n\\input{sections/results}");
|
|
372
|
+
const rule = onDisk({
|
|
373
|
+
"/p/paper.tex": main,
|
|
374
|
+
"/p/sections/results.tex": "Agents ignore 42\\% of rules.\n",
|
|
375
|
+
});
|
|
376
|
+
expect(run(rule, main)).toEqual([]);
|
|
377
|
+
});
|
|
352
378
|
});
|
package/src/claim-provenance.ts
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
segmentOf,
|
|
38
38
|
sentences,
|
|
39
39
|
} from "./domain/sentences.ts";
|
|
40
|
-
import { readPaper, reportInPaper, type
|
|
40
|
+
import { readPaper, reportInPaper, type ProseDeps } from "./tex-paper.ts";
|
|
41
41
|
import {
|
|
42
42
|
ruleDocsUrl,
|
|
43
43
|
type Located,
|
|
@@ -198,7 +198,7 @@ const META: TexRuleModule["meta"] = {
|
|
|
198
198
|
};
|
|
199
199
|
|
|
200
200
|
/** The rule, over the body's prose of the whole paper, its includes spliced (`readPaper`). */
|
|
201
|
-
export const claimProvenanceRule = (deps:
|
|
201
|
+
export const claimProvenanceRule = (deps: ProseDeps): TexRuleModule => ({
|
|
202
202
|
meta: META,
|
|
203
203
|
create(context) {
|
|
204
204
|
return {
|
package/src/cli.harness.mjs
CHANGED
|
@@ -42,6 +42,19 @@ const { PROGRAMS } = await import(join(HERE, "doctor.ts"));
|
|
|
42
42
|
// loads vigiles without recording a check is reported as having verified nothing.
|
|
43
43
|
const check = createChecker();
|
|
44
44
|
|
|
45
|
+
/** A paper.tex whose whole body is `body`. */
|
|
46
|
+
const tex = (body) =>
|
|
47
|
+
`\\documentclass{article}\n\\begin{document}\n${body}\n\\end{document}\n`;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A run that linted a paper nobody built clean: exit 0, and the one finding is the warning that no
|
|
51
|
+
* build recorded what TeX read (`paper/sources-fresh`) — what "no findings" meant before that rule.
|
|
52
|
+
*/
|
|
53
|
+
const cleanButUnbuilt = (r) =>
|
|
54
|
+
r.code === 0 &&
|
|
55
|
+
/paper\/sources-fresh/.test(r.out) &&
|
|
56
|
+
/1 problem \(0 errors, 1 warning\)/.test(r.out);
|
|
57
|
+
|
|
45
58
|
/** Runs the utility with output captured — quieter and faster than spawning a process. */
|
|
46
59
|
async function cli(args, cwd) {
|
|
47
60
|
// 🔴 THE STREAMS ARE SPLIT, AND THAT IS LOAD-BEARING. As long as the harness dumped log and
|
|
@@ -129,11 +142,11 @@ check(
|
|
|
129
142
|
const ruleBlocks = (c) => c.filter((b) => Array.isArray(b.files));
|
|
130
143
|
check(
|
|
131
144
|
"without a LaTeX language the config still gets built — a corpus with no .tex is not a reason to refuse",
|
|
132
|
-
Array.isArray(cfg) && ruleBlocks(cfg).length ===
|
|
145
|
+
Array.isArray(cfg) && ruleBlocks(cfg).length === 3,
|
|
133
146
|
);
|
|
134
147
|
check(
|
|
135
148
|
"with a LaTeX language two blocks are added: paper.tex, and the files it includes",
|
|
136
|
-
ruleBlocks(buildConfig({}, {})).length ===
|
|
149
|
+
ruleBlocks(buildConfig({}, {})).length === 5,
|
|
137
150
|
);
|
|
138
151
|
check(
|
|
139
152
|
"🔴 the project's paper TEMPLATE directory is ignored — flat config does not skip dot-directories",
|
|
@@ -178,7 +191,7 @@ check(
|
|
|
178
191
|
join(paper, "versions", "2026-07-22-submitted.pdf"),
|
|
179
192
|
"x".repeat(100),
|
|
180
193
|
);
|
|
181
|
-
writeFileSync(join(paper, "paper.
|
|
194
|
+
writeFileSync(join(paper, "paper.tex"), tex("RQ1: does it hold?"));
|
|
182
195
|
writeFileSync(
|
|
183
196
|
join(paper, "PIPELINE-STATUS.md"),
|
|
184
197
|
`---\nstages:\n - stage: submitted\n date: 2026-07-22\n pdf: versions/2026-07-22-submitted.pdf\n bytes: 100\n source: versions/s.tex\n sourceBytes: 4\n---\n# S\n\n| id | note |\n|---|---|\n| cites | bib-authors run |\n`,
|
|
@@ -192,7 +205,7 @@ check(
|
|
|
192
205
|
const bare = await cli(["lint"], root);
|
|
193
206
|
check(
|
|
194
207
|
"🔴 NO paperlint.json IS A VALID PROJECT — the default papers directory is linted",
|
|
195
|
-
|
|
208
|
+
cleanButUnbuilt(bare),
|
|
196
209
|
);
|
|
197
210
|
check(
|
|
198
211
|
"and no config line is printed when there is no file to name",
|
|
@@ -204,7 +217,7 @@ check(
|
|
|
204
217
|
const empty = await cli(["lint"], root);
|
|
205
218
|
check(
|
|
206
219
|
`an empty paperlint.json — no \`${PAPERS_DIR_FIELD}\`, so the default`,
|
|
207
|
-
|
|
220
|
+
cleanButUnbuilt(empty),
|
|
208
221
|
);
|
|
209
222
|
|
|
210
223
|
// 🔴 THE DECLARED DIRECTORY IS NOT THE DEFAULT: with `papers` declared, a CLI that ignored
|
|
@@ -217,7 +230,7 @@ check(
|
|
|
217
230
|
const r = await cli(["lint"], root);
|
|
218
231
|
check(
|
|
219
232
|
"🔴 THE UTILITY READS THE DECLARATION FROM paperlint.json — the file `paperlint init` writes and the hooks read",
|
|
220
|
-
|
|
233
|
+
cleanButUnbuilt(r),
|
|
221
234
|
);
|
|
222
235
|
check(
|
|
223
236
|
"and NAMES the carrier out loud — swapping settings is never silent",
|
|
@@ -703,13 +716,13 @@ check(
|
|
|
703
716
|
const status = (bytes) =>
|
|
704
717
|
`---\nstages:\n - stage: submitted\n date: 2026-07-22\n pdf: versions/2026-07-22-submitted.pdf\n bytes: ${bytes}\n source: versions/s.tex\n sourceBytes: 4\n---\n# S\n\n| id | note |\n|---|---|\n| cites | bib-authors run |\n`;
|
|
705
718
|
writeFileSync(join(paper, "versions", "s.tex"), "abcd");
|
|
706
|
-
writeFileSync(join(paper, "paper.
|
|
719
|
+
writeFileSync(join(paper, "paper.tex"), tex("RQ1: does it hold?"));
|
|
707
720
|
|
|
708
721
|
writeFileSync(join(paper, "PIPELINE-STATUS.md"), status(100));
|
|
709
722
|
const clean = await cli(["lint", "papers"], root);
|
|
710
723
|
check(
|
|
711
724
|
"on a clean corpus — zero and a legible report",
|
|
712
|
-
|
|
725
|
+
cleanButUnbuilt(clean),
|
|
713
726
|
);
|
|
714
727
|
|
|
715
728
|
writeFileSync(join(paper, "PIPELINE-STATUS.md"), status(999));
|
|
@@ -788,7 +801,7 @@ check(
|
|
|
788
801
|
join(paper, "versions", "2026-07-22-submitted.pdf"),
|
|
789
802
|
"x".repeat(100),
|
|
790
803
|
);
|
|
791
|
-
writeFileSync(join(paper, "paper.
|
|
804
|
+
writeFileSync(join(paper, "paper.tex"), tex("RQ1: does it hold?"));
|
|
792
805
|
const status = (bytes) =>
|
|
793
806
|
`---\nstages:\n - stage: submitted\n date: 2026-07-22\n pdf: versions/2026-07-22-submitted.pdf\n bytes: ${bytes}\n source: versions/s.tex\n sourceBytes: 4\n---\n# S\n\n| id | note |\n|---|---|\n| cites | bib-authors run |\n`;
|
|
794
807
|
writeFileSync(join(paper, "PIPELINE-STATUS.md"), status(100));
|
|
@@ -818,7 +831,7 @@ check(
|
|
|
818
831
|
const found = await cli(["lint"], root);
|
|
819
832
|
check(
|
|
820
833
|
"the config is FOUND on its own: `lint` with no argument at all runs",
|
|
821
|
-
|
|
834
|
+
cleanButUnbuilt(found),
|
|
822
835
|
);
|
|
823
836
|
check(
|
|
824
837
|
"and the found file is NAMED out loud — swapping settings silently is not acceptable",
|
|
@@ -832,7 +845,7 @@ check(
|
|
|
832
845
|
const fromSub = await cli(["lint"], paper);
|
|
833
846
|
check(
|
|
834
847
|
"the path from the config resolves relative to the CONFIG'S DIRECTORY, not the current one",
|
|
835
|
-
|
|
848
|
+
cleanButUnbuilt(fromSub),
|
|
836
849
|
);
|
|
837
850
|
|
|
838
851
|
// Consumer data from the found config actually reaches the rules, not just gets read.
|
|
@@ -861,7 +874,7 @@ check(
|
|
|
861
874
|
const noPapers = await cli(["lint"], root);
|
|
862
875
|
check(
|
|
863
876
|
"a config WITHOUT the papers-directory field lints the default `papers/`",
|
|
864
|
-
|
|
877
|
+
cleanButUnbuilt(noPapers),
|
|
865
878
|
);
|
|
866
879
|
check(
|
|
867
880
|
'an empty string as the papers directory is refused, not read as the directory ""',
|
|
@@ -897,8 +910,8 @@ check(
|
|
|
897
910
|
);
|
|
898
911
|
// Three § signs — `paper/section-word`, warn level and only warn.
|
|
899
912
|
writeFileSync(
|
|
900
|
-
join(paper, "paper.
|
|
901
|
-
"
|
|
913
|
+
join(paper, "paper.tex"),
|
|
914
|
+
tex("RQ1: does it hold?\n\nSee \u00a7 5 and \u00a7 6 and \u00a7 7."),
|
|
902
915
|
);
|
|
903
916
|
writeFileSync(
|
|
904
917
|
join(paper, "PIPELINE-STATUS.md"),
|
|
@@ -921,7 +934,7 @@ check(
|
|
|
921
934
|
);
|
|
922
935
|
check(
|
|
923
936
|
'and the failure names the NUMBER and the THRESHOLD, not just "too many"',
|
|
924
|
-
/
|
|
937
|
+
/4 warning\(s\) exceed the --max-warnings limit of 0/.test(strict.out),
|
|
925
938
|
);
|
|
926
939
|
const generous = await cli(["lint", "--max-warnings", "5"], root);
|
|
927
940
|
check(
|
|
@@ -1040,7 +1053,7 @@ check(
|
|
|
1040
1053
|
const paper = join(tree, "papers", "p");
|
|
1041
1054
|
mkdirSync(paper, { recursive: true });
|
|
1042
1055
|
// Two findings of different kinds: a structural one (no scorecard) and a rule one (`§`).
|
|
1043
|
-
writeFileSync(join(paper, "paper.
|
|
1056
|
+
writeFileSync(join(paper, "paper.tex"), tex("See § 3 and §4."));
|
|
1044
1057
|
|
|
1045
1058
|
const inside = await cli(["lint", "--json", "papers"], tree);
|
|
1046
1059
|
const outside = await cli(
|
|
@@ -1273,22 +1286,10 @@ console.log(
|
|
|
1273
1286
|
|
|
1274
1287
|
// ── the new flags parse, and none of them becomes the directory argument ─────────────────
|
|
1275
1288
|
{
|
|
1276
|
-
const a = parseArgs([
|
|
1277
|
-
"init",
|
|
1278
|
-
"--yes",
|
|
1279
|
-
"--no-hooks",
|
|
1280
|
-
"--paper",
|
|
1281
|
-
"demo",
|
|
1282
|
-
"--format",
|
|
1283
|
-
"md",
|
|
1284
|
-
]);
|
|
1289
|
+
const a = parseArgs(["init", "--yes", "--no-hooks", "--paper", "demo"]);
|
|
1285
1290
|
check(
|
|
1286
1291
|
"init flags parse into fields, not into paths",
|
|
1287
|
-
a.yes &&
|
|
1288
|
-
a.noHooks &&
|
|
1289
|
-
a.paper === "demo" &&
|
|
1290
|
-
a.format === "md" &&
|
|
1291
|
-
a.paths.length === 0,
|
|
1292
|
+
a.yes && a.noHooks && a.paper === "demo" && a.paths.length === 0,
|
|
1292
1293
|
);
|
|
1293
1294
|
check("-y is --yes", parseArgs(["init", "-y"]).yes === true);
|
|
1294
1295
|
check(
|
|
@@ -1300,11 +1301,6 @@ console.log(
|
|
|
1300
1301
|
"🔴 --hooks=local is NOT implemented, and it is refused by name rather than read as a path",
|
|
1301
1302
|
r.code === 2 && /--hooks=local is not implemented/.test(r.out),
|
|
1302
1303
|
);
|
|
1303
|
-
const f = await cli(["init", "--format", "pdf"]);
|
|
1304
|
-
check(
|
|
1305
|
-
"an unknown --format is refused before anything is written",
|
|
1306
|
-
f.code === 2 && /--format must be one of tex, md/.test(f.out),
|
|
1307
|
-
);
|
|
1308
1304
|
}
|
|
1309
1305
|
|
|
1310
1306
|
// ── init wires the hooks, and a human can say no ─────────────────────────────────────────
|
|
@@ -1315,7 +1311,7 @@ console.log(
|
|
|
1315
1311
|
const mk = (name) => {
|
|
1316
1312
|
const dir = join(root, name);
|
|
1317
1313
|
mkdirSync(join(dir, "papers", "p1"), { recursive: true });
|
|
1318
|
-
writeFileSync(join(dir, "papers", "p1", "paper.
|
|
1314
|
+
writeFileSync(join(dir, "papers", "p1", "paper.tex"), "% P\n");
|
|
1319
1315
|
writeFileSync(
|
|
1320
1316
|
join(dir, "papers", "p1", "PIPELINE-STATUS.md"),
|
|
1321
1317
|
"---\n---\n",
|
|
@@ -1426,19 +1422,17 @@ console.log(
|
|
|
1426
1422
|
ask: async (q) => {
|
|
1427
1423
|
asked.push(q);
|
|
1428
1424
|
if (/create a first paper/.test(q)) return "first";
|
|
1429
|
-
if (/format:/.test(q)) return "md";
|
|
1430
1425
|
return "";
|
|
1431
1426
|
},
|
|
1432
|
-
createPaper: async (papersRoot, name
|
|
1433
|
-
made.push({ papersRoot, name
|
|
1427
|
+
createPaper: async (papersRoot, name) => {
|
|
1428
|
+
made.push({ papersRoot, name });
|
|
1434
1429
|
return 0;
|
|
1435
1430
|
},
|
|
1436
1431
|
});
|
|
1437
1432
|
check(
|
|
1438
|
-
"a human with no paper is offered one, and the answer
|
|
1433
|
+
"a human with no paper is offered one, and the answer reaches `paperlint new`'s routine",
|
|
1439
1434
|
made.length === 1 &&
|
|
1440
1435
|
made[0].name === "first" &&
|
|
1441
|
-
made[0].format === "md" &&
|
|
1442
1436
|
made[0].papersRoot === join(dir, "papers"),
|
|
1443
1437
|
);
|
|
1444
1438
|
}
|
|
@@ -1466,22 +1460,17 @@ console.log(
|
|
|
1466
1460
|
log: () => {},
|
|
1467
1461
|
interactive: false,
|
|
1468
1462
|
paper: "given",
|
|
1469
|
-
createPaper: async (papersRoot, name
|
|
1470
|
-
made.push({ name, format }),
|
|
1471
|
-
0
|
|
1472
|
-
),
|
|
1463
|
+
createPaper: async (papersRoot, name) => (made.push({ name }), 0),
|
|
1473
1464
|
});
|
|
1474
1465
|
check(
|
|
1475
|
-
"--paper creates it without a human
|
|
1476
|
-
made.length === 1 &&
|
|
1477
|
-
made[0].name === "given" &&
|
|
1478
|
-
made[0].format === "tex",
|
|
1466
|
+
"--paper creates it without a human",
|
|
1467
|
+
made.length === 1 && made[0].name === "given",
|
|
1479
1468
|
);
|
|
1480
1469
|
}
|
|
1481
1470
|
{
|
|
1482
1471
|
const dir = bare("has-one");
|
|
1483
1472
|
mkdirSync(join(dir, "papers", "p1"), { recursive: true });
|
|
1484
|
-
writeFileSync(join(dir, "papers", "p1", "paper.
|
|
1473
|
+
writeFileSync(join(dir, "papers", "p1", "paper.tex"), "% P\n");
|
|
1485
1474
|
const asked = [];
|
|
1486
1475
|
await init(dir, {
|
|
1487
1476
|
...base,
|
|
@@ -1569,7 +1558,7 @@ console.log(
|
|
|
1569
1558
|
`{"${PAPERS_DIR_FIELD}":"papers"}\n`,
|
|
1570
1559
|
);
|
|
1571
1560
|
mkdirSync(join(root, "papers", "p1"), { recursive: true });
|
|
1572
|
-
writeFileSync(join(root, "papers", "p1", "paper.
|
|
1561
|
+
writeFileSync(join(root, "papers", "p1", "paper.tex"), "% P\n");
|
|
1573
1562
|
writeFileSync(
|
|
1574
1563
|
join(root, "papers", "p1", "PIPELINE-STATUS.md"),
|
|
1575
1564
|
"---\n---\n",
|
|
@@ -1591,7 +1580,7 @@ console.log(
|
|
|
1591
1580
|
join(root, "papers", "p2", "PIPELINE-STATUS.md"),
|
|
1592
1581
|
readFileSync(join(root, "papers", ".template", "PIPELINE-STATUS.md")),
|
|
1593
1582
|
);
|
|
1594
|
-
writeFileSync(join(root, "papers", "p2", "paper.
|
|
1583
|
+
writeFileSync(join(root, "papers", "p2", "paper.tex"), "% P\n");
|
|
1595
1584
|
const r2 = await cli(["lint"], root);
|
|
1596
1585
|
check(
|
|
1597
1586
|
"and the same scorecard in a real paper folder fails — the ignore is scoped, not a blind spot",
|
|
@@ -1617,19 +1606,20 @@ console.log(
|
|
|
1617
1606
|
);
|
|
1618
1607
|
const r = await cli(["new", "demo"], root);
|
|
1619
1608
|
check(
|
|
1620
|
-
"🔴 `paperlint new demo` creates writing/demo/ and its lint is the verdict — exit 0, and only
|
|
1609
|
+
"🔴 `paperlint new demo` creates writing/demo/ and its lint is the verdict — exit 0, and only two warnings: no venue is chosen yet, the paper is not built",
|
|
1621
1610
|
r.code === 0 &&
|
|
1622
1611
|
existsSync(join(root, "writing", "demo", "PIPELINE-STATUS.md")) &&
|
|
1623
1612
|
existsSync(join(root, "writing", "demo", "paper.tex")) &&
|
|
1624
1613
|
existsSync(join(root, "writing", "demo", "paperlint.json")) &&
|
|
1625
1614
|
/names no venue preset yet/.test(r.out) &&
|
|
1626
|
-
|
|
1615
|
+
/paper\/sources-fresh/.test(r.out) &&
|
|
1616
|
+
/\(0 errors, 2 warnings\)/.test(r.out),
|
|
1627
1617
|
);
|
|
1628
|
-
const again = await cli(["new", "demo"
|
|
1618
|
+
const again = await cli(["new", "demo"], root);
|
|
1629
1619
|
check(
|
|
1630
1620
|
"a second run on the same folder adds nothing — the tex source already stands",
|
|
1631
1621
|
again.code === 0 &&
|
|
1632
|
-
|
|
1622
|
+
!/\+ /.test(again.out) &&
|
|
1633
1623
|
/kept — never overwritten/.test(again.out),
|
|
1634
1624
|
);
|
|
1635
1625
|
const bad = await cli(["new", "Demo"], root);
|