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,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* skill-checks.mjs's two refusing halves, which the per-skill harnesses (all green on the real
|
|
3
|
-
* corpus) never reach: `checkSkill` on an unknown name and on a
|
|
4
|
-
* defect, and `checkProseSkill` — the check for skills WITHOUT the pipeline, which the package
|
|
3
|
+
* corpus) never reach: `checkSkill` on an unknown name and on a skill with a planted defect, and `checkProseSkill` — the check for skills WITHOUT the pipeline, which the package
|
|
5
4
|
* exports for a consumer's own skills and no harness here calls.
|
|
6
5
|
*
|
|
7
6
|
* The consumer is a temp root holding a COPY of the shipped skills, so a planted defect edits the
|
|
@@ -12,7 +11,7 @@ import { cpSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
12
11
|
import { dirname, join } from "node:path";
|
|
13
12
|
import { fileURLToPath } from "node:url";
|
|
14
13
|
import { test } from "vitest";
|
|
15
|
-
import { useTempDir, writeTree } from "../test/support.
|
|
14
|
+
import { useTempDir, writeTree } from "../test/support.ts";
|
|
16
15
|
|
|
17
16
|
const SHIPPED = join(dirname(fileURLToPath(import.meta.url)), "..", "skills");
|
|
18
17
|
const consumer = useTempDir("skill-checks-");
|
|
@@ -33,55 +32,13 @@ async function thrown(fn) {
|
|
|
33
32
|
return null;
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
test("checkSkill passes a real shipped skill and refuses a name
|
|
35
|
+
test("checkSkill passes a real shipped skill and refuses a name with no SKILL.md", async () => {
|
|
37
36
|
assert.deepEqual(
|
|
38
37
|
[
|
|
39
38
|
await checkSkill("cold-read-diff"),
|
|
40
39
|
await thrown(() => checkSkill("no-such-skill")),
|
|
41
40
|
],
|
|
42
|
-
[
|
|
43
|
-
1,
|
|
44
|
-
`"no-such-skill" is not a wired pipeline skill. Known: ${(await import("./skill-corpus.mjs")).SKILLS.join(", ")}`,
|
|
45
|
-
],
|
|
46
|
-
);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test("checkSkill reports EVERY finding of a broken pipeline skill at once", async () => {
|
|
50
|
-
const md = join(SKILLS, "cold-read-diff", "SKILL.md");
|
|
51
|
-
const clean = readFileSync(md, "utf8");
|
|
52
|
-
writeFileSync(
|
|
53
|
-
md,
|
|
54
|
-
clean
|
|
55
|
-
.replace("name: cold-read-diff", "name: renamed")
|
|
56
|
-
.replace("## Run me", "## Not run"),
|
|
57
|
-
);
|
|
58
|
-
try {
|
|
59
|
-
const first = await thrown(() => checkSkill("cold-read-diff"));
|
|
60
|
-
assert.equal(
|
|
61
|
-
first,
|
|
62
|
-
'2 assertion(s) failed for pipeline skill "cold-read-diff":',
|
|
63
|
-
);
|
|
64
|
-
} finally {
|
|
65
|
-
writeFileSync(md, clean);
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test("a pipeline skill whose record block is gone: ONE finding naming the heading — the ledger checks that read the block skip it", async () => {
|
|
70
|
-
const md = join(SKILLS, "cold-read-diff", "SKILL.md");
|
|
71
|
-
const clean = readFileSync(md, "utf8");
|
|
72
|
-
writeFileSync(md, clean.replaceAll("## Record the verdict", "## Afterwards"));
|
|
73
|
-
let message = "";
|
|
74
|
-
try {
|
|
75
|
-
await checkSkill("cold-read-diff");
|
|
76
|
-
} catch (e) {
|
|
77
|
-
message = e.message;
|
|
78
|
-
} finally {
|
|
79
|
-
writeFileSync(md, clean);
|
|
80
|
-
}
|
|
81
|
-
assert.equal(
|
|
82
|
-
message,
|
|
83
|
-
'1 assertion(s) failed for pipeline skill "cold-read-diff":\n\n' +
|
|
84
|
-
' [1] cold-read-diff: no "Record the verdict" heading. A run with an announce row and no verdict row is indistinguishable from a skill that started and died.',
|
|
41
|
+
[1, `"no-such-skill" has no SKILL.md under ${SKILLS}`],
|
|
85
42
|
);
|
|
86
43
|
});
|
|
87
44
|
|
|
@@ -203,95 +160,86 @@ async function brokenBy(name, edit) {
|
|
|
203
160
|
await checkSkill(name);
|
|
204
161
|
return null;
|
|
205
162
|
} catch (e) {
|
|
206
|
-
// The header, then
|
|
163
|
+
// The header, then each numbered finding.
|
|
207
164
|
const lines = e.message.split("\n");
|
|
208
165
|
return [
|
|
209
166
|
lines[0],
|
|
210
|
-
...lines
|
|
211
|
-
.filter((l) => /^ \[\d+\] /.test(l))
|
|
212
|
-
.map((l) => l.trim().slice(0, 90)),
|
|
167
|
+
...lines.filter((l) => /^ \[\d+\] /.test(l)).map((l) => l.trim()),
|
|
213
168
|
];
|
|
214
169
|
} finally {
|
|
215
170
|
writeFileSync(md, clean);
|
|
216
171
|
}
|
|
217
172
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
await brokenBy("cold-read-diff", (s) =>
|
|
224
|
-
s.replace(/^description: .*\n/m, ""),
|
|
225
|
-
),
|
|
226
|
-
await brokenBy("argument-arc", (s) =>
|
|
227
|
-
s.replace(
|
|
228
|
-
recordSection,
|
|
229
|
-
"## Record the verdict\n\nNothing named here.\n",
|
|
230
|
-
),
|
|
231
|
-
),
|
|
232
|
-
await brokenBy("draft-paper", (s) =>
|
|
233
|
-
s.replace(
|
|
234
|
-
recordSection,
|
|
235
|
-
"## Record the verdict\n\n**ABSTAINED** — and no reason is named.\n",
|
|
236
|
-
),
|
|
237
|
-
),
|
|
238
|
-
],
|
|
239
|
-
[
|
|
240
|
-
[
|
|
241
|
-
'1 assertion(s) failed for pipeline skill "cold-read-diff":',
|
|
242
|
-
"[1] cold-read-diff: description is missing or trivially short (0 chars). The description i",
|
|
243
|
-
],
|
|
244
|
-
[
|
|
245
|
-
'3 assertion(s) failed for pipeline skill "argument-arc":',
|
|
246
|
-
'[1] argument-arc: the "Record the verdict" block instructs no `node .claude/skills/paper-p',
|
|
247
|
-
"[2] argument-arc: its record block names [nothing] and not FINDING, and does not admit tha",
|
|
248
|
-
"[3] argument-arc: its record block names no abstention reason from [started, no-witness, i",
|
|
249
|
-
],
|
|
250
|
-
[
|
|
251
|
-
'5 assertion(s) failed for pipeline skill "draft-paper":',
|
|
252
|
-
'[1] draft-paper: the "Record the verdict" block instructs no `node .claude/skills/paper-pi',
|
|
253
|
-
"[2] draft-paper: its record block names [ABSTAINED] and not FINDING, and does not admit th",
|
|
254
|
-
"[3] draft-paper: its record block names no abstention reason from [started, no-witness, in",
|
|
255
|
-
"[4] draft-paper: the honest admission is gone from its record block. It is the one skill i",
|
|
256
|
-
"[5] ABSTAINED needs a reason from started|no-witness|input-missing|blocked|crashed, got nu",
|
|
257
|
-
],
|
|
258
|
-
],
|
|
173
|
+
test("checkSkill reports EVERY finding at once: a renamed skill with no description", async () => {
|
|
174
|
+
const lines = await brokenBy("cold-read-diff", (s) =>
|
|
175
|
+
s
|
|
176
|
+
.replace("name: cold-read-diff", "name: renamed")
|
|
177
|
+
.replace(/^description: .*\n/m, ""),
|
|
259
178
|
);
|
|
179
|
+
assert.deepEqual(lines?.slice(0, 1), [
|
|
180
|
+
'2 assertion(s) failed for skill "cold-read-diff":',
|
|
181
|
+
]);
|
|
182
|
+
assert.match(lines?.[1] ?? "", /frontmatter name is "renamed"/);
|
|
183
|
+
assert.match(lines?.[2] ?? "", /description is missing/);
|
|
260
184
|
});
|
|
261
185
|
|
|
262
|
-
test("
|
|
186
|
+
test("checkSkill refuses an instructed script that does not exist", async () => {
|
|
187
|
+
const lines = await brokenBy("verify-citations", (s) =>
|
|
188
|
+
s.replaceAll("scripts/extract-ref-facts.mjs", "scripts/missing.mjs"),
|
|
189
|
+
);
|
|
263
190
|
assert.equal(
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
),
|
|
271
|
-
null,
|
|
191
|
+
lines?.[0],
|
|
192
|
+
'2 assertion(s) failed for skill "verify-citations":',
|
|
193
|
+
);
|
|
194
|
+
assert.match(
|
|
195
|
+
lines?.[1] ?? "",
|
|
196
|
+
/instructs `node .*missing\.mjs.*` but .*missing\.mjs does not exist/,
|
|
272
197
|
);
|
|
273
198
|
});
|
|
274
199
|
|
|
275
|
-
test("
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
200
|
+
test("checkSkill refuses a pipeline script the skill's allowed-tools do not permit", async () => {
|
|
201
|
+
const lines = await brokenBy("verify-citations", (s) =>
|
|
202
|
+
s.replace(
|
|
203
|
+
/^allowed-tools: \[(.*)\]$/m,
|
|
204
|
+
(_, list) =>
|
|
205
|
+
`allowed-tools: [${list
|
|
206
|
+
.split(", ")
|
|
207
|
+
.filter((t) => t !== "Bash")
|
|
208
|
+
.join(", ")}]`,
|
|
282
209
|
),
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
210
|
+
);
|
|
211
|
+
assert.equal(
|
|
212
|
+
lines?.[0],
|
|
213
|
+
'2 assertion(s) failed for skill "verify-citations":',
|
|
214
|
+
);
|
|
215
|
+
assert.match(
|
|
216
|
+
lines?.[1] ?? "",
|
|
217
|
+
/extract-ref-facts\.mjs.* permit no such Bash command/,
|
|
287
218
|
);
|
|
288
219
|
});
|
|
289
220
|
|
|
290
|
-
test("
|
|
221
|
+
test("a Bash entry narrowed to the instructed script permits it, and one narrowed elsewhere does not", async () => {
|
|
222
|
+
const narrowTo = (script) => (s) =>
|
|
223
|
+
s.replace(
|
|
224
|
+
/^allowed-tools: \[(.*)\]$/m,
|
|
225
|
+
(_, list) =>
|
|
226
|
+
`allowed-tools: [${list
|
|
227
|
+
.split(", ")
|
|
228
|
+
.map((t) =>
|
|
229
|
+
t === "Bash"
|
|
230
|
+
? `"Bash(node .claude/skills/paper-pipeline/scripts/${script}:*)"`
|
|
231
|
+
: t,
|
|
232
|
+
)
|
|
233
|
+
.join(", ")}]`,
|
|
234
|
+
);
|
|
291
235
|
assert.equal(
|
|
292
|
-
await brokenBy("
|
|
293
|
-
s.replace("## Record the verdict", "## Something else"),
|
|
294
|
-
),
|
|
236
|
+
await brokenBy("verify-citations", narrowTo("extract-ref-facts.mjs")),
|
|
295
237
|
null,
|
|
296
238
|
);
|
|
239
|
+
const lines = await brokenBy("verify-citations", narrowTo("round-diff.mjs"));
|
|
240
|
+
assert.equal(
|
|
241
|
+
lines?.[0],
|
|
242
|
+
'2 assertion(s) failed for skill "verify-citations":',
|
|
243
|
+
);
|
|
244
|
+
assert.match(lines?.[1] ?? "", /permit no such Bash command/);
|
|
297
245
|
});
|
package/lib/skill-corpus.mjs
CHANGED
|
@@ -1,50 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* skill-corpus.mjs —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* that is the whole reason it was split out of `skills.harness.mjs` on 2026-08-11. That file
|
|
6
|
-
* held three things under one name: this derivation, the per-skill assertions, and the
|
|
7
|
-
* corpus-wide ones. It answered to no surface (there is no skill called `skills`), so the
|
|
8
|
-
* 21 colocated per-skill harnesses could only reach it by setting an env var and importing
|
|
9
|
-
* a script for its SIDE EFFECTS — 21 wrappers around one bag.
|
|
10
|
-
*
|
|
11
|
-
* Now: this module derives, `skill-checks.mjs` asserts about ONE skill, and
|
|
12
|
-
* `pipeline-corpus.harness.mjs` asserts about the SET. Each colocated file calls a
|
|
13
|
-
* function with its own name in it, and there is no env-var side channel.
|
|
14
|
-
*
|
|
15
|
-
* The ledger is redirected to a scratch path at import time, BEFORE `ledger.mjs` loads, so
|
|
16
|
-
* that an assertion which calls `record()` cannot leave fixture rows in real history. That
|
|
17
|
-
* import order is load-bearing and is the reason this is a module rather than a plain
|
|
18
|
-
* export list. Save-and-restore is not isolation — it lost that race on its first day.
|
|
2
|
+
* skill-corpus.mjs — what a skill IS, read from its directory: the frontmatter, the tool contract,
|
|
3
|
+
* the commands its text instructs. Derivation and helpers, no assertions — `skill-checks.mjs`
|
|
4
|
+
* asserts about one skill by calling these.
|
|
19
5
|
*/
|
|
20
6
|
import assert from "node:assert/strict";
|
|
21
|
-
|
|
22
|
-
// `rmSync` is NOT a harmless finding here, though: `tmp` below is a `mkdtempSync` that is never
|
|
23
|
-
// removed, so every import of this library leaves a directory behind in `/tmp` (two dozen per
|
|
24
|
-
// full sweep of the harnesses). Cleanup is DELIBERATELY not added in this edit: it would change
|
|
25
|
-
// the lifecycle of the directory that holds `PIPELINE_LEDGER` for every child run, and today's
|
|
26
|
-
// edit is about turning the lint on, not about harness behavior. Written down as an open item
|
|
27
|
-
// in the report, not swept under an import-that-does-something-silently.
|
|
28
|
-
import { existsSync, readFileSync, mkdtempSync, realpathSync } from "node:fs";
|
|
29
|
-
import { tmpdir } from "node:os";
|
|
7
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
30
8
|
import { join, dirname } from "node:path";
|
|
31
9
|
import { fileURLToPath } from "node:url";
|
|
32
10
|
import * as yaml from "js-yaml";
|
|
33
|
-
import {
|
|
34
|
-
|
|
35
|
-
installedSkills,
|
|
36
|
-
} from "../skills/paper-pipeline/scripts/consumer.mjs";
|
|
37
|
-
import {
|
|
38
|
-
headings as mdHeadings,
|
|
39
|
-
requireMarkdown,
|
|
40
|
-
frontmatterBlock,
|
|
41
|
-
} from "./markdown.mjs";
|
|
11
|
+
import { consumerRoot } from "../skills/paper-pipeline/scripts/consumer.mjs";
|
|
12
|
+
import { requireMarkdown, frontmatterBlock } from "#lib/markdown";
|
|
42
13
|
|
|
43
|
-
// Markup is parsed with a real parser
|
|
44
|
-
//
|
|
45
|
-
// heading list with no parser would read as "not one skill has a `Record the verdict` block" —
|
|
46
|
-
// i.e. as a finding about the corpus. A confident zero has already been mistaken for a fact in
|
|
47
|
-
// this repo four times.
|
|
14
|
+
// Markup is parsed with a real parser. Here we THROW rather than degrade: with no parser every
|
|
15
|
+
// frontmatter would read as absent, which is a finding about the corpus, not about the install.
|
|
48
16
|
requireMarkdown();
|
|
49
17
|
|
|
50
18
|
export const HERE = dirname(fileURLToPath(import.meta.url));
|
|
@@ -54,122 +22,10 @@ export const HERE = dirname(fileURLToPath(import.meta.url));
|
|
|
54
22
|
// below it — the skills directory above all — would be a directory that does not exist. That
|
|
55
23
|
// failure is silent in the worst way: `readdirSync` throws while this module is still LOADING, and
|
|
56
24
|
// a harness whose module fails to load is reported by `vigiles test` as SKIPPED with exit code 0.
|
|
57
|
-
// `consumerRoot()` is the carrier the package already uses for the
|
|
25
|
+
// `consumerRoot()` is the carrier the package already uses for the papers root.
|
|
58
26
|
export const ROOT = consumerRoot();
|
|
59
27
|
export const SKILLS_DIR = join(ROOT, ".claude", "skills");
|
|
60
28
|
|
|
61
|
-
// Redirected BEFORE ledger.mjs is imported, so a crash cannot leave fixture rows in real history.
|
|
62
|
-
// Save-and-restore is not isolation — it lost that race on its first day (see ledger.mjs's comment).
|
|
63
|
-
export const tmp = realpathSync(mkdtempSync(join(tmpdir(), "skills-harness-")));
|
|
64
|
-
process.env.PIPELINE_LEDGER = join(tmp, "runs.jsonl");
|
|
65
|
-
const { KINDS, RETIRED, ABSTENTIONS, record, parseGate } =
|
|
66
|
-
await import("../skills/paper-pipeline/scripts/ledger.mjs");
|
|
67
|
-
export { KINDS, RETIRED, ABSTENTIONS, record, parseGate };
|
|
68
|
-
const { EXPECTED_GATES } =
|
|
69
|
-
await import("../skills/paper-pipeline/scripts/status.mjs");
|
|
70
|
-
export { EXPECTED_GATES };
|
|
71
|
-
/** The skills EXPECTED_GATES names, with the `skill/check` rows collapsed onto their skill. */
|
|
72
|
-
export const GATE_SKILLS = new Set(
|
|
73
|
-
EXPECTED_GATES.map((g) => parseGate(g).skill),
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
// The paper-pipeline skills, DERIVED FROM DISK — a skill is one iff its SKILL.md instructs
|
|
77
|
-
// `node .claude/skills/paper-pipeline/scripts/announce.mjs <its own name>`. Minus EXCLUDED below.
|
|
78
|
-
//
|
|
79
|
-
// 🔴 THIS WAS A PINNED ARRAY UNTIL 2026-08-07, AND THE PIN MADE ASSERTION 12b VACUOUS IN THE ONE
|
|
80
|
-
// CASE IT EXISTS FOR. That assertion reads "<skill> is a wired pipeline skill but is absent from
|
|
81
|
-
// status.mjs EXPECTED_GATES" — and it iterated the pinned array. So it could only ever catch a skill
|
|
82
|
-
// present in the array and missing from EXPECTED_GATES. A skill wired on disk and absent from BOTH
|
|
83
|
-
// lists — the actual "I added a skill and forgot" failure — was structurally invisible.
|
|
84
|
-
//
|
|
85
|
-
// Measured, not theorised: `paper-status` is in exactly that state right now. It carries an announce
|
|
86
|
-
// command and is in neither list. That is DELIBERATE (see EXCLUDED), but nothing checked that the
|
|
87
|
-
// deliberateness was still true, and the old comment asserting it was the only thing holding it.
|
|
88
|
-
//
|
|
89
|
-
// A defect that removes a skill from EXPECTED_GATES while leaving it in the pinned array passed
|
|
90
|
-
// silently the whole time. A check proving a narrower property than its own error message claims
|
|
91
|
-
// is exactly the vacuity this file was built to hunt, sitting in this file.
|
|
92
|
-
//
|
|
93
|
-
// The old comment's objection to globbing was real and is answered rather than ignored: `.claude/skills/`
|
|
94
|
-
// does hold daily skills (search-krisha, food-log-entry, …) with no pipeline contract, so the filter
|
|
95
|
-
// cannot be "every directory". But it need not be a guess either — carrying an announce command that
|
|
96
|
-
// names YOURSELF is the pipeline contract, checked below by assertion 5c, and it is a fact on disk.
|
|
97
|
-
export const PIPELINE_MARKER =
|
|
98
|
-
/^\s*node\s+\.claude\/skills\/paper-pipeline\/scripts\/announce\.mjs\s+([a-z0-9-]+)/m;
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Skills wired into the pipeline but deliberately NOT graded as gates, each with the reason.
|
|
102
|
-
* A reason is required because these are the entries that rot: an exclusion with no argument is
|
|
103
|
-
* indistinguishable from an omission, and the previous version of this file held both in prose.
|
|
104
|
-
*
|
|
105
|
-
* 🔴 UNTIL 2026-08-11 THIS SUBTRACTED FROM THE CHECKED SET, WHICH IS NOT WHAT ITS OWN REASON SAYS.
|
|
106
|
-
* The recorded reason for `paper-status` is "it has no gate row" — a statement about ONE assertion
|
|
107
|
-
* (12b: every checked skill must appear in EXPECTED_GATES). The implementation filtered it out of
|
|
108
|
-
* `SKILLS` entirely, so it received NONE of the structural checks either: not the strict-YAML
|
|
109
|
-
* frontmatter parse, not the tool contract, not "your wiring points at scripts that exist", not
|
|
110
|
-
* "you announce under your OWN identity". A skill was silently unexamined because it lacked a
|
|
111
|
-
* grade — and this file's own governing comment, four lines below, says a defect that shrinks the
|
|
112
|
-
* checked set is worse than the defect itself.
|
|
113
|
-
*
|
|
114
|
-
* Found by the coverage sweep of 2026-08-11: `paper-status` showed as untested, and the reason was
|
|
115
|
-
* not a missing test file but this line. Now the exclusion means what it says — skipped at 12b,
|
|
116
|
-
* checked everywhere else.
|
|
117
|
-
*/
|
|
118
|
-
export const EXCLUDED = new Map([
|
|
119
|
-
[
|
|
120
|
-
"paper-status",
|
|
121
|
-
"reports on the pipeline; produces no verdict ABOUT the paper, so it has no gate row",
|
|
122
|
-
],
|
|
123
|
-
]);
|
|
124
|
-
|
|
125
|
-
// 🔴 MEMBERSHIP IS THE UNION OF TWO SOURCES, AND THAT IS THE WHOLE POINT. A skill is checked if the
|
|
126
|
-
// gate table names it OR its SKILL.md carries pipeline wiring. Either alone is corruptible by the very
|
|
127
|
-
// defects this file hunts, and a defect that shrinks the checked set is worse than the defect itself:
|
|
128
|
-
// the harness goes green by no longer looking.
|
|
129
|
-
//
|
|
130
|
-
// Both narrower versions were written and both failed within minutes, on the same two planted
|
|
131
|
-
// defects:
|
|
132
|
-
//
|
|
133
|
-
// filter `announce.mjs <own name>` -> a typo in the announced NAME dropped harden-paper out
|
|
134
|
-
// of scope; harness printed "20 pipeline skills", said nothing
|
|
135
|
-
// filter `announce.mjs` path -> a typo in the announce PATH dropped camera-ready the
|
|
136
|
-
// same way
|
|
137
|
-
//
|
|
138
|
-
// Each time, the same planted defect that used to turn the harness red went GREEN because it
|
|
139
|
-
// erased its own subject. Scope decided by a property the defect corrupts is not scope.
|
|
140
|
-
//
|
|
141
|
-
// The union cannot be shrunk by a single-point edit: break the wiring and the gate table still holds
|
|
142
|
-
// the skill; forget the gate table and the disk still does. Only removing it from BOTH — which is
|
|
143
|
-
// what actually leaving the pipeline looks like — takes it out of scope, and EXCLUDED makes that
|
|
144
|
-
// deliberate and reasoned rather than silent.
|
|
145
|
-
const wiredOnDisk = installedSkills(SKILLS_DIR).filter((d) =>
|
|
146
|
-
PIPELINE_MARKER.test(readFileSync(join(SKILLS_DIR, d, "SKILL.md"), "utf-8")),
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
// EXCLUDED deliberately does NOT filter here — see its comment. An entry means "no gate row",
|
|
150
|
-
// and that is applied at assertion 12b alone; everything structural still looks at it.
|
|
151
|
-
export const SKILLS = [...new Set([...GATE_SKILLS, ...wiredOnDisk])]
|
|
152
|
-
.filter((d) => existsSync(join(SKILLS_DIR, d, "SKILL.md"))) // 12a reports a gate with no directory
|
|
153
|
-
.sort();
|
|
154
|
-
|
|
155
|
-
// The nine that gained Bash ONLY so they could write the ledger, per
|
|
156
|
-
// `.claude/skills/ALLOWED-TOOLS-AUDIT-2026-08-03.md`. PINNED because the property is not derivable:
|
|
157
|
-
// from the frontmatter alone a plain `Bash` on `render-paper` (pdflatex, bibtex, apt-get — it needs
|
|
158
|
-
// it) is indistinguishable from a plain `Bash` on `argument-arc` (which would be a regression). The
|
|
159
|
-
// audit decided that per skill by reading each one's text; this list is that decision, made checkable.
|
|
160
|
-
// A skill moving off this list is a deliberate act and should require editing this line.
|
|
161
|
-
export const LEDGER_ONLY_BASH = new Set([
|
|
162
|
-
"argument-arc",
|
|
163
|
-
"tighten-paper",
|
|
164
|
-
"grade-paper-writing",
|
|
165
|
-
"paper-adversarial-review",
|
|
166
|
-
"draft-paper",
|
|
167
|
-
"extend-paper",
|
|
168
|
-
"research-ideate",
|
|
169
|
-
"find-venue",
|
|
170
|
-
"plan-paper-timeline",
|
|
171
|
-
]);
|
|
172
|
-
|
|
173
29
|
// ── parsing ────────────────────────────────────────────────────────────────────────────────────
|
|
174
30
|
|
|
175
31
|
/** Raw text between the opening `---` and the next `---`. Null if there is no frontmatter at all. */
|
|
@@ -233,7 +89,7 @@ export function parseFm(fmText, where = "frontmatter") {
|
|
|
233
89
|
* round trip (vigiles #107). This split understood only the scalar, so on 2026-08-17
|
|
234
90
|
* adopting five pipeline skills turned four harnesses red: the checker read the whole
|
|
235
91
|
* bracketed list as a single entry named `[Bash` and reported that the skill's own
|
|
236
|
-
* `Bash(node
|
|
92
|
+
* narrowed `Bash(node …:*)` instruction was denied. The skills were fine; the reader
|
|
237
93
|
* was not.
|
|
238
94
|
*
|
|
239
95
|
* Entries themselves carry no commas (`Bash(node …:*)` has none), so a plain split is
|
|
@@ -287,74 +143,6 @@ export function commands(src) {
|
|
|
287
143
|
}));
|
|
288
144
|
}
|
|
289
145
|
|
|
290
|
-
/**
|
|
291
|
-
* The `Record the verdict` block: from its heading to the next heading of the same or higher level.
|
|
292
|
-
*
|
|
293
|
-
* 🔴 The first version of this sliced `rest.slice(1).search(/^#{1,3} /m)` and `^` matched at offset 0
|
|
294
|
-
* of the slice, so every block came back as the single character "#". Assertions 10 and 11 then read
|
|
295
|
-
* an empty block, found no verdicts, and 10 fired on all 21 skills while 11 checked nothing at all —
|
|
296
|
-
* a checker firing on clean input and a checker that is vacuous, from one off-by-one. Caught by
|
|
297
|
-
* running it, not by reading it. Matching on `\n#` rather than `^#` is the fix.
|
|
298
|
-
*
|
|
299
|
-
* 2026-08-11 — switched to a real parser (`CLAUDE.md`: "markdown is parsed with a parser"). The
|
|
300
|
-
* same off-by-one is now INEXPRESSIBLE: a heading from the parser already carries the offset of
|
|
301
|
-
* its line start, so the block boundary no longer has to be computed from a newline's position.
|
|
302
|
-
* Along with it went two defects the old expression carried silently: `#{1,3}` counted a hash
|
|
303
|
-
* inside a ``` block as a heading (a skill QUOTING someone else's `## Run me` in an example cut
|
|
304
|
-
* its own block short at the quote), and `\n#{1,3} ` did not see a heading indented by 1-3
|
|
305
|
-
* spaces, which CommonMark still counts as a heading. The block boundary is preserved byte for
|
|
306
|
-
* byte: end = start of the next heading.
|
|
307
|
-
*/
|
|
308
|
-
export function recordBlock(src) {
|
|
309
|
-
const hs = mdHeadings(src);
|
|
310
|
-
const i = hs.findIndex(
|
|
311
|
-
(h) => h.depth <= 3 && /record the verdict/i.test(h.text),
|
|
312
|
-
);
|
|
313
|
-
if (i < 0) return null;
|
|
314
|
-
const next = hs.slice(i + 1).find((h) => h.depth <= 3);
|
|
315
|
-
return src.slice(hs[i].offset, next ? next.offset : src.length);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Three extractors, because they answer different questions and a single one would be wrong for one.
|
|
319
|
-
//
|
|
320
|
-
// `allBold` — every all-caps bolded token, whatever it is. The raw material for the other two.
|
|
321
|
-
// Bold-only on purpose: several blocks write a retired word in prose ("There is no
|
|
322
|
-
// PASS") or in backticks, and a looser match would read those DENIALS as claims —
|
|
323
|
-
// which is the exact reading error that would make the turned-around assertion below
|
|
324
|
-
// fire on every correct file.
|
|
325
|
-
// `namedKinds` — bolded tokens that are live constructors. Used to ask "can this check report a
|
|
326
|
-
// finding at all?".
|
|
327
|
-
// `namedRetired` — bolded tokens from the DELETED vocabulary. Used to ask "does this block still
|
|
328
|
-
// instruct something `record()` refuses?".
|
|
329
|
-
export const allBold = (blk) => [
|
|
330
|
-
...new Set(
|
|
331
|
-
[...blk.matchAll(/\*\*`?([A-Z][A-Z-]{2,})`?\*\*/g)].map((m) => m[1]),
|
|
332
|
-
),
|
|
333
|
-
];
|
|
334
|
-
export const namedKinds = (blk) =>
|
|
335
|
-
allBold(blk).filter((v) => KINDS.includes(v));
|
|
336
|
-
export const namedRetired = (blk) => allBold(blk).filter((v) => RETIRED.has(v));
|
|
337
|
-
export const definedKinds = (blk) => [
|
|
338
|
-
...new Set(
|
|
339
|
-
[...blk.matchAll(/\*\*`?([A-Z][A-Z-]{2,})`?\*\*\s*(?=[—(:]| \()/g)].map(
|
|
340
|
-
(m) => m[1],
|
|
341
|
-
),
|
|
342
|
-
),
|
|
343
|
-
];
|
|
344
|
-
|
|
345
|
-
// The abstention reasons a block names, as backticked tokens: `no-witness`, `blocked`, …
|
|
346
|
-
export const namedReasons = (blk) =>
|
|
347
|
-
[
|
|
348
|
-
...new Set([...blk.matchAll(/`([a-z][a-z-]{3,})`/g)].map((m) => m[1])),
|
|
349
|
-
].filter((r) => ABSTENTIONS.has(r));
|
|
350
|
-
|
|
351
|
-
// The honest-admission escape hatch, for a skill whose nature is to generate rather than judge.
|
|
352
|
-
// `draft-paper` is the designed case: it will abstain nearly always and its text says so, in the
|
|
353
|
-
// terms status.mjs uses. The regex matches that ARGUMENT, not a keyword, so an admission cannot be
|
|
354
|
-
// satisfied by pasting the word "smell".
|
|
355
|
-
export const ADMISSION =
|
|
356
|
-
/(almost always record|never returned a (negative|finding)|a generator is not a gate|only honest negative)/i;
|
|
357
|
-
|
|
358
146
|
export const load = (name) => {
|
|
359
147
|
const path = join(SKILLS_DIR, name, "SKILL.md");
|
|
360
148
|
assert.ok(
|
|
@@ -370,32 +158,3 @@ export const load = (name) => {
|
|
|
370
158
|
const fm = parseFm(fmText, name);
|
|
371
159
|
return { name, path, src, fmText, fm, tools: toolList(fm["allowed-tools"]) };
|
|
372
160
|
};
|
|
373
|
-
|
|
374
|
-
// One-skill mode, for the COLOCATED per-skill harness files that sit next to each
|
|
375
|
-
// SKILL.md. Coverage is decided by placement (vigiles, 2026-08-11: a test that only
|
|
376
|
-
// NAMES a surface no longer counts), so each skill needs a file inside its own
|
|
377
|
-
// directory. Rather than copy these assertions 21 times — the drift this file was
|
|
378
|
-
// built to avoid — each colocated file sets ONLY and imports this one.
|
|
379
|
-
//
|
|
380
|
-
// 🔴 2026-08-28: THERE USED TO BE TWO MORE LINES BELOW, AND THEY WERE DEAD LOGIC, NOT CLUTTER.
|
|
381
|
-
//
|
|
382
|
-
// const skills = (ONLY ? [ONLY] : SKILLS).map(load);
|
|
383
|
-
// const skillDirs = new Set(readdirSync(SKILLS_DIR).filter((d) => existsSync(…)));
|
|
384
|
-
//
|
|
385
|
-
// Both were computed and read by NOBODY — not here, not on export. This is a leftover from the
|
|
386
|
-
// 2026-08-11 split, when the cross-skill checks moved into `pipeline-corpus.harness.mjs`: the
|
|
387
|
-
// bodies of the checks were carried over, and their inputs were forgotten. The paragraph above
|
|
388
|
-
// about "cross-skill assertions below" described exactly that block, now gone; after the move
|
|
389
|
-
// there is NOT A SINGLE CHECK below it, i.e. the comment promised a mechanism that does not
|
|
390
|
-
// exist — exactly what this base's "prose isn't policy" rule exists to catch.
|
|
391
|
-
//
|
|
392
|
-
// The second, less obvious cost: `.map(load)` is not an inert computation. `load()` carries an
|
|
393
|
-
// `assert.ok`, so this line was an UNDECLARED check of the whole corpus, run as a side effect of
|
|
394
|
-
// `import` and costing a read of all 22 `SKILL.md` files on every import of the library. This is
|
|
395
|
-
// exactly the kind of "via an import's side effect" coupling the file was moving away from (see
|
|
396
|
-
// the header of `skill-checks.mjs`). The real, declared carrier of this check is `checkSkill()`,
|
|
397
|
-
// which does it one skill at a time and with a legible message.
|
|
398
|
-
//
|
|
399
|
-
// `PIPELINE_SKILL_ONLY` used to stay here, validated against SKILLS. Nothing read the variable
|
|
400
|
-
// any more — the validation was all that was left of the feature — so it was removed with it
|
|
401
|
-
// (2026-09-27).
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* skill-corpus.mjs's pure readers on the inputs the shipped corpus never contains: frontmatter
|
|
3
|
-
* that is not a mapping or not YAML, an `allowed-tools` that is absent
|
|
4
|
-
* block and one whose record block ends the file.
|
|
3
|
+
* that is not a mapping or not YAML, and an `allowed-tools` that is absent.
|
|
5
4
|
*/
|
|
6
5
|
import assert from "node:assert/strict";
|
|
7
6
|
import { test } from "vitest";
|
|
8
|
-
import { parseFm,
|
|
7
|
+
import { parseFm, toolList } from "./skill-corpus.mjs";
|
|
9
8
|
|
|
10
9
|
const refusal = (fn) => {
|
|
11
10
|
try {
|
|
@@ -42,13 +41,3 @@ test("toolList of nothing is an empty contract, and a flow list or a comma scala
|
|
|
42
41
|
[[], ["Bash", "Read"], ["Read", "Write"], ["Read"]],
|
|
43
42
|
);
|
|
44
43
|
});
|
|
45
|
-
|
|
46
|
-
test("recordBlock: none without the heading; to the end of the file when it is the last section", () => {
|
|
47
|
-
assert.deepEqual(
|
|
48
|
-
[
|
|
49
|
-
recordBlock("# S\n\n## Run me\n\nx\n"),
|
|
50
|
-
recordBlock("# S\n\n## Record the verdict\n\nlast\n"),
|
|
51
|
-
],
|
|
52
|
-
[null, "## Record the verdict\n\nlast\n"],
|
|
53
|
-
);
|
|
54
|
-
});
|
package/lib/skill-eval-kit.mjs
CHANGED
|
@@ -54,7 +54,7 @@ import { resolve } from "node:path";
|
|
|
54
54
|
import { consumerRoot } from "../skills/paper-pipeline/scripts/consumer.mjs";
|
|
55
55
|
|
|
56
56
|
import { BY_SKILL, irrelevantFor } from "./skill-trigger-cases.mjs";
|
|
57
|
-
import { FIXTURE } from "
|
|
57
|
+
import { FIXTURE } from "#lib/skill-eval-fixture";
|
|
58
58
|
|
|
59
59
|
// The CONSUMER's root — the live harness being measured is the consumer's, not this package's.
|
|
60
60
|
const REPO = consumerRoot();
|
|
@@ -330,7 +330,7 @@ const PACKAGE_CASES = [
|
|
|
330
330
|
//
|
|
331
331
|
// 🔴 AND THEY CANNOT SIMPLY BE CARRIED HERE: the first consumer's cases name private skills and
|
|
332
332
|
// quote private prose. A package that ships one user's private vocabulary has one possible user —
|
|
333
|
-
// the same argument that moved `papersRoot()
|
|
333
|
+
// the same argument that moved `papersRoot()` and `scriptsRoot()`
|
|
334
334
|
// out of the code and into a declaration. This is the fifth of those carriers and reads the same
|
|
335
335
|
// key from the same file.
|
|
336
336
|
//
|