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,496 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* artifact-coverage.harness.mjs — the gate that asks whether the RELEASED bundle holds the data the
|
|
3
|
+
* paper points at, handed a bundle that does not. `npx vigiles test .claude/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs`.
|
|
4
|
+
*
|
|
5
|
+
* WHY. On 2026-08-06, hours before a submission, the released bundle held NO DATA for the section
|
|
6
|
+
* the abstract leads with. Six `harden-paper` passes and five review panels had not caught it, and
|
|
7
|
+
* none of them was broken — they were asking different questions. The anonymity gate asks whether
|
|
8
|
+
* anything leaks. The numbers gate asks whether a printed number matches its data file. The artifact
|
|
9
|
+
* reviewer asks whether the code runs. A MISSING DIRECTORY passes all three: nothing leaks, nothing
|
|
10
|
+
* mismatches, and everything present still runs.
|
|
11
|
+
*
|
|
12
|
+
* That makes this checker the only thing in the pipeline looking at absence, which makes its silence
|
|
13
|
+
* the most expensive silence we have. Hence both directions below: it fires on a bundle missing the
|
|
14
|
+
* data, and it is quiet on a complete one — including on `owner/repo` strings, which a paper about
|
|
15
|
+
* repositories prints by the dozen and which the first version reported as missing directories.
|
|
16
|
+
*
|
|
17
|
+
* 🔴 Assertions run at MODULE TOP LEVEL — `vigiles test` imports the file and treats "did not throw"
|
|
18
|
+
* as a pass, so an exported test object would report ✓ having run nothing.
|
|
19
|
+
*
|
|
20
|
+
* Every fixture is a throwaway in a temp dir; no real paper or bundle is read or written.
|
|
21
|
+
*/
|
|
22
|
+
import assert from "node:assert/strict";
|
|
23
|
+
import { execFileSync } from "node:child_process";
|
|
24
|
+
import {
|
|
25
|
+
mkdtempSync,
|
|
26
|
+
mkdirSync,
|
|
27
|
+
writeFileSync,
|
|
28
|
+
rmSync,
|
|
29
|
+
realpathSync,
|
|
30
|
+
} from "node:fs";
|
|
31
|
+
import { tmpdir } from "node:os";
|
|
32
|
+
import { join, dirname } from "node:path";
|
|
33
|
+
import { fileURLToPath } from "node:url";
|
|
34
|
+
import { consumerRoot } from "./consumer.mjs";
|
|
35
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
36
|
+
|
|
37
|
+
const ROOT = consumerRoot();
|
|
38
|
+
const SCRIPT = join(HERE, "artifact-coverage.mjs");
|
|
39
|
+
const tmp = realpathSync(mkdtempSync(join(tmpdir(), "artifact-harness-")));
|
|
40
|
+
|
|
41
|
+
function run(dir, flagsOnly = true) {
|
|
42
|
+
const argv = flagsOnly ? [SCRIPT, dir, "--flags-only"] : [SCRIPT, dir];
|
|
43
|
+
try {
|
|
44
|
+
const out = execFileSync("node", argv, {
|
|
45
|
+
cwd: ROOT,
|
|
46
|
+
encoding: "utf8",
|
|
47
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
48
|
+
});
|
|
49
|
+
return { code: 0, out };
|
|
50
|
+
} catch (e) {
|
|
51
|
+
return { code: e.status ?? 1, out: (e.stdout || "") + (e.stderr || "") };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* A paper whose §4.2 reports a figure, plus a bundle. `index` is NUMBERS.md's content (null means
|
|
57
|
+
* the bundle ships no index at all); `files` are paths created inside the bundle.
|
|
58
|
+
*/
|
|
59
|
+
function fixture(
|
|
60
|
+
name,
|
|
61
|
+
{
|
|
62
|
+
index,
|
|
63
|
+
files = [],
|
|
64
|
+
bundle = true,
|
|
65
|
+
paperExtra = "",
|
|
66
|
+
readme = null,
|
|
67
|
+
runs = [],
|
|
68
|
+
allow = null,
|
|
69
|
+
bundleFiles = [],
|
|
70
|
+
},
|
|
71
|
+
) {
|
|
72
|
+
const d = join(tmp, name);
|
|
73
|
+
mkdirSync(d, { recursive: true });
|
|
74
|
+
writeFileSync(
|
|
75
|
+
join(d, "paper.md"),
|
|
76
|
+
[
|
|
77
|
+
"## Abstract",
|
|
78
|
+
"",
|
|
79
|
+
"Prose.",
|
|
80
|
+
"",
|
|
81
|
+
"## 4. Results",
|
|
82
|
+
"",
|
|
83
|
+
"Lead-in prose for the section.",
|
|
84
|
+
"",
|
|
85
|
+
"### 4.1 Setup",
|
|
86
|
+
"",
|
|
87
|
+
"Nothing numeric here.",
|
|
88
|
+
"",
|
|
89
|
+
"### 4.2 Gated runs",
|
|
90
|
+
"",
|
|
91
|
+
"The agent stopped and asked a human in **34 of 48** gated runs.",
|
|
92
|
+
"",
|
|
93
|
+
paperExtra,
|
|
94
|
+
"",
|
|
95
|
+
"## References",
|
|
96
|
+
"",
|
|
97
|
+
"[1] x.",
|
|
98
|
+
"",
|
|
99
|
+
].join("\n"),
|
|
100
|
+
);
|
|
101
|
+
const b = join(d, "repro", "artifact-anon");
|
|
102
|
+
if (bundle) {
|
|
103
|
+
mkdirSync(b, { recursive: true });
|
|
104
|
+
if (index !== null) writeFileSync(join(b, "NUMBERS.md"), index);
|
|
105
|
+
if (readme !== null) writeFileSync(join(b, "README.md"), readme);
|
|
106
|
+
for (const f of files) {
|
|
107
|
+
mkdirSync(join(b, dirname(f)), { recursive: true });
|
|
108
|
+
writeFileSync(join(b, f), "x\n");
|
|
109
|
+
}
|
|
110
|
+
for (const f of bundleFiles) {
|
|
111
|
+
mkdirSync(join(b, dirname(f)), { recursive: true });
|
|
112
|
+
writeFileSync(join(b, f), "x\n");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// `runs` are experiment directories in the WORKING tree — the input to the reverse leg. Each is
|
|
116
|
+
// ["dir-name", ["RESULTS.md", …]]; an empty file list means a directory with no write-up.
|
|
117
|
+
for (const [rname, heads] of runs) {
|
|
118
|
+
mkdirSync(join(d, "repro", rname), { recursive: true });
|
|
119
|
+
for (const h of heads) writeFileSync(join(d, "repro", rname, h), "x\n");
|
|
120
|
+
}
|
|
121
|
+
if (allow !== null)
|
|
122
|
+
writeFileSync(join(d, "repro", "unreported-grandfathered.txt"), allow);
|
|
123
|
+
return d;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ── 1. the paper points at data the bundle does not contain ────────────────────────────
|
|
127
|
+
// §4.2 reports a figure and the index has never heard of it: the released bundle may hold nothing
|
|
128
|
+
// for the one experiment the abstract leads with. This is the 2026-08-06 defect, reproduced.
|
|
129
|
+
{
|
|
130
|
+
const d = fixture("unindexed-section", {
|
|
131
|
+
index: "# numbers → data\n- §4.1: `setup/config.json`\n",
|
|
132
|
+
files: ["setup/config.json"],
|
|
133
|
+
});
|
|
134
|
+
const r = run(d);
|
|
135
|
+
assert.match(
|
|
136
|
+
r.out,
|
|
137
|
+
/§4\.2 reports 1 bolded figure\(s\) and is named NOWHERE/,
|
|
138
|
+
"artifact-coverage passed a bundle whose index never mentions the section carrying the paper's " +
|
|
139
|
+
"headline number — absence is invisible to every other gate, which is why this one exists:\n" +
|
|
140
|
+
r.out,
|
|
141
|
+
);
|
|
142
|
+
assert.equal(r.code, 1, "it reported the finding and still exited 0");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ── 2. the index promises a path the bundle does not ship ──────────────────────────────
|
|
146
|
+
// A promise to a file that is not there is worse than no promise: it reads as released.
|
|
147
|
+
{
|
|
148
|
+
const d = fixture("dangling-path", {
|
|
149
|
+
index: "# numbers → data\n- §4.2: the gated runs are in `runs/gated.tsv`\n",
|
|
150
|
+
});
|
|
151
|
+
const r = run(d);
|
|
152
|
+
assert.match(
|
|
153
|
+
r.out,
|
|
154
|
+
/the index names `runs\/gated\.tsv` and `runs` is not in the bundle/,
|
|
155
|
+
r.out,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ── 3. a bundle with no index at all ───────────────────────────────────────────────────
|
|
160
|
+
{
|
|
161
|
+
const d = fixture("no-index", { index: null });
|
|
162
|
+
const r = run(d);
|
|
163
|
+
assert.match(
|
|
164
|
+
r.out,
|
|
165
|
+
/no NUMBERS\.md and no README\.md/,
|
|
166
|
+
"a bundle that maps nothing to anything was accepted:\n" + r.out,
|
|
167
|
+
);
|
|
168
|
+
assert.equal(r.code, 1, "it reported the finding and still exited 0");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ── 4. a complete bundle produces NOTHING ──────────────────────────────────────────────
|
|
172
|
+
// Including the guard that keeps this checker switched on: a paper about repositories names dozens
|
|
173
|
+
// of `owner/repo` IN BACKTICKS, and those are not paths. Three were reported as missing directories
|
|
174
|
+
// on the first real run, and a checker whose findings are mostly noise gets muted — which is how the
|
|
175
|
+
// failure this file guards against happens a second time.
|
|
176
|
+
//
|
|
177
|
+
// 🔴 The backticks are load-bearing in this fixture, and the first version of it did not have them.
|
|
178
|
+
// Only backtick-quoted strings are extracted as candidate paths, so an unquoted `saleor/apps` never
|
|
179
|
+
// reaches the extension guard at all — the assertion looked like it was testing the guard and was
|
|
180
|
+
// testing nothing. Caught by deleting the guard and watching this file stay green.
|
|
181
|
+
{
|
|
182
|
+
const d = fixture("clean", {
|
|
183
|
+
index:
|
|
184
|
+
"# numbers → data\n- §4.1: `setup/config.json`\n- §4.2: `runs/gated.tsv`, drawn from `saleor/apps`\n",
|
|
185
|
+
files: ["setup/config.json", "runs/gated.tsv"],
|
|
186
|
+
});
|
|
187
|
+
const r = run(d);
|
|
188
|
+
assert.equal(r.code, 0, "a complete bundle was failed:\n" + r.out);
|
|
189
|
+
assert.equal(
|
|
190
|
+
r.out.trim(),
|
|
191
|
+
"",
|
|
192
|
+
"--flags-only printed something about a complete bundle:\n" + r.out,
|
|
193
|
+
);
|
|
194
|
+
// …and in report mode it says so out loud, because silence and success must not look alike.
|
|
195
|
+
assert.match(
|
|
196
|
+
run(d, false).out,
|
|
197
|
+
/every number-reporting section is indexed, every named path exists/,
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ── 5. KNOWN GAP, asserted so it stays visible ─────────────────────────────────────────
|
|
202
|
+
// 🔴 A paper with NO bundle at all exits 0 in silence, and `run-mechanical.mjs` records that silence
|
|
203
|
+
// as PASS for harden-paper. The checker written because absence is invisible is itself blind to the
|
|
204
|
+
// largest absence there is. Asserting the CURRENT behaviour: when it learns to say "this paper
|
|
205
|
+
// releases nothing", the assertion flips and must be updated — that is the point of writing a gap
|
|
206
|
+
// down rather than leaving it as a thing somebody once noticed.
|
|
207
|
+
{
|
|
208
|
+
const d = fixture("no-bundle", { index: null, bundle: false });
|
|
209
|
+
const r = run(d);
|
|
210
|
+
assert.equal(
|
|
211
|
+
r.code,
|
|
212
|
+
0,
|
|
213
|
+
"artifact-coverage now speaks up about a paper with no bundle — good; update this assertion",
|
|
214
|
+
);
|
|
215
|
+
assert.equal(
|
|
216
|
+
r.out.trim(),
|
|
217
|
+
"",
|
|
218
|
+
"artifact-coverage now speaks up about a paper with no bundle — good; update this assertion",
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ══════════════════════════════════════════════════════════════════════════════════════════════
|
|
223
|
+
// THE REVERSE LEG — a result on disk the paper never mentions (MedSci's `DISK_UNREPORTED`)
|
|
224
|
+
//
|
|
225
|
+
// Cases 1–3 all start from something the paper SAYS. This leg starts from something on DISK, which
|
|
226
|
+
// is the only direction that can see an experiment that was run and quietly dropped. Its silence is
|
|
227
|
+
// therefore even more expensive than the forward legs', so the ignore set gets as many assertions
|
|
228
|
+
// as the detector does.
|
|
229
|
+
// ══════════════════════════════════════════════════════════════════════════════════════════════
|
|
230
|
+
|
|
231
|
+
const CLEAN_INDEX =
|
|
232
|
+
"# numbers → data\n- §4.1: `setup/config.json`\n- §4.2: `runs/gated.tsv`\n";
|
|
233
|
+
const CLEAN_FILES = ["setup/config.json", "runs/gated.tsv"];
|
|
234
|
+
|
|
235
|
+
// ── 6. an experiment that ran, wrote up a result, and is named nowhere ──────────────────────────
|
|
236
|
+
{
|
|
237
|
+
const d = fixture("disk-unreported", {
|
|
238
|
+
index: CLEAN_INDEX,
|
|
239
|
+
files: CLEAN_FILES,
|
|
240
|
+
runs: [["burial-curve-2026-07-28", ["RESULTS.md"]]],
|
|
241
|
+
});
|
|
242
|
+
const r = run(d);
|
|
243
|
+
assert.match(
|
|
244
|
+
r.out,
|
|
245
|
+
/DISK_UNREPORTED\s+repro\/burial-curve-2026-07-28\/ holds RESULTS\.md and is named neither in the paper nor in the released index/,
|
|
246
|
+
"an analysis ran, wrote up its result and is reachable from nothing the reviewer sees — that is the\n" +
|
|
247
|
+
"cherry-picking case this leg was ported for, and it passed:\n" +
|
|
248
|
+
r.out,
|
|
249
|
+
);
|
|
250
|
+
assert.equal(r.code, 1, "it reported DISK_UNREPORTED and still exited 0");
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// ── 7. named in the PAPER → quiet ───────────────────────────────────────────────────────────────
|
|
254
|
+
{
|
|
255
|
+
const d = fixture("named-in-paper", {
|
|
256
|
+
index: CLEAN_INDEX,
|
|
257
|
+
files: CLEAN_FILES,
|
|
258
|
+
paperExtra:
|
|
259
|
+
"The burial curve run (`burial-curve-2026-07-28`) found no third point.",
|
|
260
|
+
runs: [["burial-curve-2026-07-28", ["RESULTS.md"]]],
|
|
261
|
+
});
|
|
262
|
+
const r = run(d);
|
|
263
|
+
assert.equal(
|
|
264
|
+
r.code,
|
|
265
|
+
0,
|
|
266
|
+
"an experiment the paper discusses by name was reported as unmentioned:\n" +
|
|
267
|
+
r.out,
|
|
268
|
+
);
|
|
269
|
+
assert.equal(r.out.trim(), "", "…and it said so out loud:\n" + r.out);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ── 8. named ONLY in the bundle's README.md → quiet ─────────────────────────────────────────────
|
|
273
|
+
// 🔴 THIS IS A REGRESSION TEST FOR A REAL FALSE POSITIVE, not a hypothetical. The first version
|
|
274
|
+
// reused check 1's `indexPath`, which resolves to NUMBERS.md *or* README.md and stops. On the live
|
|
275
|
+
// paper that accused the bundle of hiding three analyses (`enforcement-surface`, `ladder-prototype`,
|
|
276
|
+
// `tier-a-prime`) that it ships and documents — in README.md, the file the leg was not reading. A
|
|
277
|
+
// cherry-picking detector that cries wolf gets muted, and a muted detector is how the failure
|
|
278
|
+
// happens twice.
|
|
279
|
+
{
|
|
280
|
+
const d = fixture("named-in-readme-only", {
|
|
281
|
+
index: CLEAN_INDEX,
|
|
282
|
+
files: CLEAN_FILES,
|
|
283
|
+
readme:
|
|
284
|
+
"## What is deliberately not in this bundle\n\n| `ladder-prototype-2026-07-29` | not needed |\n",
|
|
285
|
+
runs: [["ladder-prototype-2026-07-29", ["RESULTS.md"]]],
|
|
286
|
+
});
|
|
287
|
+
const r = run(d);
|
|
288
|
+
assert.equal(
|
|
289
|
+
r.code,
|
|
290
|
+
0,
|
|
291
|
+
"an analysis named in the bundle's README.md and nowhere else was reported as unmentioned — the leg is\n" +
|
|
292
|
+
"reading one index file instead of all of them, which is exactly the false positive that mutes it:\n" +
|
|
293
|
+
r.out,
|
|
294
|
+
);
|
|
295
|
+
// …and the same, matched by the DATE-STRIPPED slug: the bundle is rebuilt with the dates dropped,
|
|
296
|
+
// so `enforcement-surface-2026-07-29` on disk is `enforcement-surface` in the index.
|
|
297
|
+
const e = run(
|
|
298
|
+
fixture("named-by-slug", {
|
|
299
|
+
index: CLEAN_INDEX,
|
|
300
|
+
files: CLEAN_FILES,
|
|
301
|
+
readme: "See `enforcement-surface/` for the surface scan.\n",
|
|
302
|
+
runs: [["enforcement-surface-2026-07-29", ["RESULTS.md"]]],
|
|
303
|
+
}),
|
|
304
|
+
);
|
|
305
|
+
assert.equal(
|
|
306
|
+
e.code,
|
|
307
|
+
0,
|
|
308
|
+
"an analysis the index names without its date suffix was reported as unmentioned. Every directory in\n" +
|
|
309
|
+
"this project carries a -YYYY-MM-DD the rebuild drops, so without slug matching the leg accuses the\n" +
|
|
310
|
+
"bundle of hiding most of what it ships:\n" +
|
|
311
|
+
e.out,
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// ── 9. an allow row silences it, AND the ledger prints the row with its reason ──────────────────
|
|
316
|
+
// The silencing is half the assertion. The other half is that the reason is PRINTED: an ignore set
|
|
317
|
+
// applied invisibly turns this detector into the thing it exists to catch.
|
|
318
|
+
{
|
|
319
|
+
const d = fixture("allowed-row", {
|
|
320
|
+
index: CLEAN_INDEX,
|
|
321
|
+
files: CLEAN_FILES,
|
|
322
|
+
bundleFiles: ["seeded-probe/RESULTS.md"],
|
|
323
|
+
runs: [["seeded-probe-fixtures-2026-07-31", ["RESULTS.md"]]],
|
|
324
|
+
allow:
|
|
325
|
+
"seeded-probe-fixtures-2026-07-31\tships-as:seeded-probe\ttranslated at anonymisation\n",
|
|
326
|
+
});
|
|
327
|
+
const r = run(d, false);
|
|
328
|
+
assert.equal(
|
|
329
|
+
r.code,
|
|
330
|
+
0,
|
|
331
|
+
"an allowed row still counted as a finding:\n" + r.out,
|
|
332
|
+
);
|
|
333
|
+
assert.match(
|
|
334
|
+
r.out,
|
|
335
|
+
/seeded-probe-fixtures-2026-07-31\s+→\s+ships-as:seeded-probe — translated at anonymisation/,
|
|
336
|
+
"the ignore row was applied and NOT printed. A hidden ignore list is how a cherry-picking detector\n" +
|
|
337
|
+
"becomes a cherry-pick; the whole design turns on this line being in the output:\n" +
|
|
338
|
+
r.out,
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ── 10. a `ships-as:` allowance whose bundle path is gone is a finding of its own ───────────────
|
|
343
|
+
// This is what makes that reason different in kind from prose: it carries a witness, and the witness
|
|
344
|
+
// is re-checked every run. An allowance that has rotted is hiding a real absence.
|
|
345
|
+
{
|
|
346
|
+
const d = fixture("rotted-allowance", {
|
|
347
|
+
index: CLEAN_INDEX,
|
|
348
|
+
files: CLEAN_FILES,
|
|
349
|
+
runs: [["seeded-probe-fixtures-2026-07-31", ["RESULTS.md"]]],
|
|
350
|
+
allow:
|
|
351
|
+
"seeded-probe-fixtures-2026-07-31\tships-as:seeded-probe\ttranslated at anonymisation\n",
|
|
352
|
+
});
|
|
353
|
+
const r = run(d);
|
|
354
|
+
assert.match(
|
|
355
|
+
r.out,
|
|
356
|
+
/claims it ships as `seeded-probe`, and that path is not in the bundle — the allowance has rotted/,
|
|
357
|
+
"the allow row promised the analysis ships under another name, the bundle no longer has it, and the\n" +
|
|
358
|
+
"check believed the row anyway:\n" +
|
|
359
|
+
r.out,
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// ── 11. an allow row naming nothing on disk is reported, so the list shrinks ────────────────────
|
|
364
|
+
{
|
|
365
|
+
const d = fixture("stale-row", {
|
|
366
|
+
index: CLEAN_INDEX,
|
|
367
|
+
files: CLEAN_FILES,
|
|
368
|
+
allow: "deleted-experiment-2026-01-01\tships-as:nowhere\tgone\n",
|
|
369
|
+
});
|
|
370
|
+
const r = run(d);
|
|
371
|
+
assert.match(
|
|
372
|
+
r.out,
|
|
373
|
+
/the ignore row for `deleted-experiment-2026-01-01` names no directory under repro\/ that holds a headline result/,
|
|
374
|
+
"a row for a directory that no longer exists sat in the ignore file unreported — which is how the\n" +
|
|
375
|
+
"list stops shrinking and starts being scenery:\n" +
|
|
376
|
+
r.out,
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ── 12. THE LEDGER IS PRINTED ON A CLEAN RUN TOO ────────────────────────────────────────────────
|
|
381
|
+
// 🔴 The load-bearing property of the whole design. If the ignore set only appeared beside findings,
|
|
382
|
+
// a run with nothing to report would look like coverage — which is the exact inference
|
|
383
|
+
// `check_grandfather` in repro/paper_numbers.py exists to block.
|
|
384
|
+
{
|
|
385
|
+
const d = fixture("ledger-on-clean", {
|
|
386
|
+
index: CLEAN_INDEX,
|
|
387
|
+
files: CLEAN_FILES,
|
|
388
|
+
paperExtra:
|
|
389
|
+
"The burial curve run (`burial-curve-2026-07-28`) is discussed in §4.",
|
|
390
|
+
runs: [["burial-curve-2026-07-28", ["RESULTS.md"]]],
|
|
391
|
+
allow: "# nothing here\n",
|
|
392
|
+
});
|
|
393
|
+
const r = run(d, false);
|
|
394
|
+
assert.equal(r.code, 0, "the clean fixture is not clean:\n" + r.out);
|
|
395
|
+
assert.match(
|
|
396
|
+
r.out,
|
|
397
|
+
/what the reverse check ignored, in full \(1 candidate analyses under repro\/\)/,
|
|
398
|
+
"a run with no findings printed no ignore ledger — silence then reads as coverage:\n" +
|
|
399
|
+
r.out,
|
|
400
|
+
);
|
|
401
|
+
assert.match(
|
|
402
|
+
r.out,
|
|
403
|
+
/Ratio: 0 reported as unreported, 0 deliberately ignored, 1 reachable/,
|
|
404
|
+
"the ledger printed no ratio, so nobody can tell whether this check is still doing anything:\n" +
|
|
405
|
+
r.out,
|
|
406
|
+
);
|
|
407
|
+
assert.match(
|
|
408
|
+
r.out,
|
|
409
|
+
/\(the file is empty — nothing is being waved through\)/,
|
|
410
|
+
"an empty allow file printed nothing at all, which is indistinguishable from not reading it:\n" +
|
|
411
|
+
r.out,
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// ── 13. the header states the finding COUNT, because run-mechanical.mjs parses it ───────────────
|
|
416
|
+
// `run-mechanical` reads this check in `flags` mode, where it prefers the check's own stated count
|
|
417
|
+
// and falls back to COUNTING OUTPUT LINES. The ledger is a dozen-odd lines that are not findings, so
|
|
418
|
+
// without a stated count a clean run would be recorded as a dozen findings. That regression is
|
|
419
|
+
// invisible in the check's own output and only shows up in the ledger, which is why it is asserted
|
|
420
|
+
// here, from outside, in both directions.
|
|
421
|
+
{
|
|
422
|
+
const clean = run(
|
|
423
|
+
fixture("count-clean", { index: CLEAN_INDEX, files: CLEAN_FILES }),
|
|
424
|
+
false,
|
|
425
|
+
);
|
|
426
|
+
assert.match(
|
|
427
|
+
clean.out,
|
|
428
|
+
/—\s*0\s+finding\(s\)/,
|
|
429
|
+
"a clean run does not state `— 0 finding(s)`, so run-mechanical falls back to counting the ledger's\n" +
|
|
430
|
+
"lines and records a clean paper as a dozen findings:\n" +
|
|
431
|
+
clean.out,
|
|
432
|
+
);
|
|
433
|
+
const dirty = run(
|
|
434
|
+
fixture("count-dirty", {
|
|
435
|
+
index: CLEAN_INDEX,
|
|
436
|
+
files: CLEAN_FILES,
|
|
437
|
+
runs: [
|
|
438
|
+
["a-2026-01-01", ["RESULTS.md"]],
|
|
439
|
+
["b-2026-01-01", ["SUMMARY.json"]],
|
|
440
|
+
],
|
|
441
|
+
}),
|
|
442
|
+
false,
|
|
443
|
+
);
|
|
444
|
+
assert.match(
|
|
445
|
+
dirty.out,
|
|
446
|
+
/—\s*2\s+finding\(s\)/,
|
|
447
|
+
"the header does not state the real finding count:\n" + dirty.out,
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// ── 14. KNOWN GAPS of the reverse leg, asserted so they stay visible ────────────────────────────
|
|
452
|
+
// Both are documented in the check's own output. Asserting the CURRENT behaviour: when either
|
|
453
|
+
// changes, this block flips and must be updated.
|
|
454
|
+
{
|
|
455
|
+
// (a) a directory with no write-up is invisible. Deleting RESULTS.md is a one-command way past
|
|
456
|
+
// this check, and nothing here can tell that from an experiment nobody finished.
|
|
457
|
+
const d = fixture("no-writeup", {
|
|
458
|
+
index: CLEAN_INDEX,
|
|
459
|
+
files: CLEAN_FILES,
|
|
460
|
+
runs: [["silent-run-2026-01-01", ["data.tsv"]]],
|
|
461
|
+
});
|
|
462
|
+
const r = run(d);
|
|
463
|
+
assert.equal(
|
|
464
|
+
r.code,
|
|
465
|
+
0,
|
|
466
|
+
"the reverse leg now sees a run with no write-up — good; update this assertion",
|
|
467
|
+
);
|
|
468
|
+
// (b) the bundle is not itself a candidate: it is the shipped copy of these same analyses, so
|
|
469
|
+
// counting it would report every one of them twice.
|
|
470
|
+
const e = run(
|
|
471
|
+
fixture("bundle-not-candidate", {
|
|
472
|
+
index: CLEAN_INDEX,
|
|
473
|
+
files: [...CLEAN_FILES, "RESULTS.md"],
|
|
474
|
+
}),
|
|
475
|
+
false,
|
|
476
|
+
);
|
|
477
|
+
assert.match(
|
|
478
|
+
e.out,
|
|
479
|
+
/the released bundle itself \(artifact-anon\/\)/,
|
|
480
|
+
"the structural ignore that skips the bundle is not named in the ledger:\n" +
|
|
481
|
+
e.out,
|
|
482
|
+
);
|
|
483
|
+
assert.match(
|
|
484
|
+
e.out,
|
|
485
|
+
/—\s*0\s+finding\(s\)/,
|
|
486
|
+
"the bundle's own RESULTS.md was counted as an unreported analysis:\n" +
|
|
487
|
+
e.out,
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
492
|
+
console.log(
|
|
493
|
+
"✓ artifact-coverage: unindexed section, dangling path and missing index all fire; a complete bundle is silent; " +
|
|
494
|
+
"the reverse leg fires on a result nobody reports, stays quiet on one named in the paper or in EITHER index file, " +
|
|
495
|
+
"reports rotted and stale allowances, prints its whole ignore set on a clean run, and states its count; 3 known gaps recorded",
|
|
496
|
+
);
|