paperlint 3.1.2 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +12 -7
- package/CLAUDE.md +3 -3
- package/CONTRIBUTING.md +34 -5
- package/dist/adapters/banal/index.d.ts.map +1 -1
- package/dist/adapters/banal/index.js +9 -5
- package/dist/adapters/banal/index.js.map +1 -1
- package/dist/adapters/banal/invocation.d.ts +1 -1
- package/dist/adapters/node/files.io.d.ts.map +1 -1
- package/dist/adapters/node/files.io.js +2 -1
- package/dist/adapters/node/files.io.js.map +1 -1
- package/dist/adapters/node/process.io.d.ts +0 -5
- package/dist/adapters/node/process.io.d.ts.map +1 -1
- package/dist/adapters/node/process.io.js +4 -3
- package/dist/adapters/node/process.io.js.map +1 -1
- package/dist/adapters/pdfjs/fill.d.ts.map +1 -1
- package/dist/adapters/pdfjs/fill.js +13 -7
- package/dist/adapters/pdfjs/fill.js.map +1 -1
- package/dist/adapters/references/index.d.ts.map +1 -1
- package/dist/adapters/references/index.js +4 -5
- package/dist/adapters/references/index.js.map +1 -1
- package/dist/adapters/references/pool.d.ts.map +1 -1
- package/dist/adapters/references/pool.js +4 -5
- package/dist/adapters/references/pool.js.map +1 -1
- package/dist/adapters/references/reach.io.d.ts.map +1 -1
- package/dist/adapters/references/reach.io.js +2 -1
- package/dist/adapters/references/reach.io.js.map +1 -1
- package/dist/build-engine.js +2 -2
- package/dist/build-engine.js.map +1 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +14 -11
- package/dist/build.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +31 -34
- package/dist/cli.js.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +3 -2
- package/dist/doctor.js.map +1 -1
- package/dist/domain/lookup-cache.d.ts.map +1 -1
- package/dist/domain/lookup-cache.js +10 -8
- package/dist/domain/lookup-cache.js.map +1 -1
- package/dist/domain/text.d.ts +12 -0
- package/dist/domain/text.d.ts.map +1 -1
- package/dist/domain/text.js +17 -0
- package/dist/domain/text.js.map +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +4 -3
- package/dist/engine.js.map +1 -1
- package/dist/eslint-rules/bib-reachable-entry.d.ts +22 -0
- package/dist/eslint-rules/bib-reachable-entry.d.ts.map +1 -0
- package/dist/eslint-rules/bib-reachable-entry.js +75 -0
- package/dist/eslint-rules/bib-reachable-entry.js.map +1 -0
- package/dist/eslint-rules/install-path-literals.d.ts +80 -0
- package/dist/eslint-rules/install-path-literals.d.ts.map +1 -0
- package/dist/eslint-rules/install-path-literals.js +105 -0
- package/dist/eslint-rules/install-path-literals.js.map +1 -0
- package/dist/eslint-rules/latex-language.d.ts +136 -0
- package/dist/eslint-rules/latex-language.d.ts.map +1 -0
- package/dist/eslint-rules/latex-language.js +639 -0
- package/dist/eslint-rules/latex-language.js.map +1 -0
- package/dist/eslint-rules/paper-research-question.d.ts +31 -0
- package/dist/eslint-rules/paper-research-question.d.ts.map +1 -0
- package/dist/eslint-rules/paper-research-question.js +188 -0
- package/dist/eslint-rules/paper-research-question.js.map +1 -0
- package/dist/eslint-rules/paper-stages.d.ts +83 -0
- package/dist/eslint-rules/paper-stages.d.ts.map +1 -0
- package/dist/eslint-rules/paper-stages.js +358 -0
- package/dist/eslint-rules/paper-stages.js.map +1 -0
- package/dist/eslint-rules/paper-typography.d.ts +74 -0
- package/dist/eslint-rules/paper-typography.d.ts.map +1 -0
- package/dist/eslint-rules/paper-typography.js +404 -0
- package/dist/eslint-rules/paper-typography.js.map +1 -0
- package/dist/eslint-rules/papers.d.ts +42 -0
- package/dist/eslint-rules/papers.d.ts.map +1 -0
- package/dist/eslint-rules/papers.js +145 -0
- package/dist/eslint-rules/papers.js.map +1 -0
- package/dist/eslint-rules/pdf-last-page-balance.d.ts +72 -0
- package/dist/eslint-rules/pdf-last-page-balance.d.ts.map +1 -0
- package/dist/eslint-rules/pdf-last-page-balance.js +237 -0
- package/dist/eslint-rules/pdf-last-page-balance.js.map +1 -0
- package/dist/eslint-rules/review-frontmatter.d.ts +57 -0
- package/dist/eslint-rules/review-frontmatter.d.ts.map +1 -0
- package/dist/eslint-rules/review-frontmatter.js +126 -0
- package/dist/eslint-rules/review-frontmatter.js.map +1 -0
- package/dist/eslint-rules/review-frontmatter.schema.json +36 -0
- package/dist/eslint-rules/rule-context.d.ts +55 -0
- package/dist/eslint-rules/rule-context.d.ts.map +1 -0
- package/dist/eslint-rules/rule-context.js +9 -0
- package/dist/eslint-rules/rule-context.js.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.d.ts +25 -0
- package/dist/eslint-rules/sibling-frontmatter.d.ts.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.js +16 -0
- package/dist/eslint-rules/sibling-frontmatter.js.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.schema.json +14 -0
- package/dist/eslint-rules/temp-root-realpath.d.ts +71 -0
- package/dist/eslint-rules/temp-root-realpath.d.ts.map +1 -0
- package/dist/eslint-rules/temp-root-realpath.js +139 -0
- package/dist/eslint-rules/temp-root-realpath.js.map +1 -0
- package/dist/eslint-rules/tex-build.d.ts +35 -0
- package/dist/eslint-rules/tex-build.d.ts.map +1 -0
- package/dist/eslint-rules/tex-build.js +313 -0
- package/dist/eslint-rules/tex-build.js.map +1 -0
- package/dist/facts-file.d.ts.map +1 -1
- package/dist/facts-file.js +2 -1
- package/dist/facts-file.js.map +1 -1
- package/dist/hooks-settings.d.ts +1 -1
- package/dist/hooks-settings.d.ts.map +1 -1
- package/dist/hooks-settings.js +14 -9
- package/dist/hooks-settings.js.map +1 -1
- package/dist/init.d.ts +5 -5
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +15 -7
- package/dist/init.js.map +1 -1
- package/dist/latex-loop.d.ts +2 -2
- package/dist/latex-loop.d.ts.map +1 -1
- package/dist/latex-loop.js +2 -2
- package/dist/latex-loop.js.map +1 -1
- package/dist/lib/agent-cli-version.d.ts +65 -0
- package/dist/lib/agent-cli-version.d.ts.map +1 -0
- package/dist/lib/agent-cli-version.js +77 -0
- package/dist/lib/agent-cli-version.js.map +1 -0
- package/dist/lib/check.d.ts +19 -0
- package/dist/lib/check.d.ts.map +1 -0
- package/dist/lib/check.js +52 -0
- package/dist/lib/check.js.map +1 -0
- package/dist/lib/markdown.d.ts +180 -0
- package/dist/lib/markdown.d.ts.map +1 -0
- package/dist/lib/markdown.js +302 -0
- package/dist/lib/markdown.js.map +1 -0
- package/dist/lib/paper-config.d.ts +84 -0
- package/dist/lib/paper-config.d.ts.map +1 -0
- package/dist/lib/paper-config.js +142 -0
- package/dist/lib/paper-config.js.map +1 -0
- package/dist/lib/skill-eval-fixture.d.ts +29 -0
- package/dist/lib/skill-eval-fixture.d.ts.map +1 -0
- package/dist/lib/skill-eval-fixture.js +62 -0
- package/dist/lib/skill-eval-fixture.js.map +1 -0
- package/dist/link-skills.d.ts.map +1 -1
- package/dist/link-skills.js +5 -4
- package/dist/link-skills.js.map +1 -1
- package/dist/new-paper.d.ts.map +1 -1
- package/dist/new-paper.js +2 -2
- package/dist/new-paper.js.map +1 -1
- package/dist/paper-settings.d.ts.map +1 -1
- package/dist/paper-settings.js +5 -4
- package/dist/paper-settings.js.map +1 -1
- package/dist/pdf-facts.d.ts.map +1 -1
- package/dist/pdf-facts.js +2 -1
- package/dist/pdf-facts.js.map +1 -1
- package/dist/pdf-geometry.d.ts.map +1 -1
- package/dist/pdf-geometry.js +1 -1
- package/dist/pdf-geometry.js.map +1 -1
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +3 -2
- package/dist/presets.js.map +1 -1
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +11 -6
- package/dist/references.js.map +1 -1
- package/dist/rules-config.d.ts.map +1 -1
- package/dist/rules-config.js +6 -3
- package/dist/rules-config.js.map +1 -1
- package/dist/structure.js +1 -1
- package/dist/structure.js.map +1 -1
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +8 -3
- package/dist/tex-requirements.js.map +1 -1
- package/dist/toolchain.d.ts.map +1 -1
- package/dist/toolchain.js +21 -16
- package/dist/toolchain.js.map +1 -1
- package/dist/venue-rules.js +1 -1
- package/dist/venue-rules.js.map +1 -1
- package/docs/e2e.md +7 -7
- package/docs/install.md +1 -1
- package/docs/prior-art/README.md +1 -1
- package/docs/prior-art/package-location.md +5 -5
- package/docs/prior-art/repro/README.md +1 -1
- package/docs/prior-art/test-tooling.md +2 -2
- package/docs/testing.md +11 -8
- package/docs/toolchain.md +1 -1
- package/eslint-rules/{bib-reachable-entry.mjs → bib-reachable-entry.ts} +4 -3
- package/eslint-rules/install-path-literals.harness.mjs +1 -1
- package/eslint-rules/{install-path-literals.mjs → install-path-literals.ts} +16 -8
- package/eslint-rules/latex-language.harness.mjs +2 -2
- package/eslint-rules/latex-language.test.mjs +7 -1
- package/eslint-rules/{latex-language.mjs → latex-language.ts} +297 -109
- package/eslint-rules/paper-research-question.harness.mjs +3 -3
- package/eslint-rules/{paper-research-question.mjs → paper-research-question.ts} +34 -15
- package/eslint-rules/paper-stages.harness.mjs +2 -2
- package/eslint-rules/paper-stages.test.mjs +8 -2
- package/eslint-rules/{paper-stages.mjs → paper-stages.ts} +74 -29
- package/eslint-rules/paper-typography.test.mjs +3 -3
- package/eslint-rules/{paper-typography.mjs → paper-typography.ts} +132 -54
- package/eslint-rules/papers.harness.mjs +2 -2
- package/eslint-rules/{papers.mjs → papers.ts} +7 -4
- package/eslint-rules/pdf-last-page-balance.harness.mjs +3 -3
- package/eslint-rules/{pdf-last-page-balance.mjs → pdf-last-page-balance.ts} +88 -31
- package/eslint-rules/review-frontmatter.test.mjs +2 -2
- package/eslint-rules/{review-frontmatter.mjs → review-frontmatter.ts} +37 -15
- package/eslint-rules/rule-context.ts +62 -0
- package/eslint-rules/{sibling-frontmatter.mjs → sibling-frontmatter.ts} +1 -1
- package/eslint-rules/temp-root-realpath.harness.mjs +1 -1
- package/eslint-rules/{temp-root-realpath.mjs → temp-root-realpath.ts} +19 -6
- package/eslint-rules/tex-build.harness.mjs +2 -2
- package/eslint-rules/{tex-build.mjs → tex-build.ts} +4 -3
- package/eslint.config.mjs +139 -30
- package/fixtures/pdf-facts/README.md +2 -2
- package/fixtures/real-markdown-paper/{baseline.mjs → baseline.ts} +31 -10
- package/hooks/{hooks.harness.mjs → hooks.harness.ts} +51 -34
- package/hooks/paper-edit-guard.hook.mjs +1 -1
- package/hooks/{paper-edit-guard.test.mjs → paper-edit-guard.test.ts} +3 -7
- package/hooks/paper-skills-nudge.hook.mjs +1 -1
- package/hooks/{paper-skills-nudge.test.mjs → paper-skills-nudge.test.ts} +2 -6
- package/hooks/{paper-status-gates.test.mjs → paper-status-gates.test.ts} +2 -6
- package/lib/agent-cli-version.harness.mjs +1 -1
- package/lib/{agent-cli-version.mjs → agent-cli-version.ts} +32 -18
- package/lib/check.test.ts +14 -9
- package/lib/{check.mjs → check.ts} +22 -9
- package/lib/markdown.test.mjs +7 -7
- package/lib/{markdown.mjs → markdown.ts} +68 -29
- package/lib/paper-config.harness.mjs +8 -8
- package/lib/{paper-config.mjs → paper-config.ts} +19 -14
- package/lib/skill-checks.mjs +1 -1
- package/lib/skill-checks.test.mjs +1 -1
- package/lib/skill-corpus.mjs +1 -1
- package/lib/skill-eval-kit.mjs +1 -1
- package/package.json +42 -10
- package/scripts/{check.harness.mjs → check.harness.ts} +111 -10
- package/scripts/{check.test.mjs → check.test.ts} +13 -10
- package/scripts/{check.mjs → check.ts} +116 -42
- package/scripts/coverage-config.test.ts +80 -37
- package/scripts/eslint-report-guard.test.mjs +1 -1
- package/scripts/harness-api.frozen.json +14 -15
- package/scripts/harness-api.test.ts +8 -5
- package/scripts/layer-legacy-frozen.test.ts +17 -13
- package/scripts/{layer-legacy-frozen.mjs → layer-legacy-frozen.ts} +128 -39
- package/scripts/release-config.test.ts +12 -4
- package/scripts/rules-see-files.test.mjs +1 -1
- package/scripts/test.test.ts +78 -0
- package/scripts/test.ts +87 -0
- package/skills/grade-paper-writing/prose-lint.mjs +1 -1
- package/skills/grade-paper-writing/{prose-lint.test.mjs → prose-lint.test.ts} +2 -2
- package/skills/paper-pipeline/description-language.eval.mjs +1 -1
- package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +1 -1
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +1 -1
- package/skills/paper-pipeline/pipeline-firing.eval.mjs +1 -1
- package/skills/paper-pipeline/pipeline-language.eval.mjs +1 -1
- package/skills/paper-pipeline/scripts/README.md +1 -1
- package/skills/paper-pipeline/scripts/announce.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/artifact-coverage.mjs +1 -1
- package/skills/paper-pipeline/scripts/{artifact-coverage.test.mjs → artifact-coverage.test.ts} +2 -2
- package/skills/paper-pipeline/scripts/consumer.mjs +1 -5
- package/skills/paper-pipeline/scripts/consumer.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +1 -1
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +1 -4
- package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/fixtures/{scorecard.mjs → scorecard.ts} +36 -7
- package/skills/paper-pipeline/scripts/{generated-code.test.mjs → generated-code.test.ts} +3 -3
- package/skills/paper-pipeline/scripts/ledger.cli.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/pipeline-check.cli.test.mjs +2 -2
- package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +2 -2
- package/skills/paper-pipeline/scripts/pipeline-check.mjs +1 -4
- package/skills/paper-pipeline/scripts/population-map.mjs +1 -4
- package/skills/paper-pipeline/scripts/population-map.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/{round-diff.harness.mjs → round-diff.harness.ts} +45 -18
- package/skills/paper-pipeline/scripts/round-diff.mjs +1 -1
- package/skills/paper-pipeline/scripts/round-diff.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/run-mechanical.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/status.test.mjs +1 -1
- package/skills/plan-paper-timeline/fixtures/{fake-google-calendar.mjs → fake-google-calendar.ts} +101 -70
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +3 -3
- package/skills/render-paper/{extract-pdf-facts.harness.mjs → extract-pdf-facts.harness.ts} +34 -9
- package/skills/render-paper/render-paper.harness.mjs +2 -2
- package/skills/tighten-paper/structure.mjs +1 -1
- package/skills/tighten-paper/{structure.test.mjs → structure.test.ts} +4 -3
- package/skills/verify-citations/scripts/{bib-authors.cli.test.mjs → bib-authors.cli.test.ts} +14 -4
- package/skills/verify-citations/scripts/bib-authors.d.mts +63 -0
- package/skills/verify-citations/scripts/bib-authors.mjs +1 -4
- package/skills/verify-citations/scripts/{verify-cites.cli.test.mjs → verify-cites.cli.test.ts} +32 -9
- package/skills/verify-citations/scripts/verify-cites.d.mts +58 -0
- package/skills/verify-citations/scripts/verify-cites.mjs +11 -1
- package/skills/verify-citations/scripts/verify-cites.net.test.mjs +35 -0
- package/skills/verify-citations/scripts/verify-cites.test.mjs +14 -0
- package/src/CLAUDE.md +1 -1
- package/src/adapters/banal/index.test.ts +3 -6
- package/src/adapters/banal/index.ts +7 -4
- package/src/adapters/banal/invocation.test.ts +3 -1
- package/src/adapters/banal/invocation.ts +1 -1
- package/src/adapters/banal/output.test.ts +1 -1
- package/src/adapters/banal/run.test.ts +3 -3
- package/src/adapters/banal/xml.test.ts +1 -1
- package/src/adapters/curl/download.test.ts +4 -2
- package/src/adapters/node/files.io.ts +2 -2
- package/src/adapters/node/files.test.ts +3 -1
- package/src/adapters/node/process.io.ts +4 -3
- package/src/adapters/node/process.test.ts +1 -1
- package/src/adapters/node/workspace.test.ts +3 -1
- package/src/adapters/pdfjs/fill.test.ts +2 -2
- package/src/adapters/pdfjs/fill.ts +14 -7
- package/src/adapters/references/index.test.ts +31 -38
- package/src/adapters/references/index.ts +26 -58
- package/src/adapters/references/pool.ts +3 -5
- package/src/adapters/references/reach.io.ts +2 -1
- package/src/build-engine.harness.mjs +1 -1
- package/src/build-engine.test.ts +2 -2
- package/src/build-engine.ts +2 -2
- package/src/build.harness.mjs +2 -2
- package/src/build.test.ts +15 -14
- package/src/build.ts +28 -19
- package/src/cli.harness.mjs +6 -6
- package/src/cli.test.ts +13 -6
- package/src/cli.ts +39 -37
- package/src/doctor.harness.mjs +3 -3
- package/src/doctor.ts +3 -2
- package/src/domain/lookup-cache.ts +21 -11
- package/src/domain/result.test.ts +2 -2
- package/src/domain/text.test.ts +32 -0
- package/src/domain/text.ts +23 -0
- package/src/engine.harness.mjs +2 -2
- package/src/engine.ts +4 -3
- package/src/facts-file.test.ts +9 -7
- package/src/facts-file.ts +2 -1
- package/src/{hooks-settings.harness.mjs → hooks-settings.harness.ts} +39 -20
- package/src/hooks-settings.ts +20 -9
- package/src/init.test.ts +12 -9
- package/src/init.ts +22 -12
- package/src/{latex-log.harness.mjs → latex-log.harness.ts} +14 -14
- package/src/{latex-loop.harness.mjs → latex-loop.harness.ts} +64 -36
- package/src/latex-loop.ts +3 -3
- package/src/{link-skills.harness.mjs → link-skills.harness.ts} +19 -19
- package/src/link-skills.test.ts +3 -3
- package/src/link-skills.ts +5 -4
- package/src/lint-fix.test.ts +4 -6
- package/src/linted-set.test.ts +2 -2
- package/src/new-paper.harness.mjs +2 -2
- package/src/new-paper.test.ts +8 -10
- package/src/new-paper.ts +2 -2
- package/src/paper-files.test.ts +3 -5
- package/src/paper-settings-commands.test.ts +5 -4
- package/src/paper-settings.test.ts +5 -5
- package/src/paper-settings.ts +8 -9
- package/src/{pdf-facts.harness.mjs → pdf-facts.harness.ts} +9 -8
- package/src/pdf-facts.test.ts +10 -11
- package/src/pdf-facts.ts +2 -1
- package/src/{pdf-geometry.harness.mjs → pdf-geometry.harness.ts} +19 -12
- package/src/pdf-geometry.ts +3 -1
- package/src/presets.ts +3 -2
- package/src/references.test.ts +51 -43
- package/src/references.ts +14 -7
- package/src/{rules-config.harness.mjs → rules-config.harness.ts} +7 -13
- package/src/rules-config.ts +7 -4
- package/src/rules-off.test.ts +6 -4
- package/src/{structure.harness.mjs → structure.harness.ts} +26 -15
- package/src/structure.ts +1 -1
- package/src/{tex-requirements.harness.mjs → tex-requirements.harness.ts} +50 -22
- package/src/tex-requirements.ts +8 -5
- package/src/toolchain.harness.mjs +2 -2
- package/src/toolchain.test.ts +1 -1
- package/src/toolchain.ts +31 -17
- package/src/venue-rules.test.ts +62 -64
- package/src/venue-rules.ts +1 -1
- package/tsconfig.json +6 -3
- package/tsconfig.pkg.json +10 -0
- package/lib/check.d.mts +0 -17
- package/lib/paper-config.d.mts +0 -31
- package/scripts/layer-legacy-frozen.d.mts +0 -36
- package/scripts/rules-are-content-only.harness.mjs +0 -105
- package/scripts/rules-are-content-only.mjs +0 -148
- package/scripts/rules-are-content-only.test.mjs +0 -61
- package/scripts/test.mjs +0 -44
- package/scripts/test.test.mjs +0 -52
- /package/bin/{paperlint.test.mjs → paperlint.test.ts} +0 -0
- /package/lib/{skill-eval-fixture.mjs → skill-eval-fixture.ts} +0 -0
- /package/skills/grade-paper-writing/__snapshots__/{prose-lint.test.mjs.snap → prose-lint.test.ts.snap} +0 -0
- /package/skills/osf-artifact-upload/{osf-artifact-upload.harness.mjs → osf-artifact-upload.harness.ts} +0 -0
- /package/skills/tighten-paper/__snapshots__/{structure.test.mjs.snap → structure.test.ts.snap} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* round-diff.harness.
|
|
2
|
+
* round-diff.harness.ts — plant the defect each check in `round-diff.mjs` claims to catch, assert
|
|
3
3
|
* it says no with the RIGHT WORDS, and assert it stays silent on the clean case. `npx vigiles test`.
|
|
4
4
|
*
|
|
5
5
|
* WHAT IS UNDER TEST. `.claude/skills/paper-pipeline/scripts/round-diff.mjs`: a review round may only
|
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
import { tmpdir } from "node:os";
|
|
50
50
|
import { join, dirname } from "node:path";
|
|
51
51
|
import { fileURLToPath } from "node:url";
|
|
52
|
+
import { z } from "zod";
|
|
52
53
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
53
54
|
|
|
54
55
|
const SCRIPT = join(HERE, "round-diff.mjs");
|
|
@@ -60,7 +61,9 @@ const tmp = realpathSync(mkdtempSync(join(tmpdir(), "round-diff-harness-")));
|
|
|
60
61
|
// numbered body, a `## References` boundary and an appendix after it. The boundary is load-bearing
|
|
61
62
|
// — the ratchet must not charge for words moved into an appendix.
|
|
62
63
|
|
|
63
|
-
|
|
64
|
+
type Section =
|
|
65
|
+
"abstract" | "s1" | "s2" | "s21" | "s3" | "lim" | "refs" | "appendix";
|
|
66
|
+
const P = (o: Partial<Record<Section, string>> = {}) => `---
|
|
64
67
|
title: "A fixture"
|
|
65
68
|
---
|
|
66
69
|
|
|
@@ -108,10 +111,16 @@ function fixture({
|
|
|
108
111
|
now = null,
|
|
109
112
|
rounds = {},
|
|
110
113
|
commitRounds = false,
|
|
114
|
+
}: {
|
|
115
|
+
base?: string;
|
|
116
|
+
now?: string | null;
|
|
117
|
+
rounds?: Record<string, string>;
|
|
118
|
+
commitRounds?: boolean;
|
|
111
119
|
} = {}) {
|
|
112
|
-
const dir = join(tmp, `f${++n}`, "paper-x");
|
|
120
|
+
const dir = join(tmp, `f${String(++n)}`, "paper-x");
|
|
113
121
|
mkdirSync(dir, { recursive: true });
|
|
114
|
-
const g = (...a) =>
|
|
122
|
+
const g = (...a: string[]) =>
|
|
123
|
+
spawnSync("git", a, { cwd: dir, encoding: "utf8" });
|
|
115
124
|
g("init", "-q", "-b", "main");
|
|
116
125
|
g("config", "user.email", "h@example.com");
|
|
117
126
|
g("config", "user.name", "harness");
|
|
@@ -150,18 +159,34 @@ const M = ({
|
|
|
150
159
|
hedge,
|
|
151
160
|
closed,
|
|
152
161
|
base = "__BASE__",
|
|
162
|
+
}: {
|
|
163
|
+
round?: number;
|
|
164
|
+
touches?: readonly string[];
|
|
165
|
+
allows?: readonly string[];
|
|
166
|
+
budget?: number;
|
|
167
|
+
hedge?: number;
|
|
168
|
+
closed?: string;
|
|
169
|
+
base?: string;
|
|
153
170
|
} = {}) =>
|
|
154
171
|
`---
|
|
155
|
-
round: ${round}
|
|
172
|
+
round: ${String(round)}
|
|
156
173
|
opened: 2026-08-10
|
|
157
174
|
base: ${base}
|
|
158
175
|
touches: [${touches.map((t) => `"${t}"`).join(", ")}]
|
|
159
|
-
${allows ? `allows: [${allows.join(", ")}]\n` : ""}${budget !== undefined ? `budget: ${budget}\n` : ""}${hedge !== undefined ? `hedge-budget: ${hedge}\n` : ""}${closed ? `closed: ${closed}\n` : ""}---
|
|
176
|
+
${allows ? `allows: [${allows.join(", ")}]\n` : ""}${budget !== undefined ? `budget: ${String(budget)}\n` : ""}${hedge !== undefined ? `hedge-budget: ${String(hedge)}\n` : ""}${closed ? `closed: ${closed}\n` : ""}---
|
|
160
177
|
|
|
161
|
-
Round ${round}.
|
|
178
|
+
Round ${String(round)}.
|
|
162
179
|
`;
|
|
163
180
|
|
|
164
|
-
|
|
181
|
+
/** One finding of the gate's `--json` output, with the two fields these checks read. */
|
|
182
|
+
const Findings = z.array(z.looseObject({ kind: z.string(), msg: z.string() }));
|
|
183
|
+
type Run = {
|
|
184
|
+
findings: z.infer<typeof Findings>;
|
|
185
|
+
kinds: string[];
|
|
186
|
+
text: string;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
function run(dir: string, ...args: string[]): Run {
|
|
165
190
|
const json = spawnSync("node", [SCRIPT, dir, "--json", ...args], {
|
|
166
191
|
encoding: "utf8",
|
|
167
192
|
});
|
|
@@ -169,11 +194,11 @@ function run(dir, ...args) {
|
|
|
169
194
|
assert.equal(
|
|
170
195
|
json.status,
|
|
171
196
|
0,
|
|
172
|
-
`the gate must always exit 0 (advisory); it exited ${json.status}:\n${json.stderr}`,
|
|
197
|
+
`the gate must always exit 0 (advisory); it exited ${String(json.status)}:\n${json.stderr}`,
|
|
173
198
|
);
|
|
174
|
-
let findings
|
|
199
|
+
let findings: z.infer<typeof Findings>;
|
|
175
200
|
try {
|
|
176
|
-
findings = JSON.parse(json.stdout);
|
|
201
|
+
findings = Findings.parse(JSON.parse(json.stdout));
|
|
177
202
|
} catch {
|
|
178
203
|
throw new Error(
|
|
179
204
|
`--json did not produce JSON.\nstdout:\n${json.stdout}\nstderr:\n${json.stderr}`,
|
|
@@ -187,15 +212,16 @@ function run(dir, ...args) {
|
|
|
187
212
|
}
|
|
188
213
|
|
|
189
214
|
/** Exactly these kinds, in any order — never `.includes`, which is how a deleted check stays green. */
|
|
190
|
-
const only = (r, expected, why) =>
|
|
215
|
+
const only = (r: Run, expected: readonly string[], why: string) => {
|
|
191
216
|
assert.deepEqual(
|
|
192
217
|
[...r.kinds].sort(),
|
|
193
218
|
[...expected].sort(),
|
|
194
219
|
`${why}\n got: ${JSON.stringify(r.findings, null, 2)}`,
|
|
195
220
|
);
|
|
221
|
+
};
|
|
196
222
|
|
|
197
223
|
/** The finding of this kind must say this. A kind alone does not prove which branch produced it. */
|
|
198
|
-
const msg = (r, kind, needle, why) => {
|
|
224
|
+
const msg = (r: Run, kind: string, needle: string, why: string) => {
|
|
199
225
|
const f = r.findings.find((x) => x.kind === kind);
|
|
200
226
|
assert.ok(
|
|
201
227
|
f,
|
|
@@ -452,7 +478,7 @@ const msg = (r, kind, needle, why) => {
|
|
|
452
478
|
// paid it back — which is exactly the failure the cumulative check exists for, and exactly what a
|
|
453
479
|
// per-round gate forgets the moment the round is closed.
|
|
454
480
|
{
|
|
455
|
-
const grow = (k) =>
|
|
481
|
+
const grow = (k: number) =>
|
|
456
482
|
P({
|
|
457
483
|
s3:
|
|
458
484
|
"The direction holds across all four rules. " +
|
|
@@ -460,7 +486,8 @@ const msg = (r, kind, needle, why) => {
|
|
|
460
486
|
});
|
|
461
487
|
const dir = join(tmp, "cumulative", "paper-x");
|
|
462
488
|
mkdirSync(dir, { recursive: true });
|
|
463
|
-
const g = (...a) =>
|
|
489
|
+
const g = (...a: string[]) =>
|
|
490
|
+
spawnSync("git", a, { cwd: dir, encoding: "utf8" });
|
|
464
491
|
g("init", "-q", "-b", "main");
|
|
465
492
|
g("config", "user.email", "h@e.com");
|
|
466
493
|
g("config", "user.name", "h");
|
|
@@ -468,20 +495,20 @@ const msg = (r, kind, needle, why) => {
|
|
|
468
495
|
for (const k of [0, 10, 16]) {
|
|
469
496
|
writeFileSync(join(dir, "paper.md"), grow(k));
|
|
470
497
|
g("add", "-A");
|
|
471
|
-
g("commit", "-qm", `r${k}`);
|
|
498
|
+
g("commit", "-qm", `r${String(k)}`);
|
|
472
499
|
shas.push(g("rev-parse", "HEAD").stdout.trim());
|
|
473
500
|
}
|
|
474
501
|
writeFileSync(join(dir, "paper.md"), grow(22));
|
|
475
502
|
mkdirSync(join(dir, "rounds"), { recursive: true });
|
|
476
503
|
for (const [i, sha] of shas.entries()) {
|
|
477
504
|
writeFileSync(
|
|
478
|
-
join(dir, "rounds", `0${i + 1}.md`),
|
|
505
|
+
join(dir, "rounds", `0${String(i + 1)}.md`),
|
|
479
506
|
M({
|
|
480
507
|
round: i + 1,
|
|
481
508
|
touches: ["3"],
|
|
482
509
|
budget: 20,
|
|
483
510
|
base: sha,
|
|
484
|
-
|
|
511
|
+
...(i < 2 ? { closed: `2026-08-1${String(i)}` } : {}),
|
|
485
512
|
}),
|
|
486
513
|
);
|
|
487
514
|
}
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
import { readFileSync, existsSync, readdirSync } from "node:fs";
|
|
67
67
|
import { spawnSync } from "node:child_process";
|
|
68
68
|
import { basename, join, resolve, relative } from "node:path";
|
|
69
|
-
import { stripFrontmatter, frontmatterBlock } from "
|
|
69
|
+
import { stripFrontmatter, frontmatterBlock } from "#lib/markdown";
|
|
70
70
|
import { isMain } from "./consumer.mjs";
|
|
71
71
|
|
|
72
72
|
// ── the lexicon ──────────────────────────────────────────────────────────────────────────────
|
|
@@ -10,7 +10,7 @@ import { chmodSync } from "node:fs";
|
|
|
10
10
|
import { dirname, join } from "node:path";
|
|
11
11
|
import { fileURLToPath } from "node:url";
|
|
12
12
|
import { test } from "vitest";
|
|
13
|
-
import { runNode, useTempDir, writeTree } from "../../../test/support.
|
|
13
|
+
import { runNode, useTempDir, writeTree } from "../../../test/support.ts";
|
|
14
14
|
import { census, check, covers, parseManifest } from "./round-diff.mjs";
|
|
15
15
|
|
|
16
16
|
const SCRIPT = join(dirname(fileURLToPath(import.meta.url)), "round-diff.mjs");
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
import { dirname, join } from "node:path";
|
|
19
19
|
import { fileURLToPath } from "node:url";
|
|
20
20
|
import { afterEach, test, vi } from "vitest";
|
|
21
|
-
import { runNode, useTempDir, writeTree } from "../../../test/support.
|
|
21
|
+
import { runNode, useTempDir, writeTree } from "../../../test/support.ts";
|
|
22
22
|
|
|
23
23
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
24
24
|
const SCRIPT = join(HERE, "run-mechanical.mjs");
|
|
@@ -9,7 +9,7 @@ import { rmSync, writeFileSync } from "node:fs";
|
|
|
9
9
|
import { dirname, join } from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import { afterEach, test, vi } from "vitest";
|
|
12
|
-
import { runNode, useTempDir, writeTree } from "../../../test/support.
|
|
12
|
+
import { runNode, useTempDir, writeTree } from "../../../test/support.ts";
|
|
13
13
|
|
|
14
14
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
15
15
|
const root = useTempDir("status-");
|
package/skills/plan-paper-timeline/fixtures/{fake-google-calendar.mjs → fake-google-calendar.ts}
RENAMED
|
@@ -34,6 +34,20 @@
|
|
|
34
34
|
* Calls are appended as JSONL to $FAKE_MCP_LOG.
|
|
35
35
|
*/
|
|
36
36
|
import { appendFileSync } from "node:fs";
|
|
37
|
+
/** One tool as the real server declares it: which arguments it requires and which it knows. */
|
|
38
|
+
interface ToolSpec {
|
|
39
|
+
readonly description: string;
|
|
40
|
+
readonly required: readonly string[];
|
|
41
|
+
readonly known: readonly string[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A JSON object. The narrowing is written out rather than done with zod on purpose: zod's import
|
|
46
|
+
* costs ~45 ms at startup, and the effects harness calls a tool as soon as the session opens — with
|
|
47
|
+
* zod loaded the server was measured still connecting on every run, without it ready on every run.
|
|
48
|
+
*/
|
|
49
|
+
type Msg = Readonly<Record<string, unknown>>;
|
|
50
|
+
const isObject = (v: unknown): v is Msg => typeof v === "object" && v !== null;
|
|
37
51
|
|
|
38
52
|
const LOG = process.env.FAKE_MCP_LOG;
|
|
39
53
|
if (!LOG) {
|
|
@@ -44,12 +58,14 @@ if (!LOG) {
|
|
|
44
58
|
);
|
|
45
59
|
process.exit(1);
|
|
46
60
|
}
|
|
61
|
+
/** The log, known to be set from here on. */
|
|
62
|
+
const LOG_FILE: string = LOG;
|
|
47
63
|
|
|
48
64
|
/**
|
|
49
65
|
* Transcribed from the live tool definitions, 2026-08-17. `required` is the field
|
|
50
66
|
* that carries the weight — everything else is here so an unknown key is detectable.
|
|
51
67
|
*/
|
|
52
|
-
const TOOLS = {
|
|
68
|
+
const TOOLS: Readonly<Record<string, ToolSpec>> = {
|
|
53
69
|
create_event: {
|
|
54
70
|
description: "Creates an event on the given calendar.",
|
|
55
71
|
required: ["summary", "startTime", "endTime"],
|
|
@@ -138,7 +154,7 @@ const TOOLS = {
|
|
|
138
154
|
},
|
|
139
155
|
};
|
|
140
156
|
|
|
141
|
-
const schemaOf = (spec) => ({
|
|
157
|
+
const schemaOf = (spec: ToolSpec) => ({
|
|
142
158
|
type: "object",
|
|
143
159
|
properties: Object.fromEntries(spec.known.map((k) => [k, {}])),
|
|
144
160
|
required: spec.required,
|
|
@@ -147,9 +163,12 @@ const schemaOf = (spec) => ({
|
|
|
147
163
|
let created = 0;
|
|
148
164
|
|
|
149
165
|
/** @returns {string|null} the reason this call would be rejected, or null if it is well-formed. */
|
|
150
|
-
function reject(
|
|
151
|
-
|
|
152
|
-
|
|
166
|
+
function reject(
|
|
167
|
+
name: unknown,
|
|
168
|
+
args: Readonly<Record<string, unknown>> | null | undefined,
|
|
169
|
+
): string | null {
|
|
170
|
+
const spec = TOOLS[String(name)];
|
|
171
|
+
if (!spec) return `no such tool: ${String(name)}`;
|
|
153
172
|
const a = args ?? {};
|
|
154
173
|
const missing = spec.required.filter(
|
|
155
174
|
(k) => a[k] === undefined || a[k] === "",
|
|
@@ -158,82 +177,94 @@ function reject(name, args) {
|
|
|
158
177
|
return `missing required argument(s): ${missing.join(", ")} — the real Google Calendar API rejects this call`;
|
|
159
178
|
const unknown = Object.keys(a).filter((k) => !spec.known.includes(k));
|
|
160
179
|
if (unknown.length)
|
|
161
|
-
return `unknown argument(s): ${unknown.join(", ")} — not fields of ${name} on the real server`;
|
|
180
|
+
return `unknown argument(s): ${unknown.join(", ")} — not fields of ${String(name)} on the real server`;
|
|
162
181
|
return null;
|
|
163
182
|
}
|
|
164
183
|
|
|
184
|
+
/** A `tools/call`: logged whatever its fate, then answered with an error or a fake event id. */
|
|
185
|
+
function call(msg: Msg): Record<string, unknown> {
|
|
186
|
+
const params = isObject(msg.params) ? msg.params : undefined;
|
|
187
|
+
const name = params?.name;
|
|
188
|
+
const args = isObject(params?.arguments) ? params.arguments : {};
|
|
189
|
+
const why = reject(name, args);
|
|
190
|
+
// Every call is logged, accepted or not. A rejected call that left no trace
|
|
191
|
+
// would be indistinguishable from a call that never happened.
|
|
192
|
+
appendFileSync(
|
|
193
|
+
LOG_FILE,
|
|
194
|
+
JSON.stringify({ name, args, ok: why === null, why }) + "\n",
|
|
195
|
+
);
|
|
196
|
+
if (why)
|
|
197
|
+
return {
|
|
198
|
+
result: {
|
|
199
|
+
isError: true,
|
|
200
|
+
content: [{ type: "text", text: `INVALID CALL: ${why}` }],
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
created += 1;
|
|
204
|
+
return {
|
|
205
|
+
result: {
|
|
206
|
+
content: [
|
|
207
|
+
{
|
|
208
|
+
type: "text",
|
|
209
|
+
text: JSON.stringify({
|
|
210
|
+
id: `evt_fake_${String(created)}`,
|
|
211
|
+
status: "confirmed",
|
|
212
|
+
}),
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** The reply to one message, or `null` for a notification (no id) this server does not handle. */
|
|
220
|
+
function answer(msg: Msg): Record<string, unknown> | null {
|
|
221
|
+
if (msg.method === "initialize")
|
|
222
|
+
return {
|
|
223
|
+
result: {
|
|
224
|
+
protocolVersion: "2024-11-05",
|
|
225
|
+
capabilities: { tools: {} },
|
|
226
|
+
serverInfo: { name: "fake-google-calendar", version: "1.0.0" },
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
if (msg.method === "tools/list")
|
|
230
|
+
return {
|
|
231
|
+
result: {
|
|
232
|
+
tools: Object.entries(TOOLS).map(([name, spec]) => ({
|
|
233
|
+
name,
|
|
234
|
+
description: spec.description,
|
|
235
|
+
inputSchema: schemaOf(spec),
|
|
236
|
+
})),
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
if (msg.method === "tools/call") return call(msg);
|
|
240
|
+
return msg.id !== undefined ? { result: {} } : null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** One line of input, parsed; `null` for a line that is not a JSON-RPC message. */
|
|
244
|
+
function parseLine(line: string): Msg | null {
|
|
245
|
+
let json: unknown;
|
|
246
|
+
try {
|
|
247
|
+
json = JSON.parse(line);
|
|
248
|
+
} catch {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
return isObject(json) ? json : null;
|
|
252
|
+
}
|
|
253
|
+
|
|
165
254
|
let buf = "";
|
|
166
|
-
process.stdin.on("data", (chunk) => {
|
|
167
|
-
buf += chunk;
|
|
255
|
+
process.stdin.on("data", (chunk: Buffer | string) => {
|
|
256
|
+
buf += String(chunk);
|
|
168
257
|
let nl;
|
|
169
258
|
while ((nl = buf.indexOf("\n")) >= 0) {
|
|
170
259
|
const line = buf.slice(0, nl).trim();
|
|
171
260
|
buf = buf.slice(nl + 1);
|
|
172
261
|
if (!line) continue;
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
const send = (payload) =>
|
|
262
|
+
const msg = parseLine(line);
|
|
263
|
+
if (msg === null) continue;
|
|
264
|
+
const payload = answer(msg);
|
|
265
|
+
if (payload)
|
|
180
266
|
process.stdout.write(
|
|
181
267
|
JSON.stringify({ jsonrpc: "2.0", id: msg.id, ...payload }) + "\n",
|
|
182
268
|
);
|
|
183
|
-
|
|
184
|
-
if (msg.method === "initialize") {
|
|
185
|
-
send({
|
|
186
|
-
result: {
|
|
187
|
-
protocolVersion: "2024-11-05",
|
|
188
|
-
capabilities: { tools: {} },
|
|
189
|
-
serverInfo: { name: "fake-google-calendar", version: "1.0.0" },
|
|
190
|
-
},
|
|
191
|
-
});
|
|
192
|
-
} else if (msg.method === "tools/list") {
|
|
193
|
-
send({
|
|
194
|
-
result: {
|
|
195
|
-
tools: Object.entries(TOOLS).map(([name, spec]) => ({
|
|
196
|
-
name,
|
|
197
|
-
description: spec.description,
|
|
198
|
-
inputSchema: schemaOf(spec),
|
|
199
|
-
})),
|
|
200
|
-
},
|
|
201
|
-
});
|
|
202
|
-
} else if (msg.method === "tools/call") {
|
|
203
|
-
const name = msg.params?.name;
|
|
204
|
-
const args = msg.params?.arguments ?? {};
|
|
205
|
-
const why = reject(name, args);
|
|
206
|
-
// Every call is logged, accepted or not. A rejected call that left no trace
|
|
207
|
-
// would be indistinguishable from a call that never happened.
|
|
208
|
-
appendFileSync(
|
|
209
|
-
LOG,
|
|
210
|
-
JSON.stringify({ name, args, ok: why === null, why }) + "\n",
|
|
211
|
-
);
|
|
212
|
-
if (why) {
|
|
213
|
-
send({
|
|
214
|
-
result: {
|
|
215
|
-
isError: true,
|
|
216
|
-
content: [{ type: "text", text: `INVALID CALL: ${why}` }],
|
|
217
|
-
},
|
|
218
|
-
});
|
|
219
|
-
} else {
|
|
220
|
-
created += 1;
|
|
221
|
-
send({
|
|
222
|
-
result: {
|
|
223
|
-
content: [
|
|
224
|
-
{
|
|
225
|
-
type: "text",
|
|
226
|
-
text: JSON.stringify({
|
|
227
|
-
id: `evt_fake_${String(created)}`,
|
|
228
|
-
status: "confirmed",
|
|
229
|
-
}),
|
|
230
|
-
},
|
|
231
|
-
],
|
|
232
|
-
},
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
} else if (msg.id !== undefined) {
|
|
236
|
-
send({ result: {} });
|
|
237
|
-
}
|
|
238
269
|
}
|
|
239
270
|
});
|
|
@@ -60,7 +60,7 @@ import { tmpdir } from "node:os";
|
|
|
60
60
|
import { dirname, join, resolve } from "node:path";
|
|
61
61
|
import { fileURLToPath } from "node:url";
|
|
62
62
|
import { runHarnessTest, skip } from "vigiles";
|
|
63
|
-
import { observeAgentCli } from "
|
|
63
|
+
import { observeAgentCli } from "#lib/agent-cli-version";
|
|
64
64
|
import { DEFAULT_TIMEZONE } from "../paper-pipeline/scripts/consumer.mjs";
|
|
65
65
|
|
|
66
66
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
@@ -225,7 +225,7 @@ try {
|
|
|
225
225
|
// Run A above pins that the calendar surface is absent from a headless session, which
|
|
226
226
|
// is true and worth knowing and also means the skill's CENTRAL STEP has never been
|
|
227
227
|
// checked by anything. Run B supplies the surface with a stand-in
|
|
228
|
-
// (`fixtures/fake-google-calendar.
|
|
228
|
+
// (`fixtures/fake-google-calendar.ts`) carrying the REAL API's required-field sets,
|
|
229
229
|
// so the step becomes executable for the first time.
|
|
230
230
|
//
|
|
231
231
|
// ── WHAT IS UNDER TEST, AND WHY IT IS NOT THE MODEL ─────────────────────────────
|
|
@@ -312,7 +312,7 @@ try {
|
|
|
312
312
|
mcpServers: {
|
|
313
313
|
Google_Calendar: {
|
|
314
314
|
command: process.execPath,
|
|
315
|
-
args: [join(HERE, "fixtures", "fake-google-calendar.
|
|
315
|
+
args: [join(HERE, "fixtures", "fake-google-calendar.ts")],
|
|
316
316
|
env: { FAKE_MCP_LOG: callLog },
|
|
317
317
|
},
|
|
318
318
|
},
|
|
@@ -24,7 +24,29 @@ import {
|
|
|
24
24
|
import { tmpdir } from "node:os";
|
|
25
25
|
import { dirname, join, resolve } from "node:path";
|
|
26
26
|
import { fileURLToPath } from "node:url";
|
|
27
|
-
import {
|
|
27
|
+
import { z } from "zod";
|
|
28
|
+
import { createChecker } from "../../lib/check.ts";
|
|
29
|
+
import type { ScriptResult } from "../../test/support.ts";
|
|
30
|
+
|
|
31
|
+
/** The facts file, as far as these checks read it. */
|
|
32
|
+
const Facts = z.looseObject({
|
|
33
|
+
schema: z.unknown().optional(),
|
|
34
|
+
pdf: z.unknown().optional(),
|
|
35
|
+
venue: z.unknown().optional(),
|
|
36
|
+
kind: z.unknown().optional(),
|
|
37
|
+
geometry_source: z.unknown().optional(),
|
|
38
|
+
columns: z.unknown().optional(),
|
|
39
|
+
fonts: z.unknown().optional(),
|
|
40
|
+
});
|
|
41
|
+
const Fonts = z.array(
|
|
42
|
+
z.looseObject({
|
|
43
|
+
name: z.unknown().optional(),
|
|
44
|
+
embedded: z.unknown().optional(),
|
|
45
|
+
type: z.unknown().optional(),
|
|
46
|
+
}),
|
|
47
|
+
);
|
|
48
|
+
const readFacts = (file: string) =>
|
|
49
|
+
Facts.parse(JSON.parse(readFileSync(file, "utf8")));
|
|
28
50
|
|
|
29
51
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
30
52
|
const SHIM = join(HERE, "extract-pdf-facts.mjs");
|
|
@@ -37,7 +59,7 @@ const root = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-extract-")));
|
|
|
37
59
|
* Run the shim from `root`, with no banal anywhere unless `env` names one. `HOME` is the temp root:
|
|
38
60
|
* without it Node falls back to the account's home, where `paperlint toolchain` may have installed banal.
|
|
39
61
|
*/
|
|
40
|
-
const shim = (args, env = {}) =>
|
|
62
|
+
const shim = (args: readonly string[], env: Record<string, string> = {}) =>
|
|
41
63
|
spawnSync(process.execPath, [SHIM, ...args], {
|
|
42
64
|
cwd: root,
|
|
43
65
|
encoding: "utf8",
|
|
@@ -58,7 +80,8 @@ const shim = (args, env = {}) =>
|
|
|
58
80
|
...env,
|
|
59
81
|
},
|
|
60
82
|
});
|
|
61
|
-
const said = (r
|
|
83
|
+
const said = (r: Pick<ScriptResult, "stdout" | "stderr">) =>
|
|
84
|
+
`${r.stdout}${r.stderr}`;
|
|
62
85
|
|
|
63
86
|
try {
|
|
64
87
|
const paper = join(root, "papers", "mixed");
|
|
@@ -92,7 +115,9 @@ try {
|
|
|
92
115
|
/banal not found/.test(local.stderr),
|
|
93
116
|
said(local),
|
|
94
117
|
);
|
|
95
|
-
const facts =
|
|
118
|
+
const facts = readFacts(factsFile);
|
|
119
|
+
const fonts = Fonts.safeParse(facts.fonts);
|
|
120
|
+
const fontList = fonts.success ? fonts.data : [];
|
|
96
121
|
check(
|
|
97
122
|
"the facts are schema 2, about paper.pdf, for the venue paperlint.json declares",
|
|
98
123
|
facts.schema === 2 &&
|
|
@@ -103,10 +128,10 @@ try {
|
|
|
103
128
|
);
|
|
104
129
|
check(
|
|
105
130
|
"a text PDF has fonts",
|
|
106
|
-
|
|
131
|
+
fonts.success && fontList.length === 4,
|
|
107
132
|
JSON.stringify(facts.fonts),
|
|
108
133
|
);
|
|
109
|
-
const times =
|
|
134
|
+
const times = fontList.find((f) => f.name === "Times-Roman");
|
|
110
135
|
check(
|
|
111
136
|
"🔴 Times-Roman — pdffonts `Type 1 Custom no no yes` — is embedded: false (the old reader said true)",
|
|
112
137
|
times?.embedded === false && times.type === "Type 1",
|
|
@@ -114,7 +139,7 @@ try {
|
|
|
114
139
|
);
|
|
115
140
|
check(
|
|
116
141
|
"the Type 3 font keeps poppler's type spelling, which consumers match on",
|
|
117
|
-
|
|
142
|
+
fontList.some((f) => f.type === "Type 3"),
|
|
118
143
|
);
|
|
119
144
|
|
|
120
145
|
const fake = join(root, "banal.pl");
|
|
@@ -123,7 +148,7 @@ try {
|
|
|
123
148
|
`print '{"papersize":[792,612],"columns":2,"bodyfontsize":9,"pages":[{}]}';\n`,
|
|
124
149
|
);
|
|
125
150
|
const withBanal = shim([paper, "--strict"], { BANAL: fake });
|
|
126
|
-
const g =
|
|
151
|
+
const g = readFacts(factsFile);
|
|
127
152
|
check(
|
|
128
153
|
"--strict with banal ($BANAL): exit 0, and the geometry is banal's",
|
|
129
154
|
withBanal.status === 0 && g.geometry_source === "banal" && g.columns === 2,
|
|
@@ -176,7 +201,7 @@ try {
|
|
|
176
201
|
direct.stdout.startsWith(
|
|
177
202
|
"✅ draft.pdf → papers/loose/_build/paper.facts.json (",
|
|
178
203
|
) &&
|
|
179
|
-
|
|
204
|
+
readFacts(looseFacts).venue === null,
|
|
180
205
|
said(direct),
|
|
181
206
|
);
|
|
182
207
|
// The project directory IS the facts file (nonsense, but an environment can say it): the
|
|
@@ -34,7 +34,7 @@ import { spawnSync } from "node:child_process";
|
|
|
34
34
|
import { fileURLToPath } from "node:url";
|
|
35
35
|
import { dirname, join } from "node:path";
|
|
36
36
|
import { checkSkill } from "../../lib/skill-checks.mjs";
|
|
37
|
-
import { papersRoot } from "
|
|
37
|
+
import { papersRoot } from "#eslint-rules/papers";
|
|
38
38
|
import {
|
|
39
39
|
consumerRoot,
|
|
40
40
|
settingsOf,
|
|
@@ -44,7 +44,7 @@ await checkSkill("render-paper");
|
|
|
44
44
|
|
|
45
45
|
// The TeX package pins that stood here (texlive-fonts-extra, texlive-plain-generic in
|
|
46
46
|
// ensure-toolchain.sh) moved with the list itself: the venue profiles declare the packages, and
|
|
47
|
-
// src/tex-requirements.harness.
|
|
47
|
+
// src/tex-requirements.harness.ts asserts that every acmart venue declares libertine, inconsolata,
|
|
48
48
|
// newtx, kastrup (binhex.tex) and fancyhdr — each proved by the file whose absence broke a build.
|
|
49
49
|
|
|
50
50
|
// ── TEXINPUTS: WHERE THE SCRIPTS LOOK FOR `paper-guards.tex` ─────────────────
|
|
@@ -97,7 +97,7 @@ import {
|
|
|
97
97
|
stripFences,
|
|
98
98
|
requireMarkdown,
|
|
99
99
|
stripFrontmatter,
|
|
100
|
-
} from "
|
|
100
|
+
} from "#lib/markdown";
|
|
101
101
|
|
|
102
102
|
// Markup is parsed with a PARSER (`CLAUDE.md`, 2026-08-11). We fail rather than degrade: without a
|
|
103
103
|
// parser not one section would be found in the paper, `--flags-only` would return 0 findings and
|
|
@@ -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))),
|
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:
|