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,403 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* skill-corpus.mjs — WHAT the pipeline's skills ARE. Derivation and helpers, no assertions.
|
|
3
|
+
*
|
|
4
|
+
* 🔴 THIS FILE MAKES NO CLAIMS AND RUNS NO TESTS. Importing it must be free of verdicts —
|
|
5
|
+
* that is the whole reason it was split out of `skills.harness.mjs` on 2026-08-11. That file
|
|
6
|
+
* held three things under one name: this derivation, the per-skill assertions, and the
|
|
7
|
+
* corpus-wide ones. It answered to no surface (there is no skill called `skills`), so the
|
|
8
|
+
* 21 colocated per-skill harnesses could only reach it by setting an env var and importing
|
|
9
|
+
* a script for its SIDE EFFECTS — 21 wrappers around one bag.
|
|
10
|
+
*
|
|
11
|
+
* Now: this module derives, `skill-checks.mjs` asserts about ONE skill, and
|
|
12
|
+
* `pipeline-corpus.harness.mjs` asserts about the SET. Each colocated file calls a
|
|
13
|
+
* function with its own name in it, and there is no env-var side channel.
|
|
14
|
+
*
|
|
15
|
+
* The ledger is redirected to a scratch path at import time, BEFORE `ledger.mjs` loads, so
|
|
16
|
+
* that an assertion which calls `record()` cannot leave fixture rows in real history. That
|
|
17
|
+
* import order is load-bearing and is the reason this is a module rather than a plain
|
|
18
|
+
* export list. Save-and-restore is not isolation — it lost that race on its first day.
|
|
19
|
+
*/
|
|
20
|
+
import assert from "node:assert/strict";
|
|
21
|
+
// ⚠️ 2026-08-28: `rmSync` and `writeFileSync` were removed from here — nothing called them.
|
|
22
|
+
// `rmSync` is NOT a harmless finding here, though: `tmp` below is a `mkdtempSync` that is never
|
|
23
|
+
// removed, so every import of this library leaves a directory behind in `/tmp` (two dozen per
|
|
24
|
+
// full sweep of the harnesses). Cleanup is DELIBERATELY not added in this edit: it would change
|
|
25
|
+
// the lifecycle of the directory that holds `PIPELINE_LEDGER` for every child run, and today's
|
|
26
|
+
// edit is about turning the lint on, not about harness behavior. Written down as an open item
|
|
27
|
+
// in the report, not swept under an import-that-does-something-silently.
|
|
28
|
+
import { existsSync, readFileSync, mkdtempSync, realpathSync } from "node:fs";
|
|
29
|
+
import { tmpdir } from "node:os";
|
|
30
|
+
import { join, dirname } from "node:path";
|
|
31
|
+
import { fileURLToPath } from "node:url";
|
|
32
|
+
import * as yaml from "js-yaml";
|
|
33
|
+
import {
|
|
34
|
+
consumerRoot,
|
|
35
|
+
installedSkills,
|
|
36
|
+
} from "../skills/paper-pipeline/scripts/consumer.mjs";
|
|
37
|
+
import {
|
|
38
|
+
headings as mdHeadings,
|
|
39
|
+
requireMarkdown,
|
|
40
|
+
frontmatterBlock,
|
|
41
|
+
} from "./markdown.mjs";
|
|
42
|
+
|
|
43
|
+
// Markup is parsed with a real parser (`CLAUDE.md`, 2026-08-11). Here we THROW rather than
|
|
44
|
+
// degrade: this module answers the question "what does the skill corpus contain", and an empty
|
|
45
|
+
// heading list with no parser would read as "not one skill has a `Record the verdict` block" —
|
|
46
|
+
// i.e. as a finding about the corpus. A confident zero has already been mistaken for a fact in
|
|
47
|
+
// this repo four times.
|
|
48
|
+
requireMarkdown();
|
|
49
|
+
|
|
50
|
+
export const HERE = dirname(fileURLToPath(import.meta.url));
|
|
51
|
+
// 🔴 THE CONSUMER'S ROOT, ASKED FOR — NOT WALKED TO. While this module lived in the consumer's
|
|
52
|
+
// own `.claude/lib/`, `resolve(HERE, "..", "..")` WAS the repository root. From inside
|
|
53
|
+
// `node_modules/<pkg>/lib/` the same walk lands on the consumer's `node_modules/`, and every path
|
|
54
|
+
// below it — the skills directory above all — would be a directory that does not exist. That
|
|
55
|
+
// failure is silent in the worst way: `readdirSync` throws while this module is still LOADING, and
|
|
56
|
+
// a harness whose module fails to load is reported by `vigiles test` as SKIPPED with exit code 0.
|
|
57
|
+
// `consumerRoot()` is the carrier the package already uses for the ledger and the papers root.
|
|
58
|
+
export const ROOT = consumerRoot();
|
|
59
|
+
export const SKILLS_DIR = join(ROOT, ".claude", "skills");
|
|
60
|
+
|
|
61
|
+
// Redirected BEFORE ledger.mjs is imported, so a crash cannot leave fixture rows in real history.
|
|
62
|
+
// Save-and-restore is not isolation — it lost that race on its first day (see ledger.mjs's comment).
|
|
63
|
+
export const tmp = realpathSync(mkdtempSync(join(tmpdir(), "skills-harness-")));
|
|
64
|
+
process.env.PIPELINE_LEDGER = join(tmp, "runs.jsonl");
|
|
65
|
+
const { KINDS, RETIRED, ABSTENTIONS, record, parseGate } =
|
|
66
|
+
await import("../skills/paper-pipeline/scripts/ledger.mjs");
|
|
67
|
+
export { KINDS, RETIRED, ABSTENTIONS, record, parseGate };
|
|
68
|
+
const { EXPECTED_GATES } =
|
|
69
|
+
await import("../skills/paper-pipeline/scripts/status.mjs");
|
|
70
|
+
export { EXPECTED_GATES };
|
|
71
|
+
/** The skills EXPECTED_GATES names, with the `skill/check` rows collapsed onto their skill. */
|
|
72
|
+
export const GATE_SKILLS = new Set(
|
|
73
|
+
EXPECTED_GATES.map((g) => parseGate(g).skill),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// The paper-pipeline skills, DERIVED FROM DISK — a skill is one iff its SKILL.md instructs
|
|
77
|
+
// `node .claude/skills/paper-pipeline/scripts/announce.mjs <its own name>`. Minus EXCLUDED below.
|
|
78
|
+
//
|
|
79
|
+
// 🔴 THIS WAS A PINNED ARRAY UNTIL 2026-08-07, AND THE PIN MADE ASSERTION 12b VACUOUS IN THE ONE
|
|
80
|
+
// CASE IT EXISTS FOR. That assertion reads "<skill> is a wired pipeline skill but is absent from
|
|
81
|
+
// status.mjs EXPECTED_GATES" — and it iterated the pinned array. So it could only ever catch a skill
|
|
82
|
+
// present in the array and missing from EXPECTED_GATES. A skill wired on disk and absent from BOTH
|
|
83
|
+
// lists — the actual "I added a skill and forgot" failure — was structurally invisible.
|
|
84
|
+
//
|
|
85
|
+
// Measured, not theorised: `paper-status` is in exactly that state right now. It carries an announce
|
|
86
|
+
// command and is in neither list. That is DELIBERATE (see EXCLUDED), but nothing checked that the
|
|
87
|
+
// deliberateness was still true, and the old comment asserting it was the only thing holding it.
|
|
88
|
+
//
|
|
89
|
+
// The mutation driver's case 12b passed the whole time, because it removes a skill from EXPECTED_GATES
|
|
90
|
+
// while leaving it in the pinned array. A green mutation proving a narrower property than its own
|
|
91
|
+
// error message claims is exactly the vacuity this file was built to hunt, sitting in this file.
|
|
92
|
+
//
|
|
93
|
+
// The old comment's objection to globbing was real and is answered rather than ignored: `.claude/skills/`
|
|
94
|
+
// does hold daily skills (search-krisha, food-log-entry, …) with no pipeline contract, so the filter
|
|
95
|
+
// cannot be "every directory". But it need not be a guess either — carrying an announce command that
|
|
96
|
+
// names YOURSELF is the pipeline contract, checked below by assertion 5c, and it is a fact on disk.
|
|
97
|
+
export const PIPELINE_MARKER =
|
|
98
|
+
/^\s*node\s+\.claude\/skills\/paper-pipeline\/scripts\/announce\.mjs\s+([a-z0-9-]+)/m;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Skills wired into the pipeline but deliberately NOT graded as gates, each with the reason.
|
|
102
|
+
* A reason is required because these are the entries that rot: an exclusion with no argument is
|
|
103
|
+
* indistinguishable from an omission, and the previous version of this file held both in prose.
|
|
104
|
+
*
|
|
105
|
+
* 🔴 UNTIL 2026-08-11 THIS SUBTRACTED FROM THE CHECKED SET, WHICH IS NOT WHAT ITS OWN REASON SAYS.
|
|
106
|
+
* The recorded reason for `paper-status` is "it has no gate row" — a statement about ONE assertion
|
|
107
|
+
* (12b: every checked skill must appear in EXPECTED_GATES). The implementation filtered it out of
|
|
108
|
+
* `SKILLS` entirely, so it received NONE of the structural checks either: not the strict-YAML
|
|
109
|
+
* frontmatter parse, not the tool contract, not "your wiring points at scripts that exist", not
|
|
110
|
+
* "you announce under your OWN identity". A skill was silently unexamined because it lacked a
|
|
111
|
+
* grade — and this file's own governing comment, four lines below, says a defect that shrinks the
|
|
112
|
+
* checked set is worse than the defect itself.
|
|
113
|
+
*
|
|
114
|
+
* Found by the coverage sweep of 2026-08-11: `paper-status` showed as untested, and the reason was
|
|
115
|
+
* not a missing test file but this line. Now the exclusion means what it says — skipped at 12b,
|
|
116
|
+
* checked everywhere else.
|
|
117
|
+
*/
|
|
118
|
+
export const EXCLUDED = new Map([
|
|
119
|
+
[
|
|
120
|
+
"paper-status",
|
|
121
|
+
"reports on the pipeline; produces no verdict ABOUT the paper, so it has no gate row",
|
|
122
|
+
],
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
// 🔴 MEMBERSHIP IS THE UNION OF TWO SOURCES, AND THAT IS THE WHOLE POINT. A skill is checked if the
|
|
126
|
+
// gate table names it OR its SKILL.md carries pipeline wiring. Either alone is corruptible by the very
|
|
127
|
+
// defects this file hunts, and a defect that shrinks the checked set is worse than the defect itself:
|
|
128
|
+
// the harness goes green by no longer looking.
|
|
129
|
+
//
|
|
130
|
+
// Both narrower versions were written and both were killed by the mutation driver within minutes:
|
|
131
|
+
//
|
|
132
|
+
// filter `announce.mjs <own name>` -> case 13 (typo the announced NAME) dropped harden-paper out
|
|
133
|
+
// of scope; harness printed "20 pipeline skills", said nothing
|
|
134
|
+
// filter `announce.mjs` path -> case 5b (typo the announce PATH) dropped camera-ready the
|
|
135
|
+
// same way
|
|
136
|
+
//
|
|
137
|
+
// Each time, the mutation that used to turn the harness red went GREEN because the planted defect
|
|
138
|
+
// erased its own subject. Scope decided by a property the defect corrupts is not scope.
|
|
139
|
+
//
|
|
140
|
+
// The union cannot be shrunk by a single-point edit: break the wiring and the gate table still holds
|
|
141
|
+
// the skill; forget the gate table and the disk still does. Only removing it from BOTH — which is
|
|
142
|
+
// what actually leaving the pipeline looks like — takes it out of scope, and EXCLUDED makes that
|
|
143
|
+
// deliberate and reasoned rather than silent.
|
|
144
|
+
const wiredOnDisk = installedSkills(SKILLS_DIR).filter((d) =>
|
|
145
|
+
PIPELINE_MARKER.test(readFileSync(join(SKILLS_DIR, d, "SKILL.md"), "utf-8")),
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// EXCLUDED deliberately does NOT filter here — see its comment. An entry means "no gate row",
|
|
149
|
+
// and that is applied at assertion 12b alone; everything structural still looks at it.
|
|
150
|
+
export const SKILLS = [...new Set([...GATE_SKILLS, ...wiredOnDisk])]
|
|
151
|
+
.filter((d) => existsSync(join(SKILLS_DIR, d, "SKILL.md"))) // 12a reports a gate with no directory
|
|
152
|
+
.sort();
|
|
153
|
+
|
|
154
|
+
// The nine that gained Bash ONLY so they could write the ledger, per
|
|
155
|
+
// `.claude/skills/ALLOWED-TOOLS-AUDIT-2026-08-03.md`. PINNED because the property is not derivable:
|
|
156
|
+
// from the frontmatter alone a plain `Bash` on `render-paper` (pdflatex, bibtex, apt-get — it needs
|
|
157
|
+
// it) is indistinguishable from a plain `Bash` on `argument-arc` (which would be a regression). The
|
|
158
|
+
// audit decided that per skill by reading each one's text; this list is that decision, made checkable.
|
|
159
|
+
// A skill moving off this list is a deliberate act and should require editing this line.
|
|
160
|
+
export const LEDGER_ONLY_BASH = new Set([
|
|
161
|
+
"argument-arc",
|
|
162
|
+
"tighten-paper",
|
|
163
|
+
"grade-paper-writing",
|
|
164
|
+
"paper-adversarial-review",
|
|
165
|
+
"draft-paper",
|
|
166
|
+
"extend-paper",
|
|
167
|
+
"research-ideate",
|
|
168
|
+
"find-venue",
|
|
169
|
+
"plan-paper-timeline",
|
|
170
|
+
]);
|
|
171
|
+
|
|
172
|
+
// ── parsing ────────────────────────────────────────────────────────────────────────────────────
|
|
173
|
+
|
|
174
|
+
/** Raw text between the opening `---` and the next `---`. Null if there is no frontmatter at all. */
|
|
175
|
+
export function frontmatter(src) {
|
|
176
|
+
return frontmatterBlock(src);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* THE frontmatter reader. One parser, and it is a real one.
|
|
181
|
+
*
|
|
182
|
+
* 🔴 THIS USED TO BE A HAND-ROLLED LENIENT READER, AND THE REASON IT IS GONE IS THE POINT
|
|
183
|
+
* (2026-08-19). The old comment here defended leniency: four skills did not parse as strict YAML —
|
|
184
|
+
* an unquoted `description:` containing ": ", which YAML reads as opening a nested mapping — and a
|
|
185
|
+
* strict parser would have failed them on assertion 1 and then SKIPPED assertions 2, 3, 4 and 9 on
|
|
186
|
+
* exactly the files most likely to carry other defects. So the corpus was read one way and shipped
|
|
187
|
+
* another, and a whole assertion existed to police the gap.
|
|
188
|
+
*
|
|
189
|
+
* That is compensating for our own tolerance. The corpus owner's call: fix the defect by construction instead.
|
|
190
|
+
* The two remaining offenders were quoted, so the corpus now parses strictly — measured, all 40
|
|
191
|
+
* SKILL.md files, zero failures — and this reader became the check. A file that is not valid YAML
|
|
192
|
+
* no longer produces a plausible-looking object with a missing `allowed-tools`; it throws, here,
|
|
193
|
+
* naming itself. The failure mode that mattered — "any consumer with a real parser sees NO
|
|
194
|
+
* allowed-tools, so the skill inherits every tool" — is not reachable from a reader that refuses
|
|
195
|
+
* to guess.
|
|
196
|
+
*
|
|
197
|
+
* ⚠️ The root cause is upstream and is being fixed there too: `vigiles compile` emits
|
|
198
|
+
* `description: ${spec.description}` by raw interpolation (core/compile.js:634 and :841,
|
|
199
|
+
* core/compile-generator.js:297), so a description with a colon regenerates the bad file on the
|
|
200
|
+
* next compile. Until that ships, this reader is what notices — loudly, and on the next run rather
|
|
201
|
+
* than months later.
|
|
202
|
+
*/
|
|
203
|
+
export function parseFm(fmText, where = "frontmatter") {
|
|
204
|
+
try {
|
|
205
|
+
const out = yaml.load(fmText);
|
|
206
|
+
if (out === null || typeof out !== "object" || Array.isArray(out))
|
|
207
|
+
throw new Error(
|
|
208
|
+
`parsed to ${Array.isArray(out) ? "a list" : typeof out}, not a mapping`,
|
|
209
|
+
);
|
|
210
|
+
return out;
|
|
211
|
+
} catch (e) {
|
|
212
|
+
throw new Error(
|
|
213
|
+
`${where}: frontmatter is not valid YAML — ${String(e.message).split("\n")[0]}. ` +
|
|
214
|
+
`Almost always an unquoted \`description:\` containing ": " — quote the value. ` +
|
|
215
|
+
`Nothing reads this file until it parses: a lenient fallback here is what previously let a ` +
|
|
216
|
+
`skill ship with no readable allowed-tools and inherit every tool.`,
|
|
217
|
+
// `cause` is not decoration: the message above takes `.split("\n")[0]`, i.e. it throws
|
|
218
|
+
// away the line and column that js-yaml prints as its second paragraph. Without the
|
|
219
|
+
// cause, debugging a broken frontmatter starts with locating it by hand (2026-08-28,
|
|
220
|
+
// `preserve-caught-error`).
|
|
221
|
+
{ cause: e },
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* `allowed-tools` as a list, accepting BOTH shapes the corpus now contains.
|
|
228
|
+
*
|
|
229
|
+
* Hand-written skills carry a comma scalar (`Bash, Read`); a skill compiled from a
|
|
230
|
+
* `SKILL.md.spec.ts` carries a real YAML flow sequence (`[Bash, Read]`) — which is the
|
|
231
|
+
* MORE correct form, because a scalar parses as one string and loses the contract on a
|
|
232
|
+
* round trip (vigiles #107). This split understood only the scalar, so on 2026-08-17
|
|
233
|
+
* adopting five pipeline skills turned four harnesses red: the checker read the whole
|
|
234
|
+
* bracketed list as a single entry named `[Bash` and reported that the skill's own
|
|
235
|
+
* `Bash(node …announce.mjs:*)` instruction was denied. The skills were fine; the reader
|
|
236
|
+
* was not.
|
|
237
|
+
*
|
|
238
|
+
* Entries themselves carry no commas (`Bash(node …:*)` has none), so a plain split is
|
|
239
|
+
* still exact once the flow brackets are off.
|
|
240
|
+
*/
|
|
241
|
+
export const toolList = (v) =>
|
|
242
|
+
// 2026-08-19: with a real YAML parser upstream, the flow form `[Bash, Read]` now arrives as an
|
|
243
|
+
// ACTUAL array, not the string "[Bash, Read]". It survived the switch only because
|
|
244
|
+
// `String(["Bash","Read"])` happens to produce "Bash,Read" and the split put it back — a correct
|
|
245
|
+
// answer by coincidence, which stops being correct the day an entry contains a comma. Handle the
|
|
246
|
+
// array as an array; the scalar branch stays for hand-written skills.
|
|
247
|
+
Array.isArray(v)
|
|
248
|
+
? v.map((x) => String(x).trim()).filter(Boolean)
|
|
249
|
+
: String(v ?? "")
|
|
250
|
+
.trim()
|
|
251
|
+
.replace(/^\[(.*)\]$/s, "$1")
|
|
252
|
+
.split(",")
|
|
253
|
+
.map((s) => s.trim())
|
|
254
|
+
.filter(Boolean);
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Does `entry` permit `cmd`? Mirrors Claude Code's rule: bare `Bash` permits everything;
|
|
258
|
+
* `Bash(prefix:*)` permits any command starting with `prefix`; an inner `*` is a wildcard.
|
|
259
|
+
*/
|
|
260
|
+
export function permits(entry, cmd) {
|
|
261
|
+
if (entry === "Bash") return true;
|
|
262
|
+
const m = entry.match(/^Bash\((.*)\)$/);
|
|
263
|
+
if (!m) return false;
|
|
264
|
+
const pat = m[1].replace(/:\*$/, "");
|
|
265
|
+
const rx = new RegExp(
|
|
266
|
+
"^" +
|
|
267
|
+
pat
|
|
268
|
+
.split("*")
|
|
269
|
+
.map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
|
|
270
|
+
.join(".*"),
|
|
271
|
+
);
|
|
272
|
+
return rx.test(cmd);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** Every `node|bash|python3 <script>` line the SKILL.md instructs, with its full command text. */
|
|
276
|
+
export function commands(src) {
|
|
277
|
+
return [
|
|
278
|
+
...src.matchAll(
|
|
279
|
+
/^\s*(node|bash|python3?) +([A-Za-z0-9_./@-]+\.(?:mjs|js|sh|py))(.*)$/gm,
|
|
280
|
+
),
|
|
281
|
+
].map((m) => ({
|
|
282
|
+
runner: m[1],
|
|
283
|
+
script: m[2],
|
|
284
|
+
cmd: `${m[1]} ${m[2]}${m[3]}`.trim(),
|
|
285
|
+
args: m[3].trim().split(/\s+/).filter(Boolean),
|
|
286
|
+
}));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* The `Record the verdict` block: from its heading to the next heading of the same or higher level.
|
|
291
|
+
*
|
|
292
|
+
* 🔴 The first version of this sliced `rest.slice(1).search(/^#{1,3} /m)` and `^` matched at offset 0
|
|
293
|
+
* of the slice, so every block came back as the single character "#". Assertions 10 and 11 then read
|
|
294
|
+
* an empty block, found no verdicts, and 10 fired on all 21 skills while 11 checked nothing at all —
|
|
295
|
+
* a checker firing on clean input and a checker that is vacuous, from one off-by-one. Caught by
|
|
296
|
+
* running it, not by reading it. Matching on `\n#` rather than `^#` is the fix.
|
|
297
|
+
*
|
|
298
|
+
* 2026-08-11 — switched to a real parser (`CLAUDE.md`: "markdown is parsed with a parser"). The
|
|
299
|
+
* same off-by-one is now INEXPRESSIBLE: a heading from the parser already carries the offset of
|
|
300
|
+
* its line start, so the block boundary no longer has to be computed from a newline's position.
|
|
301
|
+
* Along with it went two defects the old expression carried silently: `#{1,3}` counted a hash
|
|
302
|
+
* inside a ``` block as a heading (a skill QUOTING someone else's `## Run me` in an example cut
|
|
303
|
+
* its own block short at the quote), and `\n#{1,3} ` did not see a heading indented by 1-3
|
|
304
|
+
* spaces, which CommonMark still counts as a heading. The block boundary is preserved byte for
|
|
305
|
+
* byte: end = start of the next heading.
|
|
306
|
+
*/
|
|
307
|
+
export function recordBlock(src) {
|
|
308
|
+
const hs = mdHeadings(src);
|
|
309
|
+
const i = hs.findIndex(
|
|
310
|
+
(h) => h.depth <= 3 && /record the verdict/i.test(h.text),
|
|
311
|
+
);
|
|
312
|
+
if (i < 0) return null;
|
|
313
|
+
const next = hs.slice(i + 1).find((h) => h.depth <= 3);
|
|
314
|
+
return src.slice(hs[i].offset, next ? next.offset : src.length);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Three extractors, because they answer different questions and a single one would be wrong for one.
|
|
318
|
+
//
|
|
319
|
+
// `allBold` — every all-caps bolded token, whatever it is. The raw material for the other two.
|
|
320
|
+
// Bold-only on purpose: several blocks write a retired word in prose ("There is no
|
|
321
|
+
// PASS") or in backticks, and a looser match would read those DENIALS as claims —
|
|
322
|
+
// which is the exact reading error that would make the turned-around assertion below
|
|
323
|
+
// fire on every correct file.
|
|
324
|
+
// `namedKinds` — bolded tokens that are live constructors. Used to ask "can this check report a
|
|
325
|
+
// finding at all?".
|
|
326
|
+
// `namedRetired` — bolded tokens from the DELETED vocabulary. Used to ask "does this block still
|
|
327
|
+
// instruct something `record()` refuses?".
|
|
328
|
+
export const allBold = (blk) => [
|
|
329
|
+
...new Set(
|
|
330
|
+
[...blk.matchAll(/\*\*`?([A-Z][A-Z-]{2,})`?\*\*/g)].map((m) => m[1]),
|
|
331
|
+
),
|
|
332
|
+
];
|
|
333
|
+
export const namedKinds = (blk) =>
|
|
334
|
+
allBold(blk).filter((v) => KINDS.includes(v));
|
|
335
|
+
export const namedRetired = (blk) => allBold(blk).filter((v) => RETIRED.has(v));
|
|
336
|
+
export const definedKinds = (blk) => [
|
|
337
|
+
...new Set(
|
|
338
|
+
[...blk.matchAll(/\*\*`?([A-Z][A-Z-]{2,})`?\*\*\s*(?=[—(:]| \()/g)].map(
|
|
339
|
+
(m) => m[1],
|
|
340
|
+
),
|
|
341
|
+
),
|
|
342
|
+
];
|
|
343
|
+
|
|
344
|
+
// The abstention reasons a block names, as backticked tokens: `no-witness`, `blocked`, …
|
|
345
|
+
export const namedReasons = (blk) =>
|
|
346
|
+
[
|
|
347
|
+
...new Set([...blk.matchAll(/`([a-z][a-z-]{3,})`/g)].map((m) => m[1])),
|
|
348
|
+
].filter((r) => ABSTENTIONS.has(r));
|
|
349
|
+
|
|
350
|
+
// The honest-admission escape hatch, for a skill whose nature is to generate rather than judge.
|
|
351
|
+
// `draft-paper` is the designed case: it will abstain nearly always and its text says so, in the
|
|
352
|
+
// terms status.mjs uses. The regex matches that ARGUMENT, not a keyword, so an admission cannot be
|
|
353
|
+
// satisfied by pasting the word "smell".
|
|
354
|
+
export const ADMISSION =
|
|
355
|
+
/(almost always record|never returned a (negative|finding)|a generator is not a gate|only honest negative)/i;
|
|
356
|
+
|
|
357
|
+
export const load = (name) => {
|
|
358
|
+
const path = join(SKILLS_DIR, name, "SKILL.md");
|
|
359
|
+
assert.ok(
|
|
360
|
+
existsSync(path),
|
|
361
|
+
`${name}: no SKILL.md at ${path} — it is listed as a pipeline skill and does not exist`,
|
|
362
|
+
);
|
|
363
|
+
const src = readFileSync(path, "utf8");
|
|
364
|
+
const fmText = frontmatter(src);
|
|
365
|
+
assert.ok(
|
|
366
|
+
fmText !== null,
|
|
367
|
+
`${name}: SKILL.md has no --- frontmatter block; the skill cannot be loaded at all`,
|
|
368
|
+
);
|
|
369
|
+
const fm = parseFm(fmText, name);
|
|
370
|
+
return { name, path, src, fmText, fm, tools: toolList(fm["allowed-tools"]) };
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
// One-skill mode, for the COLOCATED per-skill harness files that sit next to each
|
|
374
|
+
// SKILL.md. Coverage is decided by placement (vigiles, 2026-08-11: a test that only
|
|
375
|
+
// NAMES a surface no longer counts), so each skill needs a file inside its own
|
|
376
|
+
// directory. Rather than copy these assertions 21 times — the drift this file was
|
|
377
|
+
// built to avoid — each colocated file sets ONLY and imports this one.
|
|
378
|
+
//
|
|
379
|
+
// 🔴 2026-08-28: THERE USED TO BE TWO MORE LINES BELOW, AND THEY WERE DEAD LOGIC, NOT CLUTTER.
|
|
380
|
+
//
|
|
381
|
+
// const skills = (ONLY ? [ONLY] : SKILLS).map(load);
|
|
382
|
+
// const skillDirs = new Set(readdirSync(SKILLS_DIR).filter((d) => existsSync(…)));
|
|
383
|
+
//
|
|
384
|
+
// Both were computed and read by NOBODY — not here, not on export. This is a leftover from the
|
|
385
|
+
// 2026-08-11 split, when the cross-skill checks moved into `pipeline-corpus.harness.mjs`: the
|
|
386
|
+
// bodies of the checks were carried over, and their inputs were forgotten. The paragraph above
|
|
387
|
+
// about "cross-skill assertions below" described exactly that block, now gone; after the move
|
|
388
|
+
// there is NOT A SINGLE CHECK below it, i.e. the comment promised a mechanism that does not
|
|
389
|
+
// exist — exactly what this base's "prose isn't policy" rule exists to catch.
|
|
390
|
+
//
|
|
391
|
+
// The second, less obvious cost: `.map(load)` is not an inert computation. `load()` carries an
|
|
392
|
+
// `assert.ok`, so this line was an UNDECLARED check of the whole corpus, run as a side effect of
|
|
393
|
+
// `import` and costing a read of all 22 `SKILL.md` files on every import of the library. This is
|
|
394
|
+
// exactly the kind of "via an import's side effect" coupling the file was moving away from (see
|
|
395
|
+
// the header of `skill-checks.mjs`). The real, declared carrier of this check is `checkSkill()`,
|
|
396
|
+
// which does it one skill at a time and with a legible message.
|
|
397
|
+
//
|
|
398
|
+
// `ONLY` stays: it is read by the check one line up — the only thing here that survived.
|
|
399
|
+
const ONLY = process.env.PIPELINE_SKILL_ONLY ?? "";
|
|
400
|
+
if (ONLY && !SKILLS.includes(ONLY))
|
|
401
|
+
throw new Error(
|
|
402
|
+
`PIPELINE_SKILL_ONLY="${ONLY}" is not a wired pipeline skill. Known: ${SKILLS.join(", ")}`,
|
|
403
|
+
);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* skill-eval-fixture.mjs — the throwaway working directory every trigger run sees.
|
|
3
|
+
*
|
|
4
|
+
* 🔴 WHY A FIXTURE IS NOT OPTIONAL, learned by getting it wrong (2026-08-11).
|
|
5
|
+
* A trigger run executes in a FRESH EMPTY cwd. Ask "grade the writing on this
|
|
6
|
+
* paper" in a directory containing no paper and the model reasonably does not
|
|
7
|
+
* reach for a skill — there is nothing to grade. Measured: without this fixture,
|
|
8
|
+
* `grade-paper-writing`, `harden-paper`, `map-prior-work` and
|
|
9
|
+
* `paper-adversarial-review` all scored 0% on prompts that had previously fired,
|
|
10
|
+
* and the zero was an artifact of the empty directory, not a fact about the skill.
|
|
11
|
+
*
|
|
12
|
+
* Extracted from `paper-pipeline/pipeline-firing.eval.mjs`, which had it right
|
|
13
|
+
* from the start, so the two runners cannot drift apart on what the model sees.
|
|
14
|
+
*
|
|
15
|
+
* ⚠️ AND THE PRICE, so this file is not a one-sided story (measured 2026-08-12).
|
|
16
|
+
* With a fixture present the model OPENS THE PAPER and looks around, so the number
|
|
17
|
+
* stops being "did the DESCRIPTION fire" and becomes "did the model go investigate
|
|
18
|
+
* and then fire". A different quantity, and a costlier one: ~590k → ~780k tokens
|
|
19
|
+
* per skill (+190k) and 67s → 84s, while this whole fixture is 1,537 characters
|
|
20
|
+
* ≈ 384 tokens. The growth is reading, not input size (`cache` 470k → 652k).
|
|
21
|
+
* Cite the trigger numbers with that caveat attached — it is written out in
|
|
22
|
+
* `vigiles/repro/skill-trigger-2026-08-11/README.md` under "How to read".
|
|
23
|
+
*/
|
|
24
|
+
export const FIXTURE = {
|
|
25
|
+
"paper/paper.md": [
|
|
26
|
+
"# Prose Isn't Policy: Measuring Whether Agent-Config Rules Are Enforceable",
|
|
27
|
+
"",
|
|
28
|
+
"## Abstract",
|
|
29
|
+
"Agent configuration files state rules in prose and assume the model obeys them. We compile a",
|
|
30
|
+
"corpus of real rules and measure what fraction can be mechanically enforced. We find that 84%",
|
|
31
|
+
"of rules in our corpus are enforceable, and that LLM-authored checkers for the remainder leak",
|
|
32
|
+
"silently in 84-96% of adversarial cases \\cite{greshake2023}.",
|
|
33
|
+
"",
|
|
34
|
+
"## 1 Introduction",
|
|
35
|
+
"Every agent harness ships a natural-language rulebook. Nothing checks it. This is the same",
|
|
36
|
+
"mistake as a code comment that claims an invariant no test enforces \\cite{thompson1984}.",
|
|
37
|
+
"",
|
|
38
|
+
"## 2 Method",
|
|
39
|
+
"We gather rules from public repositories, classify each by enforceability, and build a",
|
|
40
|
+
"two-stage adversarial gate that validates a synthesized rule against a blind gold set.",
|
|
41
|
+
"",
|
|
42
|
+
"## 3 Results",
|
|
43
|
+
"See Table 1. The headline number is 84%.",
|
|
44
|
+
"",
|
|
45
|
+
"## 4 Discussion",
|
|
46
|
+
"The result generalizes beyond our corpus in the sense that the mechanism is not corpus-specific,",
|
|
47
|
+
"though of course the specific percentages are, and it is important to note in this context that",
|
|
48
|
+
"the framing itself may be what carries, rather than the measurement.",
|
|
49
|
+
"",
|
|
50
|
+
"## 5 Threats to Validity",
|
|
51
|
+
"Our corpus is drawn from public repositories and may not represent private configurations.",
|
|
52
|
+
"",
|
|
53
|
+
"## 6 Related Work",
|
|
54
|
+
"TODO",
|
|
55
|
+
"",
|
|
56
|
+
"## 7 Conclusion",
|
|
57
|
+
"Prose is not policy.",
|
|
58
|
+
].join("\n"),
|
|
59
|
+
"paper/repro/README.md":
|
|
60
|
+
"# Reproduction artifact\n\n`python3 paper_numbers.py` recomputes every bolded figure in paper.md.\n",
|
|
61
|
+
"paper/PIPELINE-STATUS.md":
|
|
62
|
+
"# Pipeline status\n\n| gate | state |\n|---|---|\n| numbers | pass |\n| structure | not run |\n| citations | not run |\n",
|
|
63
|
+
};
|