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,344 @@
|
|
|
1
|
+
# paper-pipeline/scripts — did the check run, against what, and what did it say
|
|
2
|
+
|
|
3
|
+
Four files record paper-pipeline skill runs and compute the status view that replaced the
|
|
4
|
+
hand-maintained `PIPELINE-STATUS.md` table. Alongside them live the harnesses that plant a defect in
|
|
5
|
+
each mechanical gate and check the gate says no (see **Harnesses**, added 2026-08-07).
|
|
6
|
+
|
|
7
|
+
> **📍 Moved here 2026-08-15 from `.claude/pipeline/`.** The top-level directory was distributed:
|
|
8
|
+
> everything with one owner went to it. Here this is the paper-pipeline family —
|
|
9
|
+
> `ledger` · `announce` · `status` · `run-mechanical` plus nine checkers that already lay
|
|
10
|
+
> here, and **their tests, which until that day lay in another top-level directory**. That is,
|
|
11
|
+
> colocation was missing precisely where we preach it: checker in the skill, its harness — outside.
|
|
12
|
+
>
|
|
13
|
+
> What remained without an owner (shared library of 24 skills, `markdown.mjs`, mutations engine) —
|
|
14
|
+
> in [`.claude/lib/`](../../../lib/README.md), also there the breakdown of four forms of reference, from which
|
|
15
|
+
> path-based search sees only one.
|
|
16
|
+
|
|
17
|
+
| file | what it is for |
|
|
18
|
+
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
19
|
+
| `ledger.mjs` | The append-only run ledger. `record()` writes one JSON line carrying a FINDING or an ABSTENTION **plus a hash of the paper and a hash of the skill's own source**. `status()` derives FRESH / STALE-PAPER / STALE-SKILL / NEVER-RUN from those hashes against the bytes on disk. Everything else is derived from this file. |
|
|
20
|
+
| `announce.mjs` | `node .claude/skills/paper-pipeline/scripts/announce.mjs <skill> <paper-dir>` — prints a two-line banner to stderr and writes an `ABSTAINED started` row. Called by a skill as its first step. |
|
|
21
|
+
| `status.mjs` | The computed status view. No field here can be set by a human. |
|
|
22
|
+
| `runs.jsonl` | The ledger itself. Append-only; do not edit by hand. |
|
|
23
|
+
|
|
24
|
+
## The one command
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
node .claude/skills/paper-pipeline/scripts/status.mjs <papers-root>/<paper>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Three properties, each one a defect we actually shipped
|
|
31
|
+
|
|
32
|
+
1. **Staleness is computed, never declared.** A run is fresh only if the paper hashes to what it
|
|
33
|
+
hashed when the check ran. Nobody ticks a box, so nobody forgets to un-tick one.
|
|
34
|
+
2. **The checker's own source is part of the key.** Improve a skill and its old answers go stale —
|
|
35
|
+
they were answers from a different tool. This is what `make` cannot do and why its staleness
|
|
36
|
+
answers could not be trusted here.
|
|
37
|
+
3. **A check that has never said no is reported as suspect.** From mutation testing: a test that
|
|
38
|
+
kills no mutant is not a test. A tightening pass once returned KEEP on 80 of 81 sections and
|
|
39
|
+
still counted as run.
|
|
40
|
+
|
|
41
|
+
## 🔴 There is no `PASS` constructor (2026-08-10)
|
|
42
|
+
|
|
43
|
+
Five failures were observed on this pipeline. Three were defects of the CHECKING APPARATUS rather
|
|
44
|
+
than of any manuscript, and they are one fact wearing three coats:
|
|
45
|
+
|
|
46
|
+
1. a gate was recorded as passed while the input it needs did not exist;
|
|
47
|
+
2. a check that had never once returned a negative counted as a working check;
|
|
48
|
+
3. five model reviewers from one vendor agreeing was recorded as an acquittal.
|
|
49
|
+
|
|
50
|
+
Each is a stored value meaning _nothing was wrong_, written by a process with no evidence for it and
|
|
51
|
+
thereafter indistinguishable from one that had some. So the value is gone. **A run records exactly
|
|
52
|
+
two things:**
|
|
53
|
+
|
|
54
|
+
| constructor | must carry | means |
|
|
55
|
+
| ----------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------ |
|
|
56
|
+
| `FINDING` | a `<count> ≥ 1` **and a report path that exists** (`--blocking` if it stops the pass) | something is wrong, and here is where to read about it |
|
|
57
|
+
| `ABSTAINED` | a `<reason>` from a closed set | no judgement was produced, and here is why |
|
|
58
|
+
|
|
59
|
+
Reasons: `started` · `no-witness` · `input-missing` · `blocked` · `crashed`.
|
|
60
|
+
|
|
61
|
+
**"Nothing was wrong" is not a value that can be written down.** It is the ABSENCE of findings,
|
|
62
|
+
derived at read time by whoever is asking — who then owns the inference. A completed clean run
|
|
63
|
+
records `ABSTAINED no-witness`, which is not a pass with better manners: it says the check ran and
|
|
64
|
+
has **no witness** for the negative answer.
|
|
65
|
+
|
|
66
|
+
### Prior art, both halves of it
|
|
67
|
+
|
|
68
|
+
- **SARIF** (OASIS Standard 2.1.0, §3.27.9) defines `kind: "pass"` verbatim as _"The rule … was
|
|
69
|
+
evaluated, and no problem was found"_, and the only REQUIRED property of a `result` is `message` —
|
|
70
|
+
`locations` is optional, so a conformant acquittal can name no place, no span and no artifact.
|
|
71
|
+
🔴 **Do not write that industry lacks abstention.** `kind: "open"` sits in the same paragraph
|
|
72
|
+
(_"insufficient information to decide whether a problem exists"_) and its NOTE 1 draws exactly our
|
|
73
|
+
trichotomy. Our position is **"delete pass"**, never "add abstain". Established `[A]` against the
|
|
74
|
+
OASIS text and its JSON schema in
|
|
75
|
+
`the author's private research notes` §5.
|
|
76
|
+
- **Certifying algorithms** — McConnell, Mehlhorn, Näher & Schweitzer, _Computer Science Review_
|
|
77
|
+
5(2):119–161, 2011 — is the ancestor: BOTH answers carry a witness ("bipartite" → a 2-colouring,
|
|
78
|
+
"not bipartite" → an odd cycle). 🔴 **Our discipline is strictly WEAKER than theirs and must say
|
|
79
|
+
so rather than borrow the credit:** we witness only the finding. `no-witness` is the honest name
|
|
80
|
+
for what we have when a check comes back empty.
|
|
81
|
+
|
|
82
|
+
### What happens to the old rows
|
|
83
|
+
|
|
84
|
+
`runs.jsonl` holds real rows carrying `PASS`, `FINDINGS`, `FAIL`, `ABSENT` and `ERROR`. **The file
|
|
85
|
+
is not rewritten** — editing history so it agrees with today's design is the same move as ticking a
|
|
86
|
+
box in the old status table.
|
|
87
|
+
|
|
88
|
+
**Nor are those rows translated.** A silent reinterpretation is precisely the class of defect this
|
|
89
|
+
refactor removes: map an old `PASS` onto `ABSTAINED no-witness` and a row asserting "nothing was
|
|
90
|
+
wrong" comes back out of the reader wearing the new vocabulary's honesty without having earned it.
|
|
91
|
+
So a legacy row keeps a kind of its own, `LEGACY`, and its original word verbatim in
|
|
92
|
+
`legacyVerdict`. `LEGACY` is deliberately **not** in `KINDS`: it can be read and never written.
|
|
93
|
+
|
|
94
|
+
- it counts as a RUN — someone did run something, and pretending otherwise is its own lie;
|
|
95
|
+
- a legacy `FINDINGS`/`FAIL` row still surfaces its count and report path, because those rows carry
|
|
96
|
+
evidence and discarding evidence is the destructive direction (the same reasoning that writes a
|
|
97
|
+
broken `FINDING` row before failing the run);
|
|
98
|
+
- a legacy `PASS`/`ABSENT`/`ERROR` row carries **nothing** forward. The status view prints it as
|
|
99
|
+
`legacy:PASS` under a heading that says so. Note that real PASS rows sometimes carry a count
|
|
100
|
+
(`sweep-design-space`, `findings: 8`) and that count meant _survivors_, not defects — it is not
|
|
101
|
+
read as a finding count. Re-run the check to get an answer in a live vocabulary.
|
|
102
|
+
|
|
103
|
+
## 🔴 One check, one row (2026-08-10)
|
|
104
|
+
|
|
105
|
+
**The ledger was keyed on the SKILL, but the thing that produces a verdict is the CHECK.** `status()`
|
|
106
|
+
reduces a key's rows with `runs[runs.length - 1]` — correct across repeated runs of one check,
|
|
107
|
+
catastrophic across two. Two mechanical checks filed under one skill name took turns being the
|
|
108
|
+
answer: a clean run of the second erased a finding of the first from every derived view, while the
|
|
109
|
+
ledger file itself still honestly held both lines.
|
|
110
|
+
|
|
111
|
+
The key is now `skill` + `check`, written `skill/check` (or just `skill` for a skill with one
|
|
112
|
+
check). `EXPECTED_GATES` entries are checks, not skills.
|
|
113
|
+
|
|
114
|
+
The cost had been paid in unwired checks: `repro/arm_permutation.py` and `repro/delivered_pdf.py`
|
|
115
|
+
were left out of `run-mechanical.mjs` precisely because their natural owner is `build-benchmark` and
|
|
116
|
+
`check-provenance.mjs` was already sitting on that row. Both are wired in now, and all three appear
|
|
117
|
+
side by side:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
🟢 build-benchmark/check-provenance FRESH • 13 found
|
|
121
|
+
🟢 build-benchmark/arm-permutation FRESH abstained:no-witness
|
|
122
|
+
🟢 build-benchmark/delivered-pdf FRESH abstained:no-witness
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Legacy rows have no `check`, so they key on the bare skill name and do **not** merge into a new
|
|
126
|
+
check's row — another consequence of not reinterpreting them.
|
|
127
|
+
|
|
128
|
+
## How a skill is wired (all 22 paper skills)
|
|
129
|
+
|
|
130
|
+
Each `SKILL.md` carries exactly two blocks:
|
|
131
|
+
|
|
132
|
+
- **`## Run me`**, right after the title — the `announce.mjs` call, first thing. An advisory pass
|
|
133
|
+
cannot be observed failing, because silence is both its error state and its normal state; so
|
|
134
|
+
starting is an event and events get written down.
|
|
135
|
+
- **`## Record the verdict`**, the last step of the procedure — the `ledger.mjs record` call, in one
|
|
136
|
+
shape across all 22 files:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
node .claude/skills/paper-pipeline/scripts/ledger.mjs record <skill> <paper-dir> FINDING <count> <report-path>
|
|
140
|
+
node .claude/skills/paper-pipeline/scripts/ledger.mjs record <skill> <paper-dir> ABSTAINED <reason> "<one line>"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Each block then says, in its own words, what a finding is for that skill and which abstentions it
|
|
144
|
+
can honestly reach. `draft-paper` carries the one admission in the suite: it generates rather than
|
|
145
|
+
judges, will abstain nearly always, and says so — `skill-checks.mjs` asserts that paragraph is
|
|
146
|
+
still there, because an explained limitation deleted becomes an unexplained never-finding check.
|
|
147
|
+
|
|
148
|
+
An announce row with no terminal row after it is itself a finding: the skill began and never
|
|
149
|
+
finished. Neither row present means it did not run, whatever any status file claims.
|
|
150
|
+
|
|
151
|
+
## Two things to know before you trust the table
|
|
152
|
+
|
|
153
|
+
- `EXPECTED_GATES` in `status.mjs` is the table. A check that records rows and is absent there
|
|
154
|
+
writes rows nobody sees. Add a new skill AND its mechanical checks; `unlistedGates()` catches the
|
|
155
|
+
omission after the fact, and `pipeline-corpus.harness.mjs` catches it before.
|
|
156
|
+
- `ledger.selftest.mjs` and every harness redirect `PIPELINE_LEDGER` to a temp file **before**
|
|
157
|
+
importing `ledger.mjs`. Save-and-restore is not isolation; it lost that race on its first day.
|
|
158
|
+
|
|
159
|
+
## Harnesses (2026-08-07)
|
|
160
|
+
|
|
161
|
+
Property 3 above says a gate that has never returned a negative verdict is _suspect_. These turn
|
|
162
|
+
that suspicion into evidence or into a bug report: each one plants the exact defect a gate claims to
|
|
163
|
+
catch, asserts the gate fires, and asserts it stays **quiet on the clean case** — because a checker
|
|
164
|
+
that fires on correct text is muted within a day, which is worse than one that misses.
|
|
165
|
+
|
|
166
|
+
| harness | gate under test | what is planted |
|
|
167
|
+
| ---------------------------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
168
|
+
| `gates.harness.mjs` | `structure.mjs`, `prose-lint.mjs`, `ledger.mjs`, `status.mjs`, `run-mechanical.mjs` | unjustified section · a sentence carrying five independent claims · an answer about a rewritten paper · a gate that records nothing · **`PASS` reaching the ledger** · an abstention with a free-text reason · a finding with no evidence, dead evidence, or evidence whose count disagrees · **a sibling check's clean run erasing a finding** · two checks sharing one row key · a status view that prints ✅ or the word PASS · a retired-vocabulary row read as a current answer · **a CLI flag that is advertised and unparsed** |
|
|
169
|
+
| `population-map.harness.mjs` | `population-map.mjs` | owns its self-test (13 cases, run as a **subprocess**) + the CLI wiring the self-test never touches |
|
|
170
|
+
| `provenance.harness.mjs` | `check-provenance.mjs` | a number from the `annotated` arm cited as "exactly as committed" · a bolded figure with no provenance row |
|
|
171
|
+
| `artifact-coverage.harness.mjs` | `artifact-coverage.mjs` | a bundle with no data for the section the abstract leads with · an index promising a path that is not shipped · and, in the REVERSE direction, **a result that exists on disk and that neither the paper nor the released index mentions** — plus the ignore set that decides which of those count: an allowance applied, an allowance printed with its reason, a `ships-as:` claim whose bundle path has gone, a row naming a directory that no longer exists, and **the whole ignore ledger printed on a run with no findings** |
|
|
172
|
+
| `generated-code.harness.mjs` | `generated-code.mjs` | an analysis script that draws randomness and never seeds it · one that hard-codes an absolute path · one that reads and then overwrites its own input (literal and `argv` forms). Plus every quiet case, which is where this checker lives or dies: a seed threaded through `--seed`, a lowercase Express route, a third party's relative path, a read-here-write-there script, a variable name reused across two loops — and the released bundle, which `check-anon.sh` cat. 5 owns and this one must not enter |
|
|
173
|
+
| `pipeline-check.harness.mjs` | `pipeline-check.mjs` | six defects, **one at a time**, asserting the exact finding set. 🔴 Since 2026-08-26 here only checks whose input is OUTSIDE the file (git · clock · `reviews/` · `process.env`); sixteen others moved to `eslint-rules/pipeline-status.mjs` and are checked by `eslint-rules/pipeline-status.harness.mjs` |
|
|
174
|
+
| `paper-lint.harness.mjs` | `paper-lint.mjs` | text moving under an unchanged scorecard · appendix outweighing the body · a shaved passage |
|
|
175
|
+
| `round-diff.harness.mjs` | `paper-pipeline/scripts/round-diff.mjs` | a section changed outside the round's declaration (and one added, and one **removed**) · a cite and a bare bibliography entry arriving mid-round · a numeric literal arriving mid-round, with restating an existing one asserted FREE · the body over its budget · **three rounds compounding past the sum of their budgets while the open round is inside its own** · hedge density rising · a `touches:` list covering the paper · a base that does not resolve · edits with every round closed. Plus the clean case: a legal round must produce total silence |
|
|
176
|
+
| `delivered-pdf.harness.mjs` | `repro/delivered_pdf.py` | a quantity deleted from the built page · **a superscript minus lost in typesetting, compiled and read back** · a value moved away from its claim · an occurrence whose prose the extractor lost · a missing, unreadable and stale PDF · and all six normalisation rules, one assertion each |
|
|
177
|
+
| `bound-numbers.harness.mjs` | `repro/paper_numbers.py`, `md2submission.py`, `latex-build.sh` | a digit in a `\newcommand` name · two registry keys mangling to one macro · an unescaped `%` in the generated values · a converter handling the digits again · a missing `\input` · an unbound name that must stop the build with **no PDF** · a failing pass that leaves its PDF on disk. Plus the CONTROL: a hand-typed literal still compiles, **asserted** so nobody writes that this makes a wrong number unrepresentable |
|
|
178
|
+
| `textidote.harness.mjs` | `repro/textidote_check.py` (TeXtidote v0.9) | a misspelling planted into real prose from the paper, live through the pinned jar · a coinage with a committed row staying silent in the same run · the flagged span truncated by one letter · the caret row leaking into the message · each of the three exclusions (`{{macro}}`, `` `code` ``, the reference list) · **an appendix AFTER `## References` still being judged** · a key that stops discriminating by rule, by word, or by case · a tolerated row moved 40 lines down and indented · the tolerated set, its reasons and the stale rows going unprinted · a missing jar or JRE answering with an empty findings array |
|
|
179
|
+
| `uncited-refs.harness.mjs` | `repro/uncited_refs.py` (`checkcites`) | a bibliography entry no `\cite` points at, planted into a copy of the real build · a finding that names only the bibtex key and not the work · **`checkcites` absent, asserted against the same fixture that DOES report a finding when it is present** · a paper that was never built · a bibliography that could not be read becoming a finding named after the missing file |
|
|
180
|
+
| `<skill>/<skill>.harness.mjs` (×22, colocated) | **one SKILL.md each**, via `skill-checks.mjs` | frontmatter that is not YAML · a `name:` disagreeing with its directory · a wired block naming a script that is not there · a block filing under a **sibling's** name · a Bash command its own `allowed-tools` forbids · a ledger-only skill regressed to bare `Bash` · **a block that documents no FINDING** · a block still instructing a retired constructor · an `ABSTAINED` with no named reason |
|
|
181
|
+
| `pipeline-corpus.harness.mjs` | the **SET** of skills | a gate row pointing at a directory that does not exist · a duplicate key in `EXPECTED_GATES` · a wired skill absent from the gate table · an exclusion that has rotted (skill gone, or no longer wired) |
|
|
182
|
+
|
|
183
|
+
These two are the only ones aimed at the **skills** rather than the scripts they call, which
|
|
184
|
+
is what `vigiles audit`'s `Tested` metric actually counts — "34 surfaces with no vigiles test/eval"
|
|
185
|
+
was never about `structure.mjs`. It also fails differently: a script with a bad path crashes, a
|
|
186
|
+
**skill** with a bad path is read by a model that quietly does something adjacent and reports success.
|
|
187
|
+
Its non-vacuity proof is a separate file, `skills.mutations.mjs` — 21 planted defects, one per
|
|
188
|
+
assertion class, each on a throwaway copy of `.claude/`, plus a control asserting the clean corpus
|
|
189
|
+
produces _only_ the four known-red YAML findings so no case can pass on noise. Run it by hand after
|
|
190
|
+
touching the harness (`node .claude/skills/paper-pipeline/scripts/skills.mutations.mjs`, ~3 s); CI runs it too.
|
|
191
|
+
|
|
192
|
+
> **2026-09-24 — hand-written mutation batteries are deprecated (#52).** Do not write a new
|
|
193
|
+
> `*.mutations.mjs` or add cases to an existing one; say what an assertion guards in a comment
|
|
194
|
+
> directly above it. `npm run check` enforces this through `scripts/mutation-batteries.frozen.json`,
|
|
195
|
+
> which may only shrink. The history below is kept as a record, not as an instruction.
|
|
196
|
+
|
|
197
|
+
Nine harnesses now carry a mutations file of their own — `delivered-pdf.mutations.mjs` (25 rows), `bound-numbers.mutations.mjs` (12 rows),
|
|
198
|
+
`round-diff.mutations.mjs` (28 rows), `ledger.mutations.mjs` (28 rows, covering the 2026-08-10
|
|
199
|
+
refactor across both `gates.harness.mjs` and `skill-checks.mjs`), `artifact-coverage.mutations.mjs`
|
|
200
|
+
(22 rows), `generated-code.mutations.mjs` (22 rows), `textidote.mutations.mjs` (20 rows) and
|
|
201
|
+
`uncited-refs.mutations.mjs` (8 rows). They are run by
|
|
202
|
+
hand, not in CI, because each one rewrites a source file dozens of times and takes minutes.
|
|
203
|
+
|
|
204
|
+
> **2026-08-26 — `verify-refs.mjs` moved to the engine and deleted from here** (along with its harness and
|
|
205
|
+
> mutations). Bibliography measurement now `extract-ref-facts.mjs` in the same folder, judgment — twelve
|
|
206
|
+
> rules `refs/*` in `eslint-rules/ref-facts.mjs`, mutations — `eslint-rules/ref-facts.mutations.mjs`
|
|
207
|
+
> (21 rows). Analysis — `the author's private research notes`.
|
|
208
|
+
|
|
209
|
+
**2026-08-14 — the engine was split out into `mutation-driver.mjs`; the tables stayed.** Each file
|
|
210
|
+
now holds only its cases (which defect, which bytes, which message — data about ITS checker) and
|
|
211
|
+
calls `runMutations()`. The reason is not line count: the ten copies of the driver had DRIFTED, and
|
|
212
|
+
the protections lived in whichever file happened to earn them. Counted against the committed
|
|
213
|
+
versions — the no-op guard (a replacement equal to the original leaves a green harness proving
|
|
214
|
+
nothing) was in **4 of 10**; the retry-once on a non-kill was in **1 of 10**; the strict rule that a
|
|
215
|
+
mutation must be killed by its OWN named assertion, not merely by _something_ going red, was in
|
|
216
|
+
**1 of 10**. All ten now have all three. Two dead-path defects were closed on the way: six `ledger`
|
|
217
|
+
cases named `skills.harness.mjs`, deleted by the 2026-08-11 colocation, and `vigiles test` on a path
|
|
218
|
+
matching nothing exits 0 — so those six reported SURVIVED on every run since (they now go red at
|
|
219
|
+
their own assertions, verified by running them). And the end-of-run "restore failed" line had been
|
|
220
|
+
firing on `gates.harness.mjs`, which is red **on purpose** — the driver now baselines the harnesses
|
|
221
|
+
before touching anything, so that claim means what it says. `skills.mutations.mjs` deliberately
|
|
222
|
+
stays outside the engine: it mutates a COPY of `.claude/`, which is right for cases whose subject
|
|
223
|
+
IS the skill corpus, and it is the only one CI runs. **The
|
|
224
|
+
first run of a mutations file has never yet been clean, and that is the argument for writing one.**
|
|
225
|
+
`delivered-pdf`'s first run left five survivors and three wrong-case hits, and unpicking them found
|
|
226
|
+
three fixtures that tested nothing they claimed to (a window assertion satisfied by ordering, a
|
|
227
|
+
digit-width guard the counter never reached), two mutations that did not mutate, and **one dead rule
|
|
228
|
+
in the checker itself** — a table of sixteen Unicode space separators whose removal changed no
|
|
229
|
+
verdict, because Python's `\s` already matched every one of them. It was deleted rather than tested.
|
|
230
|
+
|
|
231
|
+
The two 2026-08-10 files kept the streak, and both findings were about the TEST rather than the
|
|
232
|
+
checker. `uncited-refs`'s heading-offset row SURVIVED, which said either that the offset was dead
|
|
233
|
+
code or that nothing watched it; reading `checkcites.lua` settled it — line 359 prints `=> <name>`
|
|
234
|
+
for an auxiliary file it could not resolve and then CARRIES ON into the report, and LaTeX writes
|
|
235
|
+
`\@input{sub.aux}` for every `\include`, so without the offset a partial build makes the wrapper
|
|
236
|
+
report `nope.aux` as an uncited bibliography entry. A fabricated finding, in a report somebody
|
|
237
|
+
would act on. `textidote`'s run reported `key/rule` as a WRONG-CASE kill three times running, and
|
|
238
|
+
each fix exposed the next layer: the key assertions sat after the delta block (so a key defect was
|
|
239
|
+
named by the delta's message), then a finding's rule was being read back out of the key (so a key
|
|
240
|
+
defect was named by the PARSER's message), then the two warnings in the fixture were in the order
|
|
241
|
+
that hides a collapsing key. **And the third fix silently deleted two assertion blocks** — a
|
|
242
|
+
scripted reorder dropped the delta and case blocks, the harness stayed green, and the only signal
|
|
243
|
+
was the next mutation run reporting `key/case` as SURVIVED. Deleting assertions can never turn a
|
|
244
|
+
harness red; nothing but a mutation run can see it.
|
|
245
|
+
|
|
246
|
+
The two 2026-08-10 arrivals kept the streak, and one of them found a defect in the CHECKER rather
|
|
247
|
+
than in a test. `generated-code`'s first run left **three survivors**:
|
|
248
|
+
|
|
249
|
+
- **A quiet case that was quiet for the wrong reason.** The fixture proving `--seed` in an argparse
|
|
250
|
+
call is recognised drew with `rnd.sample(...)` off a local `random.Random(...)`, which the
|
|
251
|
+
draw pattern does not recognise as a draw at all. So the case passed because _nothing was
|
|
252
|
+
detected_, not because the seed was — neutering the seed rule changed no verdict. This is the
|
|
253
|
+
same shape as `delivered-pdf`'s window assertion satisfied by ordering, and it is invisible on
|
|
254
|
+
inspection: the fixture reads like a correct script, because it is one.
|
|
255
|
+
- **🔴 A dead flag in the checker.** The absolute-path rule is written case-sensitive on purpose —
|
|
256
|
+
without it an Express route `/users/export` matches `/Users/`. But the matcher was built with
|
|
257
|
+
`new RegExp(ABS.source + …, 'g')`, which takes the source and **throws the flags away**. Adding
|
|
258
|
+
an `i` to the pattern therefore changed nothing, i.e. a mutation that did not mutate, i.e. the
|
|
259
|
+
property the harness asserted lived nowhere the harness could reach. Fixed by threading
|
|
260
|
+
`ABS.flags` through, which makes the flag load-bearing for the first time.
|
|
261
|
+
- **A mutation that could not change the answer.** Loosening the READ side of the in-place rule
|
|
262
|
+
alone can never produce a finding, because the WRITE side still demands a quoted literal and no
|
|
263
|
+
operand can pair. That row now spans both arrays, and says so.
|
|
264
|
+
|
|
265
|
+
`artifact-coverage`'s 22 rows all killed on the first run, but four killed at a DIFFERENT assertion
|
|
266
|
+
than named, and two of those were the expectation string matching a fragment of prose that appears
|
|
267
|
+
in more than one message (`"that is the"`) — the `no-witness` defect from `ledger.mutations.mjs`,
|
|
268
|
+
committed again three days later. Two rows legitimately kill at an earlier case that asserts the
|
|
269
|
+
same property, and each says which, so a future reader does not read it as off-target. One harness
|
|
270
|
+
case was also restructured because two rows were landing on a shared assertion: a case that tests
|
|
271
|
+
an EXCLUSION should not assert the finding count, or a mutation of the header lands there and hides
|
|
272
|
+
which property actually broke.
|
|
273
|
+
|
|
274
|
+
The reverse leg's own first run on the live paper found a third defect, this time in the port:
|
|
275
|
+
matching a directory name with `includes()` acquitted a fixture directory called `a-2026-01-01` on
|
|
276
|
+
the strength of the letter "a" appearing in the prose. Substring matching in a cherry-picking
|
|
277
|
+
detector fails in the dangerous direction — it invents evidence that a result was mentioned — and
|
|
278
|
+
it is now whole-token with a minimum length.
|
|
279
|
+
|
|
280
|
+
`round-diff`'s first run was six rows wrong out of twenty-six, and one of those was a hole in the
|
|
281
|
+
checker rather than in the test: disabling the number-prefix rule in `covers()` changed **no**
|
|
282
|
+
verdict, because a substring fallback had been silently doing its work — which also meant
|
|
283
|
+
`touches: ["2"]` covered "Section 12" and every heading containing a 2, so a one-character
|
|
284
|
+
declaration authorised most of the paper while `overbroad-scope` stayed quiet because the _list_ was
|
|
285
|
+
short. Two more rows were the test's own fault in the two documented ways: a `find` string written
|
|
286
|
+
in its already-mutated form (a mutation that did not mutate), and a fixture that MOVED text between
|
|
287
|
+
body and appendix — which keeps the total constant and therefore cannot test the boundary at all.
|
|
288
|
+
|
|
289
|
+
`ledger.mutations.mjs` kept the streak: five survivors and two wrong-case kills on its first run,
|
|
290
|
+
and **every one was a defect in the assertion rather than in the checker.** Two matched a bare word
|
|
291
|
+
(`no-witness`, `input-missing`) that the CLI's usage block prints unconditionally, so the refusal
|
|
292
|
+
message could have said anything at all. The dead-report-path case asserted only a non-zero exit —
|
|
293
|
+
which the next line's `ENOENT` supplies once the check is deleted. The status view's reason
|
|
294
|
+
assertion matched the explanatory PARAGRAPH rather than the check's own row, so the reason could
|
|
295
|
+
have been stripped from every row and the test stayed green. And the legacy fixture recorded `PASS`
|
|
296
|
+
with `findings: 0`, which made "a retired acquittal carries nothing forward" true by accident —
|
|
297
|
+
real `PASS` rows carry counts (`sweep-design-space`, 8). One mutation also turned out to be
|
|
298
|
+
**inexpressible finely**: disabling only the no-report-path arm makes the next line `resolve()` a
|
|
299
|
+
null and throw, so the run goes red for a reason unrelated to the check. That row disables the whole
|
|
300
|
+
evidence contract instead and relies on assertion order, which is written down in the row's note.
|
|
301
|
+
|
|
302
|
+
The `--blocking` flag was found the same way and is worth naming separately, because it is not a
|
|
303
|
+
test defect: it shipped **documented in the usage text and in all 22 SKILL.md files while nothing in
|
|
304
|
+
the CLI parsed it**. Every skill instructing `--blocking` for its desk-reject case would have
|
|
305
|
+
recorded an ordinary advisory finding. That is the repository's oldest failure — a documented
|
|
306
|
+
mechanism nothing implements, the same shape as three hooks dead on arrival and a nudge dead for
|
|
307
|
+
twelve days — arriving _inside_ the refactor written to end it. Block 14 of `gates.harness.mjs` now
|
|
308
|
+
asserts every advertised flag from outside the process, in both directions.
|
|
309
|
+
|
|
310
|
+
⚠️ A mutations file refuses to start on a dirty working tree, because it rewrites the source it is
|
|
311
|
+
proving. Commit first. (`ledger.mutations.mjs` touches `status.mjs` and `run-mechanical.mjs`, which
|
|
312
|
+
another session may be editing; running it against a copy of `.claude/` with `CLAUDE_PROJECT_DIR`
|
|
313
|
+
pointed at the copy works and is how it was last verified.)
|
|
314
|
+
|
|
315
|
+
Five rules, each learned by getting it wrong here:
|
|
316
|
+
|
|
317
|
+
1. **Assertions run at module top level.** `vigiles test` imports the file and treats "did not
|
|
318
|
+
throw" as a pass. An exported `tests` object runs nothing and prints ✓ — verified on a file whose
|
|
319
|
+
only assertion was `assert.equal(1, 2)`.
|
|
320
|
+
2. **Fixtures live in a temp dir, and the ledger is redirected** with `PIPELINE_LEDGER` _before_
|
|
321
|
+
importing `ledger.mjs`. Save-and-restore is not isolation; it loses the race on the first crash.
|
|
322
|
+
3. **Run them by explicit path.** `npx vigiles test` with no arguments finds **nothing** here: its
|
|
323
|
+
glob does not descend into dot-directories. Every harness is therefore named explicitly in
|
|
324
|
+
`.github/workflows/paper-gates.yml` (`hooks` job) — one not named there does not run, and its
|
|
325
|
+
silence looks exactly like having nothing to say. Add the line in the same commit as the file.
|
|
326
|
+
4. **Prove non-vacuity by MUTATION, never by reading.** An assertion you cannot see fail is an
|
|
327
|
+
assertion you have not tested. `skill-checks.mjs` (then `skills.harness.mjs`) shipped its first draft with a record-block
|
|
328
|
+
slicer matching `^#` against an already-sliced string; `^` hit offset 0, every block came back as
|
|
329
|
+
the single character `#`, one assertion then fired on all 21 skills while its neighbour checked
|
|
330
|
+
nothing at all. Both were invisible on inspection and obvious on the first planted defect.
|
|
331
|
+
5. **A red harness is a finding, not a broken test — and the finding is allowed to close.**
|
|
332
|
+
**RED now (2026-08-07):** `skill-checks.mjs` (then `skills.harness.mjs`), on four skills (`tighten-paper`,
|
|
333
|
+
`analyze-sibling-paper`, `study-accepted-papers`, `find-venue`) whose frontmatter is not valid
|
|
334
|
+
YAML — an unquoted `description:` containing `": "`. Any consumer with a real YAML parser reads
|
|
335
|
+
**no** `allowed-tools` on those four, so they inherit every tool: the exact state the 2026-08-03
|
|
336
|
+
audit existed to end. Fix: quote the four values.
|
|
337
|
+
**CLOSED:** `gates.harness.mjs` was red on `structure.mjs` being silent in `--flags-only` about a
|
|
338
|
+
body section with no `carries:` note. `structure.mjs` now pushes that finding and the harness is
|
|
339
|
+
green — verified by running it, not by reading it. This paragraph asserted the opposite for a
|
|
340
|
+
while after the fix landed, which is the same lie the hand-maintained status table used to tell:
|
|
341
|
+
**when this README and a harness disagree, run the harness and believe it.**
|
|
342
|
+
The mechanics either way: a failing assertion is deferred to the end of its file so the rest of the
|
|
343
|
+
suite still runs, and every step in the CI job carries `if: always()` so a known-red step cannot
|
|
344
|
+
mask the harnesses after it.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// announce.mjs — a skill says out loud that it started, and the ledger remembers that it did.
|
|
2
|
+
//
|
|
3
|
+
// vigiles:local-by-design — takes a skill name as an argument and so names none itself, which reads
|
|
4
|
+
// generic; it is not. It writes THIS repo's ledger row format, consumed by `status.mjs`'s gate table
|
|
5
|
+
// and `ledger.mjs`'s verdict vocabulary, both of which are conventions of this knowledge base rather
|
|
6
|
+
// than of any product. Moving it would export a schema nobody else has.
|
|
7
|
+
//
|
|
8
|
+
// WHY. Repository owner's request, 2026-08-07: "lack observability into pipeline status and whether
|
|
9
|
+
// skills are actually being called — let's ensure each skill outputs something when activated".
|
|
10
|
+
//
|
|
11
|
+
// The failure behind that sentence is specific and it has happened three times in this repo. An
|
|
12
|
+
// advisory check CANNOT BE SEEN FAILING: silence is both its error state and its normal state.
|
|
13
|
+
// `paper-skills-nudge.sh` was dead for twelve days and was found only because it writes a throttle
|
|
14
|
+
// stamp whose mtime had frozen. Two inline hooks were dead for their entire existence and nobody
|
|
15
|
+
// noticed at all. And on 2026-08-06 a whole session of paper work ran with no skill invoked once.
|
|
16
|
+
//
|
|
17
|
+
// So: starting is an EVENT, and events get written down. A skill that announces leaves two traces —
|
|
18
|
+
// one the human sees immediately, one the ledger keeps. Their disagreement is itself information:
|
|
19
|
+
//
|
|
20
|
+
// announced, never recorded → the skill started and died, or the author abandoned it midway
|
|
21
|
+
// recorded, never announced → someone wrote a verdict without running the gate
|
|
22
|
+
// neither → it did not run, whatever any status file claims
|
|
23
|
+
//
|
|
24
|
+
// This is the "no trace means it did not work" rule the project already applies to hooks, made
|
|
25
|
+
// available to skills, which had no equivalent.
|
|
26
|
+
|
|
27
|
+
import { record, skillHash, inputHash } from "./ledger.mjs";
|
|
28
|
+
import { existsSync } from "node:fs";
|
|
29
|
+
import { resolve, basename } from "node:path";
|
|
30
|
+
import { isMain } from "./consumer.mjs";
|
|
31
|
+
|
|
32
|
+
export function announce(skill, paperDir) {
|
|
33
|
+
const dir = resolve(paperDir || ".");
|
|
34
|
+
const has = existsSync(dir);
|
|
35
|
+
const input = has ? inputHash(dir) : null;
|
|
36
|
+
|
|
37
|
+
const lines = [
|
|
38
|
+
`▶ ${skill} — ${has ? basename(dir) : "no paper dir"}`,
|
|
39
|
+
` paper ${input ? input.slice(0, 8) : "(none)"} · skill ${(skillHash(skill) || "????????").slice(0, 8)}`,
|
|
40
|
+
];
|
|
41
|
+
// stderr, not stdout: the skill's own output is the product, and a banner that pollutes a pipe
|
|
42
|
+
// is a banner someone will remove.
|
|
43
|
+
console.error(lines.join("\n"));
|
|
44
|
+
|
|
45
|
+
// An abstention with reason `started`, not an ERROR row. Before 2026-08-10 this wrote `ERROR`,
|
|
46
|
+
// which meant "the check errored" — so every announce looked like a crash until its terminal row
|
|
47
|
+
// landed, and `started` and `crashed` were the same word. Both are abstentions and neither is a
|
|
48
|
+
// judgement about the paper; the reason is what tells them apart.
|
|
49
|
+
record({
|
|
50
|
+
skill,
|
|
51
|
+
check: skill,
|
|
52
|
+
paper: dir,
|
|
53
|
+
kind: "ABSTAINED",
|
|
54
|
+
reason: "started",
|
|
55
|
+
note: "no verdict recorded yet",
|
|
56
|
+
});
|
|
57
|
+
return { skill, paper: basename(dir), input };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (isMain(import.meta.url)) {
|
|
61
|
+
const [skill, paperDir] = process.argv.slice(2);
|
|
62
|
+
if (!skill) {
|
|
63
|
+
console.error("usage: announce.mjs <skill-name> [paper-dir]");
|
|
64
|
+
process.exit(2);
|
|
65
|
+
}
|
|
66
|
+
announce(skill, paperDir);
|
|
67
|
+
}
|