paperlint 2.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/dependabot.yml +72 -0
- package/.github/workflows/ci.yml +297 -0
- package/.github/workflows/dependabot-automerge.yml +70 -0
- package/.github/workflows/pr-title.yml +59 -0
- package/.github/workflows/release.yml +54 -0
- package/CLAUDE.md +598 -0
- package/CONTRIBUTING.md +159 -0
- package/LICENSE +21 -0
- package/README.md +240 -0
- package/action.harness.mjs +287 -0
- package/action.mutations.mjs +162 -0
- package/action.yml +138 -0
- package/bin/rpp.mjs +43 -0
- package/dist/action-ref.d.ts +12 -0
- package/dist/action-ref.d.ts.map +1 -0
- package/dist/action-ref.js +16 -0
- package/dist/action-ref.js.map +1 -0
- package/dist/adapters/banal/failure.d.ts +73 -0
- package/dist/adapters/banal/failure.d.ts.map +1 -0
- package/dist/adapters/banal/failure.js +58 -0
- package/dist/adapters/banal/failure.js.map +1 -0
- package/dist/adapters/banal/index.d.ts +17 -0
- package/dist/adapters/banal/index.d.ts.map +1 -0
- package/dist/adapters/banal/index.js +56 -0
- package/dist/adapters/banal/index.js.map +1 -0
- package/dist/adapters/banal/install.d.ts +26 -0
- package/dist/adapters/banal/install.d.ts.map +1 -0
- package/dist/adapters/banal/install.js +15 -0
- package/dist/adapters/banal/install.js.map +1 -0
- package/dist/adapters/banal/invocation.d.ts +48 -0
- package/dist/adapters/banal/invocation.d.ts.map +1 -0
- package/dist/adapters/banal/invocation.js +43 -0
- package/dist/adapters/banal/invocation.js.map +1 -0
- package/dist/adapters/banal/locate.d.ts +50 -0
- package/dist/adapters/banal/locate.d.ts.map +1 -0
- package/dist/adapters/banal/locate.js +34 -0
- package/dist/adapters/banal/locate.js.map +1 -0
- package/dist/adapters/banal/output.d.ts +27 -0
- package/dist/adapters/banal/output.d.ts.map +1 -0
- package/dist/adapters/banal/output.js +112 -0
- package/dist/adapters/banal/output.js.map +1 -0
- package/dist/adapters/banal/pin.d.ts +19 -0
- package/dist/adapters/banal/pin.d.ts.map +1 -0
- package/dist/adapters/banal/pin.js +15 -0
- package/dist/adapters/banal/pin.js.map +1 -0
- package/dist/adapters/banal/probe.d.ts +12 -0
- package/dist/adapters/banal/probe.d.ts.map +1 -0
- package/dist/adapters/banal/probe.js +27 -0
- package/dist/adapters/banal/probe.js.map +1 -0
- package/dist/adapters/banal/run.d.ts +89 -0
- package/dist/adapters/banal/run.d.ts.map +1 -0
- package/dist/adapters/banal/run.js +104 -0
- package/dist/adapters/banal/run.js.map +1 -0
- package/dist/adapters/banal/settings.d.ts +18 -0
- package/dist/adapters/banal/settings.d.ts.map +1 -0
- package/dist/adapters/banal/settings.js +29 -0
- package/dist/adapters/banal/settings.js.map +1 -0
- package/dist/adapters/banal/xml.d.ts +48 -0
- package/dist/adapters/banal/xml.d.ts.map +1 -0
- package/dist/adapters/banal/xml.js +67 -0
- package/dist/adapters/banal/xml.js.map +1 -0
- package/dist/adapters/curl/download.io.d.ts +14 -0
- package/dist/adapters/curl/download.io.d.ts.map +1 -0
- package/dist/adapters/curl/download.io.js +69 -0
- package/dist/adapters/curl/download.io.js.map +1 -0
- package/dist/adapters/curl/index.d.ts +6 -0
- package/dist/adapters/curl/index.d.ts.map +1 -0
- package/dist/adapters/curl/index.js +6 -0
- package/dist/adapters/curl/index.js.map +1 -0
- package/dist/adapters/memory/index.d.ts +43 -0
- package/dist/adapters/memory/index.d.ts.map +1 -0
- package/dist/adapters/memory/index.js +79 -0
- package/dist/adapters/memory/index.js.map +1 -0
- package/dist/adapters/node/files.io.d.ts +3 -0
- package/dist/adapters/node/files.io.d.ts.map +1 -0
- package/dist/adapters/node/files.io.js +31 -0
- package/dist/adapters/node/files.io.js.map +1 -0
- package/dist/adapters/node/host.io.d.ts +3 -0
- package/dist/adapters/node/host.io.d.ts.map +1 -0
- package/dist/adapters/node/host.io.js +14 -0
- package/dist/adapters/node/host.io.js.map +1 -0
- package/dist/adapters/node/index.d.ts +25 -0
- package/dist/adapters/node/index.d.ts.map +1 -0
- package/dist/adapters/node/index.js +14 -0
- package/dist/adapters/node/index.js.map +1 -0
- package/dist/adapters/node/process.io.d.ts +14 -0
- package/dist/adapters/node/process.io.d.ts.map +1 -0
- package/dist/adapters/node/process.io.js +41 -0
- package/dist/adapters/node/process.io.js.map +1 -0
- package/dist/adapters/node/workspace.io.d.ts +4 -0
- package/dist/adapters/node/workspace.io.d.ts.map +1 -0
- package/dist/adapters/node/workspace.io.js +33 -0
- package/dist/adapters/node/workspace.io.js.map +1 -0
- package/dist/adapters/pdfjs/fill.d.ts +42 -0
- package/dist/adapters/pdfjs/fill.d.ts.map +1 -0
- package/dist/adapters/pdfjs/fill.js +91 -0
- package/dist/adapters/pdfjs/fill.js.map +1 -0
- package/dist/build-engine.d.ts +48 -0
- package/dist/build-engine.d.ts.map +1 -0
- package/dist/build-engine.js +148 -0
- package/dist/build-engine.js.map +1 -0
- package/dist/build.d.ts +163 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +575 -0
- package/dist/build.js.map +1 -0
- package/dist/cli.d.ts +151 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +951 -0
- package/dist/cli.js.map +1 -0
- package/dist/doctor.d.ts +42 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +280 -0
- package/dist/doctor.js.map +1 -0
- package/dist/domain/geometry.d.ts +71 -0
- package/dist/domain/geometry.d.ts.map +1 -0
- package/dist/domain/geometry.js +35 -0
- package/dist/domain/geometry.js.map +1 -0
- package/dist/domain/host.d.ts +16 -0
- package/dist/domain/host.d.ts.map +1 -0
- package/dist/domain/host.js +8 -0
- package/dist/domain/host.js.map +1 -0
- package/dist/domain/page-layout.d.ts +34 -0
- package/dist/domain/page-layout.d.ts.map +1 -0
- package/dist/domain/page-layout.js +8 -0
- package/dist/domain/page-layout.js.map +1 -0
- package/dist/domain/paths.d.ts +5 -0
- package/dist/domain/paths.d.ts.map +1 -0
- package/dist/domain/paths.js +2 -0
- package/dist/domain/paths.js.map +1 -0
- package/dist/domain/result.d.ts +23 -0
- package/dist/domain/result.d.ts.map +1 -0
- package/dist/domain/result.js +10 -0
- package/dist/domain/result.js.map +1 -0
- package/dist/domain/sha256.d.ts +7 -0
- package/dist/domain/sha256.d.ts.map +1 -0
- package/dist/domain/sha256.js +14 -0
- package/dist/domain/sha256.js.map +1 -0
- package/dist/domain/text.d.ts +6 -0
- package/dist/domain/text.d.ts.map +1 -0
- package/dist/domain/text.js +7 -0
- package/dist/domain/text.js.map +1 -0
- package/dist/engine.d.ts +93 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +119 -0
- package/dist/engine.js.map +1 -0
- package/dist/exit-code.d.ts +22 -0
- package/dist/exit-code.d.ts.map +1 -0
- package/dist/exit-code.js +10 -0
- package/dist/exit-code.js.map +1 -0
- package/dist/facts-file.d.ts +96 -0
- package/dist/facts-file.d.ts.map +1 -0
- package/dist/facts-file.js +134 -0
- package/dist/facts-file.js.map +1 -0
- package/dist/hooks-settings.d.ts +141 -0
- package/dist/hooks-settings.d.ts.map +1 -0
- package/dist/hooks-settings.js +306 -0
- package/dist/hooks-settings.js.map +1 -0
- package/dist/init.d.ts +201 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +579 -0
- package/dist/init.js.map +1 -0
- package/dist/latex-log.d.ts +80 -0
- package/dist/latex-log.d.ts.map +1 -0
- package/dist/latex-log.js +187 -0
- package/dist/latex-log.js.map +1 -0
- package/dist/latex-loop.d.ts +129 -0
- package/dist/latex-loop.d.ts.map +1 -0
- package/dist/latex-loop.js +113 -0
- package/dist/latex-loop.js.map +1 -0
- package/dist/link-skills.d.ts +51 -0
- package/dist/link-skills.d.ts.map +1 -0
- package/dist/link-skills.js +199 -0
- package/dist/link-skills.js.map +1 -0
- package/dist/new-paper.d.ts +48 -0
- package/dist/new-paper.d.ts.map +1 -0
- package/dist/new-paper.js +110 -0
- package/dist/new-paper.js.map +1 -0
- package/dist/pdf-facts.d.ts +44 -0
- package/dist/pdf-facts.d.ts.map +1 -0
- package/dist/pdf-facts.js +239 -0
- package/dist/pdf-facts.js.map +1 -0
- package/dist/pdf-geometry.d.ts +170 -0
- package/dist/pdf-geometry.d.ts.map +1 -0
- package/dist/pdf-geometry.js +158 -0
- package/dist/pdf-geometry.js.map +1 -0
- package/dist/ports/download.d.ts +9 -0
- package/dist/ports/download.d.ts.map +1 -0
- package/dist/ports/download.js +2 -0
- package/dist/ports/download.js.map +1 -0
- package/dist/ports/files.d.ts +11 -0
- package/dist/ports/files.d.ts.map +1 -0
- package/dist/ports/files.js +2 -0
- package/dist/ports/files.js.map +1 -0
- package/dist/ports/measure-geometry.d.ts +8 -0
- package/dist/ports/measure-geometry.d.ts.map +1 -0
- package/dist/ports/measure-geometry.js +2 -0
- package/dist/ports/measure-geometry.js.map +1 -0
- package/dist/ports/process.d.ts +45 -0
- package/dist/ports/process.d.ts.map +1 -0
- package/dist/ports/process.js +2 -0
- package/dist/ports/process.js.map +1 -0
- package/dist/ports/tool-installer.d.ts +29 -0
- package/dist/ports/tool-installer.d.ts.map +1 -0
- package/dist/ports/tool-installer.js +2 -0
- package/dist/ports/tool-installer.js.map +1 -0
- package/dist/ports/workspace.d.ts +18 -0
- package/dist/ports/workspace.d.ts.map +1 -0
- package/dist/ports/workspace.js +2 -0
- package/dist/ports/workspace.js.map +1 -0
- package/dist/rules-config.d.ts +34 -0
- package/dist/rules-config.d.ts.map +1 -0
- package/dist/rules-config.js +132 -0
- package/dist/rules-config.js.map +1 -0
- package/dist/structure.d.ts +34 -0
- package/dist/structure.d.ts.map +1 -0
- package/dist/structure.js +149 -0
- package/dist/structure.js.map +1 -0
- package/dist/tex-requirements.d.ts +43 -0
- package/dist/tex-requirements.d.ts.map +1 -0
- package/dist/tex-requirements.js +127 -0
- package/dist/tex-requirements.js.map +1 -0
- package/dist/toolchain.d.ts +159 -0
- package/dist/toolchain.d.ts.map +1 -0
- package/dist/toolchain.js +542 -0
- package/dist/toolchain.js.map +1 -0
- package/dist/types.d.ts +110 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/configuration.md +235 -0
- package/docs/e2e.md +152 -0
- package/docs/incidents.md +59 -0
- package/docs/install.md +170 -0
- package/docs/optional-rules.md +107 -0
- package/docs/package-shape-options.md +262 -0
- package/docs/prior-art/README.md +76 -0
- package/docs/prior-art/blocking-vs-advisory.md +83 -0
- package/docs/prior-art/content-delivery.md +124 -0
- package/docs/prior-art/multi-mode-tools.md +106 -0
- package/docs/prior-art/nondeterministic-checks.md +99 -0
- package/docs/prior-art/package-location.md +422 -0
- package/docs/prior-art/paper-folder-scaffolding.md +538 -0
- package/docs/prior-art/readme-structure.md +69 -0
- package/docs/prior-art/repro/README.md +92 -0
- package/docs/prior-art/repro/claim1-allowedtools.mjs +66 -0
- package/docs/prior-art/repro/claim1-at2.mjs +40 -0
- package/docs/prior-art/repro/claim1-crosschannel.mjs +54 -0
- package/docs/prior-art/repro/claim1-frontmatter.mjs +76 -0
- package/docs/prior-art/repro/claim1-hook-payload-reporter.mjs +10 -0
- package/docs/prior-art/repro/claim1-plugin-frontmatter.mjs +27 -0
- package/docs/prior-art/repro/claim1-plugin-skill.mjs +52 -0
- package/docs/prior-art/repro/claim1-project-skill.mjs +81 -0
- package/docs/prior-art/repro/claim2-marketplace-flat-asclaimed.json +1 -0
- package/docs/prior-art/repro/claim2-marketplace-negative-control.json +1 -0
- package/docs/prior-art/repro/claim2-marketplace-nested-exact.json +9 -0
- package/docs/prior-art/repro/claim2-marketplace-nested-noversion.json +9 -0
- package/docs/prior-art/repro/claim2-marketplace-nested-range.json +1 -0
- package/docs/prior-art/repro/claim3-imports.mjs +50 -0
- package/docs/prior-art/repro/claim4-find-package-json.mjs +8 -0
- package/docs/prior-art/repro/claim4-package-dir.mjs +39 -0
- package/docs/prior-art/repro/claim4-parent-arg.mjs +17 -0
- package/docs/prior-art/repro/claim4-resolve-apis.mjs +21 -0
- package/docs/prior-art/repro/claim4-setup-consumers.mjs +45 -0
- package/docs/prior-art/repro/claim4-yarn-pnp.mjs +70 -0
- package/docs/prior-art/repro/claim5-bin-launch.mjs +39 -0
- package/docs/prior-art/repro/claim5-exports-mutation.mjs +57 -0
- package/docs/prior-art/repro/claim5-resolved-location-and-bin.mjs +33 -0
- package/docs/prior-art/repro/claim6-candidate-ambiguity.mjs +17 -0
- package/docs/prior-art/repro/claim6-doc-path-candidates.mjs +27 -0
- package/docs/prior-art/test-tooling.md +131 -0
- package/docs/rules.md +58 -0
- package/docs/texlive-install-decision.md +230 -0
- package/docs/toolchain.md +152 -0
- package/eslint-rules/doc-fields.harness.mjs +336 -0
- package/eslint-rules/doc-fields.mjs +186 -0
- package/eslint-rules/doc-fields.mutations.mjs +96 -0
- package/eslint-rules/install-path-literals.harness.mjs +121 -0
- package/eslint-rules/install-path-literals.mjs +108 -0
- package/eslint-rules/install-path-literals.mutations.mjs +62 -0
- package/eslint-rules/latex-language.harness.mjs +599 -0
- package/eslint-rules/latex-language.mjs +591 -0
- package/eslint-rules/latex-language.mutations.mjs +196 -0
- package/eslint-rules/paper-research-question.harness.mjs +146 -0
- package/eslint-rules/paper-research-question.mjs +180 -0
- package/eslint-rules/paper-research-question.mutations.mjs +127 -0
- package/eslint-rules/paper-stages.harness.mjs +356 -0
- package/eslint-rules/paper-stages.mjs +455 -0
- package/eslint-rules/paper-stages.mutations.mjs +157 -0
- package/eslint-rules/paper-typography.harness.mjs +291 -0
- package/eslint-rules/paper-typography.mjs +313 -0
- package/eslint-rules/paper-typography.mutations.mjs +131 -0
- package/eslint-rules/papers.harness.mjs +259 -0
- package/eslint-rules/papers.mjs +166 -0
- package/eslint-rules/papers.mutations.mjs +186 -0
- package/eslint-rules/pdf-last-page-balance.harness.mjs +206 -0
- package/eslint-rules/pdf-last-page-balance.mjs +208 -0
- package/eslint-rules/review-findings-cause.harness.mjs +228 -0
- package/eslint-rules/review-findings-cause.mjs +135 -0
- package/eslint-rules/review-findings-cause.mutations.mjs +72 -0
- package/eslint-rules/temp-root-realpath.harness.mjs +176 -0
- package/eslint-rules/temp-root-realpath.mjs +129 -0
- package/eslint-rules/temp-root-realpath.mutations.mjs +99 -0
- package/eslint-rules/tex-build.harness.mjs +753 -0
- package/eslint-rules/tex-build.mjs +322 -0
- package/eslint-rules/tex-build.mutations.mjs +258 -0
- package/eslint.config.mjs +521 -0
- package/fixtures/build-e2e/acmart/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/acmart/paper.tex +11 -0
- package/fixtures/build-e2e/acmart/venue.json +1 -0
- package/fixtures/build-e2e/broken/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/broken/paper.tex +7 -0
- package/fixtures/build-e2e/cite/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/cite/build.sh +5 -0
- package/fixtures/build-e2e/cite/paper.tex +10 -0
- package/fixtures/build-e2e/cite/refs.bib +9 -0
- package/fixtures/build-e2e/empty/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/empty/paper.tex +6 -0
- package/fixtures/build-e2e/fallback/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/fallback/paper.tex +11 -0
- package/fixtures/build-e2e/guards/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/guards/paper.tex +10 -0
- package/fixtures/build-e2e/no-source/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/unbalanced/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/unbalanced/paper.tex +28 -0
- package/fixtures/build-e2e/unbalanced/refs.bib +269 -0
- package/fixtures/install-path-literals/clean.fixture.mjs +3 -0
- package/fixtures/install-path-literals/clean.md +15 -0
- package/fixtures/install-path-literals/defect.fixture.mjs +3 -0
- package/fixtures/install-path-literals/defect.md +14 -0
- package/fixtures/latex-language/clean.tex +50 -0
- package/fixtures/latex-language/defect.tex +52 -0
- package/fixtures/paper-research-question/comment-only/PIPELINE-STATUS.md +9 -0
- package/fixtures/paper-research-question/comment-only/paper.tex +7 -0
- package/fixtures/paper-research-question/declared-not-in-paper/PIPELINE-STATUS.md +10 -0
- package/fixtures/paper-research-question/declared-not-in-paper/paper.tex +6 -0
- package/fixtures/paper-research-question/draft/PIPELINE-STATUS.md +6 -0
- package/fixtures/paper-research-question/draft/paper.tex +2 -0
- package/fixtures/paper-research-question/markdown-no-rq/PIPELINE-STATUS.md +9 -0
- package/fixtures/paper-research-question/markdown-no-rq/paper.md +4 -0
- package/fixtures/paper-research-question/shipped-no-rq/PIPELINE-STATUS.md +12 -0
- package/fixtures/paper-research-question/shipped-no-rq/paper.tex +3 -0
- package/fixtures/paper-research-question/shipped-with-rq/PIPELINE-STATUS.md +10 -0
- package/fixtures/paper-research-question/shipped-with-rq/paper.tex +2 -0
- package/fixtures/paper-stages/authors-ran/PIPELINE-STATUS.md +16 -0
- package/fixtures/paper-stages/marker-in-prose/PIPELINE-STATUS.md +17 -0
- package/fixtures/paper-stages/nofile/PIPELINE-STATUS.md +8 -0
- package/fixtures/paper-stages/noheader/PIPELINE-STATUS.md +1 -0
- package/fixtures/paper-stages/noheader/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-stages/nothing/PIPELINE-STATUS.md +3 -0
- package/fixtures/paper-stages/ok/PIPELINE-STATUS.md +9 -0
- package/fixtures/paper-stages/ok/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-stages/stale/PIPELINE-STATUS.md +1 -0
- package/fixtures/paper-stages/stale/versions/2026-07-22-submitted.STALE-WRONG-FILE.pdf +0 -0
- package/fixtures/paper-stages/twice/PIPELINE-STATUS.md +14 -0
- package/fixtures/paper-stages/twice/versions/2026-08-06-submitted.pdf +0 -0
- package/fixtures/paper-stages/twice/versions/2026-10-24-submitted.pdf +0 -0
- package/fixtures/paper-stages/undeclared/PIPELINE-STATUS.md +8 -0
- package/fixtures/paper-stages/undeclared/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-stages/undeclared/versions/2026-08-29-camera-ready.pdf +0 -0
- package/fixtures/paper-stages/wrongsize/PIPELINE-STATUS.md +8 -0
- package/fixtures/paper-stages/wrongsize/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-typography/clean-paper/paper.tex +29 -0
- package/fixtures/paper-typography/messy-paper/paper.tex +27 -0
- package/fixtures/pdf-facts/README.md +22 -0
- package/fixtures/pdf-facts/corrupt-font.pdf +0 -0
- package/fixtures/pdf-facts/encrypted.pdf +0 -0
- package/fixtures/pdf-facts/hidden-text.pdf +0 -0
- package/fixtures/pdf-facts/hidden-text.tex +28 -0
- package/fixtures/pdf-facts/t3-all.pdf +0 -0
- package/fixtures/pdf-facts/t3-all.tex +8 -0
- package/fixtures/pdf-facts/t3-mixed.pdf +0 -0
- package/fixtures/pdf-facts/t3-mixed.tex +9 -0
- package/fixtures/pdf-facts/ttf.pdf +2240 -1
- package/fixtures/pdf-facts/ttf.tex +6 -0
- package/fixtures/real-markdown-paper/baseline.json +24 -0
- package/fixtures/real-markdown-paper/baseline.mjs +48 -0
- package/fixtures/render-paper/build-clean.sh +25 -0
- package/fixtures/render-paper/build-defect.sh +15 -0
- package/fixtures/review-findings-cause/clean.md +17 -0
- package/fixtures/review-findings-cause/defect.md +14 -0
- package/fixtures/review-findings-cause/old-debt.md +14 -0
- package/fixtures/review-findings-cause/quiet-in-fence.md +16 -0
- package/fixtures/tex-build/clean.tex +21 -0
- package/fixtures/tex-build/defect.tex +24 -0
- package/fixtures/tex-build/frontmatter-clean.tex +25 -0
- package/fixtures/tex-build/frontmatter-defect.tex +23 -0
- package/fixtures/toolchain-mirror/catalog.txt +5 -0
- package/fixtures/toolchain-mirror/install-tl +27 -0
- package/fixtures/toolchain-mirror/release-texlive.txt +3 -0
- package/fixtures/toolchain-mirror/release-year +1 -0
- package/fixtures/toolchain-mirror/stub-kpsewhich +8 -0
- package/fixtures/toolchain-mirror/stub-pdflatex +3 -0
- package/fixtures/toolchain-mirror/stub-tlmgr +44 -0
- package/hooks/hooks.harness.mjs +713 -0
- package/hooks/hooks.mutations.mjs +337 -0
- package/hooks/paper-edit-guard.hook.d.mts +13 -0
- package/hooks/paper-edit-guard.hook.mjs +457 -0
- package/hooks/paper-skills-nudge.hook.mjs +136 -0
- package/hooks/paper-status-gates.hook.mjs +156 -0
- package/hooks/paper-status-gates.sh +91 -0
- package/lib/agent-cli-version.harness.mjs +165 -0
- package/lib/agent-cli-version.mjs +106 -0
- package/lib/agent-cli-version.mutations.mjs +109 -0
- package/lib/markdown.mjs +386 -0
- package/lib/mutation-driver.harness.mjs +227 -0
- package/lib/mutation-driver.mjs +397 -0
- package/lib/mutation-driver.mutations.mjs +68 -0
- package/lib/paper-config.d.mts +34 -0
- package/lib/paper-config.harness.mjs +286 -0
- package/lib/paper-config.mjs +142 -0
- package/lib/paper-config.mutations.mjs +143 -0
- package/lib/skill-checks.mjs +701 -0
- package/lib/skill-corpus.mjs +403 -0
- package/lib/skill-eval-fixture.mjs +63 -0
- package/lib/skill-eval-kit.mjs +257 -0
- package/lib/skill-trigger-cases.harness.mjs +170 -0
- package/lib/skill-trigger-cases.mjs +446 -0
- package/lib/skill-trigger-cases.mutations.mjs +65 -0
- package/lib/trigger-ledger.mjs +215 -0
- package/package.json +97 -0
- package/plugin/.claude-plugin/plugin.json +8 -0
- package/plugin/hooks/hooks.json +30 -0
- package/scripts/check.harness.mjs +177 -0
- package/scripts/check.mjs +239 -0
- package/scripts/check.mutations.mjs +110 -0
- package/scripts/eslint-report-guard.mjs +82 -0
- package/scripts/exclusive.mjs +138 -0
- package/scripts/harness-api.frozen.json +76 -0
- package/scripts/harness-api.test.ts +175 -0
- package/scripts/layer-legacy-frozen.d.mts +28 -0
- package/scripts/layer-legacy-frozen.mjs +152 -0
- package/scripts/layer-legacy-frozen.test.ts +115 -0
- package/scripts/layer-legacy.frozen.json +50 -0
- package/scripts/mutation-batteries-frozen.harness.mjs +204 -0
- package/scripts/mutation-batteries-frozen.mjs +238 -0
- package/scripts/mutation-batteries.frozen.json +117 -0
- package/scripts/release-config.test.ts +90 -0
- package/scripts/rules-are-content-only.harness.mjs +113 -0
- package/scripts/rules-are-content-only.mjs +138 -0
- package/scripts/rules-are-content-only.mutations.mjs +81 -0
- package/scripts/rules-see-files.harness.mjs +115 -0
- package/scripts/rules-see-files.mjs +99 -0
- package/scripts/rules-see-files.mutations.mjs +131 -0
- package/scripts/run-mutations.mjs +100 -0
- package/scripts/semantic-release-plugins.d.ts +16 -0
- package/skills/README.md +15 -0
- package/skills/analyze-sibling-paper/SKILL.md +170 -0
- package/skills/analyze-sibling-paper/SKILL.md.spec.ts +186 -0
- package/skills/analyze-sibling-paper/analyze-sibling-paper.eval.mjs +19 -0
- package/skills/analyze-sibling-paper/analyze-sibling-paper.harness.mjs +23 -0
- package/skills/argument-arc/SKILL.md +177 -0
- package/skills/argument-arc/SKILL.md.spec.ts +192 -0
- package/skills/argument-arc/argument-arc.eval.mjs +19 -0
- package/skills/argument-arc/argument-arc.harness.mjs +23 -0
- package/skills/build-benchmark/SKILL.md +213 -0
- package/skills/build-benchmark/SKILL.md.spec.ts +220 -0
- package/skills/build-benchmark/build-benchmark.eval.mjs +19 -0
- package/skills/build-benchmark/build-benchmark.harness.mjs +23 -0
- package/skills/build-benchmark/references/adversarial-cold-repro.md +68 -0
- package/skills/camera-ready/SKILL.md +148 -0
- package/skills/camera-ready/SKILL.md.spec.ts +164 -0
- package/skills/camera-ready/camera-ready.eval.mjs +19 -0
- package/skills/camera-ready/camera-ready.harness.mjs +23 -0
- package/skills/cold-read-diff/SKILL.md +160 -0
- package/skills/cold-read-diff/SKILL.md.spec.ts +166 -0
- package/skills/cold-read-diff/cold-read-diff.eval.mjs +19 -0
- package/skills/cold-read-diff/cold-read-diff.harness.mjs +23 -0
- package/skills/draft-paper/SKILL.md +152 -0
- package/skills/draft-paper/SKILL.md.spec.ts +169 -0
- package/skills/draft-paper/draft-paper.eval.mjs +19 -0
- package/skills/draft-paper/draft-paper.harness.mjs +23 -0
- package/skills/extend-paper/SKILL.md +99 -0
- package/skills/extend-paper/SKILL.md.spec.ts +116 -0
- package/skills/extend-paper/extend-paper.eval.mjs +19 -0
- package/skills/extend-paper/extend-paper.harness.mjs +23 -0
- package/skills/find-venue/SKILL.md +128 -0
- package/skills/find-venue/SKILL.md.spec.ts +145 -0
- package/skills/find-venue/find-venue.eval.mjs +19 -0
- package/skills/find-venue/find-venue.harness.mjs +23 -0
- package/skills/grade-paper-writing/SKILL.md +436 -0
- package/skills/grade-paper-writing/SKILL.md.spec.ts +453 -0
- package/skills/grade-paper-writing/fixtures/control_gopen.txt +1 -0
- package/skills/grade-paper-writing/fixtures/control_human_paper.txt +1 -0
- package/skills/grade-paper-writing/fixtures/rewrite.txt +1 -0
- package/skills/grade-paper-writing/fixtures/specimen.txt +1 -0
- package/skills/grade-paper-writing/fixtures/structure-checks.md +22 -0
- package/skills/grade-paper-writing/grade-paper-writing.eval.mjs +19 -0
- package/skills/grade-paper-writing/grade-paper-writing.harness.mjs +23 -0
- package/skills/grade-paper-writing/prose-lint.mjs +713 -0
- package/skills/harden-paper/SKILL.md +318 -0
- package/skills/harden-paper/SKILL.md.spec.ts +336 -0
- package/skills/harden-paper/check-numbers.sh +33 -0
- package/skills/harden-paper/check-release-claims.sh +35 -0
- package/skills/harden-paper/fixtures/uncited-assertions-sample.md +43 -0
- package/skills/harden-paper/fixtures/uncited-assertions-sample.tex +77 -0
- package/skills/harden-paper/harden-paper.eval.mjs +19 -0
- package/skills/harden-paper/harden-paper.harness.mjs +23 -0
- package/skills/map-prior-work/SKILL.md +211 -0
- package/skills/map-prior-work/SKILL.md.spec.ts +227 -0
- package/skills/map-prior-work/map-prior-work.eval.mjs +19 -0
- package/skills/map-prior-work/map-prior-work.harness.mjs +23 -0
- package/skills/osf-artifact-upload/SKILL.md +52 -0
- package/skills/osf-artifact-upload/SKILL.md.spec.ts +59 -0
- package/skills/osf-artifact-upload/osf-artifact-upload.eval.mjs +22 -0
- package/skills/osf-artifact-upload/osf-artifact-upload.harness.mjs +103 -0
- package/skills/paper-adversarial-review/SKILL.md +126 -0
- package/skills/paper-adversarial-review/SKILL.md.spec.ts +142 -0
- package/skills/paper-adversarial-review/paper-adversarial-review.eval.mjs +19 -0
- package/skills/paper-adversarial-review/paper-adversarial-review.harness.mjs +23 -0
- package/skills/paper-pipeline/PIPELINE-MAP.md +371 -0
- package/skills/paper-pipeline/SKILL.md +499 -0
- package/skills/paper-pipeline/SKILL.md.spec.ts +517 -0
- package/skills/paper-pipeline/description-language.eval.mjs +347 -0
- package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +891 -0
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +1254 -0
- package/skills/paper-pipeline/paper-pipeline.eval.mjs +22 -0
- package/skills/paper-pipeline/paper-pipeline.harness.mjs +143 -0
- package/skills/paper-pipeline/pipeline-firing.baseline.json +270 -0
- package/skills/paper-pipeline/pipeline-firing.eval.mjs +664 -0
- package/skills/paper-pipeline/pipeline-language.eval.mjs +672 -0
- package/skills/paper-pipeline/references/acceptance-gate.md +329 -0
- package/skills/paper-pipeline/references/acl-venue-rules.md +142 -0
- package/skills/paper-pipeline/references/anonymization.md +68 -0
- package/skills/paper-pipeline/references/artifact-checklist.md +93 -0
- package/skills/paper-pipeline/references/body-vs-appendix.md +97 -0
- package/skills/paper-pipeline/references/credit-criteria.md +69 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/README.md +35 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/run_retext.mjs +24 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/sentences.txt +11 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/test_sentences.py +25 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-prose-checkers.md +538 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-reproducible-tooling.md +431 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-staleness-and-orchestration.md +592 -0
- package/skills/paper-pipeline/references/pipeline-status-template.md +162 -0
- package/skills/paper-pipeline/references/review-ratchet.md +36 -0
- package/skills/paper-pipeline/references/sweep-2026-08-09-ideal-pipeline.md +585 -0
- package/skills/paper-pipeline/references/writing-craft.md +448 -0
- package/skills/paper-pipeline/repro/2026-08-07-description-language-control.log +63 -0
- package/skills/paper-pipeline/repro/2026-08-07-fork-check.log +52 -0
- package/skills/paper-pipeline/repro/2026-08-07-fork-check2.log +33 -0
- package/skills/paper-pipeline/repro/2026-08-07-language-eval-pilot.log +33 -0
- package/skills/paper-pipeline/repro/2026-08-07-language-eval-raw.log +166 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.json +338 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.log +118 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-raw.log +245 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary.json +776 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-oracle.log +53 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-raw.log +89 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.json +450 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.log +136 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-raw.log +242 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-setupdiff.log +59 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation.json +1032 -0
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.json +139 -0
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.log +98 -0
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication.mjs +92 -0
- package/skills/paper-pipeline/repro/README.md +129 -0
- package/skills/paper-pipeline/repro/analyze-language-eval.py +116 -0
- package/skills/paper-pipeline/scripts/README.md +344 -0
- package/skills/paper-pipeline/scripts/announce.mjs +67 -0
- package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +496 -0
- package/skills/paper-pipeline/scripts/artifact-coverage.mjs +397 -0
- package/skills/paper-pipeline/scripts/artifact-coverage.mutations.mjs +218 -0
- package/skills/paper-pipeline/scripts/check-provenance.mjs +184 -0
- package/skills/paper-pipeline/scripts/consumer.d.mts +32 -0
- package/skills/paper-pipeline/scripts/consumer.harness.mjs +562 -0
- package/skills/paper-pipeline/scripts/consumer.mjs +535 -0
- package/skills/paper-pipeline/scripts/consumer.mutations.mjs +190 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +457 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +656 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.mutations.mjs +54 -0
- package/skills/paper-pipeline/scripts/fixtures/clean/PIPELINE-STATUS.md +51 -0
- package/skills/paper-pipeline/scripts/fixtures/dirty/PIPELINE-STATUS.md +52 -0
- package/skills/paper-pipeline/scripts/fixtures/dirty/paper.md +6 -0
- package/skills/paper-pipeline/scripts/fixtures/real-bib/refs.bib +153 -0
- package/skills/paper-pipeline/scripts/generated-code.harness.mjs +466 -0
- package/skills/paper-pipeline/scripts/generated-code.mjs +338 -0
- package/skills/paper-pipeline/scripts/generated-code.mutations.mjs +254 -0
- package/skills/paper-pipeline/scripts/ledger.mjs +623 -0
- package/skills/paper-pipeline/scripts/ledger.selftest.mjs +286 -0
- package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +389 -0
- package/skills/paper-pipeline/scripts/pipeline-check.mjs +737 -0
- package/skills/paper-pipeline/scripts/pipeline-check.mutations.mjs +54 -0
- package/skills/paper-pipeline/scripts/pipeline-edges.mjs +169 -0
- package/skills/paper-pipeline/scripts/population-map.harness.mjs +178 -0
- package/skills/paper-pipeline/scripts/population-map.mjs +181 -0
- package/skills/paper-pipeline/scripts/population-map.mutations.mjs +65 -0
- package/skills/paper-pipeline/scripts/population-map.selftest.mjs +122 -0
- package/skills/paper-pipeline/scripts/provenance.harness.mjs +240 -0
- package/skills/paper-pipeline/scripts/provenance.mutations.mjs +59 -0
- package/skills/paper-pipeline/scripts/round-diff.harness.mjs +881 -0
- package/skills/paper-pipeline/scripts/round-diff.mjs +576 -0
- package/skills/paper-pipeline/scripts/round-diff.mutations.mjs +276 -0
- package/skills/paper-pipeline/scripts/run-mechanical.mjs +633 -0
- package/skills/paper-pipeline/scripts/status.mjs +295 -0
- package/skills/paper-status/SKILL.md +183 -0
- package/skills/paper-status/SKILL.md.spec.ts +190 -0
- package/skills/paper-status/paper-status.eval.mjs +22 -0
- package/skills/paper-status/paper-status.harness.mjs +25 -0
- package/skills/pc-panel-review/SKILL.md +263 -0
- package/skills/pc-panel-review/SKILL.md.spec.ts +280 -0
- package/skills/pc-panel-review/pc-panel-review.eval.mjs +19 -0
- package/skills/pc-panel-review/pc-panel-review.harness.mjs +23 -0
- package/skills/plan-paper-timeline/SKILL.md +182 -0
- package/skills/plan-paper-timeline/SKILL.md.spec.ts +200 -0
- package/skills/plan-paper-timeline/fixtures/fake-google-calendar.mjs +239 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +431 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.mutations.mjs +65 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.eval.mjs +19 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.harness.mjs +23 -0
- package/skills/render-paper/SKILL.md +159 -0
- package/skills/render-paper/SKILL.md.spec.ts +166 -0
- package/skills/render-paper/check-render.sh +419 -0
- package/skills/render-paper/checkers-requirements.txt +55 -0
- package/skills/render-paper/ensure-checkers.sh +69 -0
- package/skills/render-paper/extract-pdf-facts.harness.mjs +166 -0
- package/skills/render-paper/extract-pdf-facts.mjs +144 -0
- package/skills/render-paper/render-paper.eval.mjs +19 -0
- package/skills/render-paper/render-paper.harness.mjs +339 -0
- package/skills/research-ideate/SKILL.md +136 -0
- package/skills/research-ideate/SKILL.md.spec.ts +152 -0
- package/skills/research-ideate/research-ideate.eval.mjs +19 -0
- package/skills/research-ideate/research-ideate.harness.mjs +23 -0
- package/skills/skill-contract.mutations.mjs +179 -0
- package/skills/study-accepted-papers/SKILL.md +206 -0
- package/skills/study-accepted-papers/SKILL.md.spec.ts +223 -0
- package/skills/study-accepted-papers/study-accepted-papers.eval.mjs +19 -0
- package/skills/study-accepted-papers/study-accepted-papers.harness.mjs +23 -0
- package/skills/submit-paper/SKILL.md +182 -0
- package/skills/submit-paper/SKILL.md.spec.ts +199 -0
- package/skills/submit-paper/check-deanon.sh +149 -0
- package/skills/submit-paper/references/publishers/acm.md +92 -0
- package/skills/submit-paper/references/venues/agenticdev.jsonc +108 -0
- package/skills/submit-paper/references/venues/agenticdev.md +139 -0
- package/skills/submit-paper/references/venues/agenticdev.tex +19 -0
- package/skills/submit-paper/references/venues/aisec.jsonc +101 -0
- package/skills/submit-paper/references/venues/aisec.md +105 -0
- package/skills/submit-paper/references/venues/paper-guards.tex +41 -0
- package/skills/submit-paper/references/venues/realm.jsonc +81 -0
- package/skills/submit-paper/references/venues/realm.md +155 -0
- package/skills/submit-paper/references/venues/tex-base.jsonc +50 -0
- package/skills/submit-paper/references/venues/venue-profile.schema.json +74 -0
- package/skills/submit-paper/submit-paper.eval.mjs +19 -0
- package/skills/submit-paper/submit-paper.harness.mjs +23 -0
- package/skills/sweep-design-space/SKILL.md +269 -0
- package/skills/sweep-design-space/SKILL.md.spec.ts +285 -0
- package/skills/sweep-design-space/sweep-design-space.eval.mjs +19 -0
- package/skills/sweep-design-space/sweep-design-space.harness.mjs +23 -0
- package/skills/tighten-paper/SKILL.md +368 -0
- package/skills/tighten-paper/SKILL.md.spec.ts +384 -0
- package/skills/tighten-paper/structure.mjs +371 -0
- package/skills/tighten-paper/tighten-paper.eval.mjs +19 -0
- package/skills/tighten-paper/tighten-paper.harness.mjs +23 -0
- package/skills/verify-citations/SKILL.md +328 -0
- package/skills/verify-citations/SKILL.md.spec.ts +345 -0
- package/skills/verify-citations/scripts/bib-authors.mjs +479 -0
- package/skills/verify-citations/scripts/bib-authors.test.mjs +175 -0
- package/skills/verify-citations/scripts/verify-cites.mjs +1108 -0
- package/skills/verify-citations/scripts/verify-cites.test.mjs +735 -0
- package/skills/verify-citations/verify-citations.eval.mjs +19 -0
- package/skills/verify-citations/verify-citations.harness.mjs +23 -0
- package/src/CLAUDE.md +51 -0
- package/src/action-ref.test.ts +26 -0
- package/src/action-ref.ts +15 -0
- package/src/adapters/banal/failure.test.ts +63 -0
- package/src/adapters/banal/failure.ts +118 -0
- package/src/adapters/banal/index.test.ts +119 -0
- package/src/adapters/banal/index.ts +100 -0
- package/src/adapters/banal/install.test.ts +20 -0
- package/src/adapters/banal/install.ts +41 -0
- package/src/adapters/banal/invocation.test.ts +74 -0
- package/src/adapters/banal/invocation.ts +95 -0
- package/src/adapters/banal/locate.test.ts +52 -0
- package/src/adapters/banal/locate.ts +84 -0
- package/src/adapters/banal/output.test.ts +140 -0
- package/src/adapters/banal/output.ts +141 -0
- package/src/adapters/banal/pin.ts +30 -0
- package/src/adapters/banal/probe.ts +35 -0
- package/src/adapters/banal/run.test.ts +191 -0
- package/src/adapters/banal/run.ts +244 -0
- package/src/adapters/banal/settings.test.ts +31 -0
- package/src/adapters/banal/settings.ts +55 -0
- package/src/adapters/banal/xml.test.ts +111 -0
- package/src/adapters/banal/xml.ts +112 -0
- package/src/adapters/curl/download.io.ts +73 -0
- package/src/adapters/curl/download.test.ts +55 -0
- package/src/adapters/curl/index.ts +5 -0
- package/src/adapters/memory/index.ts +131 -0
- package/src/adapters/node/files.io.ts +39 -0
- package/src/adapters/node/files.test.ts +28 -0
- package/src/adapters/node/host.io.ts +15 -0
- package/src/adapters/node/index.ts +36 -0
- package/src/adapters/node/process.io.ts +49 -0
- package/src/adapters/node/process.test.ts +46 -0
- package/src/adapters/node/workspace.io.ts +40 -0
- package/src/adapters/node/workspace.test.ts +58 -0
- package/src/adapters/pdfjs/fill.test.ts +111 -0
- package/src/adapters/pdfjs/fill.ts +141 -0
- package/src/build-engine.harness.mjs +314 -0
- package/src/build-engine.ts +219 -0
- package/src/build.harness.mjs +631 -0
- package/src/build.mutations.mjs +195 -0
- package/src/build.ts +793 -0
- package/src/cli.harness.mjs +2007 -0
- package/src/cli.mutations.mjs +448 -0
- package/src/cli.ts +1189 -0
- package/src/doctor.harness.mjs +396 -0
- package/src/doctor.mutations.mjs +175 -0
- package/src/doctor.ts +356 -0
- package/src/domain/geometry.ts +108 -0
- package/src/domain/host.ts +23 -0
- package/src/domain/page-layout.ts +32 -0
- package/src/domain/paths.ts +5 -0
- package/src/domain/result.test.ts +26 -0
- package/src/domain/result.ts +29 -0
- package/src/domain/sha256.test.ts +12 -0
- package/src/domain/sha256.ts +21 -0
- package/src/domain/text.ts +11 -0
- package/src/engine.harness.mjs +252 -0
- package/src/engine.ts +176 -0
- package/src/exit-code.test.ts +21 -0
- package/src/exit-code.ts +38 -0
- package/src/facts-file.test.ts +240 -0
- package/src/facts-file.ts +241 -0
- package/src/hooks-settings.harness.mjs +386 -0
- package/src/hooks-settings.mutations.mjs +116 -0
- package/src/hooks-settings.ts +434 -0
- package/src/init.ts +900 -0
- package/src/latex-log.harness.mjs +226 -0
- package/src/latex-log.ts +234 -0
- package/src/latex-loop.harness.mjs +449 -0
- package/src/latex-loop.ts +211 -0
- package/src/link-skills.harness.mjs +273 -0
- package/src/link-skills.mutations.mjs +136 -0
- package/src/link-skills.ts +258 -0
- package/src/new-paper.harness.mjs +216 -0
- package/src/new-paper.mutations.mjs +79 -0
- package/src/new-paper.ts +158 -0
- package/src/pdf-facts.harness.mjs +188 -0
- package/src/pdf-facts.ts +327 -0
- package/src/pdf-geometry.harness.mjs +254 -0
- package/src/pdf-geometry.ts +300 -0
- package/src/ports/download.ts +10 -0
- package/src/ports/files.ts +11 -0
- package/src/ports/measure-geometry.ts +8 -0
- package/src/ports/process.ts +46 -0
- package/src/ports/tool-installer.ts +33 -0
- package/src/ports/workspace.ts +20 -0
- package/src/rules-config.harness.mjs +114 -0
- package/src/rules-config.ts +178 -0
- package/src/structure.harness.mjs +179 -0
- package/src/structure.mutations.mjs +83 -0
- package/src/structure.ts +166 -0
- package/src/tex-requirements.harness.mjs +238 -0
- package/src/tex-requirements.ts +181 -0
- package/src/toolchain.harness.mjs +651 -0
- package/src/toolchain.ts +755 -0
- package/src/types.ts +106 -0
- package/templates/paper/PIPELINE-STATUS.md +72 -0
- package/templates/paper/paper.md +4 -0
- package/templates/paper/paper.tex +8 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plan-paper-timeline — the EFFECTS tier. What the run can actually reach, not what
|
|
3
|
+
* SKILL.md says it reaches.
|
|
4
|
+
*
|
|
5
|
+
* SIBLING, NOT REPLACEMENT. `plan-paper-timeline.harness.mjs` next door is static
|
|
6
|
+
* (`checkSkill()`): frontmatter, wiring, script paths, verdict vocabulary. It is untouched.
|
|
7
|
+
* This file runs the REAL `claude` CLI against a SCRIPTED MOCK MODEL (`runHarnessTest`),
|
|
8
|
+
* so it costs $0 and no API key — and observes the one thing static analysis structurally
|
|
9
|
+
* cannot: whether the tools this skill DECLARES EXIST WHERE IT RUNS.
|
|
10
|
+
*
|
|
11
|
+
* ── WHY THIS SKILL FOR THIS CHANNEL ──────────────────────────────────────────────
|
|
12
|
+
* It is the only skill in the corpus whose core step is an MCP call. §3 says, literally,
|
|
13
|
+
* «Use `mcp__Google_Calendar__create_event` … 🔴 Read the zone, do not assume it». Four of its ten
|
|
14
|
+
* declared entries are MCP calendar tools. A declaration naming a tool that is not there is
|
|
15
|
+
* invisible to every static check in this repo and to `vigiles audit`, which scores this
|
|
16
|
+
* corpus Structure 100.
|
|
17
|
+
*
|
|
18
|
+
* ── SAFETY: WHY THIS CANNOT TOUCH A REAL CALENDAR ───────────────────────────
|
|
19
|
+
* Measured before this file was written, not assumed. The probe below is `list_events`,
|
|
20
|
+
* which is READ-ONLY; `create_event`/`update_event` are NEVER called here, on purpose,
|
|
21
|
+
* even though the surface turns out to be absent. The run also happens in a `mkdtemp`
|
|
22
|
+
* work dir the runner deletes. If a future CLI ever exposes the calendar headlessly, the
|
|
23
|
+
* read-only probe starts PASSING and this test FAILS loudly — which is the point of
|
|
24
|
+
* pinning it, and is a far better way to learn it than a created event.
|
|
25
|
+
*
|
|
26
|
+
* ── WHAT THIS PINS (a characterization test, deliberately) ───────────────────────
|
|
27
|
+
* Under `claude -p` at `CHARACTERIZED_CLI` (`lib/agent-cli-version.mjs`, 2.1.227) the whole
|
|
28
|
+
* `mcp__*` surface is ABSENT, and so is `ToolSearch`.
|
|
29
|
+
*
|
|
30
|
+
* 🔴 THAT NUMBER IS A VALUE, NOT PROSE, and it was prose until issue #7. The probe below used
|
|
31
|
+
* to run with `stdio: "ignore"`, so the version the CLI printed was never read — this tier
|
|
32
|
+
* characterized whatever the runner installed that day while this paragraph kept saying 2.1.227.
|
|
33
|
+
* Measured: 2.1.273 on the reporter's machine and on this one, forty-six patch releases of drift
|
|
34
|
+
* that nothing in a green log could show. The version is now OBSERVED, reported in the summary
|
|
35
|
+
* below, and named in every failure message — so a red says which version it characterized.
|
|
36
|
+
* That means:
|
|
37
|
+
* 1. §3 of this skill is UNEXECUTABLE in any headless / CI context. It is an
|
|
38
|
+
* interactive-session-only step. Nothing in the file says so.
|
|
39
|
+
* 2. 🔴 THE RECORDED `ToolSearch` FINDING DOES NOT REPRODUCE HERE, AND THE CORRECTION
|
|
40
|
+
* MATTERS. `.claude/lib/skill-effects-results/README.md` (2026-08-10, $0.82)
|
|
41
|
+
* recorded "the model called ToolSearch … it isn't in allowed-tools," with the caveat
|
|
42
|
+
* that it might be an artifact of the headless environment. It is the opposite:
|
|
43
|
+
* headless has NO ToolSearch to call. Adding `ToolSearch` to `allowed-tools` would
|
|
44
|
+
* therefore fix nothing here. The reachability of these four tools is a property of
|
|
45
|
+
* the ENVIRONMENT, not of the declaration.
|
|
46
|
+
*
|
|
47
|
+
* If either fact changes, this file fails and says which one — that is its whole job.
|
|
48
|
+
* It is not asserting that absence is good.
|
|
49
|
+
*
|
|
50
|
+
* Cost: $0 (scripted mock model, no API key). Wall clock ~30-60s for one CLI spawn.
|
|
51
|
+
*/
|
|
52
|
+
import {
|
|
53
|
+
existsSync,
|
|
54
|
+
mkdtempSync,
|
|
55
|
+
readFileSync,
|
|
56
|
+
rmSync,
|
|
57
|
+
realpathSync,
|
|
58
|
+
} from "node:fs";
|
|
59
|
+
import { tmpdir } from "node:os";
|
|
60
|
+
import { dirname, join, resolve } from "node:path";
|
|
61
|
+
import { fileURLToPath } from "node:url";
|
|
62
|
+
import { runHarnessTest, skip } from "vigiles";
|
|
63
|
+
import { observeAgentCli } from "../../lib/agent-cli-version.mjs";
|
|
64
|
+
import { DEFAULT_TIMEZONE } from "../paper-pipeline/scripts/consumer.mjs";
|
|
65
|
+
|
|
66
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
67
|
+
// 🔴 DON'T COUNT LEVELS, SEARCH FOR THEM. This used to be `resolve(HERE, "..", "..", "..")` —
|
|
68
|
+
// correct while the file lived at `.claude/skills/<skill>/` on the consumer (three levels to the
|
|
69
|
+
// root), and SILENTLY wrong after the move into the package, where it lives at
|
|
70
|
+
// `skills/<skill>/` (two). The miss doesn't crash: `resolve` happily hands back a directory
|
|
71
|
+
// ABOVE the repository, `pluginDir` points into nothing, and the symptom shows up as
|
|
72
|
+
// "Unknown skill" — that is, it looks like the skill is broken, not the path arithmetic.
|
|
73
|
+
// Walking up to the directory that actually contains `.claude` doesn't depend on depth at all.
|
|
74
|
+
const REPO = (() => {
|
|
75
|
+
let dir = HERE;
|
|
76
|
+
for (;;) {
|
|
77
|
+
if (existsSync(resolve(dir, ".claude"))) return dir;
|
|
78
|
+
const up = dirname(dir);
|
|
79
|
+
if (up === dir)
|
|
80
|
+
throw new Error(
|
|
81
|
+
`no root with a .claude directory found starting from ${HERE}. Without it, \`pluginDir\` ` +
|
|
82
|
+
`would point past it, and the run would report "Unknown skill" instead of "the path was computed wrong".`,
|
|
83
|
+
);
|
|
84
|
+
dir = up;
|
|
85
|
+
}
|
|
86
|
+
})();
|
|
87
|
+
const CLAUDE_DIR = resolve(REPO, ".claude");
|
|
88
|
+
|
|
89
|
+
// The deterministic tier spawns the real agent binary. Without it there is nothing to
|
|
90
|
+
// observe, and a silent ✓ would be the exact lie this tier exists to end.
|
|
91
|
+
//
|
|
92
|
+
// 🔴 OBSERVED, NOT PROBED. This spawn already existed; it ran with `stdio: "ignore"`, which
|
|
93
|
+
// discarded the one thing worth reading. `observeAgentCli` keeps the spawn and keeps the string,
|
|
94
|
+
// refuses an exit-0-with-empty-stdout as "not an observation", and carries the characterized
|
|
95
|
+
// version alongside so the two can be compared on every run instead of by a reader's memory.
|
|
96
|
+
const { spawnSync } = await import("node:child_process");
|
|
97
|
+
const cli = observeAgentCli({ spawnSync });
|
|
98
|
+
if (!cli.present) skip(`plan-paper-timeline effects: ${cli.note}`);
|
|
99
|
+
|
|
100
|
+
const declaredMcp = [
|
|
101
|
+
"mcp__Google_Calendar__list_events",
|
|
102
|
+
"mcp__Google_Calendar__search_events",
|
|
103
|
+
"mcp__Google_Calendar__create_event",
|
|
104
|
+
"mcp__Google_Calendar__update_event",
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
const r = await runHarnessTest({
|
|
108
|
+
// The REAL harness — this repo's skills and hooks, not a retyped subset.
|
|
109
|
+
pluginDir: CLAUDE_DIR,
|
|
110
|
+
// bubblewrap is absent on this machine, so `sandbox: "auto"` would REFUSE rather than
|
|
111
|
+
// run. The code being loaded is our own `.claude`, which is the trust condition
|
|
112
|
+
// `specTrusted` is about; the container is the outer confinement.
|
|
113
|
+
sandbox: false,
|
|
114
|
+
transcript: true,
|
|
115
|
+
timeoutMs: 180_000,
|
|
116
|
+
prompt: "plan the AISec timeline",
|
|
117
|
+
// The SESSION grant. Deliberately WIDER than the skill's declaration for `Read`, and
|
|
118
|
+
// deliberately missing `Bash` — that asymmetry is what turns the two probes below into
|
|
119
|
+
// a control and a measurement inside ONE permission configuration.
|
|
120
|
+
allowedTools: ["Read", "Write", "Skill", "ToolSearch", ...declaredMcp],
|
|
121
|
+
files: { "cfp.md": "AISec 2026 — submission 24 Jul AoE\n" },
|
|
122
|
+
model: [
|
|
123
|
+
{ tool: "Skill", input: { skill: "plan-paper-timeline" } },
|
|
124
|
+
// CONTROL: a tool that is present and permitted. If this failed, every "absent"
|
|
125
|
+
// verdict below would be a broken run, not a finding.
|
|
126
|
+
{ tool: "Read", input: { file_path: "cfp.md" } },
|
|
127
|
+
// READ-ONLY calendar probe. Never create_event / update_event — see header.
|
|
128
|
+
{
|
|
129
|
+
tool: "mcp__Google_Calendar__list_events",
|
|
130
|
+
input: { calendarId: "primary" },
|
|
131
|
+
},
|
|
132
|
+
// The tool the 2026-08-10 sweep said was missing from the declaration.
|
|
133
|
+
{
|
|
134
|
+
tool: "ToolSearch",
|
|
135
|
+
input: {
|
|
136
|
+
query: "select:mcp__Google_Calendar__create_event",
|
|
137
|
+
max_results: 1,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
{ text: "done" },
|
|
141
|
+
],
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const call = (name) => r.toolCalls.find((c) => c.name === name);
|
|
146
|
+
const absent = (c) =>
|
|
147
|
+
c !== undefined && c.isError && /No such tool available/.test(c.resultText);
|
|
148
|
+
|
|
149
|
+
// ── 1. The run actually happened and the skill actually loaded ──────────────────
|
|
150
|
+
// Asserted FIRST and separately from everything below. A confident "nothing was
|
|
151
|
+
// reachable" from a run where the skill never loaded is a wiring bug wearing a
|
|
152
|
+
// finding's clothes; this repo produced four of those on 2026-08-10.
|
|
153
|
+
const activation = call("Skill");
|
|
154
|
+
if (!activation || activation.isError)
|
|
155
|
+
throw new Error(
|
|
156
|
+
`plan-paper-timeline never activated (${activation ? activation.resultText : "no Skill call"}). ` +
|
|
157
|
+
`Everything below would be a fact about a broken fixture, not about the skill.`,
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const reachedModel = r.modelRequests
|
|
161
|
+
.map((q) => [q.system, ...q.messages.map((m) => m.text)].join("\n"))
|
|
162
|
+
.join("\n");
|
|
163
|
+
if (!reachedModel.includes("FILING-SPACING"))
|
|
164
|
+
throw new Error(
|
|
165
|
+
`the Skill call resolved but the skill BODY never reached the model ` +
|
|
166
|
+
`("FILING-SPACING", a §4 heading, is absent from all ${String(r.modelRequests.length)} requests). ` +
|
|
167
|
+
`Activation without a body is a loaded skill that guides nothing.`,
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const ctrl = call("Read");
|
|
171
|
+
if (!ctrl || ctrl.isError)
|
|
172
|
+
throw new Error(
|
|
173
|
+
`POSITIVE CONTROL FAILED: Read — a tool this skill declares and the session grants — ` +
|
|
174
|
+
`did not succeed (${ctrl ? ctrl.resultText.slice(0, 160) : "never called"}). ` +
|
|
175
|
+
`Until this passes, no "tool absent" result below can be believed.`,
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
// ── 2. The pinned environment fact: the declared MCP surface is not there ───────
|
|
179
|
+
const cal = call("mcp__Google_Calendar__list_events");
|
|
180
|
+
if (!absent(cal))
|
|
181
|
+
throw new Error(
|
|
182
|
+
`THE ENVIRONMENT CHANGED — and this is good news that must be read, not silenced. ` +
|
|
183
|
+
`Observed \`claude\` ${cli.version} (characterized against ${cli.characterized}). ` +
|
|
184
|
+
`\`mcp__Google_Calendar__list_events\` is now REACHABLE from \`claude -p\` ` +
|
|
185
|
+
`(result: ${cal ? cal.resultText.slice(0, 200) : "no call recorded"}). ` +
|
|
186
|
+
`Consequences: (a) §3 of this skill is now executable headlessly, so it can finally be ` +
|
|
187
|
+
`tested for real instead of characterized; (b) 🔴 every future run of this tier can now ` +
|
|
188
|
+
`WRITE A REAL CALENDAR — re-check the safety argument in this file's header BEFORE ` +
|
|
189
|
+
`adding a create_event probe; (c) update this assertion deliberately, do not delete it.`,
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
const ts = call("ToolSearch");
|
|
193
|
+
if (!absent(ts))
|
|
194
|
+
throw new Error(
|
|
195
|
+
`\`ToolSearch\` is now available under \`claude -p\` ${cli.version} ` +
|
|
196
|
+
`(characterized against ${cli.characterized}) (result: ` +
|
|
197
|
+
`${ts ? ts.resultText.slice(0, 200) : "no call recorded"}). The 2026-08-10 sweep recorded ` +
|
|
198
|
+
`"ToolSearch missing from allowed-tools" as a finding about THIS SKILL; measured on ` +
|
|
199
|
+
`2026-08-11 it was a fact about the ENVIRONMENT (headless had no ToolSearch at all). ` +
|
|
200
|
+
`If it exists now, that finding becomes actionable again: add it to allowed-tools.`,
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// ── 3. What that means, stated once, where a reader will see it ────────────────
|
|
204
|
+
console.log(
|
|
205
|
+
`✓ plan-paper-timeline [effects]: skill activated and its body reached the model; ` +
|
|
206
|
+
`Read (declared+granted) works — the run is live.\n` +
|
|
207
|
+
// 🔴 The version goes in the REPORT, not only into failures. A characterization test that
|
|
208
|
+
// silently still holds is indistinguishable from one nobody re-checked, so the run says
|
|
209
|
+
// which version it held against — and says it whether or not the numbers agree.
|
|
210
|
+
` CLI: ${cli.note}\n` +
|
|
211
|
+
` PINNED: ${String(declaredMcp.length)} of its 10 declared entries are MCP calendar tools, ` +
|
|
212
|
+
`and the whole mcp__* surface is ABSENT under \`claude -p\`; \`ToolSearch\` is absent too.\n` +
|
|
213
|
+
` → §3 ("Create the calendar events") is an INTERACTIVE-SESSION-ONLY step. Nothing in ` +
|
|
214
|
+
`SKILL.md says so, and no static check can see it.\n` +
|
|
215
|
+
` → Corrects the 2026-08-10 sweep: the ToolSearch gap was environmental, not a defect ` +
|
|
216
|
+
`in this declaration.`,
|
|
217
|
+
);
|
|
218
|
+
} finally {
|
|
219
|
+
r.cleanup();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ════════════════════════════════════════════════════════════════════════════════
|
|
223
|
+
// RUN B — §3 EXECUTED, not characterized
|
|
224
|
+
//
|
|
225
|
+
// Run A above pins that the calendar surface is absent from a headless session, which
|
|
226
|
+
// is true and worth knowing and also means the skill's CENTRAL STEP has never been
|
|
227
|
+
// checked by anything. Run B supplies the surface with a stand-in
|
|
228
|
+
// (`fixtures/fake-google-calendar.mjs`) carrying the REAL API's required-field sets,
|
|
229
|
+
// so the step becomes executable for the first time.
|
|
230
|
+
//
|
|
231
|
+
// ── WHAT IS UNDER TEST, AND WHY IT IS NOT THE MODEL ─────────────────────────────
|
|
232
|
+
// The model here is scripted, so asserting "the agent decided to create three events"
|
|
233
|
+
// would only be replaying this file's own script — a green light that means nothing.
|
|
234
|
+
// Whether a real model reads §3 and does the right thing is a PROBABILISTIC question
|
|
235
|
+
// and it belongs to `plan-paper-timeline.eval.mjs`, which costs money and runs locally.
|
|
236
|
+
//
|
|
237
|
+
// The unit under test here is §3 ITSELF: the call it prescribes. The prescribed
|
|
238
|
+
// arguments are PARSED OUT OF SKILL.md — not retyped here — and replayed against the
|
|
239
|
+
// strict double. So the harness answers a question nobody was asking: is the call this
|
|
240
|
+
// section tells the model to make actually well-formed against the real Google
|
|
241
|
+
// Calendar API? Edit §3 into something the API would reject and this goes red.
|
|
242
|
+
//
|
|
243
|
+
// That framing is the whole reason the block in §3 exists. It also found the defect
|
|
244
|
+
// that motivated it: §3 named only the title, the date and `timeZone`, while
|
|
245
|
+
// `create_event` requires `summary` + `startTime` + `endTime`. A model following the
|
|
246
|
+
// old §3 literally had no reason to pass an end time.
|
|
247
|
+
// ════════════════════════════════════════════════════════════════════════════════
|
|
248
|
+
|
|
249
|
+
const MARKER = "harness:prescribed-call create_event";
|
|
250
|
+
|
|
251
|
+
/** §3's prose, read once — the zone assertions below check the PROSE as well as the call. */
|
|
252
|
+
const skillBody = readFileSync(join(HERE, "SKILL.md"), "utf8");
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Pull §3's prescribed call out of SKILL.md.
|
|
256
|
+
*
|
|
257
|
+
* Parsed with markdown-it rather than a regex, deliberately: a fenced block is markup
|
|
258
|
+
* structure, and "the fence after the marker" is a statement about TOKENS. A regex over
|
|
259
|
+
* lines cannot tell a real fence from one quoted inside another fence, and this repo has
|
|
260
|
+
* paid for that lesson four times (root CLAUDE.md, "Markdown is parsed WITH A PARSER").
|
|
261
|
+
* A missing markdown-it must fail LOUDLY — a quiet fallback would let a degraded
|
|
262
|
+
* version of this check run forever without anyone noticing.
|
|
263
|
+
*/
|
|
264
|
+
async function prescribedCall() {
|
|
265
|
+
const { default: MarkdownIt } = await import("markdown-it");
|
|
266
|
+
const src = readFileSync(join(HERE, "SKILL.md"), "utf8");
|
|
267
|
+
// `html: true` is load-bearing, not a style choice: with the default the comment is
|
|
268
|
+
// not a token of its own at all — it lands INSIDE a paragraph's inline token, the
|
|
269
|
+
// marker is never seen, and the harness reports the marker as missing while it is
|
|
270
|
+
// sitting right there in the file. Measured while writing this.
|
|
271
|
+
const tokens = new MarkdownIt({ html: true }).parse(src, {});
|
|
272
|
+
let armed = false;
|
|
273
|
+
for (const t of tokens) {
|
|
274
|
+
// Matched on content rather than on `type === "html_block"` so that a future
|
|
275
|
+
// tokenizer change degrades into the loud error below instead of a silent miss.
|
|
276
|
+
if (!armed && (t.content ?? "").includes(MARKER)) {
|
|
277
|
+
armed = true;
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (!armed) continue;
|
|
281
|
+
if (t.type !== "fence")
|
|
282
|
+
throw new Error(
|
|
283
|
+
`SKILL.md: the token after the "${MARKER}" marker is a ${t.type}, not a fenced block. ` +
|
|
284
|
+
`The marker must sit immediately above the json fence it labels.`,
|
|
285
|
+
);
|
|
286
|
+
return JSON.parse(t.content);
|
|
287
|
+
}
|
|
288
|
+
throw new Error(
|
|
289
|
+
`SKILL.md no longer contains the "${MARKER}" marker. §3's prescribed call is the unit ` +
|
|
290
|
+
`under test here; without it this harness has nothing to check and must not pass quietly.`,
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const prescribed = await prescribedCall();
|
|
295
|
+
const logDir = realpathSync(mkdtempSync(join(tmpdir(), "ppt-effects-")));
|
|
296
|
+
const callLog = join(logDir, "calls.jsonl");
|
|
297
|
+
|
|
298
|
+
try {
|
|
299
|
+
const b = await runHarnessTest({
|
|
300
|
+
pluginDir: CLAUDE_DIR,
|
|
301
|
+
sandbox: false,
|
|
302
|
+
transcript: true,
|
|
303
|
+
timeoutMs: 180_000,
|
|
304
|
+
prompt: "plan the AISec timeline",
|
|
305
|
+
// Two lines are the entire cost of supplying the surface. Claude Code discovers a
|
|
306
|
+
// `.mcp.json` in its cwd headlessly when the settings enable project servers —
|
|
307
|
+
// measured 2026-08-17, `vigiles/repro/mcp-double-2026-08-17/spike-harness-pickup.mjs`.
|
|
308
|
+
// Naming the server `Google_Calendar` is what makes the skill's own declared
|
|
309
|
+
// `mcp__Google_Calendar__create_event` resolve to the stand-in.
|
|
310
|
+
files: {
|
|
311
|
+
".mcp.json": JSON.stringify({
|
|
312
|
+
mcpServers: {
|
|
313
|
+
Google_Calendar: {
|
|
314
|
+
command: process.execPath,
|
|
315
|
+
args: [join(HERE, "fixtures", "fake-google-calendar.mjs")],
|
|
316
|
+
env: { FAKE_MCP_LOG: callLog },
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
}),
|
|
320
|
+
},
|
|
321
|
+
settings: { enableAllProjectMcpServers: true },
|
|
322
|
+
allowedTools: ["Read", "Skill", ...declaredMcp],
|
|
323
|
+
model: [
|
|
324
|
+
{ tool: "Skill", input: { skill: "plan-paper-timeline" } },
|
|
325
|
+
{ tool: "mcp__Google_Calendar__create_event", input: prescribed },
|
|
326
|
+
{ text: "done" },
|
|
327
|
+
],
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
try {
|
|
331
|
+
const call = b.toolCalls.find(
|
|
332
|
+
(c) => c.name === "mcp__Google_Calendar__create_event",
|
|
333
|
+
);
|
|
334
|
+
if (!call)
|
|
335
|
+
throw new Error(
|
|
336
|
+
`the prescribed \`create_event\` call was never recorded. The stand-in was not reached — ` +
|
|
337
|
+
`check that \`.mcp.json\` discovery still works in this CLI version before trusting any ` +
|
|
338
|
+
`verdict from this run.`,
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
// ── The vacuousness guard, asserted BEFORE the verdict it protects ────────────
|
|
342
|
+
// Identity is established from the LOG, not from the reply text, and the order
|
|
343
|
+
// matters more than it looks. The first version of this guard keyed on `evt_fake_`,
|
|
344
|
+
// a string the stand-in prints only on SUCCESS — so a perfectly correct schema
|
|
345
|
+
// REJECTION arrived with no marker and was reported as "something else served this
|
|
346
|
+
// tool". The guard meant to protect the verdict was hiding it, and it took a
|
|
347
|
+
// mutation run to notice (2026-08-17). The log is written on both paths, so a
|
|
348
|
+
// non-empty log means our server handled the call, whatever it decided.
|
|
349
|
+
const logged = existsSync(callLog)
|
|
350
|
+
? readFileSync(callLog, "utf8")
|
|
351
|
+
.trim()
|
|
352
|
+
.split("\n")
|
|
353
|
+
.filter(Boolean)
|
|
354
|
+
.map((l) => JSON.parse(l))
|
|
355
|
+
: [];
|
|
356
|
+
if (logged.length === 0)
|
|
357
|
+
throw new Error(
|
|
358
|
+
`nothing reached the stand-in (reply was: ${call.resultText.slice(0, 200)}). Either the ` +
|
|
359
|
+
`tool was served by something else, or FAKE_MCP_LOG did not survive into the server ` +
|
|
360
|
+
`process. Every assertion below would be vacuous, so this fails instead of passing.`,
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
// ── The verdict: is §3's prescription well-formed against the real API? ───────
|
|
364
|
+
const bad = logged.filter((e) => !e.ok);
|
|
365
|
+
if (bad.length)
|
|
366
|
+
throw new Error(
|
|
367
|
+
`§3 of SKILL.md prescribes a call the REAL Google Calendar API would reject:\n` +
|
|
368
|
+
bad.map((e) => ` ${e.name}: ${e.why}`).join("\n") +
|
|
369
|
+
`\nFix the json block under the "${MARKER}" marker in SKILL.md — the prose there is an ` +
|
|
370
|
+
`instruction to a model, and an instruction that cannot succeed is worse than none.`,
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
// Only now is the success marker meaningful: the log says our server accepted this
|
|
374
|
+
// call, so a reply without the marker means the ACCEPTED call was answered by
|
|
375
|
+
// someone else — a genuinely different failure from the two above.
|
|
376
|
+
if (!/evt_fake_\d/.test(call.resultText))
|
|
377
|
+
throw new Error(
|
|
378
|
+
`the stand-in accepted the call, but the reply the agent saw did not come from it ` +
|
|
379
|
+
`(got: ${call.resultText.slice(0, 200)}). Two servers are answering this tool name.`,
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
// §3 names the timezone; this is where that claim stops being prose.
|
|
383
|
+
//
|
|
384
|
+
// 🔴 THIS USED TO COMPARE AGAINST ONE HARD-CODED ZONE — the first consumer's. That is exactly
|
|
385
|
+
// the thing a package may not know: the same CFP date is a different wall-clock hour for every
|
|
386
|
+
// author, so the zone is the `timezone` CARRIER, read from the consumer's package.json. What
|
|
387
|
+
// the PRESCRIPTION in §3 must show is therefore not anybody's real zone but the documented
|
|
388
|
+
// DEFAULT, next to the instruction telling the model to substitute the declared one.
|
|
389
|
+
//
|
|
390
|
+
// Two assertions, because they fail for different reasons: an unusable zone is a call the
|
|
391
|
+
// calendar API rejects, while a usable-but-unexplained zone is a prescription that silently
|
|
392
|
+
// schedules every other author in someone else's day.
|
|
393
|
+
const zone = logged[0].args.timeZone;
|
|
394
|
+
try {
|
|
395
|
+
new Intl.DateTimeFormat("en-US", { timeZone: zone });
|
|
396
|
+
} catch {
|
|
397
|
+
throw new Error(
|
|
398
|
+
`§3 prescribes \`timeZone: ${JSON.stringify(zone)}\`, which is not an IANA zone. An anchor ` +
|
|
399
|
+
`in an unusable zone is a call the calendar API rejects.`,
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
if (zone !== DEFAULT_TIMEZONE)
|
|
403
|
+
throw new Error(
|
|
404
|
+
`§3's example carries \`timeZone: ${JSON.stringify(zone)}\`, but the example must show the ` +
|
|
405
|
+
`documented default ${JSON.stringify(DEFAULT_TIMEZONE)} — a real zone in the example reads ` +
|
|
406
|
+
`as the zone to use, and every author who copies it schedules in someone else's day.`,
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
// …and the instruction to substitute must still be there, or the default becomes the value.
|
|
410
|
+
if (!/\?\.timezone\b/.test(skillBody))
|
|
411
|
+
throw new Error(
|
|
412
|
+
`§3 shows the default zone but no longer tells the model to READ the \`timezone\` carrier. ` +
|
|
413
|
+
`Without that line the example stops being an example and becomes the prescription.`,
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
console.log(
|
|
417
|
+
`✓ plan-paper-timeline [effects/§3]: the call §3 prescribes is WELL-FORMED against the real ` +
|
|
418
|
+
`Google Calendar schema — required ${JSON.stringify(["summary", "startTime", "endTime"])} ` +
|
|
419
|
+
`all present, no unknown fields, timeZone=${zone}.\n` +
|
|
420
|
+
` The step is now EXECUTED, not characterized: the stand-in answered ` +
|
|
421
|
+
`(${String(logged.length)} call(s) logged), so the run continued past the effectful step — ` +
|
|
422
|
+
`which a deny rule can never allow.\n` +
|
|
423
|
+
` Under \`--strict-mcp-config\` the real \`mcp__Google_Calendar__*\` surface does not exist ` +
|
|
424
|
+
`in this session, so the real calendar was not blocked here, it was UNREACHABLE.`,
|
|
425
|
+
);
|
|
426
|
+
} finally {
|
|
427
|
+
b.cleanup();
|
|
428
|
+
}
|
|
429
|
+
} finally {
|
|
430
|
+
rmSync(logDir, { recursive: true, force: true });
|
|
431
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plan-paper-timeline.effects.mutations.mjs — the non-vacuity proof for the EFFECTS harness.
|
|
3
|
+
*
|
|
4
|
+
* Run: `node skills/plan-paper-timeline/plan-paper-timeline.effects.mutations.mjs` (slow: each
|
|
5
|
+
* case spawns the real `claude` CLI against a scripted mock model).
|
|
6
|
+
*
|
|
7
|
+
* ── WHY THIS ONE IS EASY TO GET WRONG ───────────────────────────────────────
|
|
8
|
+
* The effects harness spends most of its length proving that a run HAPPENED — the skill
|
|
9
|
+
* activated, its body reached the model, a control tool answered. Those are preconditions, and a
|
|
10
|
+
* harness made of preconditions passes for reasons that have nothing to do with the thing under
|
|
11
|
+
* test. The two cases below plant defects in the two claims that are actually about §3.
|
|
12
|
+
*
|
|
13
|
+
* ── CASE 1: the zone in the prescription stops being the documented default ─
|
|
14
|
+
* §3's example must carry `DEFAULT_TIMEZONE`, because the zone is the `timezone` CARRIER — it
|
|
15
|
+
* belongs to the author, not to the pipeline. A REAL zone in the example is the silent defect:
|
|
16
|
+
* it is valid, the calendar API accepts it, the harness's schema check passes, and every author
|
|
17
|
+
* who copies the example schedules their deadlines in somebody else's day. `Asia/Tokyo` is
|
|
18
|
+
* chosen precisely because it is well-formed; a malformed zone would be caught by the other
|
|
19
|
+
* assertion and would prove the wrong half.
|
|
20
|
+
*
|
|
21
|
+
* ── CASE 2: the instruction to substitute disappears ────────────────────────
|
|
22
|
+
* With the example showing a default and the "read the carrier" line deleted, the default
|
|
23
|
+
* silently BECOMES the prescription — every anchor lands in UTC and nothing anywhere says so.
|
|
24
|
+
* Removing the line leaves a file that still reads sensibly, which is why a human diff is not
|
|
25
|
+
* enough and this case exists.
|
|
26
|
+
*/
|
|
27
|
+
import { dirname, join } from "node:path";
|
|
28
|
+
import { fileURLToPath } from "node:url";
|
|
29
|
+
import { runMutations } from "../../lib/mutation-driver.mjs";
|
|
30
|
+
import { consumerRoot } from "../paper-pipeline/scripts/consumer.mjs";
|
|
31
|
+
|
|
32
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
33
|
+
const SKILL = join(HERE, "SKILL.md");
|
|
34
|
+
const HARNESS = join(HERE, "plan-paper-timeline.effects.harness.mjs");
|
|
35
|
+
|
|
36
|
+
process.exit(
|
|
37
|
+
runMutations({
|
|
38
|
+
root: consumerRoot(),
|
|
39
|
+
runner: "vigiles",
|
|
40
|
+
cases: [
|
|
41
|
+
{
|
|
42
|
+
name: "§3's example carries a real zone instead of the default",
|
|
43
|
+
disables:
|
|
44
|
+
"the rule that the prescription shows DEFAULT_TIMEZONE — a real zone reads as the zone to use",
|
|
45
|
+
edits: [[SKILL, '"timeZone": "UTC"', '"timeZone": "Asia/Tokyo"']],
|
|
46
|
+
harness: HARNESS,
|
|
47
|
+
expect: "must show the documented default",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "§3 stops telling the model to read the `timezone` carrier",
|
|
51
|
+
disables:
|
|
52
|
+
"the substitution instruction — without it the default stops being an example and becomes the prescription",
|
|
53
|
+
edits: [
|
|
54
|
+
[
|
|
55
|
+
SKILL,
|
|
56
|
+
"(require('./package.json'))?.timezone ?? 'UTC'",
|
|
57
|
+
"(require('./package.json'))?.papersDir ?? 'papers'",
|
|
58
|
+
],
|
|
59
|
+
],
|
|
60
|
+
harness: HARNESS,
|
|
61
|
+
expect: "tells the model to READ the `timezone` carrier",
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
}),
|
|
65
|
+
);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plan-paper-timeline — the PAID tier: does this skill's description actually fire?
|
|
3
|
+
*
|
|
4
|
+
* COLOCATED ON PURPOSE (vigiles decides coverage by placement as of 2026-08-11).
|
|
5
|
+
* The prompts live in `.claude/lib/skill-trigger-cases.mjs` so all 21 cases
|
|
6
|
+
* are reviewed as one table where collisions between siblings are visible;
|
|
7
|
+
* copying them here would recreate the drift that rule exists to prevent.
|
|
8
|
+
*
|
|
9
|
+
* Measures recall (fires on its own territory) AND precision (stays quiet on a
|
|
10
|
+
* colliding sibling's territory), against the REAL `.claude` harness so the skill
|
|
11
|
+
* competes with every other installed description — an isolated run overstates
|
|
12
|
+
* recall and understates false positives.
|
|
13
|
+
*
|
|
14
|
+
* Costs money; not CI.
|
|
15
|
+
* node .claude/skills/plan-paper-timeline/plan-paper-timeline.eval.mjs [trials]
|
|
16
|
+
*/
|
|
17
|
+
import { runSkillTriggerEval } from "../../lib/skill-eval-kit.mjs";
|
|
18
|
+
|
|
19
|
+
await runSkillTriggerEval("plan-paper-timeline");
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plan-paper-timeline — the free, deterministic tier. No model, no network.
|
|
3
|
+
*
|
|
4
|
+
* COLOCATED ON PURPOSE. vigiles decides coverage by PLACEMENT as of 2026-08-11:
|
|
5
|
+
* a test that merely names a surface no longer counts, because that tier was
|
|
6
|
+
* crediting surfaces nothing touched. So each skill needs a file inside its own
|
|
7
|
+
* directory — this one.
|
|
8
|
+
*
|
|
9
|
+
* The assertions live in `.claude/lib/skill-checks.mjs` and are CALLED here with
|
|
10
|
+
* this skill's name. They are not copied: 22 copies of the same checks is the drift that
|
|
11
|
+
* module exists to avoid. (Until 2026-08-11 this was an env-var side channel into a
|
|
12
|
+
* 614-line file named after no surface; it is a function call now.)
|
|
13
|
+
*
|
|
14
|
+
* What this proves: this skill's frontmatter parses as strict YAML, its declared
|
|
15
|
+
* tool contract is sane, its pipeline wiring points at scripts that exist, and it
|
|
16
|
+
* announces/records under ITS OWN identity rather than a sibling's.
|
|
17
|
+
*
|
|
18
|
+
* What it does NOT prove: that the skill fires, or that its guidance produces a
|
|
19
|
+
* good result. Those need a real model — see `plan-paper-timeline.eval.mjs`.
|
|
20
|
+
*/
|
|
21
|
+
import { checkSkill } from "../../lib/skill-checks.mjs";
|
|
22
|
+
|
|
23
|
+
await checkSkill("plan-paper-timeline");
|