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,371 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 🔴 2026-08-26 — ALL SIXTEEN CHECKS LEFT THIS FILE. THIS FILE IS NOW ONLY AN INVENTORY.
|
|
4
|
+
*
|
|
5
|
+
* Fourteen became `@eslint/markdown` rules in `eslint-rules/paper-structure.mjs`
|
|
6
|
+
* (twelve rules — two of them have two `messageId`s each), two were deleted as duplicates of things
|
|
7
|
+
* already moved: the share of the free half duplicated `paper/appendix-ratio`, and "a numbered
|
|
8
|
+
* section without `carries:`" duplicated `paper/section-justification`, both from
|
|
9
|
+
* `eslint-rules/paper-prose.mjs`.
|
|
10
|
+
* Parity was proven BEFORE the removal: 29 inputs (a real paper + 22 "one defect at a time" fixtures
|
|
11
|
+
* + 6 edge cases), 0 divergences, every number matched byte for byte. The analysis and the
|
|
12
|
+
* classification table are in the author's private notes
|
|
13
|
+
* (`papers/research/2026-08-26-klassifikatsiya-structure.md`).
|
|
14
|
+
*
|
|
15
|
+
* WHAT IS LEFT AND WHY IT IS NOT A CHECK. The inventory is a table "own / total / share / section"
|
|
16
|
+
* with a `carries:` note under each row. It has no threshold and produces no findings: it is read by
|
|
17
|
+
* a HUMAN in the `tighten-paper` skill, to compare a section's weight with what the section claims
|
|
18
|
+
* about itself. That comparison was exactly the author's idea ("print the claim next to the
|
|
19
|
+
* weight"), and a linter cannot express it — a finding has nowhere to put a whole table.
|
|
20
|
+
*
|
|
21
|
+
* ⚠️ `--flags-only` IS KEPT, BUT IT ALWAYS STAYS SILENT AND ALWAYS EXITS 0.
|
|
22
|
+
*
|
|
23
|
+
* ── 2026-08-26, EVENING: BOTH NAMED CONSUMERS ARE CLOSED, AND A THIRD ONE TURNED UP ────────
|
|
24
|
+
*
|
|
25
|
+
* The paragraph above named two callers and said that until they are converted, the ledger writes
|
|
26
|
+
* "no findings" BY SILENCE. That was true and measured: on `the reference paper/paper.md`
|
|
27
|
+
* `--flags-only` printed 0 lines, while the same bytes through ESLint give **6 findings**
|
|
28
|
+
* (`subsection-size` ×2 · `section-lead` · `free-section-size` ×2 · `block-ungraded`).
|
|
29
|
+
*
|
|
30
|
+
* `.github/workflows/paper-gates.yml`, the `structure` step → REMOVED. An `eslint` step already
|
|
31
|
+
* stood next to it, running the same rules over the same glob; a measurement showed a
|
|
32
|
+
* byte-identical list of files. The full justification is in the comment where the removed
|
|
33
|
+
* step used to be.
|
|
34
|
+
* `run-mechanical.mjs`, the `tighten-paper/structure` line → CONVERTED to ESLint
|
|
35
|
+
* (`read: 'eslint'`, filtered by the rules from `eslint-rules/paper-structure.mjs`). Not
|
|
36
|
+
* deleted: ESLint writes nothing into the ledger, and the ledger is the only place where the
|
|
37
|
+
* fact "somebody looked at this paper's structure on these bytes" is stored with a hash of the
|
|
38
|
+
* input.
|
|
39
|
+
*
|
|
40
|
+
* 🔴 A THIRD CALLER THE BANNER DID NOT KNOW ABOUT: `.githooks/pre-commit`. It printed nothing and
|
|
41
|
+
* swallowed the exit code through `|| true`. The lesson is exactly the one already recorded in
|
|
42
|
+
* `CLAUDE.md` about the four forms of a reference: the banner listed the callers FROM MEMORY, while
|
|
43
|
+
* only a grep for the flag's name gives the full list.
|
|
44
|
+
*
|
|
45
|
+
* ✅ CLOSED 2026-08-26. The call was converted to ESLint through `.githooks/structure-gate.mjs`
|
|
46
|
+
* (the rule names are DERIVED from `eslint-rules/paper-structure.mjs`, not rewritten as a list),
|
|
47
|
+
* the `|| true` was removed, and the exit codes were separated: 0 — it judged · 2 — it did NOT
|
|
48
|
+
* judge, the commit is rejected · 3 — no `node_modules/.bin/eslint`, loudly and without blocking.
|
|
49
|
+
* The test is `.githooks/structure-gate.harness.mjs`, five mutations, each one with proof that the
|
|
50
|
+
* patch applied. Measured: on the same bytes the old call gave 0 findings and 0 bytes of output,
|
|
51
|
+
* ESLint with the same rules — **6**.
|
|
52
|
+
*
|
|
53
|
+
* ⚠️ And the COMPLETENESS check of the list, which was missing last time: in `pre-commit` the idiom
|
|
54
|
+
* `--flags-only … || true` occurs FOUR times. All of them were run, 26.08:
|
|
55
|
+
* `prose-lint.mjs` — alive (2 findings, exit 1)
|
|
56
|
+
* `artifact-coverage.mjs` — alive (6 findings, exit 1)
|
|
57
|
+
* `population-map.mjs` — silent LEGITIMATELY (line 144: in this mode a clean run prints
|
|
58
|
+
* nothing; on a real paper the registry adds up)
|
|
59
|
+
* `structure.mjs` — was dead, closed above
|
|
60
|
+
* A list without the third column is an enumeration, not a check.
|
|
61
|
+
*
|
|
62
|
+
* ⚠️ THE FLAG IS NOT DELETED AND MUST NOT BE DELETED WITHOUT EDITING OTHER PEOPLE'S FILES: its
|
|
63
|
+
* silence is pinned by two asserts — `eslint-rules/paper-structure.harness.mjs` (≈550) and
|
|
64
|
+
* `.claude/skills/paper-pipeline/scripts/gates.harness.mjs` (block 1). Both fail if this mode prints
|
|
65
|
+
* something again OR exits with a non-zero code. Printing a "see eslint" pointer here is still
|
|
66
|
+
* forbidden: `read: 'flags'` at any remaining consumer would turn it into a permanent false finding.
|
|
67
|
+
*/
|
|
68
|
+
/**
|
|
69
|
+
* structure.mjs — the mechanical leg of tighten-paper.
|
|
70
|
+
*
|
|
71
|
+
* node structure.mjs <paper.md> the whole paper
|
|
72
|
+
* node structure.mjs <paper.md> --section=3 one section and its subsections
|
|
73
|
+
* node structure.mjs <paper.md> --flags-only ⚠️ ALWAYS SILENT (see the banner above)
|
|
74
|
+
*
|
|
75
|
+
* WHY THIS EXISTS, and why it is NOT in prose-lint. Prose-lint judges sentences. Structure is a
|
|
76
|
+
* different question and was homeless: `tighten-paper` is a skill — a model reading and judging —
|
|
77
|
+
* with no script under it, so nothing mechanical ever looked at the document's SHAPE. Author,
|
|
78
|
+
* 2026-08-05: *"I thought prose was about prose, and structure is something else entirely"*, and
|
|
79
|
+
* before that *"the relevant skill needs to do the same thing for the whole paper by default, or for
|
|
80
|
+
* one section"* (both said in Russian).
|
|
81
|
+
*
|
|
82
|
+
* 🔴 The failure that motivated it. Asked whether the contribution was short-changed, word counts
|
|
83
|
+
* answered "no" — section 3 had 38% more words than related work — and that answer was true and
|
|
84
|
+
* useless. The shape showed the real defect: three top-level sections owned 0, 28 and 38 words
|
|
85
|
+
* before their first subsection. They were dividers wearing section numbers, and a reader met a
|
|
86
|
+
* heading and fell straight through it into a subsection. No metric in the toolchain asked that
|
|
87
|
+
* question, because every metric was per-sentence or per-document and none was per-SECTION.
|
|
88
|
+
*
|
|
89
|
+
* What this prints is an inventory first and findings second, deliberately. The judgement — is this
|
|
90
|
+
* the right order, does this heading name a thing, does this section earn its place — stays with the
|
|
91
|
+
* skill, because it is judgement. Only the mechanically decidable part is compiled.
|
|
92
|
+
*/
|
|
93
|
+
import { readFileSync } from "node:fs";
|
|
94
|
+
import {
|
|
95
|
+
headings as mdHeadings,
|
|
96
|
+
splitSections,
|
|
97
|
+
stripFences,
|
|
98
|
+
requireMarkdown,
|
|
99
|
+
stripFrontmatter,
|
|
100
|
+
} from "../../lib/markdown.mjs";
|
|
101
|
+
|
|
102
|
+
// Markup is parsed with a PARSER (`CLAUDE.md`, 2026-08-11). We fail rather than degrade: without a
|
|
103
|
+
// parser not one section would be found in the paper, `--flags-only` would return 0 findings and
|
|
104
|
+
// code 0 — that is, "the structure is fine" about a document nobody read. This file is precisely
|
|
105
|
+
// about the fact that a metric without a threshold and a check without observability silently mean
|
|
106
|
+
// nothing; a confident zero here would be the third instance of the same error in one file.
|
|
107
|
+
requireMarkdown();
|
|
108
|
+
|
|
109
|
+
// Headings of depth 2-3 are what this file calls a "section" and a "subsection". This used to be
|
|
110
|
+
// written as `#{2,3}`, that is, as a statement about the NUMBER OF HASHES: `#{2,3}` counted any such
|
|
111
|
+
// line as a section, including a line inside a ``` block. The papers in this repo quote other
|
|
112
|
+
// people's markup in chunks, so "an extra section out of a quotation" is not a hypothesis.
|
|
113
|
+
const SECTION_MIN = 2,
|
|
114
|
+
SECTION_MAX = 3;
|
|
115
|
+
|
|
116
|
+
// The thresholds (60 words of lead · 350 words of a subsection · 100% of the free half · 1.0
|
|
117
|
+
// against the heaviest section) left together with the checks — they live as rule options in
|
|
118
|
+
// `eslint-rules/paper-structure.mjs`, and the CALIBRATION of each (why exactly 60 and exactly 350,
|
|
119
|
+
// and on which corpus it was measured) moved there as comments. They must not be kept here: a number
|
|
120
|
+
// without a threshold is prose, and a threshold without a check is a second source of truth that
|
|
121
|
+
// will drift away from the first.
|
|
122
|
+
|
|
123
|
+
const args = process.argv.slice(2);
|
|
124
|
+
const file = args.find((a) => !a.startsWith("--"));
|
|
125
|
+
if (!file) {
|
|
126
|
+
console.error(
|
|
127
|
+
"usage: node structure.mjs <paper.md> [--section=N] (--flags-only is accepted and stays silent)",
|
|
128
|
+
);
|
|
129
|
+
process.exit(0);
|
|
130
|
+
}
|
|
131
|
+
const only =
|
|
132
|
+
(args.find((a) => a.startsWith("--section=")) || "").split("=")[1] || null;
|
|
133
|
+
const flagsOnly = args.includes("--flags-only");
|
|
134
|
+
|
|
135
|
+
let t = readFileSync(file, "utf8");
|
|
136
|
+
// `stripFences()` instead of `/^```[\s\S]*?^```/gm`: with an ODD number of fences that expression
|
|
137
|
+
// glued the end of one block to the beginning of the next and cut out the prose between them — and
|
|
138
|
+
// prose cut out here means an understated section weight, that is, a finding of "this is a divider,
|
|
139
|
+
// not a section" on a section that is perfectly fine.
|
|
140
|
+
// `blank: true` — the block's lines become empty rather than disappear: a code block is a paragraph
|
|
141
|
+
// boundary, and removing the lines would glue neighbouring paragraphs together. Here it does not
|
|
142
|
+
// affect the numbers (words and headings are counted the same either way), but the form must match
|
|
143
|
+
// `prose-lint`, where it does.
|
|
144
|
+
t = stripFences(
|
|
145
|
+
stripFrontmatter(t) // frontmatter
|
|
146
|
+
.replace(/<!--[\s\S]*?-->/g, ""), // working comments
|
|
147
|
+
{ blank: true },
|
|
148
|
+
);
|
|
149
|
+
// Body and FREE SECTIONS are measured separately. They hold different bars — the body has a page
|
|
150
|
+
// limit and the free sections do not — but "does not count against the limit" is not "unmeasured".
|
|
151
|
+
// 🔴 Until 2026-08-05 this script cut at `## Limitations` and never looked further, so every answer
|
|
152
|
+
// it gave to "do all the sections survive?" silently excluded Limitations, Ethics and every
|
|
153
|
+
// appendix. The author asked the question about those exact sections and the tool could not have
|
|
154
|
+
// answered it. A free section is where unbudgeted prose accumulates precisely because nothing
|
|
155
|
+
// prices it.
|
|
156
|
+
// 2026-08-11: the three boundaries (free sections · bibliography · appendices) are found by the
|
|
157
|
+
// parser. The `-1 / offset` convention and the `> 0`, `< 0` comparisons are kept as they were — the
|
|
158
|
+
// previous expressions did not treat a heading on the very first line of the file as a boundary
|
|
159
|
+
// either, and changing that here would mean introducing new behaviour under the guise of moving to a
|
|
160
|
+
// parser.
|
|
161
|
+
const headOffset = (re) => {
|
|
162
|
+
const h = mdHeadings(t).find((x) => x.depth === 2 && re.test(x.text));
|
|
163
|
+
return h ? h.offset : -1;
|
|
164
|
+
};
|
|
165
|
+
const cut = headOffset(/^(Limitations|References|Ethical)/u);
|
|
166
|
+
const body = cut > 0 ? t.slice(0, cut) : t;
|
|
167
|
+
const refs = headOffset(/^References/u);
|
|
168
|
+
const appx = headOffset(/^Appendix/u);
|
|
169
|
+
const freeText =
|
|
170
|
+
cut > 0
|
|
171
|
+
? refs > cut
|
|
172
|
+
? t.slice(cut, refs) + t.slice(appx < 0 ? t.length : appx)
|
|
173
|
+
: t.slice(cut)
|
|
174
|
+
: "";
|
|
175
|
+
|
|
176
|
+
const wordsOf = (s) => (s.match(/[A-Za-z0-9%.'’-]+/g) || []).length;
|
|
177
|
+
|
|
178
|
+
// 🔴 `carries:` notes are read back OUT, not just written. The convention puts a working comment
|
|
179
|
+
// above each heading saying what that section carries and whether it is justified — and until
|
|
180
|
+
// 2026-08-05 nothing ever read one. They were write-only: authored during a tighten pass, then
|
|
181
|
+
// invisible to every later pass, including the passes deciding whether a section earns its place.
|
|
182
|
+
// Author's idea, and it is the right one: print the claim beside the weight, so "this section is
|
|
183
|
+
// 564 words" and "this section claims to carry X" are read in the same glance.
|
|
184
|
+
// 🔴 A justification with no SCALE and no VERDICT cannot decide anything, and for a week none of
|
|
185
|
+
// these notes ever concluded "cut me". Author, 2026-08-06 (both quotes said in Russian): *"reading
|
|
186
|
+
// the paper you get the impression — damn, is this section really needed"*, and *"every appendix
|
|
187
|
+
// section must also have a comment justifying itself, with a score from 0 to 10 and a note on
|
|
188
|
+
// whether it should be cut / moved / shortened"*.
|
|
189
|
+
//
|
|
190
|
+
// The defect is structural, not laziness. A free-text note is a section arguing its own case, and a
|
|
191
|
+
// section always wins that argument — there is no scale to rank it against its neighbours and no
|
|
192
|
+
// slot in which the answer can come out negative. So the note now carries three things:
|
|
193
|
+
//
|
|
194
|
+
// score: 0-10, how much a REVIEWER'S DECISION depends on this. Not how interesting it is.
|
|
195
|
+
// verdict: KEEP | SHORTEN | MOVE | MERGE | CUT — what to DO, in a fixed vocabulary
|
|
196
|
+
// carries: what it holds, as before
|
|
197
|
+
//
|
|
198
|
+
// The pairing is what makes it work: a low score with a KEEP verdict is a visible contradiction,
|
|
199
|
+
// and the script reports it. Free text could never contradict itself.
|
|
200
|
+
// The colon is optional, because the skill's own documented example writes `· score 7/10 ·` and the
|
|
201
|
+
// first version of this regex demanded `score:`. The spec and the check disagreed, so the checker
|
|
202
|
+
// reported "no score:" on notes that carried a perfectly good score — the same defect class this
|
|
203
|
+
// paper is about, committed between a skill and its own compiled leg. Caught 2026-08-06 within
|
|
204
|
+
// minutes, because the check was running.
|
|
205
|
+
const NOTE_RE = /score:?\s*(\d{1,2})\s*(?:\/\s*10)?/i;
|
|
206
|
+
const VERDICT_RE = /verdict:\s*(KEEP|SHORTEN|MOVE|MERGE|CUT)\b/i;
|
|
207
|
+
|
|
208
|
+
const rawSections = stripFrontmatter(readFileSync(file, "utf8"));
|
|
209
|
+
const carriesFor = new Map();
|
|
210
|
+
const scoreFor = new Map();
|
|
211
|
+
const verdictFor = new Map();
|
|
212
|
+
// `(?:(?!-->)[\s\S])*?` and not `[\s\S]*?`: a lazy match will happily jump ACROSS a closing `-->`
|
|
213
|
+
// when the comment it belongs to is not followed by a heading, swallowing the next comment whole and
|
|
214
|
+
// attributing its note to the wrong section. Caught 2026-08-05 within a minute of the check existing,
|
|
215
|
+
// which is the same reason the check exists.
|
|
216
|
+
for (const m of rawSections.matchAll(
|
|
217
|
+
/<!--((?:(?!-->)[\s\S])*?)-->\s*\n(#{2,3}) (.+)/g,
|
|
218
|
+
)) {
|
|
219
|
+
const note = m[1].replace(/\s+/g, " ");
|
|
220
|
+
const c = note.match(
|
|
221
|
+
/carries:\s*(.+?)(?:·|justified:|note:|costs:|verdict:|because:|-->|$)/i,
|
|
222
|
+
);
|
|
223
|
+
if (c) carriesFor.set(m[3].trim(), c[1].trim());
|
|
224
|
+
const s = note.match(NOTE_RE);
|
|
225
|
+
if (s) scoreFor.set(m[3].trim(), Number(s[1]));
|
|
226
|
+
const v = note.match(VERDICT_RE);
|
|
227
|
+
if (v) verdictFor.set(m[3].trim(), v[1].toUpperCase());
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// The notes on the appendix's bold lead-ins (`score:`/`verdict:` above a `**Block.**`) are now read
|
|
231
|
+
// by `paper/block-ungraded` · `paper/block-note` · `paper/block-verdict`. The inventory does not
|
|
232
|
+
// print them, so they are not parsed here.
|
|
233
|
+
|
|
234
|
+
const outline = [];
|
|
235
|
+
for (const chunk of splitSections(body, {
|
|
236
|
+
min: SECTION_MIN,
|
|
237
|
+
max: SECTION_MAX,
|
|
238
|
+
})) {
|
|
239
|
+
if (!chunk.heading) continue; // the preamble before the first heading is not a section
|
|
240
|
+
const title = chunk.heading.text;
|
|
241
|
+
outline.push({
|
|
242
|
+
// `chunk.body` is the chunk ALREADY without the heading line, so the previous
|
|
243
|
+
// `chunk.replace(/^#{2,3} .+$/m, '')` is no longer needed: removing the heading line is part of
|
|
244
|
+
// the splitting, not a separate operation over the text.
|
|
245
|
+
level: chunk.heading.depth,
|
|
246
|
+
title,
|
|
247
|
+
words: wordsOf(chunk.body),
|
|
248
|
+
carries: carriesFor.get(title) ?? null,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Own words + everything underneath, so a section's true weight is visible next to its lead.
|
|
253
|
+
for (let i = 0; i < outline.length; i++) {
|
|
254
|
+
let total = outline[i].words;
|
|
255
|
+
if (outline[i].level === 2)
|
|
256
|
+
for (let j = i + 1; j < outline.length && outline[j].level === 3; j++)
|
|
257
|
+
total += outline[j].words;
|
|
258
|
+
outline[i].total = total;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const inScope = (row, i) => {
|
|
262
|
+
if (!only) return true;
|
|
263
|
+
const num = (s) => (s.match(/^(\d+)/) || [])[1];
|
|
264
|
+
if (row.level === 2) return num(row.title) === only;
|
|
265
|
+
for (let j = i; j >= 0; j--)
|
|
266
|
+
if (outline[j].level === 2) return num(outline[j].title) === only;
|
|
267
|
+
return false;
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const bodyTotal = outline
|
|
271
|
+
.filter((r) => r.level === 2)
|
|
272
|
+
.reduce((a, r) => a + r.total, 0);
|
|
273
|
+
|
|
274
|
+
// The free sections are inventoried HERE, above the findings loop, and not down in the printing
|
|
275
|
+
// block where they used to live. That placement was the whole defect: measured in a branch that
|
|
276
|
+
// only the human-readable mode reaches, the ratio could never become a finding, so `--flags-only`
|
|
277
|
+
// — the mode hooks and CI run — reported a clean document while the unpriced half stood at 165%.
|
|
278
|
+
const freeSections = [];
|
|
279
|
+
for (const chunk of splitSections(freeText || "", {
|
|
280
|
+
min: SECTION_MIN,
|
|
281
|
+
max: SECTION_MAX,
|
|
282
|
+
})) {
|
|
283
|
+
if (!chunk.heading) continue;
|
|
284
|
+
freeSections.push({
|
|
285
|
+
level: chunk.heading.depth,
|
|
286
|
+
title: chunk.heading.text,
|
|
287
|
+
words: wordsOf(chunk.body),
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
const freeTotal = freeSections.reduce((a, r) => a + r.words, 0);
|
|
291
|
+
|
|
292
|
+
// 🔴 THERE WERE SIXTEEN FINDINGS HERE — see the banner in the header. There is DELIBERATELY not a
|
|
293
|
+
// single check left in this file: two sources of truth about one fact drift apart on the very first
|
|
294
|
+
// edit, and there is nothing to settle the question of which one is right.
|
|
295
|
+
|
|
296
|
+
if (flagsOnly) {
|
|
297
|
+
// Silence and exit code 0 — ALWAYS. Deliberately not one line on stdout/stderr:
|
|
298
|
+
// `run-mechanical.mjs` reads this mode as `read: 'flags'`, that is, "non-empty output = a
|
|
299
|
+
// finding", and a pointer saying "the checks have moved" would turn into a permanent false finding
|
|
300
|
+
// in the ledger. The limitation of this mode is named in the banner at the top; it is cured by
|
|
301
|
+
// converting both callers to `npx eslint`, not here.
|
|
302
|
+
process.exit(0);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
console.log(`\n=== ${file.split("/").pop()} — outline, in order ===`);
|
|
306
|
+
console.log(
|
|
307
|
+
`${"own".padStart(6)} ${"total".padStart(6)} ${"share".padStart(6)} section`,
|
|
308
|
+
);
|
|
309
|
+
for (const [i, r] of outline.entries()) {
|
|
310
|
+
if (!inScope(r, i)) continue;
|
|
311
|
+
const share =
|
|
312
|
+
r.level === 2
|
|
313
|
+
? `${((100 * r.total) / bodyTotal).toFixed(1)}%`.padStart(6)
|
|
314
|
+
: " ".repeat(6);
|
|
315
|
+
console.log(
|
|
316
|
+
`${String(r.words).padStart(6)} ${String(r.level === 2 ? r.total : "").padStart(6)} ${share} ` +
|
|
317
|
+
`${r.level === 3 ? " " : ""}${r.title}`,
|
|
318
|
+
);
|
|
319
|
+
// The section's own claim about itself, printed where its weight is read. A section whose note
|
|
320
|
+
// does not survive being read next to its size is a section to cut, and that comparison was
|
|
321
|
+
// impossible to make until both appeared in one place.
|
|
322
|
+
if (r.carries) {
|
|
323
|
+
const s = scoreFor.get(r.title),
|
|
324
|
+
v = verdictFor.get(r.title);
|
|
325
|
+
const tag =
|
|
326
|
+
s === undefined && !v ? "" : `[${s ?? "?"}/10 ${v ?? "NO VERDICT"}] `;
|
|
327
|
+
console.log(
|
|
328
|
+
`${" ".repeat(21)}${r.level === 3 ? " " : ""}↳ ${tag}carries: ${r.carries.slice(0, 80)}`,
|
|
329
|
+
);
|
|
330
|
+
} else if (/^\d/.test(r.title))
|
|
331
|
+
console.log(
|
|
332
|
+
`${" ".repeat(21)}${r.level === 3 ? " " : ""}↳ 🔴 no carries: note — nobody has said why this section is here`,
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
console.log(
|
|
336
|
+
`\nbody total ${bodyTotal} words across ${outline.filter((r) => r.level === 2).length} sections`,
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
// The free sections, measured against the body they hang off. A paper whose unbudgeted prose
|
|
340
|
+
// outweighs its budgeted prose is telling you where its author was allowed to keep writing.
|
|
341
|
+
if (freeSections.length) {
|
|
342
|
+
console.log(`\n=== outside the page limit ===`);
|
|
343
|
+
for (const f of freeSections) {
|
|
344
|
+
console.log(
|
|
345
|
+
`${String(f.words).padStart(6)} ${" ".repeat(14)}${f.level === 3 ? " " : ""}${f.title}`,
|
|
346
|
+
);
|
|
347
|
+
const c = carriesFor.get(f.title);
|
|
348
|
+
if (c)
|
|
349
|
+
console.log(
|
|
350
|
+
`${" ".repeat(21)}${f.level === 3 ? " " : ""}↳ carries: ${c.slice(0, 96)}`,
|
|
351
|
+
);
|
|
352
|
+
else if (f.level === 2)
|
|
353
|
+
console.log(
|
|
354
|
+
`${" ".repeat(21)}↳ 🔴 no carries: note — and no page limit forcing the question`,
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
console.log(
|
|
358
|
+
`\nfree total ${freeTotal} words = ${((100 * freeTotal) / bodyTotal).toFixed(0)}% of the body`,
|
|
359
|
+
);
|
|
360
|
+
if (freeTotal > bodyTotal)
|
|
361
|
+
console.log(
|
|
362
|
+
`🔴 The unbudgeted half is LARGER than the paper. Nothing prices these sections, which\n` +
|
|
363
|
+
` is exactly why prose settles here. Ask of each: would a reviewer miss it?`,
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
console.log(
|
|
368
|
+
"\nThe rest is judgement and stays with the skill: is this the ORDER a stranger needs, does each\n" +
|
|
369
|
+
"heading name a thing, and does each section earn its place. Read the headings as a set — a\n" +
|
|
370
|
+
"stranger should be able to reconstruct the argument from them alone.",
|
|
371
|
+
);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tighten-paper — the PAID tier: does this skill's description actually fire?
|
|
3
|
+
*
|
|
4
|
+
* COLOCATED ON PURPOSE (vigiles decides coverage by placement as of 2026-08-11).
|
|
5
|
+
* The prompts live in `.claude/lib/skill-trigger-cases.mjs` so all 21 cases
|
|
6
|
+
* are reviewed as one table where collisions between siblings are visible;
|
|
7
|
+
* copying them here would recreate the drift that rule exists to prevent.
|
|
8
|
+
*
|
|
9
|
+
* Measures recall (fires on its own territory) AND precision (stays quiet on a
|
|
10
|
+
* colliding sibling's territory), against the REAL `.claude` harness so the skill
|
|
11
|
+
* competes with every other installed description — an isolated run overstates
|
|
12
|
+
* recall and understates false positives.
|
|
13
|
+
*
|
|
14
|
+
* Costs money; not CI.
|
|
15
|
+
* node .claude/skills/tighten-paper/tighten-paper.eval.mjs [trials]
|
|
16
|
+
*/
|
|
17
|
+
import { runSkillTriggerEval } from "../../lib/skill-eval-kit.mjs";
|
|
18
|
+
|
|
19
|
+
await runSkillTriggerEval("tighten-paper");
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tighten-paper — the free, deterministic tier. No model, no network.
|
|
3
|
+
*
|
|
4
|
+
* COLOCATED ON PURPOSE. vigiles decides coverage by PLACEMENT as of 2026-08-11:
|
|
5
|
+
* a test that merely names a surface no longer counts, because that tier was
|
|
6
|
+
* crediting surfaces nothing touched. So each skill needs a file inside its own
|
|
7
|
+
* directory — this one.
|
|
8
|
+
*
|
|
9
|
+
* The assertions live in `.claude/lib/skill-checks.mjs` and are CALLED here with
|
|
10
|
+
* this skill's name. They are not copied: 22 copies of the same checks is the drift that
|
|
11
|
+
* module exists to avoid. (Until 2026-08-11 this was an env-var side channel into a
|
|
12
|
+
* 614-line file named after no surface; it is a function call now.)
|
|
13
|
+
*
|
|
14
|
+
* What this proves: this skill's frontmatter parses as strict YAML, its declared
|
|
15
|
+
* tool contract is sane, its pipeline wiring points at scripts that exist, and it
|
|
16
|
+
* announces/records under ITS OWN identity rather than a sibling's.
|
|
17
|
+
*
|
|
18
|
+
* What it does NOT prove: that the skill fires, or that its guidance produces a
|
|
19
|
+
* good result. Those need a real model — see `tighten-paper.eval.mjs`.
|
|
20
|
+
*/
|
|
21
|
+
import { checkSkill } from "../../lib/skill-checks.mjs";
|
|
22
|
+
|
|
23
|
+
await checkSkill("tighten-paper");
|