paperlint 3.1.0 → 3.1.2
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/workflows/ci.yml +28 -41
- package/.github/workflows/dependabot-automerge.yml +1 -1
- package/CLAUDE.md +21 -105
- package/CONTRIBUTING.md +23 -25
- package/README.md +11 -7
- package/bin/paperlint.test.mjs +45 -0
- package/dist/adapters/node/files.io.d.ts +2 -0
- package/dist/adapters/node/files.io.d.ts.map +1 -1
- package/dist/adapters/node/files.io.js +3 -2
- package/dist/adapters/node/files.io.js.map +1 -1
- package/dist/adapters/pdfjs/fill.d.ts +15 -4
- package/dist/adapters/pdfjs/fill.d.ts.map +1 -1
- package/dist/adapters/pdfjs/fill.js +31 -21
- package/dist/adapters/pdfjs/fill.js.map +1 -1
- package/dist/adapters/references/clock.io.d.ts +5 -0
- package/dist/adapters/references/clock.io.d.ts.map +1 -0
- package/dist/adapters/references/clock.io.js +7 -0
- package/dist/adapters/references/clock.io.js.map +1 -0
- package/dist/adapters/references/index.d.ts +24 -2
- package/dist/adapters/references/index.d.ts.map +1 -1
- package/dist/adapters/references/index.js +131 -12
- package/dist/adapters/references/index.js.map +1 -1
- package/dist/adapters/references/pool.d.ts +8 -0
- package/dist/adapters/references/pool.d.ts.map +1 -0
- package/dist/adapters/references/pool.js +19 -0
- package/dist/adapters/references/pool.js.map +1 -0
- package/dist/build.d.ts +14 -2
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +17 -11
- package/dist/build.js.map +1 -1
- package/dist/cli.d.ts +27 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +71 -55
- package/dist/cli.js.map +1 -1
- package/dist/doctor.d.ts +27 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +4 -3
- package/dist/doctor.js.map +1 -1
- package/dist/domain/lookup-cache.d.ts +75 -0
- package/dist/domain/lookup-cache.d.ts.map +1 -0
- package/dist/domain/lookup-cache.js +137 -0
- package/dist/domain/lookup-cache.js.map +1 -0
- package/dist/domain/result.d.ts +0 -2
- package/dist/domain/result.d.ts.map +1 -1
- package/dist/domain/result.js +0 -4
- package/dist/domain/result.js.map +1 -1
- package/dist/hooks-settings.d.ts +8 -5
- package/dist/hooks-settings.d.ts.map +1 -1
- package/dist/hooks-settings.js +32 -32
- package/dist/hooks-settings.js.map +1 -1
- package/dist/init.d.ts +8 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +3 -3
- package/dist/init.js.map +1 -1
- package/dist/latex-log.d.ts +0 -2
- package/dist/latex-log.d.ts.map +1 -1
- package/dist/latex-log.js +7 -13
- package/dist/latex-log.js.map +1 -1
- package/dist/link-skills.d.ts +15 -2
- package/dist/link-skills.d.ts.map +1 -1
- package/dist/link-skills.js +36 -20
- package/dist/link-skills.js.map +1 -1
- package/dist/paper-files.d.ts.map +1 -1
- package/dist/paper-files.js +6 -4
- package/dist/paper-files.js.map +1 -1
- package/dist/pdf-facts.d.ts +11 -0
- package/dist/pdf-facts.d.ts.map +1 -1
- package/dist/pdf-facts.js +16 -18
- package/dist/pdf-facts.js.map +1 -1
- package/dist/pdf-geometry.js +1 -1
- package/dist/pdf-geometry.js.map +1 -1
- package/dist/ports/check-references.d.ts +12 -2
- package/dist/ports/check-references.d.ts.map +1 -1
- package/dist/ports/check-references.js +0 -5
- package/dist/ports/check-references.js.map +1 -1
- package/dist/presets.d.ts +2 -0
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +3 -1
- package/dist/presets.js.map +1 -1
- package/dist/references.d.ts +11 -3
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +63 -15
- package/dist/references.js.map +1 -1
- package/dist/tex-requirements.d.ts +8 -0
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +6 -5
- package/dist/tex-requirements.js.map +1 -1
- package/dist/toolchain.d.ts +3 -2
- package/dist/toolchain.d.ts.map +1 -1
- package/dist/toolchain.js +4 -5
- package/dist/toolchain.js.map +1 -1
- package/dist/venue-rules.d.ts.map +1 -1
- package/dist/venue-rules.js +11 -9
- package/dist/venue-rules.js.map +1 -1
- package/docs/prior-art/README.md +11 -10
- package/docs/prior-art/citation-checkers.md +174 -0
- package/docs/prior-art/test-tooling.md +7 -30
- package/docs/references.md +126 -0
- package/docs/testing.md +118 -0
- package/docs/toolchain.md +6 -4
- package/eslint-rules/install-path-literals.harness.mjs +17 -1
- package/eslint-rules/latex-language.harness.mjs +3 -3
- package/eslint-rules/latex-language.mjs +12 -9
- package/eslint-rules/latex-language.test.mjs +200 -0
- package/eslint-rules/paper-research-question.harness.mjs +73 -7
- package/eslint-rules/paper-stages.harness.mjs +9 -14
- package/eslint-rules/paper-stages.test.mjs +135 -0
- package/eslint-rules/paper-typography.mjs +9 -2
- package/eslint-rules/paper-typography.test.mjs +146 -1
- package/eslint-rules/pdf-last-page-balance.harness.mjs +63 -7
- package/eslint-rules/review-frontmatter.mjs +2 -1
- package/eslint-rules/review-frontmatter.test.mjs +37 -1
- package/eslint-rules/temp-root-realpath.harness.mjs +49 -0
- package/eslint-rules/tex-build.mjs +13 -13
- package/eslint.config.mjs +6 -2
- package/hooks/hooks.harness.mjs +6 -11
- package/hooks/paper-edit-guard.hook.mjs +2 -2
- package/hooks/paper-edit-guard.test.mjs +61 -0
- package/hooks/paper-skills-nudge.hook.mjs +2 -2
- package/hooks/paper-skills-nudge.test.mjs +38 -0
- package/hooks/paper-status-gates.test.mjs +42 -0
- package/lib/agent-cli-version.harness.mjs +7 -0
- package/lib/check.d.mts +17 -0
- package/lib/check.mjs +50 -0
- package/lib/check.test.ts +67 -0
- package/lib/markdown.mjs +16 -69
- package/lib/markdown.test.mjs +246 -0
- package/lib/paper-config.harness.mjs +28 -9
- package/lib/paper-config.mjs +7 -2
- package/lib/skill-checks.mjs +18 -46
- package/lib/skill-checks.test.mjs +297 -0
- package/lib/skill-corpus.mjs +11 -13
- package/lib/skill-corpus.test.mjs +54 -0
- package/lib/skill-trigger-cases.harness.mjs +57 -4
- package/package.json +6 -5
- package/scripts/check.harness.mjs +4 -10
- package/scripts/check.mjs +45 -42
- package/scripts/check.test.mjs +104 -0
- package/scripts/coverage-config.test.ts +406 -0
- package/scripts/eslint-report-guard.test.mjs +48 -0
- package/scripts/harness-api.frozen.json +0 -2
- package/scripts/layer-legacy-frozen.d.mts +9 -1
- package/scripts/layer-legacy-frozen.mjs +28 -11
- package/scripts/layer-legacy-frozen.test.ts +143 -1
- package/scripts/rules-are-content-only.harness.mjs +3 -11
- package/scripts/rules-are-content-only.mjs +29 -19
- package/scripts/rules-are-content-only.test.mjs +61 -0
- package/scripts/rules-see-files.mjs +11 -9
- package/scripts/rules-see-files.test.mjs +71 -0
- package/scripts/test.mjs +44 -0
- package/scripts/test.test.mjs +52 -0
- package/skills/grade-paper-writing/__snapshots__/prose-lint.test.mjs.snap +57 -0
- package/skills/grade-paper-writing/prose-lint.mjs +31 -12
- package/skills/grade-paper-writing/prose-lint.test.mjs +153 -0
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +3 -3
- package/skills/paper-pipeline/references/artifact-checklist.md +7 -7
- package/skills/paper-pipeline/scripts/README.md +13 -132
- package/skills/paper-pipeline/scripts/announce.test.mjs +102 -0
- package/skills/paper-pipeline/scripts/artifact-coverage.test.mjs +86 -0
- package/skills/paper-pipeline/scripts/consumer.d.mts +8 -1
- package/skills/paper-pipeline/scripts/consumer.harness.mjs +3 -3
- package/skills/paper-pipeline/scripts/consumer.mjs +13 -25
- package/skills/paper-pipeline/scripts/consumer.test.mjs +113 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +9 -4
- package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +380 -0
- package/skills/paper-pipeline/scripts/fixtures/scorecard.mjs +71 -0
- package/skills/paper-pipeline/scripts/generated-code.harness.mjs +4 -4
- package/skills/paper-pipeline/scripts/generated-code.mjs +3 -2
- package/skills/paper-pipeline/scripts/generated-code.test.mjs +62 -0
- package/skills/paper-pipeline/scripts/ledger.cli.test.mjs +302 -0
- package/skills/paper-pipeline/scripts/ledger.mjs +1 -1
- package/skills/paper-pipeline/scripts/{ledger.selftest.mjs → ledger.test.mjs} +44 -76
- package/skills/paper-pipeline/scripts/pipeline-check.cli.test.mjs +474 -0
- package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +2 -63
- package/skills/paper-pipeline/scripts/pipeline-check.mjs +37 -21
- package/skills/paper-pipeline/scripts/pipeline-edges.mjs +2 -2
- package/skills/paper-pipeline/scripts/pipeline-edges.test.mjs +73 -0
- package/skills/paper-pipeline/scripts/population-map.harness.mjs +4 -47
- package/skills/paper-pipeline/scripts/population-map.mjs +12 -2
- package/skills/paper-pipeline/scripts/population-map.test.mjs +154 -0
- package/skills/paper-pipeline/scripts/provenance.harness.mjs +26 -0
- package/skills/paper-pipeline/scripts/round-diff.harness.mjs +1 -2
- package/skills/paper-pipeline/scripts/round-diff.mjs +13 -6
- package/skills/paper-pipeline/scripts/round-diff.test.mjs +159 -0
- package/skills/paper-pipeline/scripts/run-mechanical.mjs +30 -6
- package/skills/paper-pipeline/scripts/run-mechanical.test.mjs +468 -0
- package/skills/paper-pipeline/scripts/status.mjs +2 -2
- package/skills/paper-pipeline/scripts/status.test.mjs +232 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +2 -2
- package/skills/render-paper/checkers-requirements.txt +30 -19
- package/skills/render-paper/ensure-checkers.sh +20 -4
- package/skills/render-paper/extract-pdf-facts.harness.mjs +56 -7
- package/skills/tighten-paper/__snapshots__/structure.test.mjs.snap +133 -0
- package/skills/tighten-paper/structure.test.mjs +116 -0
- package/skills/verify-citations/SKILL.md +30 -14
- package/skills/verify-citations/SKILL.md.spec.ts +29 -13
- package/skills/verify-citations/scripts/__snapshots__/verify-cites.net.test.mjs.snap +253 -0
- package/skills/verify-citations/scripts/bib-authors.check.test.mjs +274 -0
- package/skills/verify-citations/scripts/bib-authors.cli.test.mjs +178 -0
- package/skills/verify-citations/scripts/bib-authors.mjs +6 -6
- package/skills/verify-citations/scripts/verify-cites.cli.test.mjs +160 -0
- package/skills/verify-citations/scripts/verify-cites.mjs +360 -215
- package/skills/verify-citations/scripts/verify-cites.net.test.mjs +588 -0
- package/skills/verify-citations/scripts/verify-cites.test.mjs +151 -5
- package/src/adapters/banal/failure.test.ts +70 -1
- package/src/adapters/banal/index.test.ts +16 -0
- package/src/adapters/banal/output.test.ts +5 -0
- package/src/adapters/curl/download.test.ts +43 -1
- package/src/adapters/memory/index.test.ts +25 -0
- package/src/adapters/node/files.io.ts +3 -2
- package/src/adapters/node/files.test.ts +13 -1
- package/src/adapters/node/process.test.ts +38 -0
- package/src/adapters/pdfjs/fill.test.ts +34 -0
- package/src/adapters/pdfjs/fill.ts +44 -25
- package/src/adapters/references/clock.io.ts +8 -0
- package/src/adapters/references/index.test.ts +560 -0
- package/src/adapters/references/index.ts +203 -19
- package/src/adapters/references/pool.test.ts +31 -0
- package/src/adapters/references/pool.ts +24 -0
- package/src/build-engine.harness.mjs +3 -7
- package/src/build-engine.test.ts +137 -0
- package/src/build.harness.mjs +5 -9
- package/src/build.test.ts +362 -0
- package/src/build.ts +23 -12
- package/src/cli.harness.mjs +10 -17
- package/src/cli.test.ts +584 -0
- package/src/cli.ts +112 -66
- package/src/doctor.harness.mjs +3 -8
- package/src/doctor.test.ts +64 -0
- package/src/doctor.ts +12 -4
- package/src/domain/lookup-cache.test.ts +178 -0
- package/src/domain/lookup-cache.ts +202 -0
- package/src/domain/result.test.ts +4 -0
- package/src/domain/result.ts +0 -5
- package/src/engine.harness.mjs +27 -7
- package/src/facts-file.test.ts +110 -0
- package/src/hooks-settings.harness.mjs +43 -13
- package/src/hooks-settings.ts +41 -35
- package/src/init.test.ts +308 -5
- package/src/init.ts +3 -3
- package/src/latex-log.harness.mjs +32 -7
- package/src/latex-log.ts +6 -13
- package/src/latex-loop.harness.mjs +8 -7
- package/src/link-skills.harness.mjs +3 -8
- package/src/link-skills.test.ts +105 -0
- package/src/link-skills.ts +75 -21
- package/src/new-paper.harness.mjs +3 -7
- package/src/new-paper.test.ts +31 -1
- package/src/paper-files.test.ts +12 -0
- package/src/paper-files.ts +9 -5
- package/src/paper-settings.test.ts +20 -0
- package/src/pdf-facts.harness.mjs +3 -6
- package/src/pdf-facts.test.ts +144 -0
- package/src/pdf-facts.ts +17 -19
- package/src/pdf-geometry.harness.mjs +3 -7
- package/src/pdf-geometry.ts +1 -1
- package/src/ports/check-references.ts +16 -2
- package/src/presets.test.ts +2 -0
- package/src/presets.ts +7 -1
- package/src/references.test.ts +257 -10
- package/src/references.ts +93 -17
- package/src/rules-config.harness.mjs +10 -8
- package/src/structure.harness.mjs +3 -7
- package/src/tex-requirements.harness.mjs +34 -7
- package/src/tex-requirements.ts +12 -6
- package/src/toolchain.harness.mjs +3 -7
- package/src/toolchain.test.ts +280 -0
- package/src/toolchain.ts +8 -7
- package/src/venue-rules.test.ts +60 -0
- package/src/venue-rules.ts +13 -11
- package/action.mutations.mjs +0 -162
- package/eslint-rules/install-path-literals.mutations.mjs +0 -62
- package/eslint-rules/latex-language.mutations.mjs +0 -196
- package/eslint-rules/paper-research-question.mutations.mjs +0 -127
- package/eslint-rules/paper-stages.mutations.mjs +0 -81
- package/eslint-rules/papers.mutations.mjs +0 -186
- package/eslint-rules/temp-root-realpath.mutations.mjs +0 -99
- package/eslint-rules/tex-build.mutations.mjs +0 -258
- package/hooks/hooks.mutations.mjs +0 -337
- package/lib/agent-cli-version.mutations.mjs +0 -109
- package/lib/mutation-driver.harness.mjs +0 -227
- package/lib/mutation-driver.mjs +0 -397
- package/lib/mutation-driver.mutations.mjs +0 -68
- package/lib/paper-config.mutations.mjs +0 -143
- package/lib/skill-trigger-cases.mutations.mjs +0 -65
- package/scripts/check.mutations.mjs +0 -110
- package/scripts/exclusive.mjs +0 -138
- package/scripts/mutation-batteries-frozen.harness.mjs +0 -204
- package/scripts/mutation-batteries-frozen.mjs +0 -238
- package/scripts/mutation-batteries.frozen.json +0 -108
- package/scripts/rules-are-content-only.mutations.mjs +0 -81
- package/scripts/rules-see-files.mutations.mjs +0 -131
- package/scripts/run-mutations.mjs +0 -100
- package/skills/paper-pipeline/scripts/artifact-coverage.mutations.mjs +0 -218
- package/skills/paper-pipeline/scripts/consumer.mutations.mjs +0 -190
- package/skills/paper-pipeline/scripts/extract-ref-facts.mutations.mjs +0 -54
- package/skills/paper-pipeline/scripts/generated-code.mutations.mjs +0 -254
- package/skills/paper-pipeline/scripts/pipeline-check.mutations.mjs +0 -54
- package/skills/paper-pipeline/scripts/population-map.mutations.mjs +0 -65
- package/skills/paper-pipeline/scripts/population-map.selftest.mjs +0 -122
- package/skills/paper-pipeline/scripts/provenance.mutations.mjs +0 -59
- package/skills/paper-pipeline/scripts/round-diff.mutations.mjs +0 -276
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.mutations.mjs +0 -65
- package/skills/skill-contract.mutations.mjs +0 -179
- package/src/build.mutations.mjs +0 -196
- package/src/cli.mutations.mjs +0 -414
- package/src/doctor.mutations.mjs +0 -175
- package/src/hooks-settings.mutations.mjs +0 -99
- package/src/link-skills.mutations.mjs +0 -140
- package/src/new-paper.mutations.mjs +0 -65
- package/src/structure.mutations.mjs +0 -83
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* structure.mjs — the section inventory the tighten-paper skill prints for a human to read. No
|
|
3
|
+
* threshold, no finding: the table IS the product, so the whole printed table is compared.
|
|
4
|
+
*/
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { expect, test } from "vitest";
|
|
9
|
+
import { runNode, useTempDir, writeTree } from "../../test/support.mjs";
|
|
10
|
+
|
|
11
|
+
const SCRIPT = join(dirname(fileURLToPath(import.meta.url)), "structure.mjs");
|
|
12
|
+
const root = useTempDir("structure-");
|
|
13
|
+
const words = (n) => Array.from({ length: n }, (_, i) => `w${i}`).join(" ");
|
|
14
|
+
|
|
15
|
+
writeTree(root, {
|
|
16
|
+
// Every row shape: a sub-section before any section, carries notes with a score and a verdict,
|
|
17
|
+
// with only one of them, with neither, a numbered section with no note, and a free half — after
|
|
18
|
+
// Limitations, between References and the Appendix — larger than the body.
|
|
19
|
+
"full.md": `---
|
|
20
|
+
title: x
|
|
21
|
+
---
|
|
22
|
+
Preamble before any heading.
|
|
23
|
+
|
|
24
|
+
### Orphan sub
|
|
25
|
+
${words(3)}
|
|
26
|
+
|
|
27
|
+
<!-- carries: the claim · score: 7/10 · verdict: keep -->
|
|
28
|
+
## 1 Introduction
|
|
29
|
+
${words(10)}
|
|
30
|
+
|
|
31
|
+
<!-- carries: a sub-point -->
|
|
32
|
+
### 1.1 Detail
|
|
33
|
+
${words(4)}
|
|
34
|
+
|
|
35
|
+
### 1.2 Bare sub
|
|
36
|
+
${words(2)}
|
|
37
|
+
|
|
38
|
+
## 2 Method
|
|
39
|
+
${words(6)}
|
|
40
|
+
|
|
41
|
+
<!-- carries: only a verdict · verdict: CUT -->
|
|
42
|
+
## 3 Results
|
|
43
|
+
${words(5)}
|
|
44
|
+
|
|
45
|
+
<!-- carries: score only · score: 4 -->
|
|
46
|
+
## Discussion
|
|
47
|
+
${words(3)}
|
|
48
|
+
|
|
49
|
+
## Limitations
|
|
50
|
+
${words(40)}
|
|
51
|
+
|
|
52
|
+
<!-- carries: why the scope note -->
|
|
53
|
+
### Scope
|
|
54
|
+
${words(5)}
|
|
55
|
+
|
|
56
|
+
## References
|
|
57
|
+
[1] Not counted.
|
|
58
|
+
|
|
59
|
+
<!-- carries: the raw tables -->
|
|
60
|
+
## Appendix A
|
|
61
|
+
${words(30)}
|
|
62
|
+
`,
|
|
63
|
+
// References come first, so the free half runs from it to the end; no appendix.
|
|
64
|
+
"refs-first.md": `## 1 Intro\n${words(8)}\n\n## References\n[1] x\n\n## Ethical note\n${words(2)}\n`,
|
|
65
|
+
// Limitations then References and no appendix: the free half stops at References.
|
|
66
|
+
"no-appendix.md": `## 1 Intro\n${words(8)}\n\n## Limitations\n${words(3)}\n\n## References\n[1] x\n`,
|
|
67
|
+
// No free half at all.
|
|
68
|
+
// ...and a section with no words at all.
|
|
69
|
+
"body-only.md": `## 1 Intro\n${words(8)}\n\n## 2 Rest\n${words(4)}\n\n## 3 Empty\n`,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const run = (...args) => {
|
|
73
|
+
const r = runNode(
|
|
74
|
+
SCRIPT,
|
|
75
|
+
args.map((a) => (a.startsWith("--") ? a : join(root, a))),
|
|
76
|
+
);
|
|
77
|
+
return { ...r, stdout: r.stdout.replaceAll(root, "<root>") };
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
test("no file: the usage line, exit 0", () => {
|
|
81
|
+
assert.deepEqual(run(), {
|
|
82
|
+
status: 0,
|
|
83
|
+
stdout: "",
|
|
84
|
+
stderr:
|
|
85
|
+
"usage: node structure.mjs <paper.md> [--section=N] (--flags-only is accepted and stays silent)\n",
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("--flags-only stays silent and exits 0 — every check moved to ESLint", () => {
|
|
90
|
+
assert.deepEqual(run("--flags-only", "full.md"), {
|
|
91
|
+
status: 0,
|
|
92
|
+
stdout: "",
|
|
93
|
+
stderr: "",
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test.each(["full.md", "refs-first.md", "no-appendix.md", "body-only.md"])(
|
|
98
|
+
"the inventory of %s",
|
|
99
|
+
(file) => {
|
|
100
|
+
const r = run(file);
|
|
101
|
+
assert.deepEqual(
|
|
102
|
+
{ status: r.status, stderr: r.stderr },
|
|
103
|
+
{ status: 0, stderr: "" },
|
|
104
|
+
);
|
|
105
|
+
expect(r.stdout).toMatchSnapshot();
|
|
106
|
+
},
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
test("--section=1 keeps section 1 and its sub-sections, and nothing before it", () => {
|
|
110
|
+
const r = run("full.md", "--section=1");
|
|
111
|
+
assert.deepEqual(
|
|
112
|
+
{ status: r.status, stderr: r.stderr },
|
|
113
|
+
{ status: 0, stderr: "" },
|
|
114
|
+
);
|
|
115
|
+
expect(r.stdout).toMatchSnapshot();
|
|
116
|
+
});
|
|
@@ -4,7 +4,7 @@ description: Verify every citation is a real work with correct metadata, and tha
|
|
|
4
4
|
allowed-tools: [Read, Write, Edit, Grep, Glob, Bash, WebSearch, WebFetch, Agent]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
<!-- vigiles:sha256:
|
|
7
|
+
<!-- vigiles:sha256:3a7e10662d0084c4 compiled from skills/verify-citations/SKILL.md.spec.ts -->
|
|
8
8
|
|
|
9
9
|
# verify-citations — every cite real, the delta explicit
|
|
10
10
|
|
|
@@ -133,17 +133,26 @@ node scripts/verify-cites.mjs cites.json --offline # no network (everything de
|
|
|
133
133
|
```
|
|
134
134
|
It prints per-cite `{id, verdict, reason, matched_db, matched_title?, flags?}` + a summary, and **exits 1 iff
|
|
135
135
|
any `false` (fabrication)** — `unresolvable` alone is advisory and does NOT fail the gate, matching the
|
|
136
|
-
narrowed-false philosophy. API responses are cached to
|
|
137
|
-
cheap and deterministic
|
|
136
|
+
narrowed-false philosophy. API responses are cached to `~/.cache/paperlint/cite-cache.json`
|
|
137
|
+
(`VERIFY_CITES_CACHE` overrides) so re-runs are cheap and deterministic — outside the installed package,
|
|
138
|
+
which `npm ci` wipes.
|
|
138
139
|
|
|
139
|
-
## 5a. Both checks run on every `paperlint build`
|
|
140
|
+
## 5a. Both checks run on every `paperlint build` — from a committed cache
|
|
140
141
|
|
|
141
142
|
`npx paperlint build` runs `verify-cites` and `bib-authors` over the paper's bibliography after the PDF is
|
|
142
143
|
built and records the verdicts, with the SHA-256 of the bibliography it checked, in
|
|
143
144
|
`<paper>/_build/references.json`. `paperlint lint` reads that record offline: `paper/cite-exists` and
|
|
144
145
|
`paper/author-list` report a failing entry on its own line, `paper/refs-fresh` says when the bibliography
|
|
145
146
|
changed since, and `paper/refs-checked` warns when nothing was recorded or the build had no network. The
|
|
146
|
-
step never fails the build.
|
|
147
|
+
step never fails the build.
|
|
148
|
+
|
|
149
|
+
What the services ANSWERED is kept in `<paper>/repro/references-cache.json` — **commit it**. A build asks
|
|
150
|
+
only the questions that file cannot answer (a new or edited entry, or an answer older than 30 days), so an
|
|
151
|
+
unchanged bibliography builds with no network at all, in CI too, until its answers age out. It holds responses, never verdicts: the verdicts are derived again on
|
|
152
|
+
every build. A service that refuses (429, HTML instead of JSON, a timeout) is not asked again for the rest of the
|
|
153
|
+
build (the requests already in flight still land); the entries it would have answered say `not asked: <service>: <reason>`, and DBLP's become
|
|
154
|
+
`authors: unchecked`. Answers are asked again after 30 days; to refresh one sooner, delete its entry, or delete the file. Details:
|
|
155
|
+
`docs/references.md` in the package. Run the scripts by hand (below) to read a single verdict in full.
|
|
147
156
|
|
|
148
157
|
## 5b. The author-list gate — `scripts/bib-authors.mjs`
|
|
149
158
|
|
|
@@ -179,11 +188,18 @@ difference is real and not a metadata glitch. Entries that themselves declare a
|
|
|
179
188
|
scope (a preprint entry is allowed to carry preprint metadata).
|
|
180
189
|
|
|
181
190
|
⚠️ **Discovery credit, and why it is NOT a dependency.** The class was found by running **rebiber**
|
|
182
|
-
(`yuchenlin/rebiber`), which rewrites bib entries into their DBLP records.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
191
|
+
(`yuchenlin/rebiber`), which rewrites bib entries into their DBLP records. Re-measured 2026-09-27 on rebiber
|
|
192
|
+
1.4.0 (GitHub commit `b917e36`, the pin in `render-paper/checkers-requirements.txt`; the PyPI package is
|
|
193
|
+
a 2021 release upstream disowns):
|
|
194
|
+
- **Install: no longer broken, but heavy.** It installs in 36 s in a clean venv (bibtexparser 2.0.1 wheel,
|
|
195
|
+
rebiber built from source) — and carries **409 MB** of bundled venue data.
|
|
196
|
+
- **Rewrites: now optional.** `--dry-run --report` previews and leaves the file untouched; `-s True` shortens
|
|
197
|
+
venues (NAACL 2019 becomes `Proc. of NAACL-HLT` instead of a 160-character booktitle).
|
|
198
|
+
- **Bundled data still misses what live DBLP finds.** `raji2021benchmark` stayed an arXiv entry, local
|
|
199
|
+
index and `--live-lookup` alike, as it did in August.
|
|
200
|
+
|
|
201
|
+
So the reason not to depend on it is now scope, not breakage: we need the **comparison** of an author list
|
|
202
|
+
against the published record, not a rewrite of the entry, and that is one DBLP query with zero dependencies.
|
|
187
203
|
|
|
188
204
|
🔴 **Two design decisions that are load-bearing, both learned by measurement the same day:**
|
|
189
205
|
1. **Compare surname SEQUENCES, nothing else.** A first cut reported 13 differences of which **11 were
|
|
@@ -259,7 +275,7 @@ the same one MedSci Skills (arXiv:2606.09500) closes with `verify_refs.py`.
|
|
|
259
275
|
| year | ±1 on a title search | exact, and only against the record that dates the cited version |
|
|
260
276
|
| preprint→published | not asked | arXiv `journal_ref`/`doi` present and no venue named → say so |
|
|
261
277
|
| what it could not check | folded into `unresolvable` | **enumerated, one rule per reason, never counted as verified** |
|
|
262
|
-
| tests | `verify-cites.test.mjs` — not a `*.harness.mjs`, so `npx vigiles test` never runs it | `
|
|
278
|
+
| tests | `verify-cites.test.mjs` — not a `*.harness.mjs`, so `npx vigiles test` never runs it | `src/references.test.ts` (the `refs/*` rules in `src/reference-rules.ts`) + `skills/paper-pipeline/scripts/extract-ref-facts.test.mjs` and `.harness.mjs`, under the package's 100% line and branch coverage gate |
|
|
263
279
|
|
|
264
280
|
**The uncovered set is the point.** A reference with no resolvable identifier is a FINDING here
|
|
265
281
|
(`refs/no-identifier`), and every partial comparison is another (`refs/partial-check`, one messageId per
|
|
@@ -274,9 +290,9 @@ and its `.bib` regex required a closing `}` on its own line, which neither real
|
|
|
274
290
|
references`). The `.bib` half now goes through `@retorquere/bibtex-parser`. Two of the three papers are
|
|
275
291
|
therefore being checked for the first time.
|
|
276
292
|
|
|
277
|
-
**Every check is
|
|
278
|
-
|
|
279
|
-
|
|
293
|
+
**Every check is exercised.** `src/references.test.ts` drives the `refs/*` rules, and `npm run coverage`
|
|
294
|
+
fails CI if any line or branch of them goes unexercised. Run `npx vitest run src/references.test.ts`
|
|
295
|
+
after any edit to the rules.
|
|
280
296
|
|
|
281
297
|
## 6. Record the verdict
|
|
282
298
|
|
|
@@ -149,17 +149,26 @@ node scripts/verify-cites.mjs cites.json --offline # no network (everything de
|
|
|
149
149
|
\`\`\`
|
|
150
150
|
It prints per-cite \`{id, verdict, reason, matched_db, matched_title?, flags?}\` + a summary, and **exits 1 iff
|
|
151
151
|
any \`false\` (fabrication)** — \`unresolvable\` alone is advisory and does NOT fail the gate, matching the
|
|
152
|
-
narrowed-false philosophy. API responses are cached to
|
|
153
|
-
cheap and deterministic
|
|
152
|
+
narrowed-false philosophy. API responses are cached to \`~/.cache/paperlint/cite-cache.json\`
|
|
153
|
+
(\`VERIFY_CITES_CACHE\` overrides) so re-runs are cheap and deterministic — outside the installed package,
|
|
154
|
+
which \`npm ci\` wipes.
|
|
154
155
|
|
|
155
|
-
## 5a. Both checks run on every \`paperlint build\`
|
|
156
|
+
## 5a. Both checks run on every \`paperlint build\` — from a committed cache
|
|
156
157
|
|
|
157
158
|
\`npx paperlint build\` runs \`verify-cites\` and \`bib-authors\` over the paper's bibliography after the PDF is
|
|
158
159
|
built and records the verdicts, with the SHA-256 of the bibliography it checked, in
|
|
159
160
|
\`<paper>/_build/references.json\`. \`paperlint lint\` reads that record offline: \`paper/cite-exists\` and
|
|
160
161
|
\`paper/author-list\` report a failing entry on its own line, \`paper/refs-fresh\` says when the bibliography
|
|
161
162
|
changed since, and \`paper/refs-checked\` warns when nothing was recorded or the build had no network. The
|
|
162
|
-
step never fails the build.
|
|
163
|
+
step never fails the build.
|
|
164
|
+
|
|
165
|
+
What the services ANSWERED is kept in \`<paper>/repro/references-cache.json\` — **commit it**. A build asks
|
|
166
|
+
only the questions that file cannot answer (a new or edited entry, or an answer older than 30 days), so an
|
|
167
|
+
unchanged bibliography builds with no network at all, in CI too, until its answers age out. It holds responses, never verdicts: the verdicts are derived again on
|
|
168
|
+
every build. A service that refuses (429, HTML instead of JSON, a timeout) is not asked again for the rest of the
|
|
169
|
+
build (the requests already in flight still land); the entries it would have answered say \`not asked: <service>: <reason>\`, and DBLP's become
|
|
170
|
+
\`authors: unchecked\`. Answers are asked again after 30 days; to refresh one sooner, delete its entry, or delete the file. Details:
|
|
171
|
+
\`docs/references.md\` in the package. Run the scripts by hand (below) to read a single verdict in full.
|
|
163
172
|
|
|
164
173
|
## 5b. The author-list gate — \`scripts/bib-authors.mjs\`
|
|
165
174
|
|
|
@@ -195,11 +204,18 @@ difference is real and not a metadata glitch. Entries that themselves declare a
|
|
|
195
204
|
scope (a preprint entry is allowed to carry preprint metadata).
|
|
196
205
|
|
|
197
206
|
⚠️ **Discovery credit, and why it is NOT a dependency.** The class was found by running **rebiber**
|
|
198
|
-
(\`yuchenlin/rebiber\`), which rewrites bib entries into their DBLP records.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
207
|
+
(\`yuchenlin/rebiber\`), which rewrites bib entries into their DBLP records. Re-measured 2026-09-27 on rebiber
|
|
208
|
+
1.4.0 (GitHub commit \`b917e36\`, the pin in \`render-paper/checkers-requirements.txt\`; the PyPI package is
|
|
209
|
+
a 2021 release upstream disowns):
|
|
210
|
+
- **Install: no longer broken, but heavy.** It installs in 36 s in a clean venv (bibtexparser 2.0.1 wheel,
|
|
211
|
+
rebiber built from source) — and carries **409 MB** of bundled venue data.
|
|
212
|
+
- **Rewrites: now optional.** \`--dry-run --report\` previews and leaves the file untouched; \`-s True\` shortens
|
|
213
|
+
venues (NAACL 2019 becomes \`Proc. of NAACL-HLT\` instead of a 160-character booktitle).
|
|
214
|
+
- **Bundled data still misses what live DBLP finds.** \`raji2021benchmark\` stayed an arXiv entry, local
|
|
215
|
+
index and \`--live-lookup\` alike, as it did in August.
|
|
216
|
+
|
|
217
|
+
So the reason not to depend on it is now scope, not breakage: we need the **comparison** of an author list
|
|
218
|
+
against the published record, not a rewrite of the entry, and that is one DBLP query with zero dependencies.
|
|
203
219
|
|
|
204
220
|
🔴 **Two design decisions that are load-bearing, both learned by measurement the same day:**
|
|
205
221
|
1. **Compare surname SEQUENCES, nothing else.** A first cut reported 13 differences of which **11 were
|
|
@@ -275,7 +291,7 @@ the same one MedSci Skills (arXiv:2606.09500) closes with \`verify_refs.py\`.
|
|
|
275
291
|
| year | ±1 on a title search | exact, and only against the record that dates the cited version |
|
|
276
292
|
| preprint→published | not asked | arXiv \`journal_ref\`/\`doi\` present and no venue named → say so |
|
|
277
293
|
| what it could not check | folded into \`unresolvable\` | **enumerated, one rule per reason, never counted as verified** |
|
|
278
|
-
| tests | \`verify-cites.test.mjs\` — not a \`*.harness.mjs\`, so \`npx vigiles test\` never runs it | \`
|
|
294
|
+
| tests | \`verify-cites.test.mjs\` — not a \`*.harness.mjs\`, so \`npx vigiles test\` never runs it | \`src/references.test.ts\` (the \`refs/*\` rules in \`src/reference-rules.ts\`) + \`skills/paper-pipeline/scripts/extract-ref-facts.test.mjs\` and \`.harness.mjs\`, under the package's 100% line and branch coverage gate |
|
|
279
295
|
|
|
280
296
|
**The uncovered set is the point.** A reference with no resolvable identifier is a FINDING here
|
|
281
297
|
(\`refs/no-identifier\`), and every partial comparison is another (\`refs/partial-check\`, one messageId per
|
|
@@ -290,9 +306,9 @@ and its \`.bib\` regex required a closing \`}\` on its own line, which neither r
|
|
|
290
306
|
references\`). The \`.bib\` half now goes through \`@retorquere/bibtex-parser\`. Two of the three papers are
|
|
291
307
|
therefore being checked for the first time.
|
|
292
308
|
|
|
293
|
-
**Every check is
|
|
294
|
-
|
|
295
|
-
|
|
309
|
+
**Every check is exercised.** \`src/references.test.ts\` drives the \`refs/*\` rules, and \`npm run coverage\`
|
|
310
|
+
fails CI if any line or branch of them goes unexercised. Run \`npx vitest run src/references.test.ts\`
|
|
311
|
+
after any edit to the rules.
|
|
296
312
|
|
|
297
313
|
## 6. Record the verdict
|
|
298
314
|
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`CVEs: found by count, found by list, not found, NVD down, and a malformed id 1`] = `
|
|
4
|
+
{
|
|
5
|
+
"calls": [
|
|
6
|
+
"https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2024-0001",
|
|
7
|
+
"https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2024-0002",
|
|
8
|
+
"https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2024-0003",
|
|
9
|
+
"https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2024-0004",
|
|
10
|
+
"https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2024-0005",
|
|
11
|
+
],
|
|
12
|
+
"results": [
|
|
13
|
+
{
|
|
14
|
+
"id": "cve-2024-0001",
|
|
15
|
+
"matched_db": "nvd",
|
|
16
|
+
"matched_title": "cve-2024-0001",
|
|
17
|
+
"reason": "confirmed by nvd — "cve-2024-0001"",
|
|
18
|
+
"verdict": "true",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "CVE-2024-0002",
|
|
22
|
+
"matched_db": "nvd",
|
|
23
|
+
"matched_title": "CVE-2024-0002",
|
|
24
|
+
"reason": "confirmed by nvd — "CVE-2024-0002"",
|
|
25
|
+
"verdict": "true",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "CVE-2024-0003",
|
|
29
|
+
"matched_db": null,
|
|
30
|
+
"reason": "RESOLVED_TO_NOTHING: the provided CVE CVE-2024-0003 resolves to no record (checked nvd) — fabrication evidence",
|
|
31
|
+
"verdict": "false",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "CVE-2024-0004",
|
|
35
|
+
"matched_db": null,
|
|
36
|
+
"reason": "all databases unreachable (network/rate-limit) — degraded to unresolvable, never to false",
|
|
37
|
+
"refused": [
|
|
38
|
+
"nvd: http 500",
|
|
39
|
+
],
|
|
40
|
+
"verdict": "unresolvable",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "CVE-2024-0005",
|
|
44
|
+
"matched_db": null,
|
|
45
|
+
"reason": "RESOLVED_TO_NOTHING: the provided CVE CVE-2024-0005 resolves to no record (checked nvd) — fabrication evidence",
|
|
46
|
+
"verdict": "false",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "CVE-24-1",
|
|
50
|
+
"matched_db": null,
|
|
51
|
+
"reason": "RESOLVED_TO_NOTHING: the provided CVE CVE-24-1 resolves to no record (checked nvd) — fabrication evidence",
|
|
52
|
+
"verdict": "false",
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
exports[`S2 and arXiv unreachable for an arXiv id 1`] = `
|
|
59
|
+
{
|
|
60
|
+
"calls": [
|
|
61
|
+
"https://api.semanticscholar.org/graph/v1/paper/arXiv%3A1706.03762?fields=title,year",
|
|
62
|
+
"https://export.arxiv.org/api/query?id_list=1706.03762&max_results=1",
|
|
63
|
+
],
|
|
64
|
+
"result": {
|
|
65
|
+
"id": "x",
|
|
66
|
+
"matched_db": null,
|
|
67
|
+
"reason": "all databases unreachable (network/rate-limit) — degraded to unresolvable, never to false",
|
|
68
|
+
"refused": [
|
|
69
|
+
"semantic_scholar: http 500",
|
|
70
|
+
"arxiv: http 500",
|
|
71
|
+
],
|
|
72
|
+
"verdict": "unresolvable",
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
exports[`a DOI every registry resolves to the claimed paper, and the authority knows: true 1`] = `
|
|
78
|
+
{
|
|
79
|
+
"calls": [
|
|
80
|
+
"https://api.crossref.org/works/10.1%2Fx",
|
|
81
|
+
],
|
|
82
|
+
"result": {
|
|
83
|
+
"id": "v",
|
|
84
|
+
"matched_db": "crossref",
|
|
85
|
+
"matched_title": "Attention Is All You Need",
|
|
86
|
+
"reason": "confirmed by crossref — "Attention Is All You Need"",
|
|
87
|
+
"verdict": "true",
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
`;
|
|
91
|
+
|
|
92
|
+
exports[`a DOI nobody knows, and the authority says it does not exist: false 1`] = `
|
|
93
|
+
{
|
|
94
|
+
"calls": [
|
|
95
|
+
"https://api.crossref.org/works/10.1%2Fnope",
|
|
96
|
+
"https://api.openalex.org/works/doi:10.1%2Fnope",
|
|
97
|
+
"https://api.semanticscholar.org/graph/v1/paper/DOI%3A10.1%2Fnope?fields=title,year",
|
|
98
|
+
"https://doi.org/api/handles/10.1/nope",
|
|
99
|
+
],
|
|
100
|
+
"result": {
|
|
101
|
+
"id": "f",
|
|
102
|
+
"matched_db": null,
|
|
103
|
+
"reason": "RESOLVED_TO_NOTHING: DOI 10.1/nope does not resolve at the DOI authority (doi.org handle responseCode 100 = not found) — fabrication evidence",
|
|
104
|
+
"verdict": "false",
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
exports[`a title only: every registry's search endpoint, hits and all 1`] = `
|
|
110
|
+
{
|
|
111
|
+
"calls": [
|
|
112
|
+
"https://api.crossref.org/works?query.bibliographic=Attention%20Is%20All%20You%20Need&rows=5",
|
|
113
|
+
"https://api.openalex.org/works?search=Attention%20Is%20All%20You%20Need&per-page=5",
|
|
114
|
+
"https://api.semanticscholar.org/graph/v1/paper/search?query=Attention%20Is%20All%20You%20Need&fields=title,year&limit=5",
|
|
115
|
+
"https://export.arxiv.org/api/query?search_query=ti:%22Attention%20Is%20All%20You%20Need%22&max_results=5",
|
|
116
|
+
],
|
|
117
|
+
"result": {
|
|
118
|
+
"id": "t",
|
|
119
|
+
"matched_db": "arxiv",
|
|
120
|
+
"matched_title": "Attention Is All You Need",
|
|
121
|
+
"reason": "confirmed by arxiv — "Attention Is All You Need"",
|
|
122
|
+
"verdict": "true",
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
`;
|
|
126
|
+
|
|
127
|
+
exports[`a title search that cannot reach any registry is unresolvable 1`] = `
|
|
128
|
+
{
|
|
129
|
+
"calls": [
|
|
130
|
+
"https://api.crossref.org/works?query.bibliographic=Attention%20Is%20All%20You%20Need&rows=5",
|
|
131
|
+
"https://api.openalex.org/works?search=Attention%20Is%20All%20You%20Need&per-page=5",
|
|
132
|
+
"https://api.semanticscholar.org/graph/v1/paper/search?query=Attention%20Is%20All%20You%20Need&fields=title,year&limit=5",
|
|
133
|
+
"https://export.arxiv.org/api/query?search_query=ti:%22Attention%20Is%20All%20You%20Need%22&max_results=5",
|
|
134
|
+
],
|
|
135
|
+
"result": {
|
|
136
|
+
"id": "d",
|
|
137
|
+
"matched_db": null,
|
|
138
|
+
"reason": "all databases unreachable (network/rate-limit) — degraded to unresolvable, never to false",
|
|
139
|
+
"refused": [
|
|
140
|
+
"crossref: http 500",
|
|
141
|
+
"openalex: http 502",
|
|
142
|
+
"semantic_scholar: timeout",
|
|
143
|
+
"arxiv: http 503",
|
|
144
|
+
],
|
|
145
|
+
"verdict": "unresolvable",
|
|
146
|
+
},
|
|
147
|
+
}
|
|
148
|
+
`;
|
|
149
|
+
|
|
150
|
+
exports[`a title search whose answers are empty or shapeless is a miss, not a match 1`] = `
|
|
151
|
+
{
|
|
152
|
+
"calls": [
|
|
153
|
+
"https://api.crossref.org/works?query.bibliographic=Attention%20Is%20All%20You%20Need&rows=5",
|
|
154
|
+
"https://api.openalex.org/works?search=Attention%20Is%20All%20You%20Need&per-page=5",
|
|
155
|
+
"https://api.semanticscholar.org/graph/v1/paper/search?query=Attention%20Is%20All%20You%20Need&fields=title,year&limit=5",
|
|
156
|
+
"https://export.arxiv.org/api/query?search_query=ti:%22Attention%20Is%20All%20You%20Need%22&max_results=5",
|
|
157
|
+
],
|
|
158
|
+
"result": {
|
|
159
|
+
"id": "e",
|
|
160
|
+
"matched_db": null,
|
|
161
|
+
"reason": "not found by title in any database (no resolvable id to disprove) — could be a legit unindexed/regional/pre-digital work, NOT fabrication",
|
|
162
|
+
"verdict": "unresolvable",
|
|
163
|
+
},
|
|
164
|
+
}
|
|
165
|
+
`;
|
|
166
|
+
|
|
167
|
+
exports[`an arXiv id nobody has: the arXiv API's empty feed and S2's 404 1`] = `
|
|
168
|
+
{
|
|
169
|
+
"calls": [
|
|
170
|
+
"https://api.semanticscholar.org/graph/v1/paper/arXiv%3A9999.99999?fields=title,year",
|
|
171
|
+
"https://export.arxiv.org/api/query?id_list=9999.99999&max_results=1",
|
|
172
|
+
],
|
|
173
|
+
"result": {
|
|
174
|
+
"id": "n",
|
|
175
|
+
"matched_db": null,
|
|
176
|
+
"reason": "RESOLVED_TO_NOTHING: arXiv id 9999.99999 returns a definitive not-found from the reachable arXiv API — fabrication evidence",
|
|
177
|
+
"verdict": "false",
|
|
178
|
+
},
|
|
179
|
+
}
|
|
180
|
+
`;
|
|
181
|
+
|
|
182
|
+
exports[`an arXiv id: Semantic Scholar by arXiv id, the arXiv API by id_list 1`] = `
|
|
183
|
+
{
|
|
184
|
+
"calls": [
|
|
185
|
+
"https://api.crossref.org/works?query.bibliographic=Attention%20Is%20All%20You%20Need&rows=5",
|
|
186
|
+
"https://api.openalex.org/works?search=Attention%20Is%20All%20You%20Need&per-page=5",
|
|
187
|
+
"https://api.semanticscholar.org/graph/v1/paper/arXiv%3A1706.03762?fields=title,year",
|
|
188
|
+
],
|
|
189
|
+
"result": {
|
|
190
|
+
"id": "a",
|
|
191
|
+
"matched_db": "semantic_scholar",
|
|
192
|
+
"matched_title": "Attention Is All You Need",
|
|
193
|
+
"reason": "confirmed by semantic_scholar — "Attention Is All You Need"",
|
|
194
|
+
"refused": [
|
|
195
|
+
"crossref: Error: unrouted request: https://api.crossref.org/works?query.bibliographic=Attention%20Is%20All%20You%20Need&rows=5",
|
|
196
|
+
"openalex: Error: unrouted request: https://api.openalex.org/works?search=Attention%20Is%20All%20You%20Need&per-page=5",
|
|
197
|
+
],
|
|
198
|
+
"verdict": "true",
|
|
199
|
+
},
|
|
200
|
+
}
|
|
201
|
+
`;
|
|
202
|
+
|
|
203
|
+
exports[`every registry down (429, 503, 403, a thrown error): unresolvable, never false 1`] = `
|
|
204
|
+
{
|
|
205
|
+
"calls": [
|
|
206
|
+
"https://api.crossref.org/works/10.1%2Fx",
|
|
207
|
+
"https://api.openalex.org/works/doi:10.1%2Fx",
|
|
208
|
+
"https://api.semanticscholar.org/graph/v1/paper/DOI%3A10.1%2Fx?fields=title,year",
|
|
209
|
+
"https://doi.org/api/handles/10.1/x",
|
|
210
|
+
],
|
|
211
|
+
"result": {
|
|
212
|
+
"id": "u",
|
|
213
|
+
"matched_db": null,
|
|
214
|
+
"reason": "all databases unreachable (network/rate-limit) — degraded to unresolvable, never to false",
|
|
215
|
+
"refused": [
|
|
216
|
+
"crossref: http 429",
|
|
217
|
+
"openalex: http 503",
|
|
218
|
+
"semantic_scholar: http 403",
|
|
219
|
+
"doi_authority: TypeError: fetch failed",
|
|
220
|
+
],
|
|
221
|
+
"verdict": "unresolvable",
|
|
222
|
+
},
|
|
223
|
+
}
|
|
224
|
+
`;
|
|
225
|
+
|
|
226
|
+
exports[`offline: no request at all, the verdict from the citation alone 1`] = `
|
|
227
|
+
{
|
|
228
|
+
"calls": [],
|
|
229
|
+
"result": {
|
|
230
|
+
"id": "o",
|
|
231
|
+
"matched_db": null,
|
|
232
|
+
"reason": "all databases unreachable (network/rate-limit) — degraded to unresolvable, never to false",
|
|
233
|
+
"verdict": "unresolvable",
|
|
234
|
+
},
|
|
235
|
+
}
|
|
236
|
+
`;
|
|
237
|
+
|
|
238
|
+
exports[`records with fields missing: no title, a subtitle, a year only in \`published\`, no id 1`] = `
|
|
239
|
+
{
|
|
240
|
+
"calls": [
|
|
241
|
+
"https://api.crossref.org/works/10.1%2Fx",
|
|
242
|
+
"https://api.openalex.org/works/doi:10.1%2Fx",
|
|
243
|
+
"https://api.semanticscholar.org/graph/v1/paper/DOI%3A10.1%2Fx?fields=title,year",
|
|
244
|
+
"https://doi.org/api/handles/10.1/x",
|
|
245
|
+
],
|
|
246
|
+
"result": {
|
|
247
|
+
"id": "m",
|
|
248
|
+
"matched_db": null,
|
|
249
|
+
"reason": "DOI_MISMATCH: identifier resolves via crossref to an UNRELATED paper "" (claimed "Attention Is All You Need") — DOI misdirection",
|
|
250
|
+
"verdict": "false",
|
|
251
|
+
},
|
|
252
|
+
}
|
|
253
|
+
`;
|