paperlint 13.3.0 → 13.3.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.
@@ -204,6 +204,28 @@ jobs:
204
204
  run: npm run test:e2e:studies
205
205
  timeout-minutes: 5
206
206
 
207
+ # ── DOES THIS PULL REQUEST CHANGE ANYTHING BUT DOCS? ────────────────────────────────────────────
208
+ # dorny/paths-filter lists a PR's files from the API (no checkout). With `every`, a file counts as
209
+ # code only if it matches all three patterns: anything, but not root or docs/ Markdown.
210
+ changes:
211
+ if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
212
+ runs-on: ubuntu-latest
213
+ timeout-minutes: 3
214
+ permissions:
215
+ pull-requests: read
216
+ outputs:
217
+ code: ${{ steps.filter.outputs.code }}
218
+ steps:
219
+ - uses: dorny/paths-filter@v3
220
+ id: filter
221
+ with:
222
+ predicate-quantifier: every
223
+ filters: |
224
+ code:
225
+ - "**"
226
+ - "!*.md"
227
+ - "!docs/**/*.md"
228
+
207
229
  # ── BUILDING THE PAPER WITH REAL pdflatex, ON LINUX AND macOS ──────────────────────────────────
208
230
  #
209
231
  # 🔴 ONE JOB, TWO OPERATING SYSTEMS. TeX Live comes from `paperlint toolchain` — the same command a user
@@ -220,8 +242,18 @@ jobs:
220
242
  # paper typeset in the wrong font. This repository is public, so the macOS minutes are free.
221
243
  #
222
244
  # `CI` is set: absence of TeX locally is a legitimate skip; here it is an environment failure.
245
+ #
246
+ # 🔴 EXCEPT A PULL REQUEST THAT CHANGES ONLY DOCS. A README or docs/*.md edit cannot change what
247
+ # this job builds, and it used to wait for TeX Live on two systems anyway (#343: one new
248
+ # docs/website.md). `changes` below decides; `gates` still runs on such a PR, because tests read
249
+ # the docs. On a push to main or a manual run `changes` does not run and this job always does.
223
250
  build-e2e:
224
- if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
251
+ needs: changes
252
+ # `!cancelled()`: without it a skipped `changes` (every push to main) would skip this job too.
253
+ if: >-
254
+ !cancelled() && (github.event_name != 'pull_request' ||
255
+ (github.event.pull_request.draft == false && needs.changes.result == 'success' &&
256
+ needs.changes.outputs.code == 'true'))
225
257
  strategy:
226
258
  fail-fast: false
227
259
  matrix:
package/CLAUDE.md CHANGED
@@ -1,4 +1,4 @@
1
- <!-- vigiles:sha256:08ea3aae9448b383 compiled from CLAUDE.md.spec.ts -->
1
+ <!-- vigiles:sha256:58c66e374c084cc5 compiled from CLAUDE.md.spec.ts -->
2
2
 
3
3
  # CLAUDE.md
4
4
 
@@ -37,9 +37,9 @@ This file loads on every turn, so it holds only what applies to **any** change h
37
37
  | changing CI, Dependabot or the release | [`.github/CLAUDE.md`](.github/CLAUDE.md) | cost, Dependabot cadence, release from the PR title |
38
38
  | writing a test | [`docs/testing.md`](docs/testing.md), [`docs/e2e.md`](docs/e2e.md) | which kind of test, fakes, the e2e triggers |
39
39
 
40
- ## Before every push
40
+ ## Before leaving draft and before merge
41
41
 
42
- `npm run check`, exit code read without a pipe. Red first · assert the whole value · no test touches the real network · 100% coverage, no `c8 ignore`. In a container without TeX Live, only the TeX e2e gate fails, and it says so; `npx paperlint toolchain` installs it.
42
+ `npm run check`, exit code read without a pipe; a `wip:` push to a DRAFT pull request needs only the tests of what it touched (`npx vitest run <dir>`), and is never reported as "the gates", because CI does not run the gates on a draft. Red first · assert the whole value · no test touches the real network · 100% coverage, no `c8 ignore`. In a container without TeX Live, only the TeX e2e gate fails, and it says so; `npx paperlint toolchain` installs it.
43
43
 
