paperlint 3.1.3 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +12 -7
- package/CLAUDE.md +5 -5
- package/CONTRIBUTING.md +34 -5
- package/dist/adapters/banal/failure.d.ts +1 -7
- package/dist/adapters/banal/failure.d.ts.map +1 -1
- package/dist/adapters/banal/failure.js +5 -7
- package/dist/adapters/banal/failure.js.map +1 -1
- package/dist/adapters/banal/index.d.ts.map +1 -1
- package/dist/adapters/banal/index.js +4 -3
- package/dist/adapters/banal/index.js.map +1 -1
- package/dist/adapters/banal/install.d.ts.map +1 -1
- package/dist/adapters/banal/install.js +4 -3
- package/dist/adapters/banal/install.js.map +1 -1
- package/dist/adapters/banal/invocation.d.ts +10 -5
- package/dist/adapters/banal/invocation.d.ts.map +1 -1
- package/dist/adapters/banal/invocation.js +13 -1
- package/dist/adapters/banal/invocation.js.map +1 -1
- package/dist/adapters/banal/locate.d.ts +12 -9
- package/dist/adapters/banal/locate.d.ts.map +1 -1
- package/dist/adapters/banal/locate.js +19 -12
- package/dist/adapters/banal/locate.js.map +1 -1
- package/dist/adapters/banal/run.d.ts +10 -1
- package/dist/adapters/banal/run.d.ts.map +1 -1
- package/dist/adapters/banal/run.js +8 -4
- package/dist/adapters/banal/run.js.map +1 -1
- package/dist/adapters/banal/settings.d.ts +1 -1
- package/dist/adapters/banal/settings.d.ts.map +1 -1
- package/dist/adapters/banal/settings.js +2 -1
- package/dist/adapters/banal/settings.js.map +1 -1
- package/dist/adapters/memory/index.d.ts.map +1 -1
- package/dist/adapters/memory/index.js +8 -2
- package/dist/adapters/memory/index.js.map +1 -1
- package/dist/adapters/node/files.io.d.ts.map +1 -1
- package/dist/adapters/node/files.io.js +2 -1
- package/dist/adapters/node/files.io.js.map +1 -1
- package/dist/adapters/node/process.io.d.ts +5 -7
- package/dist/adapters/node/process.io.d.ts.map +1 -1
- package/dist/adapters/node/process.io.js +4 -3
- package/dist/adapters/node/process.io.js.map +1 -1
- package/dist/adapters/node/workspace.io.d.ts.map +1 -1
- package/dist/adapters/node/workspace.io.js +4 -2
- package/dist/adapters/node/workspace.io.js.map +1 -1
- package/dist/adapters/pdfjs/fill.d.ts.map +1 -1
- package/dist/adapters/pdfjs/fill.js +13 -7
- package/dist/adapters/pdfjs/fill.js.map +1 -1
- package/dist/adapters/references/index.d.ts.map +1 -1
- package/dist/adapters/references/index.js +8 -7
- package/dist/adapters/references/index.js.map +1 -1
- package/dist/adapters/references/pool.d.ts.map +1 -1
- package/dist/adapters/references/pool.js +4 -5
- package/dist/adapters/references/pool.js.map +1 -1
- package/dist/adapters/references/reach.io.d.ts.map +1 -1
- package/dist/adapters/references/reach.io.js +2 -1
- package/dist/adapters/references/reach.io.js.map +1 -1
- package/dist/build-engine.d.ts +1 -1
- package/dist/build-engine.d.ts.map +1 -1
- package/dist/build-engine.js +10 -3
- package/dist/build-engine.js.map +1 -1
- package/dist/build.d.ts +2 -3
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +19 -17
- package/dist/build.js.map +1 -1
- package/dist/caller-path.d.ts +3 -0
- package/dist/caller-path.d.ts.map +1 -0
- package/dist/caller-path.js +13 -0
- package/dist/caller-path.js.map +1 -0
- package/dist/cli.d.ts +10 -17
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +93 -46
- package/dist/cli.js.map +1 -1
- package/dist/doctor.d.ts +12 -3
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +5 -3
- package/dist/doctor.js.map +1 -1
- package/dist/domain/lookup-cache.d.ts.map +1 -1
- package/dist/domain/lookup-cache.js +10 -8
- package/dist/domain/lookup-cache.js.map +1 -1
- package/dist/domain/paths.d.ts +8 -2
- package/dist/domain/paths.d.ts.map +1 -1
- package/dist/domain/paths.js +12 -1
- package/dist/domain/paths.js.map +1 -1
- package/dist/domain/ready.d.ts +24 -0
- package/dist/domain/ready.d.ts.map +1 -0
- package/dist/domain/ready.js +11 -0
- package/dist/domain/ready.js.map +1 -0
- package/dist/domain/record.d.ts +8 -0
- package/dist/domain/record.d.ts.map +1 -0
- package/dist/domain/record.js +8 -0
- package/dist/domain/record.js.map +1 -0
- package/dist/domain/sha256.d.ts.map +1 -1
- package/dist/domain/sha256.js +4 -2
- package/dist/domain/sha256.js.map +1 -1
- package/dist/domain/text.d.ts +12 -0
- package/dist/domain/text.d.ts.map +1 -1
- package/dist/domain/text.js +17 -0
- package/dist/domain/text.js.map +1 -1
- package/dist/engine.d.ts +23 -22
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +4 -3
- package/dist/engine.js.map +1 -1
- package/dist/eslint-rules/bib-reachable-entry.d.ts +22 -0
- package/dist/eslint-rules/bib-reachable-entry.d.ts.map +1 -0
- package/dist/eslint-rules/bib-reachable-entry.js +75 -0
- package/dist/eslint-rules/bib-reachable-entry.js.map +1 -0
- package/dist/eslint-rules/install-path-literals.d.ts +80 -0
- package/dist/eslint-rules/install-path-literals.d.ts.map +1 -0
- package/dist/eslint-rules/install-path-literals.js +105 -0
- package/dist/eslint-rules/install-path-literals.js.map +1 -0
- package/dist/eslint-rules/latex-language.d.ts +136 -0
- package/dist/eslint-rules/latex-language.d.ts.map +1 -0
- package/dist/eslint-rules/latex-language.js +639 -0
- package/dist/eslint-rules/latex-language.js.map +1 -0
- package/dist/eslint-rules/paper-research-question.d.ts +31 -0
- package/dist/eslint-rules/paper-research-question.d.ts.map +1 -0
- package/dist/eslint-rules/paper-research-question.js +188 -0
- package/dist/eslint-rules/paper-research-question.js.map +1 -0
- package/dist/eslint-rules/paper-stages.d.ts +83 -0
- package/dist/eslint-rules/paper-stages.d.ts.map +1 -0
- package/dist/eslint-rules/paper-stages.js +358 -0
- package/dist/eslint-rules/paper-stages.js.map +1 -0
- package/dist/eslint-rules/paper-typography.d.ts +74 -0
- package/dist/eslint-rules/paper-typography.d.ts.map +1 -0
- package/dist/eslint-rules/paper-typography.js +404 -0
- package/dist/eslint-rules/paper-typography.js.map +1 -0
- package/dist/eslint-rules/papers.d.ts +42 -0
- package/dist/eslint-rules/papers.d.ts.map +1 -0
- package/dist/eslint-rules/papers.js +145 -0
- package/dist/eslint-rules/papers.js.map +1 -0
- package/dist/eslint-rules/pdf-last-page-balance.d.ts +72 -0
- package/dist/eslint-rules/pdf-last-page-balance.d.ts.map +1 -0
- package/dist/eslint-rules/pdf-last-page-balance.js +237 -0
- package/dist/eslint-rules/pdf-last-page-balance.js.map +1 -0
- package/dist/eslint-rules/review-frontmatter.d.ts +57 -0
- package/dist/eslint-rules/review-frontmatter.d.ts.map +1 -0
- package/dist/eslint-rules/review-frontmatter.js +126 -0
- package/dist/eslint-rules/review-frontmatter.js.map +1 -0
- package/dist/eslint-rules/review-frontmatter.schema.json +36 -0
- package/dist/eslint-rules/rule-context.d.ts +55 -0
- package/dist/eslint-rules/rule-context.d.ts.map +1 -0
- package/dist/eslint-rules/rule-context.js +9 -0
- package/dist/eslint-rules/rule-context.js.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.d.ts +25 -0
- package/dist/eslint-rules/sibling-frontmatter.d.ts.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.js +16 -0
- package/dist/eslint-rules/sibling-frontmatter.js.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.schema.json +14 -0
- package/dist/eslint-rules/temp-root-realpath.d.ts +71 -0
- package/dist/eslint-rules/temp-root-realpath.d.ts.map +1 -0
- package/dist/eslint-rules/temp-root-realpath.js +139 -0
- package/dist/eslint-rules/temp-root-realpath.js.map +1 -0
- package/dist/eslint-rules/tex-build.d.ts +35 -0
- package/dist/eslint-rules/tex-build.d.ts.map +1 -0
- package/dist/eslint-rules/tex-build.js +313 -0
- package/dist/eslint-rules/tex-build.js.map +1 -0
- package/dist/facts-file.d.ts.map +1 -1
- package/dist/facts-file.js +31 -27
- package/dist/facts-file.js.map +1 -1
- package/dist/hooks-settings.d.ts +1 -1
- package/dist/hooks-settings.d.ts.map +1 -1
- package/dist/hooks-settings.js +31 -11
- package/dist/hooks-settings.js.map +1 -1
- package/dist/init.d.ts +8 -9
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +33 -15
- package/dist/init.js.map +1 -1
- package/dist/latex-loop.d.ts +2 -2
- package/dist/latex-loop.d.ts.map +1 -1
- package/dist/latex-loop.js +2 -2
- package/dist/latex-loop.js.map +1 -1
- package/dist/lib/agent-cli-version.d.ts +65 -0
- package/dist/lib/agent-cli-version.d.ts.map +1 -0
- package/dist/lib/agent-cli-version.js +77 -0
- package/dist/lib/agent-cli-version.js.map +1 -0
- package/dist/lib/check.d.ts +19 -0
- package/dist/lib/check.d.ts.map +1 -0
- package/dist/lib/check.js +52 -0
- package/dist/lib/check.js.map +1 -0
- package/dist/lib/markdown.d.ts +180 -0
- package/dist/lib/markdown.d.ts.map +1 -0
- package/dist/lib/markdown.js +302 -0
- package/dist/lib/markdown.js.map +1 -0
- package/dist/lib/paper-config.d.ts +84 -0
- package/dist/lib/paper-config.d.ts.map +1 -0
- package/dist/lib/paper-config.js +138 -0
- package/dist/lib/paper-config.js.map +1 -0
- package/dist/lib/skill-eval-fixture.d.ts +29 -0
- package/dist/lib/skill-eval-fixture.d.ts.map +1 -0
- package/dist/lib/skill-eval-fixture.js +62 -0
- package/dist/lib/skill-eval-fixture.js.map +1 -0
- package/dist/link-skills.d.ts.map +1 -1
- package/dist/link-skills.js +5 -4
- package/dist/link-skills.js.map +1 -1
- package/dist/new-paper.d.ts.map +1 -1
- package/dist/new-paper.js +7 -3
- package/dist/new-paper.js.map +1 -1
- package/dist/paper-files.d.ts +4 -4
- package/dist/paper-files.d.ts.map +1 -1
- package/dist/paper-files.js +6 -6
- package/dist/paper-files.js.map +1 -1
- package/dist/paper-settings.d.ts.map +1 -1
- package/dist/paper-settings.js +8 -7
- package/dist/paper-settings.js.map +1 -1
- package/dist/pdf-facts.d.ts +51 -1
- package/dist/pdf-facts.d.ts.map +1 -1
- package/dist/pdf-facts.js +25 -16
- package/dist/pdf-facts.js.map +1 -1
- package/dist/pdf-geometry.d.ts.map +1 -1
- package/dist/pdf-geometry.js +3 -4
- package/dist/pdf-geometry.js.map +1 -1
- package/dist/ports/tool-installer.d.ts +2 -14
- package/dist/ports/tool-installer.d.ts.map +1 -1
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +19 -6
- package/dist/presets.js.map +1 -1
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +15 -10
- package/dist/references.js.map +1 -1
- package/dist/rules-config.d.ts +26 -1
- package/dist/rules-config.d.ts.map +1 -1
- package/dist/rules-config.js +15 -12
- package/dist/rules-config.js.map +1 -1
- package/dist/structure.js +1 -1
- package/dist/structure.js.map +1 -1
- package/dist/tex-requirements.d.ts +4 -0
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +22 -5
- package/dist/tex-requirements.js.map +1 -1
- package/dist/toolchain.d.ts.map +1 -1
- package/dist/toolchain.js +21 -16
- package/dist/toolchain.js.map +1 -1
- package/dist/types.d.ts +5 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/venue-rules.d.ts.map +1 -1
- package/dist/venue-rules.js +21 -8
- package/dist/venue-rules.js.map +1 -1
- package/docs/configuration.md +2 -2
- package/docs/e2e.md +7 -7
- package/docs/install.md +1 -1
- package/docs/prior-art/README.md +1 -1
- package/docs/prior-art/package-location.md +5 -5
- package/docs/prior-art/repro/README.md +1 -1
- package/docs/prior-art/test-tooling.md +2 -2
- package/docs/testing.md +11 -8
- package/docs/toolchain.md +1 -1
- package/eslint-rules/{bib-reachable-entry.mjs → bib-reachable-entry.ts} +5 -4
- package/eslint-rules/install-path-literals.harness.mjs +1 -1
- package/eslint-rules/{install-path-literals.mjs → install-path-literals.ts} +18 -10
- package/eslint-rules/latex-language.harness.mjs +2 -2
- package/eslint-rules/latex-language.test.mjs +7 -1
- package/eslint-rules/{latex-language.mjs → latex-language.ts} +297 -109
- package/eslint-rules/paper-research-question.harness.mjs +3 -3
- package/eslint-rules/{paper-research-question.mjs → paper-research-question.ts} +35 -16
- package/eslint-rules/paper-stages.harness.mjs +2 -2
- package/eslint-rules/paper-stages.test.mjs +8 -2
- package/eslint-rules/{paper-stages.mjs → paper-stages.ts} +76 -31
- package/eslint-rules/paper-typography.test.mjs +3 -3
- package/eslint-rules/{paper-typography.mjs → paper-typography.ts} +134 -56
- package/eslint-rules/papers.harness.mjs +2 -2
- package/eslint-rules/{papers.mjs → papers.ts} +7 -4
- package/eslint-rules/pdf-last-page-balance.harness.mjs +3 -3
- package/eslint-rules/{pdf-last-page-balance.mjs → pdf-last-page-balance.ts} +89 -32
- package/eslint-rules/review-frontmatter.test.mjs +2 -2
- package/eslint-rules/{review-frontmatter.mjs → review-frontmatter.ts} +38 -16
- package/eslint-rules/rule-context.ts +62 -0
- package/eslint-rules/{sibling-frontmatter.mjs → sibling-frontmatter.ts} +1 -1
- package/eslint-rules/temp-root-realpath.harness.mjs +1 -1
- package/eslint-rules/{temp-root-realpath.mjs → temp-root-realpath.ts} +20 -7
- package/eslint-rules/tex-build.harness.mjs +2 -2
- package/eslint-rules/{tex-build.mjs → tex-build.ts} +6 -5
- package/eslint.config.mjs +146 -30
- package/fixtures/install-path-literals/clean.fixture.mjs +1 -1
- package/fixtures/install-path-literals/clean.md +2 -2
- package/fixtures/install-path-literals/defect.fixture.mjs +1 -1
- package/fixtures/install-path-literals/defect.md +2 -2
- package/fixtures/pdf-facts/README.md +2 -2
- package/fixtures/real-markdown-paper/{baseline.mjs → baseline.ts} +31 -10
- package/hooks/{hooks.harness.mjs → hooks.harness.ts} +51 -34
- package/hooks/paper-edit-guard.hook.mjs +1 -1
- package/hooks/{paper-edit-guard.test.mjs → paper-edit-guard.test.ts} +3 -7
- package/hooks/paper-skills-nudge.hook.mjs +1 -1
- package/hooks/{paper-skills-nudge.test.mjs → paper-skills-nudge.test.ts} +2 -6
- package/hooks/paper-status-gates.sh +9 -19
- package/hooks/{paper-status-gates.test.mjs → paper-status-gates.test.ts} +2 -6
- package/lib/agent-cli-version.harness.mjs +1 -1
- package/lib/{agent-cli-version.mjs → agent-cli-version.ts} +32 -18
- package/lib/check.test.ts +14 -9
- package/lib/{check.mjs → check.ts} +22 -9
- package/lib/markdown.test.mjs +7 -7
- package/lib/{markdown.mjs → markdown.ts} +68 -29
- package/lib/paper-config.harness.mjs +9 -9
- package/lib/{paper-config.mjs → paper-config.ts} +20 -19
- package/lib/skill-checks.mjs +24 -381
- package/lib/skill-checks.test.mjs +64 -116
- package/lib/skill-corpus.mjs +10 -251
- package/lib/skill-corpus.test.mjs +2 -13
- package/lib/skill-eval-kit.mjs +1 -1
- package/lib/skill-trigger-cases.mjs +1 -1
- package/package.json +42 -10
- package/scripts/{check.harness.mjs → check.harness.ts} +111 -10
- package/scripts/{check.test.mjs → check.test.ts} +13 -10
- package/scripts/{check.mjs → check.ts} +116 -42
- package/scripts/coverage-config.test.ts +80 -38
- package/scripts/eslint-report-guard.test.mjs +1 -1
- package/scripts/harness-api.frozen.json +14 -16
- package/scripts/harness-api.test.ts +8 -5
- package/scripts/layer-legacy-frozen.test.ts +17 -13
- package/scripts/{layer-legacy-frozen.mjs → layer-legacy-frozen.ts} +128 -39
- package/scripts/release-config.test.ts +12 -4
- package/scripts/rules-see-files.test.mjs +1 -1
- package/scripts/test.test.ts +78 -0
- package/scripts/test.ts +87 -0
- package/skills/analyze-sibling-paper/SKILL.md +1 -39
- package/skills/analyze-sibling-paper/SKILL.md.spec.ts +0 -38
- package/skills/argument-arc/SKILL.md +2 -36
- package/skills/argument-arc/SKILL.md.spec.ts +1 -43
- package/skills/build-benchmark/SKILL.md +1 -41
- package/skills/build-benchmark/SKILL.md.spec.ts +0 -40
- package/skills/camera-ready/SKILL.md +1 -34
- package/skills/camera-ready/SKILL.md.spec.ts +0 -33
- package/skills/cold-read-diff/SKILL.md +1 -35
- package/skills/cold-read-diff/SKILL.md.spec.ts +0 -34
- package/skills/draft-paper/SKILL.md +2 -41
- package/skills/draft-paper/SKILL.md.spec.ts +1 -50
- package/skills/extend-paper/SKILL.md +2 -33
- package/skills/extend-paper/SKILL.md.spec.ts +1 -42
- package/skills/find-venue/SKILL.md +2 -38
- package/skills/find-venue/SKILL.md.spec.ts +1 -47
- package/skills/grade-paper-writing/SKILL.md +3 -38
- package/skills/grade-paper-writing/SKILL.md.spec.ts +2 -47
- package/skills/grade-paper-writing/prose-lint.mjs +3 -4
- package/skills/grade-paper-writing/{prose-lint.test.mjs → prose-lint.test.ts} +3 -3
- package/skills/harden-paper/SKILL.md +1 -39
- package/skills/harden-paper/SKILL.md.spec.ts +0 -38
- package/skills/map-prior-work/SKILL.md +1 -35
- package/skills/map-prior-work/SKILL.md.spec.ts +0 -34
- package/skills/osf-artifact-upload/{osf-artifact-upload.harness.mjs → osf-artifact-upload.harness.ts} +3 -4
- package/skills/paper-adversarial-review/SKILL.md +2 -35
- package/skills/paper-adversarial-review/SKILL.md.spec.ts +1 -43
- package/skills/paper-pipeline/SKILL.md +2 -28
- package/skills/paper-pipeline/SKILL.md.spec.ts +1 -27
- package/skills/paper-pipeline/description-language.eval.mjs +1 -1
- package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +1 -1
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +2 -2
- package/skills/paper-pipeline/paper-pipeline.harness.mjs +25 -40
- package/skills/paper-pipeline/pipeline-firing.eval.mjs +1 -1
- package/skills/paper-pipeline/pipeline-language.eval.mjs +1 -1
- package/skills/paper-pipeline/references/acceptance-gate.md +1 -6
- package/skills/paper-pipeline/references/pipeline-status-template.md +8 -35
- package/skills/paper-pipeline/scripts/README.md +30 -225
- package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +4 -8
- package/skills/paper-pipeline/scripts/artifact-coverage.mjs +1 -1
- package/skills/paper-pipeline/scripts/{artifact-coverage.test.mjs → artifact-coverage.test.ts} +2 -2
- package/skills/paper-pipeline/scripts/consumer.harness.mjs +6 -104
- package/skills/paper-pipeline/scripts/consumer.mjs +24 -109
- package/skills/paper-pipeline/scripts/consumer.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +1 -1
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +1 -4
- package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/generated-code.harness.mjs +2 -3
- package/skills/paper-pipeline/scripts/{generated-code.test.mjs → generated-code.test.ts} +3 -3
- package/skills/paper-pipeline/scripts/population-map.harness.mjs +3 -3
- package/skills/paper-pipeline/scripts/population-map.mjs +1 -4
- package/skills/paper-pipeline/scripts/population-map.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/{round-diff.harness.mjs → round-diff.harness.ts} +45 -18
- package/skills/paper-pipeline/scripts/round-diff.mjs +1 -1
- package/skills/paper-pipeline/scripts/round-diff.test.mjs +1 -1
- package/skills/paper-status/SKILL.md +2 -42
- package/skills/paper-status/SKILL.md.spec.ts +1 -41
- package/skills/pc-panel-review/SKILL.md +2 -46
- package/skills/pc-panel-review/SKILL.md.spec.ts +1 -45
- package/skills/plan-paper-timeline/SKILL.md +2 -36
- package/skills/plan-paper-timeline/SKILL.md.spec.ts +0 -36
- package/skills/plan-paper-timeline/fixtures/{fake-google-calendar.mjs → fake-google-calendar.ts} +101 -70
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +3 -3
- package/skills/render-paper/SKILL.md +1 -37
- package/skills/render-paper/SKILL.md.spec.ts +0 -36
- package/skills/render-paper/{extract-pdf-facts.harness.mjs → extract-pdf-facts.harness.ts} +34 -9
- package/skills/render-paper/render-paper.harness.mjs +2 -2
- package/skills/research-ideate/SKILL.md +2 -39
- package/skills/research-ideate/SKILL.md.spec.ts +1 -47
- package/skills/study-accepted-papers/SKILL.md +1 -34
- package/skills/study-accepted-papers/SKILL.md.spec.ts +0 -33
- package/skills/submit-paper/SKILL.md +1 -33
- package/skills/submit-paper/SKILL.md.spec.ts +0 -32
- package/skills/sweep-design-space/SKILL.md +1 -38
- package/skills/sweep-design-space/SKILL.md.spec.ts +0 -37
- package/skills/tighten-paper/SKILL.md +2 -39
- package/skills/tighten-paper/SKILL.md.spec.ts +1 -47
- package/skills/tighten-paper/structure.mjs +5 -12
- package/skills/tighten-paper/{structure.test.mjs → structure.test.ts} +4 -3
- package/skills/verify-citations/SKILL.md +2 -38
- package/skills/verify-citations/SKILL.md.spec.ts +1 -37
- package/skills/verify-citations/scripts/{bib-authors.cli.test.mjs → bib-authors.cli.test.ts} +14 -4
- package/skills/verify-citations/scripts/bib-authors.d.mts +63 -0
- package/skills/verify-citations/scripts/bib-authors.mjs +1 -4
- package/skills/verify-citations/scripts/{verify-cites.cli.test.mjs → verify-cites.cli.test.ts} +32 -9
- package/skills/verify-citations/scripts/verify-cites.d.mts +58 -0
- package/src/CLAUDE.md +1 -1
- package/src/adapters/banal/failure.ts +9 -10
- package/src/adapters/banal/index.test.ts +5 -8
- package/src/adapters/banal/index.ts +8 -7
- package/src/adapters/banal/install.ts +9 -4
- package/src/adapters/banal/invocation.test.ts +23 -9
- package/src/adapters/banal/invocation.ts +24 -9
- package/src/adapters/banal/locate.test.ts +2 -2
- package/src/adapters/banal/locate.ts +27 -21
- package/src/adapters/banal/output.test.ts +1 -1
- package/src/adapters/banal/run.test.ts +5 -5
- package/src/adapters/banal/run.ts +16 -10
- package/src/adapters/banal/settings.ts +2 -2
- package/src/adapters/banal/xml.test.ts +1 -1
- package/src/adapters/curl/download.test.ts +8 -6
- package/src/adapters/memory/index.ts +4 -3
- package/src/adapters/node/files.io.ts +2 -2
- package/src/adapters/node/files.test.ts +8 -4
- package/src/adapters/node/process.io.ts +13 -5
- package/src/adapters/node/process.test.ts +5 -5
- package/src/adapters/node/workspace.io.ts +9 -4
- package/src/adapters/node/workspace.test.ts +3 -1
- package/src/adapters/pdfjs/fill.test.ts +2 -2
- package/src/adapters/pdfjs/fill.ts +14 -7
- package/src/adapters/references/index.test.ts +31 -38
- package/src/adapters/references/index.ts +33 -60
- package/src/adapters/references/pool.ts +3 -5
- package/src/adapters/references/reach.io.ts +2 -1
- package/src/build-engine.harness.mjs +1 -1
- package/src/build-engine.test.ts +13 -8
- package/src/build-engine.ts +12 -7
- package/src/build.harness.mjs +2 -2
- package/src/build.test.ts +24 -24
- package/src/build.ts +34 -26
- package/src/caller-path.ts +13 -0
- package/src/cli.harness.mjs +6 -8
- package/src/cli.test.ts +31 -13
- package/src/cli.ts +117 -58
- package/src/doctor.harness.mjs +3 -3
- package/src/doctor.test.ts +1 -1
- package/src/doctor.ts +17 -7
- package/src/domain/lookup-cache.ts +21 -11
- package/src/domain/paths.test.ts +15 -0
- package/src/domain/paths.ts +20 -1
- package/src/domain/ready.ts +31 -0
- package/src/domain/record.test.ts +20 -0
- package/src/domain/record.ts +12 -0
- package/src/domain/result.test.ts +2 -2
- package/src/domain/sha256.ts +5 -3
- package/src/domain/text.test.ts +32 -0
- package/src/domain/text.ts +23 -0
- package/src/engine.harness.mjs +2 -2
- package/src/engine.ts +34 -5
- package/src/facts-file.test.ts +9 -7
- package/src/facts-file.ts +45 -29
- package/src/{hooks-settings.harness.mjs → hooks-settings.harness.ts} +56 -20
- package/src/hooks-settings.ts +48 -13
- package/src/init.test.ts +37 -14
- package/src/init.ts +48 -24
- package/src/{latex-log.harness.mjs → latex-log.harness.ts} +14 -14
- package/src/{latex-loop.harness.mjs → latex-loop.harness.ts} +64 -36
- package/src/latex-loop.ts +3 -3
- package/src/{link-skills.harness.mjs → link-skills.harness.ts} +19 -19
- package/src/link-skills.test.ts +3 -3
- package/src/link-skills.ts +5 -4
- package/src/lint-fix.test.ts +4 -6
- package/src/linted-set.test.ts +2 -2
- package/src/new-paper.harness.mjs +9 -25
- package/src/new-paper.test.ts +8 -10
- package/src/new-paper.ts +9 -5
- package/src/paper-files.test.ts +25 -24
- package/src/paper-files.ts +16 -27
- package/src/paper-settings-commands.test.ts +5 -4
- package/src/paper-settings.test.ts +7 -7
- package/src/paper-settings.ts +13 -14
- package/src/{pdf-facts.harness.mjs → pdf-facts.harness.ts} +9 -8
- package/src/pdf-facts.test.ts +25 -15
- package/src/pdf-facts.ts +81 -23
- package/src/{pdf-geometry.harness.mjs → pdf-geometry.harness.ts} +19 -12
- package/src/pdf-geometry.ts +8 -4
- package/src/ports/tool-installer.ts +2 -17
- package/src/presets.ts +31 -16
- package/src/references.test.ts +76 -62
- package/src/references.ts +18 -12
- package/src/{rules-config.harness.mjs → rules-config.harness.ts} +7 -13
- package/src/rules-config.ts +46 -14
- package/src/rules-off.test.ts +6 -4
- package/src/{structure.harness.mjs → structure.harness.ts} +26 -15
- package/src/structure.ts +1 -1
- package/src/{tex-requirements.harness.mjs → tex-requirements.harness.ts} +50 -22
- package/src/tex-requirements.test.ts +21 -0
- package/src/tex-requirements.ts +37 -11
- package/src/toolchain.harness.mjs +2 -2
- package/src/toolchain.test.ts +10 -6
- package/src/toolchain.ts +31 -17
- package/src/types.ts +5 -3
- package/src/venue-rules.test.ts +70 -68
- package/src/venue-rules.ts +22 -12
- package/tsconfig.json +6 -3
- package/tsconfig.pkg.json +10 -0
- package/lib/check.d.mts +0 -17
- package/lib/paper-config.d.mts +0 -31
- package/scripts/layer-legacy-frozen.d.mts +0 -36
- package/scripts/rules-are-content-only.harness.mjs +0 -105
- package/scripts/rules-are-content-only.mjs +0 -148
- package/scripts/rules-are-content-only.test.mjs +0 -61
- package/scripts/test.mjs +0 -44
- package/scripts/test.test.mjs +0 -52
- package/skills/paper-pipeline/scripts/announce.mjs +0 -67
- package/skills/paper-pipeline/scripts/announce.test.mjs +0 -102
- package/skills/paper-pipeline/scripts/fixtures/clean/PIPELINE-STATUS.md +0 -51
- package/skills/paper-pipeline/scripts/fixtures/dirty/PIPELINE-STATUS.md +0 -52
- package/skills/paper-pipeline/scripts/fixtures/dirty/paper.md +0 -6
- package/skills/paper-pipeline/scripts/fixtures/scorecard.mjs +0 -71
- package/skills/paper-pipeline/scripts/ledger.cli.test.mjs +0 -302
- package/skills/paper-pipeline/scripts/ledger.mjs +0 -623
- package/skills/paper-pipeline/scripts/ledger.test.mjs +0 -254
- package/skills/paper-pipeline/scripts/pipeline-check.cli.test.mjs +0 -474
- package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +0 -328
- package/skills/paper-pipeline/scripts/pipeline-check.mjs +0 -753
- package/skills/paper-pipeline/scripts/pipeline-edges.mjs +0 -169
- package/skills/paper-pipeline/scripts/pipeline-edges.test.mjs +0 -73
- package/skills/paper-pipeline/scripts/run-mechanical.mjs +0 -648
- package/skills/paper-pipeline/scripts/run-mechanical.test.mjs +0 -468
- package/skills/paper-pipeline/scripts/status.mjs +0 -295
- package/skills/paper-pipeline/scripts/status.test.mjs +0 -232
- /package/bin/{paperlint.test.mjs → paperlint.test.ts} +0 -0
- /package/lib/{skill-eval-fixture.mjs → skill-eval-fixture.ts} +0 -0
- /package/skills/grade-paper-writing/__snapshots__/{prose-lint.test.mjs.snap → prose-lint.test.ts.snap} +0 -0
- /package/skills/tighten-paper/__snapshots__/{structure.test.mjs.snap → structure.test.ts.snap} +0 -0
|
@@ -1,24 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tighten-paper
|
|
3
3
|
description: "Use when a paper \"feels long / bloated / overly complex / hard to read / doesn't deliver its point / the middle drags\" — or after review rounds deposited hedge-mass. The developmental / editorial pass on a DRAFTED paper: what a real human editor thinks reading it cover to cover — the ONE point, does every section earn its place, can it be SKIMMED, is it too long for its contribution, which sections are TMI that belong in the artifact/appendix, does the conclusion pay off the intro's promise. Produces a structural verdict + a concrete cut / fold / merge / reorder plan — NOT sentence fixes. Its verdict is a required INPUT to pc-panel-review and a hard gate in harden-paper. NOT sentence craft or a writing grade (grade-paper-writing), NOT jargon stalls (the persona stall pass), NOT scientific defects (pc-panel-review / paper-adversarial-review). Run it BEFORE those on a bloated draft — no point polishing sentences in a section that should be cut."
|
|
4
|
-
allowed-tools: [Read, Write, Edit, Grep, Glob, Agent
|
|
4
|
+
allowed-tools: [Read, Write, Edit, Grep, Glob, Agent]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
<!-- vigiles:sha256:
|
|
7
|
+
<!-- vigiles:sha256:fd00cde33ea4b81c compiled from skills/tighten-paper/SKILL.md.spec.ts -->
|
|
8
8
|
|
|
9
9
|
# tighten-paper — the developmental edit
|
|
10
10
|
|
|
11
|
-
## Run me
|
|
12
|
-
|
|
13
|
-
🔴 FIRST, before any other step:
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
node .claude/skills/paper-pipeline/scripts/announce.mjs tighten-paper <paper-dir>
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
An advisory pass cannot be observed failing — silence is both its error state and its normal
|
|
20
|
-
state — so starting is an event, and events get written down.
|
|
21
|
-
|
|
22
11
|
Sentence-level passes make a wall of text into readable text. They do **not** tell you the wall shouldn't be there
|
|
23
12
|
at all. This pass is the editor who reads the whole paper once and asks: *is this the right paper, at the right
|
|
24
13
|
length, delivering the right point?* Its deliverable is a **cut/fold/merge/reorder plan and a length verdict**, not
|
|
@@ -321,32 +310,6 @@ Produce:
|
|
|
321
310
|
- **The middle-sag diagnosis** and the fix (consolidate these findings, lead each with its point).
|
|
322
311
|
- **Skim-test failures** (headings/lead-sentences that don't carry the argument).
|
|
323
312
|
|
|
324
|
-
## Record the verdict
|
|
325
|
-
|
|
326
|
-
🔴 LAST step, once the deliverable exists:
|
|
327
|
-
|
|
328
|
-
```
|
|
329
|
-
node .claude/skills/paper-pipeline/scripts/ledger.mjs record tighten-paper <paper-dir> FINDING <count> <report-path>
|
|
330
|
-
node .claude/skills/paper-pipeline/scripts/ledger.mjs record tighten-paper <paper-dir> ABSTAINED <reason> "<one line>"
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
**FINDING** is the normal row — `<count>` is the number of CUT / FOLD / MERGE / REORDER actions in
|
|
334
|
-
the plan and `<report-path>` is the plan itself. Add `--blocking` when the paper is over its limit
|
|
335
|
-
and the plan cannot get it under.
|
|
336
|
-
**ABSTAINED** — `no-witness`: a cover-to-cover read produced no action at all, which on a real draft
|
|
337
|
-
should be rare. `blocked`: the declined pass of the section above — content is still being added, so
|
|
338
|
-
there was nothing to read whole.
|
|
339
|
-
|
|
340
|
-
🔴 **This is the check with the documented history of never saying no**: a tightening pass once
|
|
341
|
-
returned KEEP on 80 of 81 sections and still counted as run. It is also one of the three failures
|
|
342
|
-
that deleted `PASS` — the word made "I read it all and nothing moved" indistinguishable from "I did
|
|
343
|
-
not really read it". `status.mjs` reports a check that has never recorded a finding, on the
|
|
344
|
-
mutation-testing principle that a test which kills no mutant is not a test. So an `ABSTAINED
|
|
345
|
-
no-witness` here owes a note saying what was read and why nothing moved.
|
|
346
|
-
|
|
347
|
-
The mechanical half of this skill files separately, under `tighten-paper/structure` — one check, one
|
|
348
|
-
row. Do not record a `structure.mjs` result here.
|
|
349
|
-
|
|
350
313
|
## Compose
|
|
351
314
|
Run this FIRST on a draft that "feels bloated" — before grade-paper-writing / the stall pass / pc-panel, because
|
|
352
315
|
polishing sentences in a section that should be folded to the artifact is wasted work. After the plan is applied,
|
|
@@ -10,30 +10,10 @@ export default experimental_skill({
|
|
|
10
10
|
name: "tighten-paper",
|
|
11
11
|
description:
|
|
12
12
|
"Use when a paper \"feels long / bloated / overly complex / hard to read / doesn't deliver its point / the middle drags\" — or after review rounds deposited hedge-mass. The developmental / editorial pass on a DRAFTED paper: what a real human editor thinks reading it cover to cover — the ONE point, does every section earn its place, can it be SKIMMED, is it too long for its contribution, which sections are TMI that belong in the artifact/appendix, does the conclusion pay off the intro's promise. Produces a structural verdict + a concrete cut / fold / merge / reorder plan — NOT sentence fixes. Its verdict is a required INPUT to pc-panel-review and a hard gate in harden-paper. NOT sentence craft or a writing grade (grade-paper-writing), NOT jargon stalls (the persona stall pass), NOT scientific defects (pc-panel-review / paper-adversarial-review). Run it BEFORE those on a bloated draft — no point polishing sentences in a section that should be cut.",
|
|
13
|
-
tools: [
|
|
14
|
-
"Read",
|
|
15
|
-
"Write",
|
|
16
|
-
"Edit",
|
|
17
|
-
"Grep",
|
|
18
|
-
"Glob",
|
|
19
|
-
"Agent",
|
|
20
|
-
"Bash(node .claude/skills/paper-pipeline/scripts/announce.mjs:*)",
|
|
21
|
-
"Bash(node .claude/skills/paper-pipeline/scripts/ledger.mjs:*)",
|
|
22
|
-
],
|
|
13
|
+
tools: ["Read", "Write", "Edit", "Grep", "Glob", "Agent"],
|
|
23
14
|
body: `
|
|
24
15
|
# tighten-paper — the developmental edit
|
|
25
16
|
|
|
26
|
-
## Run me
|
|
27
|
-
|
|
28
|
-
🔴 FIRST, before any other step:
|
|
29
|
-
|
|
30
|
-
\`\`\`
|
|
31
|
-
node .claude/skills/paper-pipeline/scripts/announce.mjs tighten-paper <paper-dir>
|
|
32
|
-
\`\`\`
|
|
33
|
-
|
|
34
|
-
An advisory pass cannot be observed failing — silence is both its error state and its normal
|
|
35
|
-
state — so starting is an event, and events get written down.
|
|
36
|
-
|
|
37
17
|
Sentence-level passes make a wall of text into readable text. They do **not** tell you the wall shouldn't be there
|
|
38
18
|
at all. This pass is the editor who reads the whole paper once and asks: *is this the right paper, at the right
|
|
39
19
|
length, delivering the right point?* Its deliverable is a **cut/fold/merge/reorder plan and a length verdict**, not
|
|
@@ -336,32 +316,6 @@ Produce:
|
|
|
336
316
|
- **The middle-sag diagnosis** and the fix (consolidate these findings, lead each with its point).
|
|
337
317
|
- **Skim-test failures** (headings/lead-sentences that don't carry the argument).
|
|
338
318
|
|
|
339
|
-
## Record the verdict
|
|
340
|
-
|
|
341
|
-
🔴 LAST step, once the deliverable exists:
|
|
342
|
-
|
|
343
|
-
\`\`\`
|
|
344
|
-
node .claude/skills/paper-pipeline/scripts/ledger.mjs record tighten-paper <paper-dir> FINDING <count> <report-path>
|
|
345
|
-
node .claude/skills/paper-pipeline/scripts/ledger.mjs record tighten-paper <paper-dir> ABSTAINED <reason> "<one line>"
|
|
346
|
-
\`\`\`
|
|
347
|
-
|
|
348
|
-
**FINDING** is the normal row — \`<count>\` is the number of CUT / FOLD / MERGE / REORDER actions in
|
|
349
|
-
the plan and \`<report-path>\` is the plan itself. Add \`--blocking\` when the paper is over its limit
|
|
350
|
-
and the plan cannot get it under.
|
|
351
|
-
**ABSTAINED** — \`no-witness\`: a cover-to-cover read produced no action at all, which on a real draft
|
|
352
|
-
should be rare. \`blocked\`: the declined pass of the section above — content is still being added, so
|
|
353
|
-
there was nothing to read whole.
|
|
354
|
-
|
|
355
|
-
🔴 **This is the check with the documented history of never saying no**: a tightening pass once
|
|
356
|
-
returned KEEP on 80 of 81 sections and still counted as run. It is also one of the three failures
|
|
357
|
-
that deleted \`PASS\` — the word made "I read it all and nothing moved" indistinguishable from "I did
|
|
358
|
-
not really read it". \`status.mjs\` reports a check that has never recorded a finding, on the
|
|
359
|
-
mutation-testing principle that a test which kills no mutant is not a test. So an \`ABSTAINED
|
|
360
|
-
no-witness\` here owes a note saying what was read and why nothing moved.
|
|
361
|
-
|
|
362
|
-
The mechanical half of this skill files separately, under \`tighten-paper/structure\` — one check, one
|
|
363
|
-
row. Do not record a \`structure.mjs\` result here.
|
|
364
|
-
|
|
365
319
|
## Compose
|
|
366
320
|
Run this FIRST on a draft that "feels bloated" — before grade-paper-writing / the stall pass / pc-panel, because
|
|
367
321
|
polishing sentences in a section that should be folded to the artifact is wasted work. After the plan is applied,
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*
|
|
23
23
|
* ── 2026-08-26, EVENING: BOTH NAMED CONSUMERS ARE CLOSED, AND A THIRD ONE TURNED UP ────────
|
|
24
24
|
*
|
|
25
|
-
* The paragraph above named two callers and said that until they are converted,
|
|
25
|
+
* The paragraph above named two callers and said that until they are converted, they read
|
|
26
26
|
* "no findings" BY SILENCE. That was true and measured: on `the reference paper/paper.md`
|
|
27
27
|
* `--flags-only` printed 0 lines, while the same bytes through ESLint give **6 findings**
|
|
28
28
|
* (`subsection-size` ×2 · `section-lead` · `free-section-size` ×2 · `block-ungraded`).
|
|
@@ -31,11 +31,6 @@
|
|
|
31
31
|
* stood next to it, running the same rules over the same glob; a measurement showed a
|
|
32
32
|
* byte-identical list of files. The full justification is in the comment where the removed
|
|
33
33
|
* step used to be.
|
|
34
|
-
* `run-mechanical.mjs`, the `tighten-paper/structure` line → CONVERTED to ESLint
|
|
35
|
-
* (`read: 'eslint'`, filtered by the rules from `eslint-rules/paper-structure.mjs`). Not
|
|
36
|
-
* deleted: ESLint writes nothing into the ledger, and the ledger is the only place where the
|
|
37
|
-
* fact "somebody looked at this paper's structure on these bytes" is stored with a hash of the
|
|
38
|
-
* input.
|
|
39
34
|
*
|
|
40
35
|
* 🔴 A THIRD CALLER THE BANNER DID NOT KNOW ABOUT: `.githooks/pre-commit`. It printed nothing and
|
|
41
36
|
* swallowed the exit code through `|| true`. The lesson is exactly the one already recorded in
|
|
@@ -97,7 +92,7 @@ import {
|
|
|
97
92
|
stripFences,
|
|
98
93
|
requireMarkdown,
|
|
99
94
|
stripFrontmatter,
|
|
100
|
-
} from "
|
|
95
|
+
} from "#lib/markdown";
|
|
101
96
|
|
|
102
97
|
// Markup is parsed with a PARSER (`CLAUDE.md`, 2026-08-11). We fail rather than degrade: without a
|
|
103
98
|
// parser not one section would be found in the paper, `--flags-only` would return 0 findings and
|
|
@@ -294,11 +289,9 @@ const freeTotal = freeSections.reduce((a, r) => a + r.words, 0);
|
|
|
294
289
|
// edit, and there is nothing to settle the question of which one is right.
|
|
295
290
|
|
|
296
291
|
if (flagsOnly) {
|
|
297
|
-
// Silence and exit code 0 — ALWAYS. Deliberately not one line on stdout/stderr:
|
|
298
|
-
//
|
|
299
|
-
//
|
|
300
|
-
// in the ledger. The limitation of this mode is named in the banner at the top; it is cured by
|
|
301
|
-
// converting both callers to `npx eslint`, not here.
|
|
292
|
+
// Silence and exit code 0 — ALWAYS. Deliberately not one line on stdout/stderr: a caller that
|
|
293
|
+
// reads "non-empty output = a finding" would turn a pointer saying "the checks have moved" into a
|
|
294
|
+
// permanent false finding. The limitation of this mode is named in the banner at the top.
|
|
302
295
|
process.exit(0);
|
|
303
296
|
}
|
|
304
297
|
|
|
@@ -6,11 +6,12 @@ import assert from "node:assert/strict";
|
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { expect, test } from "vitest";
|
|
9
|
-
import { runNode, useTempDir, writeTree } from "../../test/support.
|
|
9
|
+
import { runNode, useTempDir, writeTree } from "../../test/support.ts";
|
|
10
10
|
|
|
11
11
|
const SCRIPT = join(dirname(fileURLToPath(import.meta.url)), "structure.mjs");
|
|
12
12
|
const root = useTempDir("structure-");
|
|
13
|
-
const words = (n
|
|
13
|
+
const words = (n: number) =>
|
|
14
|
+
Array.from({ length: n }, (_, i) => `w${String(i)}`).join(" ");
|
|
14
15
|
|
|
15
16
|
writeTree(root, {
|
|
16
17
|
// Every row shape: a sub-section before any section, carries notes with a score and a verdict,
|
|
@@ -69,7 +70,7 @@ ${words(30)}
|
|
|
69
70
|
"body-only.md": `## 1 Intro\n${words(8)}\n\n## 2 Rest\n${words(4)}\n\n## 3 Empty\n`,
|
|
70
71
|
});
|
|
71
72
|
|
|
72
|
-
const run = (...args) => {
|
|
73
|
+
const run = (...args: string[]) => {
|
|
73
74
|
const r = runNode(
|
|
74
75
|
SCRIPT,
|
|
75
76
|
args.map((a) => (a.startsWith("--") ? a : join(root, a))),
|
|
@@ -4,21 +4,10 @@ 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:fe3634eb63db5c0d compiled from skills/verify-citations/SKILL.md.spec.ts -->
|
|
8
8
|
|
|
9
9
|
# verify-citations — every cite real, the delta explicit
|
|
10
10
|
|
|
11
|
-
## Run me
|
|
12
|
-
|
|
13
|
-
🔴 FIRST, before any other step:
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
node .claude/skills/paper-pipeline/scripts/announce.mjs verify-citations <paper-dir>
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
An advisory pass cannot be observed failing — silence is both its error state and its normal
|
|
20
|
-
state — so starting is an event, and events get written down.
|
|
21
|
-
|
|
22
11
|
Reviewers reject on a single fabricated or wrong citation — it reads as either sloppiness or a
|
|
23
12
|
hallucinated bibliography, and either one is fatal. And they reject on "this was already done by ___" if
|
|
24
13
|
your delta over the nearest prior work isn't spelled out. This skill closes both holes before submit.
|
|
@@ -30,8 +19,7 @@ bibliography changes, including the edit you make on the last day. Scorecard row
|
|
|
30
19
|
`<paper-dir>/PIPELINE-STATUS.md`; see the CONTINUOUS section of `paper-pipeline`.
|
|
31
20
|
|
|
32
21
|
- **A pass dated before the current text is STALE, and stale reads as green.** The date in the `cites`
|
|
33
|
-
row is compared against the paper's own mtime
|
|
34
|
-
mismatch — because a `☑` from three days and forty edits ago is worse than a `☐`: it looks closed.
|
|
22
|
+
row is compared against the paper's own mtime — because a `☑` from three days and forty edits ago is worse than a `☐`: it looks closed.
|
|
35
23
|
- **"We checked last cycle" does not count.** Say it plainly, because it is the exact sentence that
|
|
36
24
|
ships a fabricated cite: a review round adds text, added text adds citations, and the run that
|
|
37
25
|
cleared the bibliography never saw them.
|
|
@@ -294,30 +282,6 @@ therefore being checked for the first time.
|
|
|
294
282
|
fails CI if any line or branch of them goes unexercised. Run `npx vitest run src/references.test.ts`
|
|
295
283
|
after any edit to the rules.
|
|
296
284
|
|
|
297
|
-
## 6. Record the verdict
|
|
298
|
-
|
|
299
|
-
🔴 LAST step, once the deliverable exists:
|
|
300
|
-
|
|
301
|
-
```
|
|
302
|
-
node .claude/skills/paper-pipeline/scripts/ledger.mjs record verify-citations <paper-dir> FINDING <count> <report-path>
|
|
303
|
-
node .claude/skills/paper-pipeline/scripts/ledger.mjs record verify-citations <paper-dir> ABSTAINED <reason> "<one line>"
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
**FINDING** — `<count>` is the number of cites still `% VERIFY` or `unresolvable`; `<report-path>`
|
|
307
|
-
lists them. Add `--blocking` for a cite an authoritative source disproves, or a missing delta
|
|
308
|
-
sentence; either is a submit blocker.
|
|
309
|
-
**ABSTAINED** — `no-witness`: every cite resolved with correct metadata and the delta sentence is in
|
|
310
|
-
the prose. `crashed`: the registries were unreachable, so nothing was verified — which is emphatically
|
|
311
|
-
not the same as everything checking out, and under the old vocabulary both came back `PASS`.
|
|
312
|
-
|
|
313
|
-
🔴 **There is no PASS.** `no-witness` here means every cite the paper makes was looked up and none
|
|
314
|
-
of them came back wrong. It does not mean the bibliography is complete: this check cannot see a work
|
|
315
|
-
that should have been cited and is not. That gap belongs to `map-prior-work`, and no row here covers it.
|
|
316
|
-
|
|
317
|
-
Because this check is CONTINUOUS, it writes a row per run and never a final one. That is the point:
|
|
318
|
-
the ledger compares the row against the paper's current bytes, so a pass that predates the last three
|
|
319
|
-
`\cite` additions reports as STALE-PAPER instead of reading as green.
|
|
320
|
-
|
|
321
285
|
## Rules
|
|
322
286
|
- **Re-run on every `\cite` added or moved.** A `cites` row older than the paper's text is a stale pass,
|
|
323
287
|
not a green one — "we checked last cycle" is not a verification.
|
|
@@ -24,17 +24,6 @@ export default experimental_skill({
|
|
|
24
24
|
body: `
|
|
25
25
|
# verify-citations — every cite real, the delta explicit
|
|
26
26
|
|
|
27
|
-
## Run me
|
|
28
|
-
|
|
29
|
-
🔴 FIRST, before any other step:
|
|
30
|
-
|
|
31
|
-
\`\`\`
|
|
32
|
-
node .claude/skills/paper-pipeline/scripts/announce.mjs verify-citations <paper-dir>
|
|
33
|
-
\`\`\`
|
|
34
|
-
|
|
35
|
-
An advisory pass cannot be observed failing — silence is both its error state and its normal
|
|
36
|
-
state — so starting is an event, and events get written down.
|
|
37
|
-
|
|
38
27
|
Reviewers reject on a single fabricated or wrong citation — it reads as either sloppiness or a
|
|
39
28
|
hallucinated bibliography, and either one is fatal. And they reject on "this was already done by ___" if
|
|
40
29
|
your delta over the nearest prior work isn't spelled out. This skill closes both holes before submit.
|
|
@@ -46,8 +35,7 @@ bibliography changes, including the edit you make on the last day. Scorecard row
|
|
|
46
35
|
\`<paper-dir>/PIPELINE-STATUS.md\`; see the CONTINUOUS section of \`paper-pipeline\`.
|
|
47
36
|
|
|
48
37
|
- **A pass dated before the current text is STALE, and stale reads as green.** The date in the \`cites\`
|
|
49
|
-
row is compared against the paper's own mtime
|
|
50
|
-
mismatch — because a \`☑\` from three days and forty edits ago is worse than a \`☐\`: it looks closed.
|
|
38
|
+
row is compared against the paper's own mtime — because a \`☑\` from three days and forty edits ago is worse than a \`☐\`: it looks closed.
|
|
51
39
|
- **"We checked last cycle" does not count.** Say it plainly, because it is the exact sentence that
|
|
52
40
|
ships a fabricated cite: a review round adds text, added text adds citations, and the run that
|
|
53
41
|
cleared the bibliography never saw them.
|
|
@@ -310,30 +298,6 @@ therefore being checked for the first time.
|
|
|
310
298
|
fails CI if any line or branch of them goes unexercised. Run \`npx vitest run src/references.test.ts\`
|
|
311
299
|
after any edit to the rules.
|
|
312
300
|
|
|
313
|
-
## 6. Record the verdict
|
|
314
|
-
|
|
315
|
-
🔴 LAST step, once the deliverable exists:
|
|
316
|
-
|
|
317
|
-
\`\`\`
|
|
318
|
-
node .claude/skills/paper-pipeline/scripts/ledger.mjs record verify-citations <paper-dir> FINDING <count> <report-path>
|
|
319
|
-
node .claude/skills/paper-pipeline/scripts/ledger.mjs record verify-citations <paper-dir> ABSTAINED <reason> "<one line>"
|
|
320
|
-
\`\`\`
|
|
321
|
-
|
|
322
|
-
**FINDING** — \`<count>\` is the number of cites still \`% VERIFY\` or \`unresolvable\`; \`<report-path>\`
|
|
323
|
-
lists them. Add \`--blocking\` for a cite an authoritative source disproves, or a missing delta
|
|
324
|
-
sentence; either is a submit blocker.
|
|
325
|
-
**ABSTAINED** — \`no-witness\`: every cite resolved with correct metadata and the delta sentence is in
|
|
326
|
-
the prose. \`crashed\`: the registries were unreachable, so nothing was verified — which is emphatically
|
|
327
|
-
not the same as everything checking out, and under the old vocabulary both came back \`PASS\`.
|
|
328
|
-
|
|
329
|
-
🔴 **There is no PASS.** \`no-witness\` here means every cite the paper makes was looked up and none
|
|
330
|
-
of them came back wrong. It does not mean the bibliography is complete: this check cannot see a work
|
|
331
|
-
that should have been cited and is not. That gap belongs to \`map-prior-work\`, and no row here covers it.
|
|
332
|
-
|
|
333
|
-
Because this check is CONTINUOUS, it writes a row per run and never a final one. That is the point:
|
|
334
|
-
the ledger compares the row against the paper's current bytes, so a pass that predates the last three
|
|
335
|
-
\`\\cite\` additions reports as STALE-PAPER instead of reading as green.
|
|
336
|
-
|
|
337
301
|
## Rules
|
|
338
302
|
- **Re-run on every \`\\cite\` added or moved.** A \`cites\` row older than the paper's text is a stale pass,
|
|
339
303
|
not a green one — "we checked last cycle" is not a verification.
|
package/skills/verify-citations/scripts/{bib-authors.cli.test.mjs → bib-authors.cli.test.ts}
RENAMED
|
@@ -9,7 +9,12 @@ import { join } from "node:path";
|
|
|
9
9
|
import { dirname } from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import { test } from "vitest";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
parseJson,
|
|
14
|
+
runNode,
|
|
15
|
+
useTempDir,
|
|
16
|
+
writeTree,
|
|
17
|
+
} from "../../../test/support.ts";
|
|
13
18
|
|
|
14
19
|
const SCRIPT = join(dirname(fileURLToPath(import.meta.url)), "bib-authors.mjs");
|
|
15
20
|
const root = useTempDir("bib-authors-cli-");
|
|
@@ -19,7 +24,12 @@ const DBLP_ANSWERS = {
|
|
|
19
24
|
"Swapped Order": [["Ada Lovelace", "Alan Turing"]],
|
|
20
25
|
"All Good": [["Ada Lovelace"]],
|
|
21
26
|
};
|
|
22
|
-
const bib = (
|
|
27
|
+
const bib = (
|
|
28
|
+
key: string,
|
|
29
|
+
author: string,
|
|
30
|
+
title: string,
|
|
31
|
+
venue = "booktitle = {Proc. X}",
|
|
32
|
+
) =>
|
|
23
33
|
`@inproceedings{${key}, author = {${author}}, title = {${title}}, ${venue}}\n`;
|
|
24
34
|
|
|
25
35
|
writeTree(root, {
|
|
@@ -54,7 +64,7 @@ writeTree(root, {
|
|
|
54
64
|
"empty/.keep": "",
|
|
55
65
|
});
|
|
56
66
|
|
|
57
|
-
const run = (...args) => {
|
|
67
|
+
const run = (...args: string[]) => {
|
|
58
68
|
const r = runNode(SCRIPT, args, {
|
|
59
69
|
nodeArgs: ["--import", join(root, "dblp.mjs")],
|
|
60
70
|
});
|
|
@@ -126,7 +136,7 @@ test("a paper directory: the canonical paper.bib is read, and each difference is
|
|
|
126
136
|
test("two .tex files and no paper.tex: the first by name, said out loud; its filecontents is read", () => {
|
|
127
137
|
const r = run(join(root, "twotex"), "--json");
|
|
128
138
|
assert.deepEqual(
|
|
129
|
-
{ status: r.status, stderr: r.stderr, report:
|
|
139
|
+
{ status: r.status, stderr: r.stderr, report: parseJson(r.stdout) },
|
|
130
140
|
{
|
|
131
141
|
status: 0,
|
|
132
142
|
stderr:
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Types for bib-authors.mjs, for the exports `src/` imports — the same arrangement as
|
|
2
|
+
// `verify-cites.d.mts` beside it. Only what `src/` imports is declared.
|
|
3
|
+
|
|
4
|
+
/** One entry as `parseBib` returns it; a field the entry lacks is "". */
|
|
5
|
+
export interface BibEntry {
|
|
6
|
+
readonly type: string;
|
|
7
|
+
readonly key: string;
|
|
8
|
+
readonly author: string;
|
|
9
|
+
readonly title: string;
|
|
10
|
+
readonly booktitle: string;
|
|
11
|
+
readonly journal: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export declare function parseBib(text: string): BibEntry[];
|
|
15
|
+
|
|
16
|
+
/** True when the author list ends in `and others` — there is nothing complete to compare. */
|
|
17
|
+
export declare const truncated: (authorField: string) => boolean;
|
|
18
|
+
|
|
19
|
+
/** True when the entry names a venue that is not a preprint server. */
|
|
20
|
+
export declare function claimsPublished(
|
|
21
|
+
entry: Pick<BibEntry, "booktitle" | "journal">,
|
|
22
|
+
): boolean;
|
|
23
|
+
|
|
24
|
+
/** One DBLP search hit, as `dblpHits` normalises it. */
|
|
25
|
+
export interface DblpHit {
|
|
26
|
+
readonly venue: string;
|
|
27
|
+
readonly year: string;
|
|
28
|
+
readonly type: string;
|
|
29
|
+
readonly title: string;
|
|
30
|
+
readonly authors: readonly string[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** DBLP's hits for a title; throws on a failed request (with `retryable` for a 429). */
|
|
34
|
+
export declare function dblpHits(title: string): Promise<DblpHit[]>;
|
|
35
|
+
|
|
36
|
+
export interface AuthorFinding {
|
|
37
|
+
readonly key: string;
|
|
38
|
+
readonly venue: string;
|
|
39
|
+
readonly missing: readonly string[];
|
|
40
|
+
readonly extra: readonly string[];
|
|
41
|
+
readonly orderDiffers: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface KeyWhy {
|
|
45
|
+
readonly key: string;
|
|
46
|
+
readonly why: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** The buckets of one comparison; an entry in none of them was a preprint entry. */
|
|
50
|
+
export interface AuthorBuckets {
|
|
51
|
+
readonly findings: readonly AuthorFinding[];
|
|
52
|
+
readonly skipped: readonly KeyWhy[];
|
|
53
|
+
readonly unchecked: readonly KeyWhy[];
|
|
54
|
+
readonly matched: readonly string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export declare function checkAuthors(
|
|
58
|
+
parsed: readonly BibEntry[],
|
|
59
|
+
options?: {
|
|
60
|
+
readonly lookup?: (title: string) => Promise<readonly DblpHit[]>;
|
|
61
|
+
readonly pause?: (ms: number) => Promise<void> | void;
|
|
62
|
+
},
|
|
63
|
+
): Promise<AuthorBuckets>;
|
|
@@ -45,10 +45,7 @@
|
|
|
45
45
|
*/
|
|
46
46
|
import { readFileSync, existsSync, readdirSync } from "node:fs";
|
|
47
47
|
import { join, extname } from "node:path";
|
|
48
|
-
import {
|
|
49
|
-
headings as mdHeadings,
|
|
50
|
-
requireMarkdown,
|
|
51
|
-
} from "../../../lib/markdown.mjs";
|
|
48
|
+
import { headings as mdHeadings, requireMarkdown } from "#lib/markdown";
|
|
52
49
|
import { isMain } from "../../paper-pipeline/scripts/consumer.mjs";
|
|
53
50
|
|
|
54
51
|
const DBLP = "https://dblp.org/search/publ/api";
|
package/skills/verify-citations/scripts/{verify-cites.cli.test.mjs → verify-cites.cli.test.ts}
RENAMED
|
@@ -9,7 +9,23 @@ import { readFileSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
|
9
9
|
import { dirname, join } from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import { test } from "vitest";
|
|
12
|
-
import {
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import {
|
|
14
|
+
runNode,
|
|
15
|
+
useTempDir,
|
|
16
|
+
writeTree,
|
|
17
|
+
type ScriptResult,
|
|
18
|
+
} from "../../../test/support.ts";
|
|
19
|
+
|
|
20
|
+
/** The verdicts `verify-cites --json`… prints: one per citation, with the fields these tests read. */
|
|
21
|
+
const Verdicts = z.array(
|
|
22
|
+
z.looseObject({
|
|
23
|
+
id: z.unknown().optional(),
|
|
24
|
+
verdict: z.unknown().optional(),
|
|
25
|
+
flags: z.array(z.unknown()).nullish(),
|
|
26
|
+
}),
|
|
27
|
+
);
|
|
28
|
+
const Cache = z.record(z.string(), z.unknown());
|
|
13
29
|
|
|
14
30
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
15
31
|
const root = useTempDir("verify-cites-cli-");
|
|
@@ -48,12 +64,17 @@ writeTree(root, {
|
|
|
48
64
|
" return new Response(null, { status: 404 });\n};\n",
|
|
49
65
|
"consumer/paperlint.json": JSON.stringify({ contactEmail: "me@example.org" }),
|
|
50
66
|
});
|
|
51
|
-
const offline = (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
67
|
+
const offline = (
|
|
68
|
+
args: readonly string[],
|
|
69
|
+
opts: { input?: string } = {},
|
|
70
|
+
): ScriptResult => runNode(SCRIPT, [...args, "--offline"], { env, ...opts });
|
|
71
|
+
const parsed = (r: ScriptResult) => ({
|
|
72
|
+
...r,
|
|
73
|
+
stdout: Verdicts.parse(JSON.parse(r.stdout)),
|
|
74
|
+
});
|
|
75
|
+
const SUMMARY_PASS = (n: number, t: number, u: number, flagged: number) =>
|
|
76
|
+
`\nverify-cites: ${String(n)} citation(s) — ${String(t)} true, 0 false (fabrication), ${String(u)} unresolvable` +
|
|
77
|
+
(flagged ? `, ${String(flagged)} with hygiene flag(s)` : "") +
|
|
57
78
|
"\nPASS — no fabrication (unresolvable is advisory)\n";
|
|
58
79
|
|
|
59
80
|
test("-h is --help", () => {
|
|
@@ -118,9 +139,11 @@ test("online, a DOI the authority does not know is FALSE: FAIL, exit 1, and the
|
|
|
118
139
|
assert.deepEqual(
|
|
119
140
|
{
|
|
120
141
|
status: r.status,
|
|
121
|
-
verdicts: JSON.parse(r.stdout).map((x) => x.verdict),
|
|
142
|
+
verdicts: Verdicts.parse(JSON.parse(r.stdout)).map((x) => x.verdict),
|
|
122
143
|
tail: r.stderr.trim().split("\n").pop(),
|
|
123
|
-
cached: Object.keys(
|
|
144
|
+
cached: Object.keys(
|
|
145
|
+
Cache.parse(JSON.parse(readFileSync(CACHE, "utf8"))),
|
|
146
|
+
).sort(),
|
|
124
147
|
},
|
|
125
148
|
{
|
|
126
149
|
status: 1,
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Types for verify-cites.mjs, for the exports `src/` imports — the same arrangement as
|
|
2
|
+
// `skills/paper-pipeline/scripts/consumer.d.mts`. Without this file TypeScript sees an untyped
|
|
3
|
+
// module, and every import needed a `@ts-expect-error` plus a cast at each call.
|
|
4
|
+
//
|
|
5
|
+
// Only what `src/` imports is declared. The `.mjs` is not typechecked, so a declaration nobody
|
|
6
|
+
// compiles against would drift without anyone noticing.
|
|
7
|
+
|
|
8
|
+
/** One entry as `parseBib` returns it: the BibTeX key as `id`, plus the fields it recognised. */
|
|
9
|
+
export interface Citation {
|
|
10
|
+
readonly id: string;
|
|
11
|
+
readonly [field: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Every entry of a BibTeX text, `@comment`/`@string`/`@preamble` skipped. */
|
|
15
|
+
export declare function parseBib(text: string): Citation[];
|
|
16
|
+
|
|
17
|
+
/** The title reduced to what identifies it: case, braces and punctuation removed. */
|
|
18
|
+
export declare function titleIdentity(title: string): string;
|
|
19
|
+
|
|
20
|
+
/** Answers the resolvers gave, keyed by request — shared between citations and runs. */
|
|
21
|
+
export type ResponseCache = Record<string, unknown>;
|
|
22
|
+
|
|
23
|
+
/** Would verifying `citation` make a request, given `cache`? Answers from the cache only. */
|
|
24
|
+
export declare function wouldAsk(
|
|
25
|
+
citation: Citation,
|
|
26
|
+
cache: ResponseCache,
|
|
27
|
+
): Promise<boolean>;
|
|
28
|
+
|
|
29
|
+
/** One run's memory of which service refused: a refusing service is not asked again. */
|
|
30
|
+
export interface Breaker {
|
|
31
|
+
call<T>(
|
|
32
|
+
service: string,
|
|
33
|
+
attempt: () => Promise<
|
|
34
|
+
{ ok: true; value: T } | { ok: false; reason: string }
|
|
35
|
+
>,
|
|
36
|
+
): Promise<{ ok: true; value: T } | { ok: false; reason: string }>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export declare function createBreaker(): Breaker;
|
|
40
|
+
|
|
41
|
+
/** The verdict on one citation. */
|
|
42
|
+
export interface CiteVerdict {
|
|
43
|
+
readonly id: string;
|
|
44
|
+
readonly verdict: "true" | "false" | "unresolvable";
|
|
45
|
+
readonly reason?: string;
|
|
46
|
+
/** "<service>: <reason>" for each question a refusing service did not answer. */
|
|
47
|
+
readonly refused?: readonly string[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Verify ONE citation live, answering from `cache` first and writing new answers into it. */
|
|
51
|
+
export declare function verifyCitationLive(
|
|
52
|
+
citation: Citation,
|
|
53
|
+
options?: {
|
|
54
|
+
readonly cache?: ResponseCache;
|
|
55
|
+
readonly offline?: boolean;
|
|
56
|
+
readonly breaker?: Breaker;
|
|
57
|
+
},
|
|
58
|
+
): Promise<CiteVerdict>;
|
package/src/CLAUDE.md
CHANGED
|
@@ -35,7 +35,7 @@ through the `io` file category in `boundaries/dependencies`; the `process` and `
|
|
|
35
35
|
carries `// eslint-disable-next-line <rule> -- legacy <I/O|layer>, moves behind a port in #76`.
|
|
36
36
|
Unused directives are errors (`reportUnusedDisableDirectives`), so a fixed site must drop its
|
|
37
37
|
comment; the per-file counts are frozen in `scripts/layer-legacy.frozen.json` and checked by
|
|
38
|
-
`scripts/layer-legacy-frozen.
|
|
38
|
+
`scripts/layer-legacy-frozen.ts` (`npm run check`, CI), which counts ESLint's own
|
|
39
39
|
`suppressedMessages` and fails when a file's count grows, a file not in the list carries one, or
|
|
40
40
|
anything under `src/domain/`, `src/ports/` or `src/adapters/` does. A NEW module that needs the
|
|
41
41
|
world is a new `*.io.ts` in the adapter of the program it talks to — never a new directive.
|
|
@@ -59,9 +59,8 @@ function missing(m: BanalMissing): string {
|
|
|
59
59
|
|
|
60
60
|
type Kind = BanalFailure["kind"];
|
|
61
61
|
/** One sentence-writer per variant. A mapped type over `kind`, so a new variant without one is a compile error. */
|
|
62
|
-
type
|
|
63
|
-
|
|
64
|
-
};
|
|
62
|
+
type FailureOf = { readonly [K in Kind]: Extract<BanalFailure, { kind: K }> };
|
|
63
|
+
type Describers = { readonly [K in Kind]: (f: FailureOf[K]) => Lines };
|
|
65
64
|
|
|
66
65
|
const orNone = (s: string): string => s || "no output";
|
|
67
66
|
|
|
@@ -101,15 +100,15 @@ const DESCRIBE: Describers = {
|
|
|
101
100
|
};
|
|
102
101
|
|
|
103
102
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* The one assertion below narrows the table's entry for `f.kind` to a function of `f`: the mapped
|
|
107
|
-
* type already pairs each kind with its own variant, and TypeScript cannot correlate the index with
|
|
108
|
-
* the argument on its own.
|
|
103
|
+
* The describer for `kind`, applied to its own variant. Generic over the kind, so the table's entry
|
|
104
|
+
* and the argument are correlated through `FailureOf[K]` and need no assertion (TypeScript#47109).
|
|
109
105
|
*/
|
|
106
|
+
const describeKind = <K extends Kind>(kind: K, f: FailureOf[K]): Lines =>
|
|
107
|
+
DESCRIBE[kind](f);
|
|
108
|
+
|
|
109
|
+
/** What to tell a person. Most failures are one line; a sha256 mismatch shows both hashes. */
|
|
110
110
|
export function describe(f: BanalFailure): Lines {
|
|
111
|
-
|
|
112
|
-
return write(f);
|
|
111
|
+
return describeKind(f.kind, f);
|
|
113
112
|
}
|
|
114
113
|
|
|
115
114
|
/** `describe` as one line, for a caller that has a single line to fill. */
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* `adapters/banal/index.ts` — banal as the two ports the app sees. What `run.test.ts` checks in
|
|
3
3
|
* banal's own terms is checked here as the domain receives it: a `Geometry` whose provenance names
|
|
4
4
|
* banal and the rule that found it, a reason that is one line of text, a `Ready` that says what was
|
|
5
|
-
* verified. In-memory ports throughout; the real banal is `test/e2e/banal.
|
|
5
|
+
* verified. In-memory ports throughout; the real banal is `test/e2e/banal.ts`.
|
|
6
6
|
*/
|
|
7
7
|
import assert from "node:assert/strict";
|
|
8
8
|
import { test } from "vitest";
|
|
9
9
|
import { whyNoGeometry } from "../../domain/geometry.ts";
|
|
10
|
-
import
|
|
10
|
+
import { absolutePath } from "../../domain/paths.ts";
|
|
11
11
|
import type { ProcessExit } from "../../ports/process.ts";
|
|
12
12
|
import { sha256Hex } from "../../domain/sha256.ts";
|
|
13
13
|
import {
|
|
@@ -21,7 +21,7 @@ import type { Ready } from "../../ports/tool-installer.ts";
|
|
|
21
21
|
import { banalInstaller, banalMeasurer, parseBanalSettings } from "./index.ts";
|
|
22
22
|
|
|
23
23
|
const dirs = { home: "/h", tmp: "/t", cwd: "/r" };
|
|
24
|
-
const project = "/r"
|
|
24
|
+
const project = absolutePath("/r");
|
|
25
25
|
const MEASURED =
|
|
26
26
|
'{"papersize":[792,612],"columns":2,"bodyfontsize":9,"pages":[{},{"type":"bib","reffontsize":7}]}';
|
|
27
27
|
|
|
@@ -44,15 +44,12 @@ test("with $BANAL: banal gets the .xml and a quoted $PDFTOHTML, and the geometry
|
|
|
44
44
|
const { m, run } = measurer({ BANAL: "/own/banal" });
|
|
45
45
|
const g = m.measure([]);
|
|
46
46
|
assert.equal(g.kind, "measured");
|
|
47
|
-
assert.deepEqual(g.
|
|
47
|
+
assert.deepEqual(g.by, {
|
|
48
48
|
tool: "banal",
|
|
49
49
|
path: "/own/banal",
|
|
50
50
|
how: "$BANAL",
|
|
51
51
|
});
|
|
52
|
-
assert.deepEqual(
|
|
53
|
-
g.kind === "measured" && [g.geometry.columns, g.geometry.bodyPages],
|
|
54
|
-
[2, 1],
|
|
55
|
-
);
|
|
52
|
+
assert.deepEqual([g.geometry.columns, g.geometry.bodyPages], [2, 1]);
|
|
56
53
|
const c = run.calls[0];
|
|
57
54
|
// Guards: the poppler-free path — banal is handed paperlint's XML, never the PDF.
|
|
58
55
|
assert.match(c?.args.at(-1) ?? "", /\.xml$/);
|