paperlint 9.0.0 โ 10.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/CLAUDE.md +22 -0
- package/.github/CLAUDE.md.spec.ts +20 -0
- package/CLAUDE.md +59 -515
- package/CONTRIBUTING.md +2 -2
- package/README.md +68 -128
- package/dist/CLAUDE.md.spec.d.ts +3 -0
- package/dist/CLAUDE.md.spec.d.ts.map +1 -0
- package/dist/CLAUDE.md.spec.js +62 -0
- package/dist/CLAUDE.md.spec.js.map +1 -0
- package/dist/build.d.ts +1 -1
- package/dist/build.js +2 -2
- package/dist/cli.d.ts +5 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +52 -6
- package/dist/cli.js.map +1 -1
- package/dist/domain/layout-tree.d.ts +19 -0
- package/dist/domain/layout-tree.d.ts.map +1 -0
- package/dist/domain/layout-tree.js +32 -0
- package/dist/domain/layout-tree.js.map +1 -0
- package/dist/domain/lookup-cache.d.ts +2 -2
- package/dist/domain/lookup-cache.js +2 -2
- package/dist/domain/paper-layout.d.ts +81 -0
- package/dist/domain/paper-layout.d.ts.map +1 -0
- package/dist/domain/paper-layout.js +259 -0
- package/dist/domain/paper-layout.js.map +1 -0
- package/dist/domain/repro-layout.d.ts +80 -0
- package/dist/domain/repro-layout.d.ts.map +1 -0
- package/dist/domain/repro-layout.js +247 -0
- package/dist/domain/repro-layout.js.map +1 -0
- package/dist/domain/scaffold-layout.d.ts +28 -0
- package/dist/domain/scaffold-layout.d.ts.map +1 -0
- package/dist/domain/scaffold-layout.js +81 -0
- package/dist/domain/scaffold-layout.js.map +1 -0
- package/dist/eslint-rules/CLAUDE.md.spec.d.ts +3 -0
- package/dist/eslint-rules/CLAUDE.md.spec.d.ts.map +1 -0
- package/dist/eslint-rules/CLAUDE.md.spec.js +39 -0
- package/dist/eslint-rules/CLAUDE.md.spec.js.map +1 -0
- package/dist/eslint-rules/install-path-literals.d.ts +1 -1
- package/dist/eslint-rules/install-path-literals.js +4 -4
- package/dist/eslint-rules/install-path-literals.js.map +1 -1
- package/dist/eslint-rules/pdf-last-page-balance.js +1 -1
- package/dist/eslint-rules/prereg-coverage.d.ts +98 -0
- package/dist/eslint-rules/prereg-coverage.d.ts.map +1 -0
- package/dist/eslint-rules/prereg-coverage.js +274 -0
- package/dist/eslint-rules/prereg-coverage.js.map +1 -0
- package/dist/folder-layout-hook.d.ts +21 -0
- package/dist/folder-layout-hook.d.ts.map +1 -0
- package/dist/folder-layout-hook.js +117 -0
- package/dist/folder-layout-hook.js.map +1 -0
- package/dist/folder-layout-rule.d.ts +22 -0
- package/dist/folder-layout-rule.d.ts.map +1 -0
- package/dist/folder-layout-rule.js +107 -0
- package/dist/folder-layout-rule.js.map +1 -0
- package/dist/init.js +1 -1
- package/dist/init.js.map +1 -1
- package/dist/lib/agent-cli-version.js +1 -1
- package/dist/lib/agent-cli-version.js.map +1 -1
- package/dist/new-paper.d.ts +4 -1
- package/dist/new-paper.d.ts.map +1 -1
- package/dist/new-paper.js +40 -6
- package/dist/new-paper.js.map +1 -1
- package/dist/references.d.ts +1 -1
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +2 -2
- package/dist/references.js.map +1 -1
- package/dist/repro-layout-rule.d.ts +16 -0
- package/dist/repro-layout-rule.d.ts.map +1 -0
- package/dist/repro-layout-rule.js +81 -0
- package/dist/repro-layout-rule.js.map +1 -0
- package/dist/tex-requirements.js +1 -1
- package/dist/toolchain.js +1 -1
- package/docs/ci.md +60 -0
- package/docs/configuration.md +2 -0
- package/docs/e2e.md +3 -3
- package/docs/incidents.md +5 -5
- package/docs/install.md +20 -16
- package/docs/layout.md +154 -0
- package/docs/optional-rules.md +5 -4
- package/docs/references.md +5 -2
- package/docs/rules/paper/allowed-files-only.md +192 -0
- package/docs/rules/repro/allowed-files-only.md +128 -0
- package/docs/rules/study/declare-sample.md +146 -0
- package/docs/rules.md +53 -50
- package/docs/skills.md +6 -3
- package/docs/venues.md +94 -0
- package/eslint-rules/CLAUDE.md +44 -0
- package/eslint-rules/CLAUDE.md.spec.ts +43 -0
- package/eslint-rules/install-path-literals.harness.mjs +1 -1
- package/eslint-rules/install-path-literals.ts +4 -4
- package/eslint-rules/pdf-last-page-balance.ts +1 -1
- package/eslint-rules/prereg-coverage.test.ts +448 -0
- package/eslint-rules/prereg-coverage.ts +419 -0
- package/eslint.config.mjs +6 -3
- package/hooks/CLAUDE.md +34 -0
- package/hooks/CLAUDE.md.spec.ts +30 -0
- package/lib/agent-cli-version.ts +1 -1
- package/lib/skill-trigger-cases.harness.mjs +51 -5
- package/lib/skill-trigger-cases.mjs +53 -15
- package/package.json +2 -2
- package/plugin/CLAUDE.md +22 -0
- package/plugin/CLAUDE.md.spec.ts +22 -0
- package/plugin/hooks/hooks.json +9 -0
- package/scripts/coverage-config.test.ts +2 -1
- package/skills/CLAUDE.md +19 -0
- package/skills/CLAUDE.md.spec.ts +18 -0
- package/skills/analyze-sibling-paper/SKILL.md +2 -2
- package/skills/analyze-sibling-paper/SKILL.md.spec.ts +1 -1
- package/skills/build-benchmark/SKILL.md +14 -1
- package/skills/build-benchmark/SKILL.md.spec.ts +13 -0
- package/skills/build-label-page/SKILL.md +118 -0
- package/skills/build-label-page/SKILL.md.spec.ts +129 -0
- package/skills/build-label-page/build-label-page.eval.mjs +15 -0
- package/skills/build-label-page/build-label-page.test.ts +959 -0
- package/skills/build-label-page/references/checklist.md +148 -0
- package/skills/build-label-page/references/example-generator.py +803 -0
- package/skills/find-venue/SKILL.md +5 -3
- package/skills/find-venue/SKILL.md.spec.ts +4 -2
- package/skills/grade-paper-writing/SKILL.md +7 -7
- package/skills/grade-paper-writing/SKILL.md.spec.ts +6 -6
- package/skills/map-prior-work/SKILL.md +2 -2
- package/skills/map-prior-work/SKILL.md.spec.ts +1 -1
- package/skills/paper-pipeline/SKILL.md +8 -6
- package/skills/paper-pipeline/SKILL.md.spec.ts +7 -5
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +19 -9
- package/skills/paper-pipeline/references/acceptance-gate.md +19 -19
- package/skills/paper-pipeline/references/artifact-checklist.md +5 -0
- package/skills/paper-pipeline/references/pipeline-status-template.md +30 -1
- package/skills/paper-pipeline/repro/2026-08-07-fork-check.log +1 -1
- package/skills/paper-pipeline/repro/2026-08-07-fork-check2.log +1 -1
- package/skills/paper-pipeline/repro/2026-08-07-language-eval-pilot.log +1 -1
- package/skills/paper-pipeline/repro/2026-08-07-language-eval-raw.log +1 -1
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.log +48 -48
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-oracle.log +8 -8
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.log +48 -48
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.log +2 -2
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication.mjs +3 -3
- package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-ablation-preflight-after.log +81 -0
- package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-ablation-preflight-before.log +81 -0
- package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-ablation-setupdiff.log +59 -0
- package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-trigger.jsonl +1 -0
- package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-trigger.log +22 -0
- package/skills/paper-pipeline/repro/README.md +4 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +2 -2
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +2 -2
- package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +4 -2
- package/skills/paper-pipeline/scripts/generated-code.harness.mjs +7 -2
- package/skills/paper-pipeline/scripts/generated-code.mjs +8 -4
- package/skills/paper-pipeline/scripts/generated-code.test.ts +15 -1
- package/skills/paper-status/SKILL.md +2 -2
- package/skills/paper-status/SKILL.md.spec.ts +1 -1
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +21 -102
- package/skills/study-accepted-papers/SKILL.md +163 -161
- package/skills/study-accepted-papers/SKILL.md.spec.ts +167 -165
- package/skills/study-accepted-papers/references/base-rates.md +69 -0
- package/skills/study-accepted-papers/references/mechanisms.md +181 -0
- package/skills/verify-citations/SKILL.md +2 -2
- package/skills/verify-citations/SKILL.md.spec.ts +1 -1
- package/src/CLAUDE.md +23 -1
- package/src/CLAUDE.md.spec.ts +72 -0
- package/src/build.ts +2 -2
- package/src/cli.harness.mjs +42 -0
- package/src/cli.test.ts +90 -6
- package/src/cli.ts +78 -5
- package/src/domain/layout-tree.test.ts +32 -0
- package/src/domain/layout-tree.ts +47 -0
- package/src/domain/lookup-cache.ts +2 -2
- package/src/domain/paper-layout.test.ts +264 -0
- package/src/domain/paper-layout.ts +336 -0
- package/src/domain/repro-layout.test.ts +222 -0
- package/src/domain/repro-layout.ts +311 -0
- package/src/domain/scaffold-layout.ts +104 -0
- package/src/folder-layout-hook.test.ts +214 -0
- package/src/folder-layout-hook.ts +141 -0
- package/src/folder-layout-rule.test.ts +235 -0
- package/src/folder-layout-rule.ts +151 -0
- package/src/hooks-settings.harness.ts +9 -8
- package/src/init.ts +1 -1
- package/src/layout-docs.test.ts +52 -0
- package/src/new-paper.harness.mjs +23 -1
- package/src/new-paper.test.ts +299 -6
- package/src/new-paper.ts +51 -5
- package/src/paper-files.test.ts +9 -6
- package/src/references.test.ts +5 -5
- package/src/references.ts +2 -2
- package/src/repro-layout-rule.test.ts +114 -0
- package/src/repro-layout-rule.ts +121 -0
- package/src/rule-cases.test.ts +98 -0
- package/src/tex-requirements.ts +1 -1
- package/src/toolchain.ts +1 -1
- package/templates/paper/PIPELINE-STATUS.md +41 -7
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `repro/allowed-files-only` (`src/repro-layout-rule.ts`) on paper folders held in memory, run the way
|
|
3
|
+
* ESLint runs it: `create`, then `root`. Both halves: each misplaced entry at the top of `repro/` is
|
|
4
|
+
* reported once, on the first line of `PIPELINE-STATUS.md`; the layout, a paper with no `repro/`, and
|
|
5
|
+
* any file but the scorecard are not.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, expect, it } from "vitest";
|
|
8
|
+
import { memoryFiles, memoryListDir } from "./adapters/memory/index.ts";
|
|
9
|
+
import { reproMessage, SCRIPT_CURE } from "./domain/repro-layout.ts";
|
|
10
|
+
import {
|
|
11
|
+
REPRO_LAYOUT_RULE_LEVELS,
|
|
12
|
+
reproLayoutRules,
|
|
13
|
+
} from "./repro-layout-rule.ts";
|
|
14
|
+
|
|
15
|
+
const DIR = "/work/papers/p";
|
|
16
|
+
const SCAFFOLD = {
|
|
17
|
+
[`${DIR}/PIPELINE-STATUS.md`]: "# status\n",
|
|
18
|
+
[`${DIR}/paper.tex`]: "\\documentclass{article}\n",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
interface Reported {
|
|
22
|
+
readonly messageId: string;
|
|
23
|
+
readonly message: string;
|
|
24
|
+
readonly line: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** The rule over the folder `tree` describes, reached through `file` (default the scorecard). */
|
|
28
|
+
function lint(
|
|
29
|
+
tree: Readonly<Record<string, string>>,
|
|
30
|
+
file = `${DIR}/PIPELINE-STATUS.md`,
|
|
31
|
+
): Reported[] {
|
|
32
|
+
const files = memoryFiles(tree);
|
|
33
|
+
const rule = reproLayoutRules({ files, listDir: memoryListDir(files) })[
|
|
34
|
+
"allowed-files-only"
|
|
35
|
+
];
|
|
36
|
+
const out: Reported[] = [];
|
|
37
|
+
const visitor = rule.create({
|
|
38
|
+
filename: file,
|
|
39
|
+
sourceCode: { getLocFromIndex: () => ({ line: 1, column: 0 }) },
|
|
40
|
+
report: (d) => {
|
|
41
|
+
const template = rule.meta.messages[d.messageId] ?? "";
|
|
42
|
+
const message = Object.entries(d.data).reduce(
|
|
43
|
+
(m, [k, v]) => m.split(`{{${k}}}`).join(String(v)),
|
|
44
|
+
template,
|
|
45
|
+
);
|
|
46
|
+
out.push({ messageId: d.messageId, message, line: d.loc.start.line });
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
visitor.root?.();
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
describe("repro/allowed-files-only โ reports", () => {
|
|
54
|
+
it("a loose script, once, on the first line, with the one text", () => {
|
|
55
|
+
expect(lint({ ...SCAFFOLD, [`${DIR}/repro/run.py`]: "x" })).toEqual([
|
|
56
|
+
{
|
|
57
|
+
messageId: "misplaced",
|
|
58
|
+
message: reproMessage({
|
|
59
|
+
path: "repro/run.py",
|
|
60
|
+
problem: "stray",
|
|
61
|
+
cure: SCRIPT_CURE,
|
|
62
|
+
}),
|
|
63
|
+
line: 1,
|
|
64
|
+
},
|
|
65
|
+
]);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("an undated folder and a study without a README.md, each once, in name order", () => {
|
|
69
|
+
expect(
|
|
70
|
+
lint({
|
|
71
|
+
...SCAFFOLD,
|
|
72
|
+
[`${DIR}/repro/spike/x.py`]: "x",
|
|
73
|
+
[`${DIR}/repro/ablation-2026-10-12/run.py`]: "x",
|
|
74
|
+
}).map((r) => r.message.split(" ", 1).join("")),
|
|
75
|
+
).toEqual(["`repro/ablation-2026-10-12/`", "`repro/spike/`"]);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("repro/allowed-files-only โ silent", () => {
|
|
80
|
+
it("on the layout, whatever tools/, data/ and the studies hold", () => {
|
|
81
|
+
expect(
|
|
82
|
+
lint({
|
|
83
|
+
...SCAFFOLD,
|
|
84
|
+
[`${DIR}/repro/README.md`]: "x",
|
|
85
|
+
[`${DIR}/repro/.gitignore`]: "x",
|
|
86
|
+
[`${DIR}/repro/tools/paper_numbers.py`]: "x",
|
|
87
|
+
[`${DIR}/repro/data/numbers.tsv`]: "x",
|
|
88
|
+
[`${DIR}/repro/data/references-cache.json`]: "{}",
|
|
89
|
+
[`${DIR}/repro/ablation-2026-10-12/README.md`]: "x",
|
|
90
|
+
[`${DIR}/repro/ablation-2026-10-12/loose.py`]: "x",
|
|
91
|
+
[`${DIR}/repro/human-labels-2026-10/README.md`]: "x",
|
|
92
|
+
[`${DIR}/repro/__pycache__/x.pyc`]: "x",
|
|
93
|
+
}),
|
|
94
|
+
).toEqual([]);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("on a paper with no repro/", () => {
|
|
98
|
+
expect(lint(SCAFFOLD)).toEqual([]);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("on any file but PIPELINE-STATUS.md", () => {
|
|
102
|
+
expect(
|
|
103
|
+
lint({ ...SCAFFOLD, [`${DIR}/repro/run.py`]: "x" }, `${DIR}/paper.tex`),
|
|
104
|
+
).toEqual([]);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe("repro/allowed-files-only โ level", () => {
|
|
109
|
+
it("is an error from the start", () => {
|
|
110
|
+
expect(REPRO_LAYOUT_RULE_LEVELS).toEqual({
|
|
111
|
+
"repro/allowed-files-only": "error",
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `repro/allowed-files-only` โ the top of a paper's `repro/` holds an index, the shared `tools/` and
|
|
3
|
+
* `data/`, and one dated folder per study, each with a `README.md`.
|
|
4
|
+
*
|
|
5
|
+
* What may stand there, and why, is `src/domain/repro-layout.ts` and docs/rules/repro/allowed-files-only.md.
|
|
6
|
+
* This file lists the folder and reports.
|
|
7
|
+
*
|
|
8
|
+
* โโ WHY A RULE OF ITS OWN, NOT MORE OF `paper/allowed-files-only` โโโโโโโโโโโโโโโโโโโโโ
|
|
9
|
+
* Each entry is judged by exactly one rule: `paper/allowed-files-only` judges the paper folder's root,
|
|
10
|
+
* where `repro/` is allowed, and this rule judges what is inside `repro/`. Neither reports an entry
|
|
11
|
+
* the other does, so a stray entry gets one message. A rule of its own also has its own page and its
|
|
12
|
+
* own switch: a paper whose `repro/` predates the layout can turn this one off and keep its root
|
|
13
|
+
* judged.
|
|
14
|
+
*
|
|
15
|
+
* โโ WHICH FILE IT REPORTS ON โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
16
|
+
* `PIPELINE-STATUS.md`, first line, like `paper/allowed-files-only`: every paper has it, and `repro/` may
|
|
17
|
+
* have no file ESLint lints at all.
|
|
18
|
+
*/
|
|
19
|
+
import { basename, dirname, join } from "node:path";
|
|
20
|
+
import { callerPath } from "./caller-path.ts";
|
|
21
|
+
import {
|
|
22
|
+
README,
|
|
23
|
+
REPRO_DIR,
|
|
24
|
+
judgeRepro,
|
|
25
|
+
reproMessage,
|
|
26
|
+
type ReproEntry,
|
|
27
|
+
} from "./domain/repro-layout.ts";
|
|
28
|
+
import type {
|
|
29
|
+
FolderRuleContext,
|
|
30
|
+
FolderRuleModule,
|
|
31
|
+
} from "./folder-venue-rule.ts";
|
|
32
|
+
import type { FolderLayoutDeps } from "./folder-layout-rule.ts";
|
|
33
|
+
import type { Files } from "./ports/files.ts";
|
|
34
|
+
import type { LayoutEntry } from "./domain/paper-layout.ts";
|
|
35
|
+
import { rulePageUrl } from "./venue-rules.ts";
|
|
36
|
+
|
|
37
|
+
const STATUS_FILE = "PIPELINE-STATUS.md";
|
|
38
|
+
|
|
39
|
+
/** `entries` of `<dir>/repro/`, each folder with whether it holds a README.md. */
|
|
40
|
+
const withReadme = (
|
|
41
|
+
dir: string,
|
|
42
|
+
entries: readonly LayoutEntry[],
|
|
43
|
+
files: Files,
|
|
44
|
+
): readonly ReproEntry[] =>
|
|
45
|
+
entries.map((e) => ({
|
|
46
|
+
...e,
|
|
47
|
+
hasReadme:
|
|
48
|
+
e.kind === "dir" &&
|
|
49
|
+
files.isFile(callerPath(join(dir, REPRO_DIR, e.name, README))),
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The text of each finding among `entries` at the top of the paper folder `dir`'s `repro/` โ THE one
|
|
54
|
+
* function behind both the rule and the `paper-folder-layout` hook, so the two cannot disagree.
|
|
55
|
+
*/
|
|
56
|
+
export const reproTexts = (
|
|
57
|
+
dir: string,
|
|
58
|
+
entries: readonly LayoutEntry[],
|
|
59
|
+
files: Files,
|
|
60
|
+
): readonly string[] =>
|
|
61
|
+
judgeRepro(withReadme(dir, entries, files)).map(reproMessage);
|
|
62
|
+
|
|
63
|
+
/** The entries at the top of `<dir>/repro/`, each a file or a folder; none without a `repro/`. */
|
|
64
|
+
export const reproEntriesOf = (
|
|
65
|
+
dir: string,
|
|
66
|
+
deps: FolderLayoutDeps,
|
|
67
|
+
): readonly LayoutEntry[] =>
|
|
68
|
+
[...deps.listDir(callerPath(join(dir, REPRO_DIR)))].sort().map((name) => ({
|
|
69
|
+
name,
|
|
70
|
+
kind: deps.files.isFile(callerPath(join(dir, REPRO_DIR, name)))
|
|
71
|
+
? "file"
|
|
72
|
+
: "dir",
|
|
73
|
+
}));
|
|
74
|
+
|
|
75
|
+
const META: FolderRuleModule["meta"] = {
|
|
76
|
+
type: "suggestion",
|
|
77
|
+
docs: {
|
|
78
|
+
description:
|
|
79
|
+
"the top of a paper's repro/ holds README.md, .gitignore, tools/, data/ and one <slug>-YYYY-MM-DD folder per study, each with a README.md",
|
|
80
|
+
url: rulePageUrl("repro/allowed-files-only"),
|
|
81
|
+
},
|
|
82
|
+
schema: [],
|
|
83
|
+
messages: {
|
|
84
|
+
// The whole text is `reproMessage` (src/domain/repro-layout.ts), the one the hook sends too.
|
|
85
|
+
misplaced: "{{text}}",
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/** The rule, reading the folder through `deps`. */
|
|
90
|
+
export function reproLayoutRules(
|
|
91
|
+
deps: FolderLayoutDeps,
|
|
92
|
+
): Readonly<Record<"allowed-files-only", FolderRuleModule>> {
|
|
93
|
+
return {
|
|
94
|
+
"allowed-files-only": {
|
|
95
|
+
meta: META,
|
|
96
|
+
create(context: FolderRuleContext) {
|
|
97
|
+
if (basename(context.filename) !== STATUS_FILE) return {};
|
|
98
|
+
return {
|
|
99
|
+
root() {
|
|
100
|
+
const dir = dirname(context.filename);
|
|
101
|
+
const at = context.sourceCode.getLocFromIndex(0);
|
|
102
|
+
reproTexts(dir, reproEntriesOf(dir, deps), deps.files).forEach(
|
|
103
|
+
(text) => {
|
|
104
|
+
context.report({
|
|
105
|
+
loc: { start: at, end: at },
|
|
106
|
+
messageId: "misplaced",
|
|
107
|
+
data: { text },
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** The level it is on at in paperlint's own config, for every paper's `PIPELINE-STATUS.md`. */
|
|
119
|
+
export const REPRO_LAYOUT_RULE_LEVELS: Readonly<
|
|
120
|
+
Record<"repro/allowed-files-only", "error">
|
|
121
|
+
> = { "repro/allowed-files-only": "error" };
|
package/src/rule-cases.test.ts
CHANGED
|
@@ -39,6 +39,14 @@ const paper = (
|
|
|
39
39
|
...extra,
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
+
/** `study/declare-sample` is off by default; a paper turns it on in its `paperlint.json`. */
|
|
43
|
+
const DECLARE_SAMPLE_ON: Readonly<Record<string, string>> = {
|
|
44
|
+
[`${P}/paperlint.json`]: JSON.stringify({
|
|
45
|
+
extends: null,
|
|
46
|
+
rules: { "study/declare-sample": "error" },
|
|
47
|
+
}),
|
|
48
|
+
};
|
|
49
|
+
|
|
42
50
|
// โโ the venues: presets of the tree's own, never the shipped ones โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
43
51
|
//
|
|
44
52
|
// A case judges a RULE, so every number it is sized against is written here, beside the facts and
|
|
@@ -693,6 +701,78 @@ const CASES: Readonly<Record<string, RuleCases>> = {
|
|
|
693
701
|
...onBlindConf("papers/blindconf-2026"),
|
|
694
702
|
},
|
|
695
703
|
},
|
|
704
|
+
// A plan beside the paper: the root holds only paperlint's own files and folders.
|
|
705
|
+
"paper/allowed-files-only": {
|
|
706
|
+
reports: {
|
|
707
|
+
tree: paper(tex("x"), { [`${P}/PLAN.md`]: "# plan\n" }),
|
|
708
|
+
file: `${P}/PIPELINE-STATUS.md`,
|
|
709
|
+
severity: 2,
|
|
710
|
+
line: 1,
|
|
711
|
+
},
|
|
712
|
+
// The same material where the layout puts it.
|
|
713
|
+
silent: paper(tex("x"), {
|
|
714
|
+
[`${P}/research/plan.md`]: "# plan\n",
|
|
715
|
+
[`${P}/extra/PLAN.md`]: "# plan\n",
|
|
716
|
+
}),
|
|
717
|
+
},
|
|
718
|
+
// A loose script at the top of repro/: it belongs in repro/tools/.
|
|
719
|
+
"repro/allowed-files-only": {
|
|
720
|
+
reports: {
|
|
721
|
+
tree: paper(tex("x"), { [`${P}/repro/run.py`]: "print(1)\n" }),
|
|
722
|
+
file: `${P}/PIPELINE-STATUS.md`,
|
|
723
|
+
severity: 2,
|
|
724
|
+
line: 1,
|
|
725
|
+
},
|
|
726
|
+
// The same script where the layout puts it, and a dated study with its README.md.
|
|
727
|
+
silent: paper(tex("x"), {
|
|
728
|
+
[`${P}/repro/README.md`]: "# repro\n",
|
|
729
|
+
[`${P}/repro/tools/run.py`]: "print(1)\n",
|
|
730
|
+
[`${P}/repro/ablation-2026-10-12/README.md`]: "# ablation\n",
|
|
731
|
+
}),
|
|
732
|
+
},
|
|
733
|
+
// A study's prereg.json that declares none of population, strata, screens, coverage, author_check.
|
|
734
|
+
// Off by default: the paper turns it on, as both trees do.
|
|
735
|
+
"study/declare-sample": {
|
|
736
|
+
reports: {
|
|
737
|
+
tree: paper(tex("x"), {
|
|
738
|
+
...DECLARE_SAMPLE_ON,
|
|
739
|
+
[`${P}/repro/README.md`]: "# repro\n",
|
|
740
|
+
[`${P}/repro/draw-2026-10-12/README.md`]: "# draw\n",
|
|
741
|
+
[`${P}/repro/draw-2026-10-12/prereg.json`]: JSON.stringify({
|
|
742
|
+
hypotheses: ["H1"],
|
|
743
|
+
}),
|
|
744
|
+
}),
|
|
745
|
+
file: `${P}/PIPELINE-STATUS.md`,
|
|
746
|
+
severity: 2,
|
|
747
|
+
line: 1,
|
|
748
|
+
},
|
|
749
|
+
// The same study, declaring them.
|
|
750
|
+
silent: paper(tex("x"), {
|
|
751
|
+
...DECLARE_SAMPLE_ON,
|
|
752
|
+
[`${P}/repro/README.md`]: "# repro\n",
|
|
753
|
+
[`${P}/repro/draw-2026-10-12/README.md`]: "# draw\n",
|
|
754
|
+
[`${P}/repro/draw-2026-10-12/prereg.json`]: JSON.stringify({
|
|
755
|
+
hypotheses: ["H1"],
|
|
756
|
+
population: {
|
|
757
|
+
about: "rules in instruction files",
|
|
758
|
+
source: "a census, 2026-07-28",
|
|
759
|
+
size: 4210,
|
|
760
|
+
},
|
|
761
|
+
strata: { by: "rule type", values: ["crisp", "vague"] },
|
|
762
|
+
screens: [
|
|
763
|
+
{ name: "fits one excerpt", removed: { crisp: 10, vague: 300 } },
|
|
764
|
+
],
|
|
765
|
+
coverage: { share: 0.3, unreachable: ["vague"] },
|
|
766
|
+
author_check: {
|
|
767
|
+
who: "first author",
|
|
768
|
+
when: "2026-10-12",
|
|
769
|
+
units_seen: 12,
|
|
770
|
+
units_drawn: 12,
|
|
771
|
+
verdict: "representative",
|
|
772
|
+
},
|
|
773
|
+
}),
|
|
774
|
+
}),
|
|
775
|
+
},
|
|
696
776
|
// EMNLP, a shipped venue's name (REALM's conference), in a BlindConf paper's text.
|
|
697
777
|
"tex/venue-leftover": {
|
|
698
778
|
reports: {
|
|
@@ -944,6 +1024,24 @@ describe("every registered rule has cases, and every case names a registered rul
|
|
|
944
1024
|
});
|
|
945
1025
|
});
|
|
946
1026
|
|
|
1027
|
+
describe("study/declare-sample is off by default", () => {
|
|
1028
|
+
it("is silent on an empty prereg.json until the paper turns it on", async () => {
|
|
1029
|
+
const study = {
|
|
1030
|
+
[`${P}/repro/README.md`]: "# repro\n",
|
|
1031
|
+
[`${P}/repro/draw-2026-10-12/README.md`]: "# draw\n",
|
|
1032
|
+
[`${P}/repro/draw-2026-10-12/prereg.json`]: "{}",
|
|
1033
|
+
};
|
|
1034
|
+
const ids = async (tree: Record<string, string>) =>
|
|
1035
|
+
(await lintTree(tree)).map((m) => m.ruleId);
|
|
1036
|
+
expect(await ids(paper(tex("x"), study))).not.toContain(
|
|
1037
|
+
"study/declare-sample",
|
|
1038
|
+
);
|
|
1039
|
+
expect(
|
|
1040
|
+
await ids(paper(tex("x"), { ...study, ...DECLARE_SAMPLE_ON })),
|
|
1041
|
+
).toContain("study/declare-sample");
|
|
1042
|
+
});
|
|
1043
|
+
});
|
|
1044
|
+
|
|
947
1045
|
/**
|
|
948
1046
|
* A case lints its tree through the whole CLI: a body long enough for a rate to be judged (2 000
|
|
949
1047
|
* words and more) takes seconds under coverage, on a machine running every suite at once.
|
package/src/tex-requirements.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* of every violation, it is never read as "no packages".
|
|
18
18
|
*
|
|
19
19
|
* Where the profiles live is answered by `presetsDir()` in `package-dirs.ts` โ the one module
|
|
20
|
-
* allowed to know where this package's data is installed (
|
|
20
|
+
* allowed to know where this package's data is installed (root CLAUDE.md).
|
|
21
21
|
*/
|
|
22
22
|
// eslint-disable-next-line boundaries/dependencies -- legacy I/O, moves behind a port in #76
|
|
23
23
|
import { readFileSync, readdirSync } from "node:fs";
|
package/src/toolchain.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* declare, into paperlint's own cache, and banal (the page-geometry script HotCRP runs) at its pinned
|
|
4
4
|
* commit (`adapters/banal/`). `paperlint build` offers the TeX Live install on a terminal.
|
|
5
5
|
*
|
|
6
|
-
* ๐ด WHY paperlint INSTALLS TeX AT ALL (
|
|
6
|
+
* ๐ด WHY paperlint INSTALLS TeX AT ALL (root CLAUDE.md: installing must be smooth). "Install TeX Live yourself" was a manual step with a
|
|
7
7
|
* trap in it: the distribution packages cost 2.1 GB, and a smaller hand-picked set silently typeset
|
|
8
8
|
* acmart papers in Computer Modern. The engine decision (2026-09-24) is TeX Live's pdflatex,
|
|
9
9
|
* installed the way Playwright installs browsers: one command, a cache directory, idempotent.
|
|
@@ -13,7 +13,41 @@ Updated: <ISO>
|
|
|
13
13
|
**Readiness verdict:** <one line โ submit-ready? if not, the WORST failing gate first. Any failing
|
|
14
14
|
gate caps "ready"; an accept probability alone is not a verdict.>
|
|
15
15
|
|
|
16
|
-
**Gates at a glance (worst first โ mark any failing gate โ):** readability(persona stalls) <density/verdict> ยท structure(tighten-paper) <verdict> ยท claims-honest(claims) <clean?> ยท defects(panel) <accept-prob/decision> ยท writing <NN/60> ยท content(
|
|
16
|
+
**Gates at a glance (worst first โ mark any failing gate โ):** readability(persona stalls) <density/verdict> ยท structure(tighten-paper) <verdict> ยท claims-honest(claims) <clean?> ยท defects(panel) <accept-prob/decision> ยท writing <NN/60> ยท content(strong-accept scorecard) <run?/mode>
|
|
17
|
+
|
|
18
|
+
## Plan / now
|
|
19
|
+
|
|
20
|
+
The paper's live plan: this file is the paper's one live-state file, so the plan lives here, not in a
|
|
21
|
+
`PLAN.md` beside it (`paper/allowed-files-only` reports one). Rewrite this section as the plan changes; a
|
|
22
|
+
decision's history goes to `research/` as a dated record.
|
|
23
|
+
|
|
24
|
+
- **Now (<ISO date>):** <what is being worked on, and the next step>
|
|
25
|
+
- **Decisions in force:** <the decision โ who made it, ISO date, link to the record>
|
|
26
|
+
- **Schedule:** <ISO date โ milestone, in the author's own zone>
|
|
27
|
+
|
|
28
|
+
## Strong-accept scorecard (M1โM10)
|
|
29
|
+
|
|
30
|
+
Filled by `study-accepted-papers`: mode C scores every row, mode D checks M2 and M4 again. What each
|
|
31
|
+
mechanism is, and what it is not, is that skill's `references/mechanisms.md`. The verdicts live only
|
|
32
|
+
here; the dated report in `reviews/` keeps the reasoning and points here.
|
|
33
|
+
|
|
34
|
+
Status: `pass` ยท `partial` ยท `fail` ยท `โ` (not scored). A `fail` names an owner and either a plan or
|
|
35
|
+
`accept ceiling: <why>`.
|
|
36
|
+
|
|
37
|
+
| # | mechanism | status | evidence (file:line) | owner | plan or accept ceiling |
|
|
38
|
+
| --- | ----------------------------------------------- | ------ | -------------------- | ----- | ---------------------- |
|
|
39
|
+
| M1 | a non-model oracle under the headline | โ | โ | โ | โ |
|
|
40
|
+
| M2 | the claim is aimed at a belief stated on record | โ | โ | โ | โ |
|
|
41
|
+
| M3 | the construct is the claim | โ | โ | โ | โ |
|
|
42
|
+
| M4 | the denominator in the claim sentence, no drift | โ | โ | โ | โ |
|
|
43
|
+
| M5 | a negative or "X alone" claim shape | โ | โ | โ | โ |
|
|
44
|
+
| M6 | the generalised-over factor is a column | โ | โ | โ | โ |
|
|
45
|
+
| M7 | the phenomenon measured directly | โ | โ | โ | โ |
|
|
46
|
+
| M8 | a reusable thing the field pays for once | โ | โ | โ | โ |
|
|
47
|
+
| M9 | relevance to every reviewer on the PC | โ | โ | โ | โ |
|
|
48
|
+
| M10 | where the paper's own result loses | โ | โ | โ | โ |
|
|
49
|
+
|
|
50
|
+
## Stages
|
|
17
51
|
|
|
18
52
|
### SETUP
|
|
19
53
|
|
|
@@ -22,18 +56,18 @@ gate caps "ready"; an accept probability alone is not a verdict.>
|
|
|
22
56
|
| idea | Validate idea | research-ideate | โ | โ | โ | โ |
|
|
23
57
|
| priorwork | Map competitors | map-prior-work | โ | โ | โ | siblings/ |
|
|
24
58
|
| venue | Pick venue | find-venue | โ | โ | โ | โ |
|
|
25
|
-
| venuebar |
|
|
59
|
+
| venuebar | What the venue rewards | study-accepted-papers | โ | โ | โ | โ |
|
|
26
60
|
| **access** | ๐ด **Can you physically submit?** | plan-paper-timeline | โ | โ | account ยท profile ACTIVE since <ISO> ยท portal reachable ยท form fields known ยท artifact host known | โ |
|
|
27
61
|
| schedule | Schedule + calendar | plan-paper-timeline | โ | โ | โ | โ |
|
|
28
62
|
| **frame** | **Claim stated before the runs** | argument-arc (frame) | โ | โ | one paragraph: what will this paper claim? | โ |
|
|
29
63
|
|
|
30
64
|
### LOOP
|
|
31
65
|
|
|
32
|
-
| id | Work | Skill | Status | Date | Result | Open
|
|
33
|
-
| ----- | ---------------- | --------------- | ------ | ---- | ------ |
|
|
34
|
-
| study | Study + artifact | build-benchmark | โ | โ | โ |
|
|
35
|
-
| draft | Draft | draft-paper | โ | โ | โ | โ
|
|
36
|
-
| arc | Argument arc | argument-arc | โ | โ | โ | โ
|
|
66
|
+
| id | Work | Skill | Status | Date | Result | Open |
|
|
67
|
+
| ----- | ---------------- | --------------- | ------ | ---- | ------ | ------ |
|
|
68
|
+
| study | Study + artifact | build-benchmark | โ | โ | โ | repro/ |
|
|
69
|
+
| draft | Draft | draft-paper | โ | โ | โ | โ |
|
|
70
|
+
| arc | Argument arc | argument-arc | โ | โ | โ | โ |
|
|
37
71
|
|
|
38
72
|
### CONTINUOUS
|
|
39
73
|
|