44
44
  ## Commits
45
45
 
@@ -1 +1 @@
1
- {"version":3,"file":"repro-layout.d.ts","sourceRoot":"","sources":["../../src/domain/repro-layout.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"repro-layout.d.ts","sourceRoot":"","sources":["../../src/domain/repro-layout.ts"],"names":[],"mappings":"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"}
@@ -13,7 +13,7 @@
13
13
  * ├── tools/ scripts used across the paper (numbers, figures, anonymity check, harnesses)
14
14
  * └── data/ shared data (tables, caches)
15
15
  *
16
- * plus `.gitignore`. A loose note belongs in its study's folder or in the paper's `research/`.
16
+ * plus `.gitignore`, and optionally the agent instruction files `CLAUDE.md` and `AGENTS.md`. A loose note belongs in its study's folder or in the paper's `research/`.
17
17
  *
18
18
  * ── A STUDY'S NAME ───────────────────────────────────────────────────────────────
19
19
  * `<id>-<slug>` (`P1-S03-baseline`): the id of the study's row in `repro/studies.json` — the paper's
@@ -51,6 +51,14 @@ export const REPRO_FILE_NOTES = [
51
51
  note: "the study registry: id, title, status and result of each study",
52
52
  },
53
53
  { name: ".gitignore", note: "keeps the scripts' byproducts out of git" },
54
+ {
55
+ name: "CLAUDE.md",
56
+ note: "optional: instructions an agent loads when it works in repro/",
57
+ },
58
+ {
59
+ name: "AGENTS.md",
60
+ note: "optional: the same, for agents that read AGENTS.md",
61
+ },
54
62
  ];
55
63
  export const REPRO_FILES = REPRO_FILE_NOTES.map((f) => f.name);
56
64
  /** The shared folders `repro/` may hold besides its study folders. */
@@ -1 +1 @@
1
- {"version":3,"file":"repro-layout.js","sourceRoot":"","sources":["../../src/domain/repro-layout.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"repro-layout.js","sourceRoot":"","sources":["../../src/domain/repro-layout.ts"],"names":[],"mappings":"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"}
package/docs/layout.md CHANGED
@@ -72,6 +72,8 @@ creates one.
72
72
  │ ├── README.md ← the index: one line per study, generated from studies.json
73
73
  │ ├── studies.json ← the study registry: id, title, status and result of each study
74
74
  │ ├── .gitignore ← keeps the scripts' byproducts out of git
75
+ │ ├── CLAUDE.md ← optional: instructions an agent loads when it works in repro/
76
+ │ ├── AGENTS.md ← optional: the same, for agents that read AGENTS.md
75
77
  │ ├── tools/ ← scripts used across the paper (numbers, figures, anonymity check, harnesses)
76
78
  │ ├── data/ ← shared data (tables, caches)
77
79
  │ └── <id>-<slug>/ ← one folder per study or spike, named by its row in studies.json (P1-S03-baseline)
@@ -37,6 +37,8 @@ it: [the map](../../layout.md).
37
37
  │ ├── README.md ← the index: one line per study, generated from studies.json
38
38
  │ ├── studies.json ← the study registry: id, title, status and result of each study
39
39
  │ ├── .gitignore ← keeps the scripts' byproducts out of git
40
+ │ ├── CLAUDE.md ← optional: instructions an agent loads when it works in repro/
41
+ │ ├── AGENTS.md ← optional: the same, for agents that read AGENTS.md
40
42
  │ ├── tools/ ← scripts used across the paper (numbers, figures, anonymity check, harnesses)
41
43
  │ ├── data/ ← shared data (tables, caches)
42
44
  │ └── <id>-<slug>/ ← one folder per study or spike, named by its row in studies.json (P1-S03-baseline)
@@ -19,6 +19,8 @@ repro/
19
19
  ├── README.md ← the index: one line per study, generated from studies.json
20
20
  ├── studies.json ← the study registry: id, title, status and result of each study
21
21
  ├── .gitignore ← keeps the scripts' byproducts out of git
22
+ ├── CLAUDE.md ← optional: instructions an agent loads when it works in repro/
23
+ ├── AGENTS.md ← optional: the same, for agents that read AGENTS.md
22
24
  ├── tools/ ← scripts used across the paper (numbers, figures, anonymity check, harnesses)
23
25
  ├── data/ ← shared data (tables, caches)
24
26
  └── <id>-<slug>/ ← one folder per study or spike, named by its row in studies.json (P1-S03-baseline)
package/docs/testing.md CHANGED
@@ -105,8 +105,11 @@ npm run check # every gate CI runs, and the list of what it
105
105
  ```
106
106
 
107
107
  While editing, run the tests you touched, `npx tsc --noEmit`, `npx eslint <files>` and
108
- `npm run fmt:check`. **Before every push, run `npm run check` and read its exit code** — the
109
- coverage gate cannot be judged from one file.
108
+ `npm run fmt:check`. A `wip:` push to a draft pull request needs only that: CI skips the gates on a
109
+ draft, and the push is not "the gates passed". **Before taking a pull request out of draft, and
110
+ before merging, run `npm run check` and read its exit code** — the coverage gate cannot be judged
111
+ from one file. Waiting ten minutes for the whole suite on every wip push bought nothing the
112
+ pre-ready run does not (2026-11-03).
110
113
 
111
114
  ⚠️ **Not `vigiles test .`**, and never drop `--min=1` from `npm test`. Measured 2026-09-11:
112
115
 
@@ -0,0 +1,10 @@
1
+ # Website
2
+
3
+ The project's domain is **[paperlint.dev](https://paperlint.dev)**, registered in November 2026
4
+ through Vercel. No site is deployed on it yet: the first version is
5
+ [#342](https://github.com/zernie/paperlint/issues/342).
6
+
7
+ - `.dev` is on the HSTS preload list, so browsers only open it over HTTPS: the site needs a
8
+ certificate from its first deploy.
9
+ - Until the site exists, the npm page links to the GitHub README: `package.json` sets no
10
+ `homepage`, only `repository`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paperlint",
3
- "version": "13.3.0",
3
+ "version": "13.3.2",
4
4
  "description": "A linter for scientific papers written in LaTeX: catches mistakes before you submit to a conference or journal.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/scripts/check.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * `npm run check` — ONE command you run on yourself before pushing.
3
+ * `npm run check` — ONE command you run on yourself before a pull request leaves draft, and before merge.
4
4
  *
5
5
  * ── WHY IT EXISTS, AND IT IS A MEASURED FAILURE, NOT A TIDINESS IDEA ───────────────────────
6
6
  * On 2026-09-19 a change to a lint rule was pushed that broke the test suite.
@@ -201,6 +201,9 @@ export function commandOf(gate: Gate): readonly [string, ...string[]] {
201
201
  export const NOT_COVERED: Readonly<Record<string, string>> = {
202
202
  // build-e2e's macOS cell is the one part of a covered job no local command can stand in for:
203
203
  // it exists because issue #9 appeared on macOS alone (/var is a symlink to /private/var).
204
+ changes:
205
+ "ci.yml asks dorny/paths-filter whether a pull request changes anything but docs, so " +
206
+ "build-e2e can skip; it checks nothing itself, and a local run always builds.",
204
207
  merge:
205
208
  "dependabot-automerge.yml merges the bot's own pull requests once CI is green. It checks " +
206
209
  "nothing itself; there is no local equivalent because it acts on GitHub, not on the tree.",
@@ -12,7 +12,11 @@ The verdict is computed from the cells, never stored, over neighbours read in fu
12
12
  Errors: a 'yes' cell without a quote or with a quote not in the text · a 'no' cell on an abstract-only
13
13
  neighbour · a missing search source · a priority sentence in paper.tex (first / none of the studies /
14
14
  to our knowledge / ...) not mapped to a 🟢 claim · CLAIMS.md disagreeing with the computed verdict ·
15
- a 🟢 claim without a refutation file · a claim without a known type or an objection_answer.
15
+ a 🟢 claim without a refutation file · a claim without a known type or an objection_answer · a saved text
16
+ with more than 30 % glued lines (two page columns merged by `pdftotext -layout`).
17
+ The ledger may be MORE cautious than the computed verdict (🟢 > 🟡 > 🔴), never bolder: a claim then carries
18
+ "ledger_narrower_because", a reason naming an existing file in the paper folder. A bolder ledger, a ⚪ on either
19
+ side, or the field beside equal statuses (a stale reason) is an error.
16
20
  A quote is a presence check (quote_match.py's matcher), not a proof that it supports the cell.
17
21
  A neighbour read 'full' proves it with `coverage`, one quote per chunk of its text (novelty_coverage.py).
18
22
  A work a refutation or a search surfaced is a candidate until the author decides on it (novelty_candidates.py):
@@ -29,6 +33,9 @@ from novelty_coverage import coverage_errors # noqa: E402
29
33
  import novelty_candidates # noqa: E402
30
34
  import novelty_cells # noqa: E402
31
35
 
36
+ # Boldness of a ledger status; ⚪ is not on the scale (unchecked is neither more nor less cautious).
37
+ BOLDNESS = {"🟢": 2, "🟡": 1, "🔴": 0}
38
+ FILE_NAME = re.compile(r"[\w./-]+\.(?:md|txt|json|tex|tsv|csv|py|ts|mjs)\b")
32
39
  SOURCES = ("venue-pc", "prev-edition", "arxiv-recent")
33
40
  TYPES = {"new-object", "transfer", "installed-base", "instrument", "refutation", "replication"}
34
41
  # The result itself: what is examined and what number comes out. A neighbour with both already reported the
@@ -53,6 +60,17 @@ PRIORITY = re.compile(
53
60
  )
54
61
 
55
62
 
63
+ # `pdftotext -layout` prints a two-column page as one line per row, the columns joined by a run of spaces, so a
64
+ # sentence is split across two lines and a quote is not contiguous.
65
+ GLUED = re.compile(r"\S {6,}\S")
66
+ GLUED_LIMIT = 0.30
67
+
68
+
69
+ def glued_share(text):
70
+ lines = [l.strip() for l in text.splitlines() if l.strip()]
71
+ return sum(1 for l in lines if GLUED.search(l)) / len(lines) if lines else 0.0
72
+
73
+
56
74
  def tex_sentences(tex):
57
75
  tex = re.sub(r"(?<!\\)%.*", "", tex)
58
76
  tex = re.sub(r"\\(?:cite|ref|label|eqref|autoref|cref|documentclass|usepackage|begin|end|input|include)\*?"
@@ -74,6 +92,17 @@ def ledger_status(claims_md, row):
74
92
  return None
75
93
 
76
94
 
95
+ def narrower_reason_errors(cid, reason, paper):
96
+ """Problems with a claim's ledger_narrower_because: empty, naming no file, or naming a missing one."""
97
+ if not isinstance(reason, str) or not reason.strip():
98
+ return [f"{cid}: ledger_narrower_because is empty (a reason naming a file in the paper folder)"]
99
+ names = FILE_NAME.findall(reason)
100
+ if not names:
101
+ return [f"{cid}: ledger_narrower_because names no file (give the path of the file holding the reason)"]
102
+ return [f"{cid}: ledger_narrower_because names {n}, which does not exist"
103
+ for n in names if not os.path.isfile(os.path.join(paper, n))]
104
+
105
+
77
106
  def verdict(claim, neighbours):
78
107
  pieces = list(claim.get("pieces", {}))
79
108
  if not pieces or not neighbours:
@@ -138,6 +167,10 @@ def main():
138
167
  errors.append(f"neighbour {n['id']}: read 'full' but no text at {n.get('text')}")
139
168
  if os.path.isfile(path):
140
169
  t = open(path, encoding="utf-8", errors="replace").read()
170
+ share = glued_share(t)
171
+ if share > GLUED_LIMIT:
172
+ errors.append(f"neighbour {n['id']}: {n.get('text')} has {share:.0%} glued lines (two page columns"
173
+ " merged into one line); re-extract with plain pdftotext (no -layout)")
141
174
  texts[n["id"]] = (norm(t), letters(t))
142
175
  if n.get("read") == "full":
143
176
  errors += coverage_errors(n["id"], t, n.get("coverage"))
@@ -176,7 +209,19 @@ def main():
176
209
  elif status is None:
177
210
  errors.append(f"{cid}: ledger row {row!r} not found in CLAIMS.md")
178
211
  elif status != v:
179
- errors.append(f"{cid}: CLAIMS.md row {row} says {status}, the cells give {v} ({why})")
212
+ narrower = status in BOLDNESS and v in BOLDNESS and BOLDNESS[status] < BOLDNESS[v]
213
+ reason = c.get("ledger_narrower_because")
214
+ if narrower and "ledger_narrower_because" in c:
215
+ errors += narrower_reason_errors(cid, reason, paper)
216
+ else:
217
+ hint = ("; a more cautious ledger needs ledger_narrower_because naming the file with the reason"
218
+ if narrower else
219
+ "; ledger_narrower_because excuses only a MORE cautious ledger" if "ledger_narrower_because" in c
220
+ else "")
221
+ errors.append(f"{cid}: CLAIMS.md row {row} says {status}, the cells give {v} ({why}){hint}")
222
+ elif "ledger_narrower_because" in c:
223
+ errors.append(f"{cid}: ledger_narrower_because is set but the ledger equals the computed verdict {v}"
224
+ " (stale reason; remove the field)")
180
225
  if v == "🟢":
181
226
  ref = c.get("refutation") or {}
182
227
  if not ref.get("file"):
@@ -21,50 +21,51 @@ import { fileURLToPath } from "node:url";
21
21
  import assert from "node:assert/strict";
22
22
  import { test } from "vitest";
23
23
 
24
- test("check_novelty", () => {
25
- const here = dirname(fileURLToPath(import.meta.url));
26
- const FIXTURE = join(here, "fixtures", "novelty");
27
- const CHECKER = join(here, "check_novelty.py");
24
+ const here = dirname(fileURLToPath(import.meta.url));
25
+ const FIXTURE = join(here, "fixtures", "novelty");
26
+ const CHECKER = join(here, "check_novelty.py");
28
27
 
29
- // Copy the fixture, apply one change, run the checker; returns {code, out}.
30
- type Files = { json: any; tex: string; claims: string };
31
- type Change = (f: Files) => void;
32
- const run = (change: Change = () => undefined) => {
33
- const dir = realpathSync(mkdtempSync(join(tmpdir(), "novelty-")));
34
- try {
35
- cpSync(FIXTURE, dir, { recursive: true });
36
- const jsonPath = join(dir, "siblings", "novelty.json");
37
- const files = {
38
- json: JSON.parse(readFileSync(jsonPath, "utf8")),
39
- tex: readFileSync(join(dir, "paper.tex"), "utf8"),
40
- claims: readFileSync(join(dir, "CLAIMS.md"), "utf8"),
41
- };
42
- change(files);
43
- writeFileSync(jsonPath, JSON.stringify(files.json, null, 2));
44
- writeFileSync(join(dir, "paper.tex"), files.tex);
45
- writeFileSync(join(dir, "CLAIMS.md"), files.claims);
46
- const r = spawnSync("python3", ["-I", CHECKER, dir], {
47
- encoding: "utf8",
48
- });
49
- return { code: r.status, out: `${r.stdout}\n${r.stderr}` };
50
- } finally {
51
- rmSync(dir, { recursive: true, force: true });
52
- }
53
- };
54
- const passes = (change: Change, what: string) => {
55
- const r = run(change);
56
- assert.equal(r.code, 0, `must pass: ${what}\n${r.out}`);
57
- return r.out;
58
- };
59
- const fails = (change: Change, re: RegExp, what: string) => {
60
- const r = run(change);
61
- assert.equal(r.code, 1, `must fail: ${what}\n${r.out}`);
62
- assert.match(r.out, re, `wrong reason for: ${what}\n${r.out}`);
63
- };
64
- const claim = (j: any, id: string) => j.claims.find((c: any) => c.id === id);
65
- const FIRST =
66
- "This is the first rule finder recall reported in this literature.";
28
+ // Copy the fixture, apply one change, run the checker; returns {code, out}.
29
+ type Files = { json: any; tex: string; claims: string; dir: string };
30
+ type Change = (f: Files) => void;
31
+ const run = (change: Change = () => undefined) => {
32
+ const dir = realpathSync(mkdtempSync(join(tmpdir(), "novelty-")));
33
+ try {
34
+ cpSync(FIXTURE, dir, { recursive: true });
35
+ const jsonPath = join(dir, "siblings", "novelty.json");
36
+ const files = {
37
+ json: JSON.parse(readFileSync(jsonPath, "utf8")),
38
+ tex: readFileSync(join(dir, "paper.tex"), "utf8"),
39
+ claims: readFileSync(join(dir, "CLAIMS.md"), "utf8"),
40
+ dir,
41
+ };
42
+ change(files);
43
+ writeFileSync(jsonPath, JSON.stringify(files.json, null, 2));
44
+ writeFileSync(join(dir, "paper.tex"), files.tex);
45
+ writeFileSync(join(dir, "CLAIMS.md"), files.claims);
46
+ const r = spawnSync("python3", ["-I", CHECKER, dir], {
47
+ encoding: "utf8",
48
+ });
49
+ return { code: r.status, out: `${r.stdout}\n${r.stderr}` };
50
+ } finally {
51
+ rmSync(dir, { recursive: true, force: true });
52
+ }
53
+ };
54
+ const passes = (change: Change, what: string) => {
55
+ const r = run(change);
56
+ assert.equal(r.code, 0, `must pass: ${what}\n${r.out}`);
57
+ return r.out;
58
+ };
59
+ const fails = (change: Change, re: RegExp, what: string) => {
60
+ const r = run(change);
61
+ assert.equal(r.code, 1, `must fail: ${what}\n${r.out}`);
62
+ assert.match(r.out, re, `wrong reason for: ${what}\n${r.out}`);
63
+ };
64
+ const claim = (j: any, id: string) => j.claims.find((c: any) => c.id === id);
65
+ const FIRST =
66
+ "This is the first rule finder recall reported in this literature.";
67
67
 
68
+ test("check_novelty", () => {
68
69
  // ── clean fixture: passes, verdicts computed from the cells ────────────────────
69
70
  {
70
71
  const out = passes(() => undefined, "the fixture as committed");
@@ -485,6 +486,139 @@ test("check_novelty", () => {
485
486
  "abstract-only neighbour whose text is saved",
486
487
  );
487
488
  }
489
+ }, 300_000);
488
490
 
489
- console.log("check_novelty.harness: all cases pass");
491
+ test("check_novelty: a ledger more cautious than the cells", () => {
492
+ // ── 4b. a ledger MORE cautious than the cells, with a written reason ──────────
493
+ // A claim may carry "ledger_narrower_because": a reason naming an existing file. With it a ledger status
494
+ // strictly more cautious than the computed one (🟢 > 🟡 > 🔴) is accepted; a bolder one never is, and the
495
+ // field beside equal statuses is a stale reason. P5-1 computes 🟢 in the fixture (refutation file exists).
496
+ {
497
+ const REASON =
498
+ "siblings/2026-10-27-novelty-refutation.md: the refutation pass kept the claim narrower than the cells";
499
+ const P51_GREEN =
500
+ "| P5-1 | The repository's linter does not make 58.3 % of lint-checkable requirements an error | 🟢 ours |";
501
+ const P51_AMBER =
502
+ "| P5-1 | The repository's linter does not make 58.3 % of lint-checkable requirements an error | 🟡 narrowed |";
503
+ const amber = (f: Files, reason?: string) => {
504
+ assert.ok(f.claims.includes(P51_GREEN), "fixture row P5-1 changed");
505
+ f.claims = f.claims.replace(P51_GREEN, P51_AMBER);
506
+ if (reason !== undefined)
507
+ claim(f.json, "P5-1").ledger_narrower_because = reason;
508
+ };
509
+ // (a) ledger 🟡, cells 🟢, field naming an existing file: accepted, the printed verdict stays computed
510
+ {
511
+ const out = passes(
512
+ (f) => amber(f, REASON),
513
+ "(a) ledger 🟡, cells 🟢, reason names an existing file",
514
+ );
515
+ assert.match(
516
+ out,
517
+ /P5-1\s+🟢/,
518
+ "the printed verdict stays the computed one",
519
+ );
520
+ }
521
+ // (b) the same without the field: the error ends with the hint
522
+ fails(
523
+ (f) => amber(f),
524
+ /P5-1: CLAIMS\.md row P5-1 says 🟡, the cells give 🟢.*; a more cautious ledger needs ledger_narrower_because naming the file with the reason/s,
525
+ "(b) ledger 🟡, cells 🟢, no field",
526
+ );
527
+ // (c) ledger bolder than the cells: an error with or without the field
528
+ const oneNo = (f: Files) => {
529
+ claim(f.json, "R1").cells.doe26.measure = { has: "no", quote: null };
530
+ };
531
+ const bolder = (f: Files, reason?: string) => {
532
+ oneNo(f); // R1 computes 🟡
533
+ assert.ok(
534
+ f.claims.includes("| 🔴 taken (Doe 2026, recall 66.3 %) |"),
535
+ "fixture row R1 changed",
536
+ );
537
+ f.claims = f.claims.replace(
538
+ "| 🔴 taken (Doe 2026, recall 66.3 %) |",
539
+ "| 🟢 ours |",
540
+ );
541
+ if (reason !== undefined)
542
+ claim(f.json, "R1").ledger_narrower_because = reason;
543
+ };
544
+ fails(
545
+ (f) => bolder(f, REASON),
546
+ /R1: CLAIMS\.md row R1 says 🟢, the cells give 🟡/,
547
+ "(c) ledger 🟢, cells 🟡, with the field",
548
+ );
549
+ fails(
550
+ (f) => bolder(f),
551
+ /R1: CLAIMS\.md row R1 says 🟢, the cells give 🟡/,
552
+ "(c) ledger 🟢, cells 🟡, no field",
553
+ );
554
+ // (d) the named file is missing, no file is named, or the field is empty
555
+ fails(
556
+ (f) => amber(f, "siblings/no-such-refutation.md: kept narrower"),
557
+ /P5-1: ledger_narrower_because names siblings\/no-such-refutation\.md.*does not exist/,
558
+ "(d) the named file is missing",
559
+ );
560
+ fails(
561
+ (f) => amber(f, "kept narrower by hand"),
562
+ /P5-1: ledger_narrower_because names no file/,
563
+ "(d) a reason naming no file",
564
+ );
565
+ fails(
566
+ (f) => amber(f, " "),
567
+ /P5-1: ledger_narrower_because is empty/,
568
+ "(d) an empty reason",
569
+ );
570
+ // (e) equal statuses with the field: a stale reason
571
+ fails(
572
+ (f) => {
573
+ claim(f.json, "P5-1").ledger_narrower_because = REASON;
574
+ },
575
+ /P5-1: ledger_narrower_because is set but the ledger equals the computed verdict 🟢/,
576
+ "(e) equal statuses with the field",
577
+ );
578
+ // ⚪ is not on the scale: a ledger ⚪ over a computed 🟢 is unchecked, not more cautious
579
+ fails(
580
+ (f) => {
581
+ f.claims = f.claims.replace(
582
+ P51_GREEN,
583
+ P51_GREEN.replace("🟢 ours", "⚪ unchecked"),
584
+ );
585
+ claim(f.json, "P5-1").ledger_narrower_because = REASON;
586
+ },
587
+ /P5-1: CLAIMS\.md row P5-1 says ⚪, the cells give 🟢/,
588
+ "ledger ⚪ over computed 🟢, with the field",
589
+ );
590
+ // the more cautious ledger on a 🔴: ledger 🔴 (as committed), cells 🟡, accepted with the field
591
+ passes((f) => {
592
+ oneNo(f);
593
+ claim(f.json, "R1").ledger_narrower_because = REASON;
594
+ }, "ledger 🔴, cells 🟡, reason names an existing file");
595
+ }
596
+ }, 300_000);
597
+
598
+ test("check_novelty: a text with its columns glued", () => {
599
+ // ── 10. a text extracted with `pdftotext -layout` has its two columns glued into one line ──
600
+ // A sentence is then split across two lines and a verbatim quote is not contiguous. The glued copy pairs
601
+ // the fixture's own lines with an 8-space gap; the clean twin is the fixture as committed.
602
+ {
603
+ const DOE = "doe-preprint.txt";
604
+ const glue = (f: Files) => {
605
+ const p = join(f.dir, "siblings", "data", DOE);
606
+ const lines = readFileSync(p, "utf8")
607
+ .split("\n")
608
+ .filter((l) => l.trim());
609
+ const pairs: string[] = [];
610
+ for (let i = 0; i < lines.length; i += 2)
611
+ pairs.push(`${lines[i]} ${lines[i + 1] ?? ""}`);
612
+ writeFileSync(p, pairs.join("\n") + "\n");
613
+ };
614
+ fails(
615
+ glue,
616
+ /neighbour doe26: siblings\/data\/doe-preprint\.txt has \d+% glued lines.*re-extract with plain pdftotext \(no -layout\)/,
617
+ "a neighbour text with its columns glued",
618
+ );
619
+ passes(
620
+ () => undefined,
621
+ "the same text extracted without -layout (the fixture as committed)",
622
+ );
623
+ }
490
624
  }, 300_000);
@@ -208,6 +208,8 @@ describe("reproMessage — stands alone", () => {
208
208
  "├── README.md ← the index: one line per study, generated from studies.json",
209
209
  "├── studies.json ← the study registry: id, title, status and result of each study",
210
210
  "├── .gitignore ← keeps the scripts' byproducts out of git",
211
+ "├── CLAUDE.md ← optional: instructions an agent loads when it works in repro/",
212
+ "├── AGENTS.md ← optional: the same, for agents that read AGENTS.md",
211
213
  "├── tools/ ← scripts used across the paper (numbers, figures, anonymity check, harnesses)",
212
214
  "├── data/ ← shared data (tables, caches)",
213
215
  "└── <id>-<slug>/ ← one folder per study or spike, named by its row in studies.json (P1-S03-baseline)",
@@ -13,7 +13,7 @@
13
13
  * ├── tools/ scripts used across the paper (numbers, figures, anonymity check, harnesses)
14
14
  * └── data/ shared data (tables, caches)
15
15
  *
16
- * plus `.gitignore`. A loose note belongs in its study's folder or in the paper's `research/`.
16
+ * plus `.gitignore`, and optionally the agent instruction files `CLAUDE.md` and `AGENTS.md`. A loose note belongs in its study's folder or in the paper's `research/`.
17
17
  *
18
18
  * ── A STUDY'S NAME ───────────────────────────────────────────────────────────────
19
19
  * `<id>-<slug>` (`P1-S03-baseline`): the id of the study's row in `repro/studies.json` — the paper's
@@ -58,6 +58,14 @@ export const REPRO_FILE_NOTES: readonly {
58
58
  note: "the study registry: id, title, status and result of each study",
59
59
  },
60
60
  { name: ".gitignore", note: "keeps the scripts' byproducts out of git" },
61
+ {
62
+ name: "CLAUDE.md",
63
+ note: "optional: instructions an agent loads when it works in repro/",
64
+ },
65
+ {
66
+ name: "AGENTS.md",
67
+ note: "optional: the same, for agents that read AGENTS.md",
68
+ },
61
69
  ];
62
70
  export const REPRO_FILES: readonly string[] = REPRO_FILE_NOTES.map(
63
71
  (f) => f.name,
@@ -94,6 +94,16 @@ describe("repro/allowed-files-only — silent", () => {
94
94
  ).toEqual([]);
95
95
  });
96
96
 
97
+ it("on agent instruction files at the top (CLAUDE.md, AGENTS.md)", () => {
98
+ expect(
99
+ lint({
100
+ ...SCAFFOLD,
101
+ [`${DIR}/repro/CLAUDE.md`]: "x",
102
+ [`${DIR}/repro/AGENTS.md`]: "x",
103
+ }),
104
+ ).toEqual([]);
105
+ });
106
+
97
107
  it("on a paper with no repro/", () => {
98
108
  expect(lint(SCAFFOLD)).toEqual([]);
99
109
  });