paperlint 12.0.0 → 13.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/CONTRIBUTING.md +2 -1
- package/dist/adapters/memory/index.d.ts +5 -0
- package/dist/adapters/memory/index.d.ts.map +1 -1
- package/dist/adapters/memory/index.js +16 -0
- package/dist/adapters/memory/index.js.map +1 -1
- package/dist/adapters/node/index.d.ts +1 -0
- package/dist/adapters/node/index.d.ts.map +1 -1
- package/dist/adapters/node/index.js +1 -0
- package/dist/adapters/node/index.js.map +1 -1
- package/dist/adapters/node/move-file.io.d.ts +4 -0
- package/dist/adapters/node/move-file.io.d.ts.map +1 -0
- package/dist/adapters/node/move-file.io.js +12 -0
- package/dist/adapters/node/move-file.io.js.map +1 -0
- package/dist/adapters/yaml/index.d.ts +3 -0
- package/dist/adapters/yaml/index.d.ts.map +1 -0
- package/dist/adapters/yaml/index.js +17 -0
- package/dist/adapters/yaml/index.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +48 -7
- package/dist/cli.js.map +1 -1
- package/dist/cycle-rules.d.ts +3 -3
- package/dist/cycle-rules.d.ts.map +1 -1
- package/dist/cycle-rules.js +1 -2
- package/dist/cycle-rules.js.map +1 -1
- package/dist/domain/cycle.d.ts +16 -2
- package/dist/domain/cycle.d.ts.map +1 -1
- package/dist/domain/cycle.js +21 -42
- package/dist/domain/cycle.js.map +1 -1
- package/dist/domain/paper-layout.d.ts.map +1 -1
- package/dist/domain/paper-layout.js +5 -2
- package/dist/domain/paper-layout.js.map +1 -1
- package/dist/domain/paths.d.ts +12 -0
- package/dist/domain/paths.d.ts.map +1 -1
- package/dist/domain/paths.js +22 -1
- package/dist/domain/paths.js.map +1 -1
- package/dist/domain/scaffold-layout.js +1 -1
- package/dist/domain/versions-migration.d.ts +51 -0
- package/dist/domain/versions-migration.d.ts.map +1 -0
- package/dist/domain/versions-migration.js +252 -0
- package/dist/domain/versions-migration.js.map +1 -0
- package/dist/domain/versions.d.ts +167 -0
- package/dist/domain/versions.d.ts.map +1 -0
- package/dist/domain/versions.js +457 -0
- package/dist/domain/versions.js.map +1 -0
- package/dist/eslint-rules/paper-stages.d.ts +7 -0
- package/dist/eslint-rules/paper-stages.d.ts.map +1 -1
- package/dist/eslint-rules/paper-stages.js +62 -7
- package/dist/eslint-rules/paper-stages.js.map +1 -1
- package/dist/eslint-rules/rule-context.d.ts +2 -0
- package/dist/eslint-rules/rule-context.d.ts.map +1 -1
- package/dist/ports/move-file.d.ts +7 -0
- package/dist/ports/move-file.d.ts.map +1 -0
- package/dist/ports/move-file.js +2 -0
- package/dist/ports/move-file.js.map +1 -0
- package/dist/ports/yaml.d.ts +5 -0
- package/dist/ports/yaml.d.ts.map +1 -0
- package/dist/ports/yaml.js +2 -0
- package/dist/ports/yaml.js.map +1 -0
- package/dist/submission.d.ts +2 -0
- package/dist/submission.d.ts.map +1 -1
- package/dist/submission.js +49 -9
- package/dist/submission.js.map +1 -1
- package/dist/version-rules.d.ts +16 -0
- package/dist/version-rules.d.ts.map +1 -0
- package/dist/version-rules.js +201 -0
- package/dist/version-rules.js.map +1 -0
- package/dist/versions-command.d.ts +37 -0
- package/dist/versions-command.d.ts.map +1 -0
- package/dist/versions-command.js +209 -0
- package/dist/versions-command.js.map +1 -0
- package/dist/versions.d.ts +76 -0
- package/dist/versions.d.ts.map +1 -0
- package/dist/versions.js +164 -0
- package/dist/versions.js.map +1 -0
- package/docs/configuration.md +76 -2
- package/docs/layout.md +5 -5
- package/docs/rules/cycle/camera-ready-source.md +62 -0
- package/docs/rules/cycle/draft-drift.md +47 -0
- package/docs/rules/cycle/port-done.md +2 -2
- package/docs/rules/cycle/record.md +2 -1
- package/docs/rules/cycle/submitted-frozen.md +65 -0
- package/docs/rules/paper/allowed-files-only.md +1 -1
- package/docs/rules/versions/layout.md +88 -0
- package/docs/rules.md +25 -11
- package/docs/submission.md +21 -3
- package/eslint-rules/paper-stages.test.mjs +62 -0
- package/eslint-rules/paper-stages.ts +67 -8
- package/eslint-rules/prereg-coverage.test.ts +1 -0
- package/eslint-rules/rule-context.ts +2 -0
- package/package.json +2 -1
- package/presets/agent.jsonc +75 -0
- package/presets/agent.md +70 -0
- package/presets/agentverify.jsonc +72 -0
- package/presets/agentverify.md +85 -0
- package/presets/ieee-conference.md +1 -1
- package/presets/nlbse.jsonc +76 -0
- package/presets/nlbse.md +85 -0
- package/scripts/coverage-config.test.ts +3 -0
- package/skills/CLAUDE.md +2 -2
- package/skills/CLAUDE.md.spec.ts +1 -1
- package/skills/paper-pipeline/references/pipeline-status-template.md +4 -1
- package/skills/submit-paper/SKILL.md +18 -10
- package/skills/submit-paper/SKILL.md.spec.ts +17 -9
- package/skills/submit-paper/references/publishers/acm.md +5 -3
- package/src/adapters/memory/index.test.ts +22 -0
- package/src/adapters/memory/index.ts +21 -0
- package/src/adapters/node/index.ts +1 -0
- package/src/adapters/node/move-file.io.ts +21 -0
- package/src/adapters/node/move-file.test.ts +51 -0
- package/src/adapters/yaml/index.test.ts +16 -0
- package/src/adapters/yaml/index.ts +17 -0
- package/src/cli.test.ts +43 -0
- package/src/cli.ts +58 -6
- package/src/cycle-rules.ts +4 -5
- package/src/domain/cycle.test.ts +27 -0
- package/src/domain/cycle.ts +38 -6
- package/src/domain/paper-layout.ts +5 -2
- package/src/domain/paths.test.ts +28 -1
- package/src/domain/paths.ts +34 -1
- package/src/domain/scaffold-layout.ts +1 -1
- package/src/domain/versions-migration.test.ts +410 -0
- package/src/domain/versions-migration.ts +429 -0
- package/src/domain/versions.test.ts +564 -0
- package/src/domain/versions.ts +706 -0
- package/src/paper-settings-commands.test.ts +1 -1
- package/src/ports/move-file.ts +8 -0
- package/src/ports/yaml.ts +5 -0
- package/src/preset-card-rule.test.ts +3 -0
- package/src/presets.test.ts +55 -31
- package/src/rule-cases.test.ts +157 -8
- package/src/submission-freeze.test.ts +320 -0
- package/src/submission-new.test.ts +11 -1
- package/src/submission.ts +82 -11
- package/src/tex-venue-rules.test.ts +3 -0
- package/src/venue-rules.test.ts +2 -2
- package/src/version-rules.test.ts +222 -0
- package/src/version-rules.ts +282 -0
- package/src/versions-command.test.ts +594 -0
- package/src/versions-command.ts +360 -0
- package/src/versions.ts +286 -0
- package/dist/eslint-rules/cycle-stage.d.ts +0 -39
- package/dist/eslint-rules/cycle-stage.d.ts.map +0 -1
- package/dist/eslint-rules/cycle-stage.js +0 -119
- package/dist/eslint-rules/cycle-stage.js.map +0 -1
- package/docs/rules/cycle/stage.md +0 -85
- package/eslint-rules/cycle-stage.test.mjs +0 -131
- package/eslint-rules/cycle-stage.ts +0 -141
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE FROZEN VERSIONS OF A PAPER, by convention: one folder per attempt at a venue, named by the
|
|
3
|
+
* attempt's id in `paperlint.json` `cycles`, holding a file per stage and nothing else.
|
|
4
|
+
*
|
|
5
|
+
* versions/
|
|
6
|
+
* ├── alpha-2026/ ← a cycle id
|
|
7
|
+
* │ ├── submitted.pdf ← what the venue's reviewers read
|
|
8
|
+
* │ └── submitted.tex ← the source it was built from (a multi-file paper: assembled)
|
|
9
|
+
* ├── beta-2027/
|
|
10
|
+
* │ ├── submitted.pdf
|
|
11
|
+
* │ ├── submitted.tex
|
|
12
|
+
* │ └── camera-ready.pdf ← only after the attempt is accepted
|
|
13
|
+
* └── lock.json ← generated: bytes, sha256 and the time each file was frozen
|
|
14
|
+
*
|
|
15
|
+
* ── WHY NAMES, NOT A RECORD ─────────────────────────────────────────────────────────────
|
|
16
|
+
* The `stages:` list in a paper's scorecard said the same thing as the file names, a second time, by
|
|
17
|
+
* hand, and a rule kept the two in step. Here the names ARE the record: which attempt a file belongs
|
|
18
|
+
* to is its folder, which stage is its name, and what state it is in follows from the attempt's
|
|
19
|
+
* `outcome` — the one value an author changes when a decision arrives. Nothing is typed twice.
|
|
20
|
+
*
|
|
21
|
+
* ── WHY A LOCK ──────────────────────────────────────────────────────────────────────────
|
|
22
|
+
* What a name cannot carry — the bytes a file had when it was frozen, its digest, when it was frozen,
|
|
23
|
+
* and that a source was lost — goes into `lock.json`, which paperlint writes and nobody edits
|
|
24
|
+
* (`package-lock.json`, `schema.rb`). A frozen file is never supposed to change, so a file that does
|
|
25
|
+
* not match its lock entry is the finding.
|
|
26
|
+
*
|
|
27
|
+
* ── NO DATES IN NAMES ───────────────────────────────────────────────────────────────────
|
|
28
|
+
* A second upload to the same attempt replaces the first: the last upload is the one under review.
|
|
29
|
+
* When it was frozen is a fact paperlint records in the lock, not one a person types into a name.
|
|
30
|
+
*
|
|
31
|
+
* Pure: the disk is read by the caller and handed in.
|
|
32
|
+
*/
|
|
33
|
+
import { diffLines } from "diff";
|
|
34
|
+
import * as R from "remeda";
|
|
35
|
+
import { CYCLE_ID_PATTERN, } from "./cycle.js";
|
|
36
|
+
import { isRecord } from "./record.js";
|
|
37
|
+
import { err, ok } from "./result.js";
|
|
38
|
+
export const VERSIONS_DIR = "versions";
|
|
39
|
+
export const LOCK_FILE = "lock.json";
|
|
40
|
+
/** The stages a version is frozen at, in the order an attempt reaches them. */
|
|
41
|
+
export const VERSION_STAGES = ["submitted", "camera-ready", "arxiv"];
|
|
42
|
+
const isStage = (s) => VERSION_STAGES.some((v) => v === s);
|
|
43
|
+
/** `submitted.pdf` → its stage and extension; null for a name outside the convention. Pure. */
|
|
44
|
+
export function frozenName(name) {
|
|
45
|
+
const m = /^([a-z-]+)\.(pdf|tex)$/.exec(name);
|
|
46
|
+
const stage = m?.[1];
|
|
47
|
+
const ext = m?.[2];
|
|
48
|
+
return isStage(stage) && (ext === "pdf" || ext === "tex")
|
|
49
|
+
? { stage, ext }
|
|
50
|
+
: null;
|
|
51
|
+
}
|
|
52
|
+
/** `<cycle>/<stage>.<ext>`: a frozen file's path under `versions/`, and its key in the lock. */
|
|
53
|
+
export const frozenKey = (cycle, stage, ext) => `${cycle}/${stage}.${ext}`;
|
|
54
|
+
/**
|
|
55
|
+
* A frozen file in the layout before folders per attempt, `versions/<date>-<stage>.<ext>`: what
|
|
56
|
+
* `paperlint versions migrate` moves into its attempt's folder.
|
|
57
|
+
*/
|
|
58
|
+
export const DATED_NAME = /^(\d{4}-\d{2}-\d{2})-([a-z-]+)\.(pdf|tex)$/;
|
|
59
|
+
export const LOCK_VERSION = 1;
|
|
60
|
+
/** What the lock says about itself, so nobody takes it for a file to edit. */
|
|
61
|
+
export const LOCK_NOTE = "generated by paperlint — do not edit: `paperlint submission update --save` and `paperlint versions lock` write it";
|
|
62
|
+
const SHA256 = /^[0-9a-f]{64}$/;
|
|
63
|
+
const WHEN = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2}(\.\d+)?)?Z)?$/;
|
|
64
|
+
/** `<cycle>/<stage>.<ext>`, the cycle part by the same rule `parseCycles` holds an id to. */
|
|
65
|
+
const KEY = new RegExp(`^(${CYCLE_ID_PATTERN})/([a-z-]+\\.(?:pdf|tex))$`);
|
|
66
|
+
const isWhen = (v) => typeof v === "string" && WHEN.test(v);
|
|
67
|
+
/** The cycle and the file name of a lock key; null for a key that is not `<cycle>/<stage>.<ext>`. */
|
|
68
|
+
export function keyParts(key) {
|
|
69
|
+
const m = KEY.exec(key);
|
|
70
|
+
const name = frozenName(m?.[2] ?? "");
|
|
71
|
+
return m?.[1] === undefined || name === null ? null : { cycle: m[1], name };
|
|
72
|
+
}
|
|
73
|
+
const FILE_KEYS = ["bytes", "sha256", "frozen"];
|
|
74
|
+
const LOST_KEYS = ["sourceLost", "frozen"];
|
|
75
|
+
const whenProblem = (at) => `${at}: "frozen" must be a date or an instant`;
|
|
76
|
+
/** An entry recording a lost source: `{ "sourceLost": true, "frozen"? }`. */
|
|
77
|
+
function parseLost(at, ext, frozen) {
|
|
78
|
+
if (ext !== "tex")
|
|
79
|
+
return err(`${at}: only a source (.tex) can be recorded as lost`);
|
|
80
|
+
return frozen === undefined || isWhen(frozen)
|
|
81
|
+
? ok({ kind: "lost", frozen: frozen ?? null })
|
|
82
|
+
: err(whenProblem(at));
|
|
83
|
+
}
|
|
84
|
+
/** An entry recording a frozen file: `{ "bytes", "sha256", "frozen" }`. */
|
|
85
|
+
function parseFile(at, v) {
|
|
86
|
+
const { bytes, sha256, frozen } = v;
|
|
87
|
+
if (typeof bytes !== "number" || !Number.isInteger(bytes) || bytes < 0)
|
|
88
|
+
return err(`${at}: "bytes" must be a whole number`);
|
|
89
|
+
if (typeof sha256 !== "string" || !SHA256.test(sha256))
|
|
90
|
+
return err(`${at}: "sha256" must be 64 lowercase hex digits`);
|
|
91
|
+
return isWhen(frozen)
|
|
92
|
+
? ok({ kind: "file", bytes, sha256, frozen })
|
|
93
|
+
: err(whenProblem(at));
|
|
94
|
+
}
|
|
95
|
+
function parseEntry(key, v) {
|
|
96
|
+
const at = `"files"["${key}"]`;
|
|
97
|
+
const parts = keyParts(key);
|
|
98
|
+
if (parts === null)
|
|
99
|
+
return err(`${at}: a key is <cycle>/<stage>.pdf or .tex`);
|
|
100
|
+
if (!isRecord(v))
|
|
101
|
+
return err(`${at} must be an object`);
|
|
102
|
+
const lost = v["sourceLost"] === true;
|
|
103
|
+
const unknown = Object.keys(v).find((k) => !(lost ? LOST_KEYS : FILE_KEYS).includes(k));
|
|
104
|
+
if (unknown !== undefined)
|
|
105
|
+
return err(`${at}: unknown key "${unknown}"`);
|
|
106
|
+
return lost ? parseLost(at, parts.name.ext, v["frozen"]) : parseFile(at, v);
|
|
107
|
+
}
|
|
108
|
+
/** `lock.json`, parsed → the lock, or one line naming what is wrong. Pure. */
|
|
109
|
+
export function parseLock(json) {
|
|
110
|
+
if (!isRecord(json) || json["lockfileVersion"] !== LOCK_VERSION)
|
|
111
|
+
return err(`not a lock this paperlint writes ("lockfileVersion": ${String(LOCK_VERSION)})`);
|
|
112
|
+
const files = json["files"];
|
|
113
|
+
if (!isRecord(files))
|
|
114
|
+
return err(`"files" must be an object`);
|
|
115
|
+
return Object.entries(files).reduce((acc, [key, v]) => {
|
|
116
|
+
if (!acc.ok)
|
|
117
|
+
return acc;
|
|
118
|
+
const e = parseEntry(key, v);
|
|
119
|
+
return e.ok ? ok({ ...acc.value, [key]: e.value }) : e;
|
|
120
|
+
}, ok({}));
|
|
121
|
+
}
|
|
122
|
+
const entryJson = (e) => e.kind === "lost"
|
|
123
|
+
? { sourceLost: true, ...(e.frozen === null ? {} : { frozen: e.frozen }) }
|
|
124
|
+
: { bytes: e.bytes, sha256: e.sha256, frozen: e.frozen };
|
|
125
|
+
/** The lock as paperlint writes it: keys sorted, two spaces, a newline at the end. Pure. */
|
|
126
|
+
export const lockText = (lock) => `${JSON.stringify({
|
|
127
|
+
lockfileVersion: LOCK_VERSION,
|
|
128
|
+
note: LOCK_NOTE,
|
|
129
|
+
files: Object.fromEntries(R.sortBy(Object.entries(lock), ([k]) => k).map(([k, e]) => [
|
|
130
|
+
k,
|
|
131
|
+
entryJson(e),
|
|
132
|
+
])),
|
|
133
|
+
}, null, 2)}\n`;
|
|
134
|
+
const STAGES_TEXT = VERSION_STAGES.join(", ");
|
|
135
|
+
const stray = (path, hint) => ({
|
|
136
|
+
messageId: "stray",
|
|
137
|
+
data: { path, hint },
|
|
138
|
+
});
|
|
139
|
+
const MIGRATE_HINT = "a frozen version in the layout before folders per attempt: `npx paperlint versions migrate <paper>` moves it into versions/<cycle>/";
|
|
140
|
+
const LAYOUT_HINT = `versions/ holds lock.json and one folder per cycle, each with <stage>.pdf and <stage>.tex (${STAGES_TEXT}) — anything else goes to extra/`;
|
|
141
|
+
/** An entry directly in `versions/`: fine, a cycle's folder, or a finding. */
|
|
142
|
+
function rootFinding(e, ids) {
|
|
143
|
+
if (e.name.startsWith("."))
|
|
144
|
+
return [];
|
|
145
|
+
if (e.kind === "dir")
|
|
146
|
+
return ids.includes(e.name)
|
|
147
|
+
? []
|
|
148
|
+
: [
|
|
149
|
+
{
|
|
150
|
+
messageId: "unknownCycle",
|
|
151
|
+
data: { folder: e.name, known: ids.join(" · ") || "(none)" },
|
|
152
|
+
},
|
|
153
|
+
];
|
|
154
|
+
if (e.name === LOCK_FILE)
|
|
155
|
+
return [];
|
|
156
|
+
return [stray(e.name, DATED_NAME.test(e.name) ? MIGRATE_HINT : LAYOUT_HINT)];
|
|
157
|
+
}
|
|
158
|
+
/** The frozen files of the cycles' folders, and every entry there outside the convention. */
|
|
159
|
+
function folderContents(input) {
|
|
160
|
+
// Each cycle's folder by the cycle's own id, so every key below is built from a parsed one.
|
|
161
|
+
const folders = input.cycles
|
|
162
|
+
.map((c) => c.id)
|
|
163
|
+
.filter((id) => input.root.some((e) => e.kind === "dir" && e.name === id));
|
|
164
|
+
const listed = folders.flatMap((id) => input.inFolder(id).map((e) => ({ cycle: id, e })));
|
|
165
|
+
const named = listed.map(({ cycle, e }) => ({
|
|
166
|
+
cycle,
|
|
167
|
+
e,
|
|
168
|
+
name: e.kind === "file" ? frozenName(e.name) : null,
|
|
169
|
+
}));
|
|
170
|
+
return {
|
|
171
|
+
found: named.flatMap(({ cycle, name }) => name === null
|
|
172
|
+
? []
|
|
173
|
+
: [{ ...name, cycle, key: frozenKey(cycle, name.stage, name.ext) }]),
|
|
174
|
+
strays: named
|
|
175
|
+
.filter(({ e, name }) => name === null && !e.name.startsWith("."))
|
|
176
|
+
.map(({ cycle, e }) => stray(`${cycle}/${e.name}`, LAYOUT_HINT)),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const isLost = (lock, key) => lock[key]?.kind === "lost";
|
|
180
|
+
/** Each frozen PDF without its source beside it, unless the lock records the source as lost. */
|
|
181
|
+
function missingSources(found, lock) {
|
|
182
|
+
const keys = found.map((f) => f.key);
|
|
183
|
+
return found
|
|
184
|
+
.filter((f) => f.ext === "pdf")
|
|
185
|
+
.map((f) => ({ f, tex: frozenKey(f.cycle, f.stage, "tex") }))
|
|
186
|
+
.filter(({ tex }) => !keys.includes(tex) && !isLost(lock, tex))
|
|
187
|
+
.map(({ f, tex }) => ({
|
|
188
|
+
messageId: "noSource",
|
|
189
|
+
data: { pdf: f.key, tex },
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
/** A version sent from a cycle whose source is still declared to be in the previous venue's template. */
|
|
193
|
+
const portingSends = (found, cycles) => found
|
|
194
|
+
.filter((f) => f.ext === "pdf" &&
|
|
195
|
+
(f.stage === "submitted" || f.stage === "camera-ready") &&
|
|
196
|
+
cycles.find((c) => c.id === f.cycle)?.phase === "porting")
|
|
197
|
+
.map((f) => ({ messageId: "porting", data: { file: f.key, id: f.cycle } }));
|
|
198
|
+
/** Each frozen file against its lock entry, and each lock entry against the disk. */
|
|
199
|
+
function lockFindings(found, lock, factsOf) {
|
|
200
|
+
const keys = found.map((f) => f.key);
|
|
201
|
+
const onDisk = found.flatMap((f) => {
|
|
202
|
+
const e = lock[f.key];
|
|
203
|
+
if (e === undefined || e.kind === "lost")
|
|
204
|
+
return [{ messageId: "notLocked", data: { file: f.key } }];
|
|
205
|
+
const got = factsOf(f.key);
|
|
206
|
+
return got.bytes === e.bytes && got.sha256 === e.sha256
|
|
207
|
+
? []
|
|
208
|
+
: [
|
|
209
|
+
{
|
|
210
|
+
messageId: "changed",
|
|
211
|
+
data: {
|
|
212
|
+
file: f.key,
|
|
213
|
+
want: String(e.bytes),
|
|
214
|
+
got: String(got.bytes),
|
|
215
|
+
frozen: e.frozen,
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
];
|
|
219
|
+
});
|
|
220
|
+
const gone = Object.entries(lock)
|
|
221
|
+
.filter(([k, e]) => e.kind === "file" && !keys.includes(k))
|
|
222
|
+
.map(([k]) => ({ messageId: "lockedGone", data: { file: k } }));
|
|
223
|
+
return [...onDisk, ...gone];
|
|
224
|
+
}
|
|
225
|
+
/** The lock's findings: none needed, none there, unreadable, or each file against it. */
|
|
226
|
+
function lockOf(input, found) {
|
|
227
|
+
if (input.lock === null)
|
|
228
|
+
return found.length === 0 ? [] : [{ messageId: "noLock", data: {} }];
|
|
229
|
+
if (!input.lock.ok)
|
|
230
|
+
return [{ messageId: "badLock", data: { why: input.lock.error } }];
|
|
231
|
+
return lockFindings(found, input.lock.value, input.factsOf);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* `versions/layout`: every entry of `versions/` against the convention — a folder per cycle, a file
|
|
235
|
+
* per stage — the porting declaration, and every frozen file against the lock. Pure.
|
|
236
|
+
*/
|
|
237
|
+
export function judgeVersions(input) {
|
|
238
|
+
const ids = input.cycles.map((c) => c.id);
|
|
239
|
+
const { found, strays } = folderContents(input);
|
|
240
|
+
const lock = input.lock?.ok === true ? input.lock.value : {};
|
|
241
|
+
return [
|
|
242
|
+
...input.root.flatMap((e) => rootFinding(e, ids)),
|
|
243
|
+
...strays,
|
|
244
|
+
...missingSources(found, lock),
|
|
245
|
+
...portingSends(found, input.cycles),
|
|
246
|
+
...lockOf(input, found),
|
|
247
|
+
];
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* `cycle/submitted-frozen`: each cycle that has a submission on the portal and no
|
|
251
|
+
* `versions/<id>/submitted.pdf`. Pure over whether a file exists, by its key.
|
|
252
|
+
*/
|
|
253
|
+
export const unfrozenSubmissions = (cycles, has) => cycles
|
|
254
|
+
.filter((c) => c.submission !== null && !has(frozenKey(c.id, "submitted", "pdf")))
|
|
255
|
+
.map((c) => ({
|
|
256
|
+
messageId: "unfrozen",
|
|
257
|
+
data: {
|
|
258
|
+
id: c.id,
|
|
259
|
+
submission: String(c.submission?.id),
|
|
260
|
+
pdf: frozenKey(c.id, "submitted", "pdf"),
|
|
261
|
+
},
|
|
262
|
+
}));
|
|
263
|
+
/** The lines `draft` adds to `frozen` and removes from it (a line diff, as `git diff --numstat`). Pure. */
|
|
264
|
+
export function lineDrift(frozen, draft) {
|
|
265
|
+
const parts = diffLines(frozen, draft);
|
|
266
|
+
return {
|
|
267
|
+
added: R.sumBy(parts, (p) => (p.added ? p.count : 0)),
|
|
268
|
+
removed: R.sumBy(parts, (p) => (p.removed ? p.count : 0)),
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
/** The lines of `accepted` that `later` keeps unchanged, counted with the same line diff as the drift. Pure. */
|
|
272
|
+
export function lineage(accepted, later) {
|
|
273
|
+
const parts = diffLines(accepted, later);
|
|
274
|
+
const kept = R.sumBy(parts, (p) => (p.added || p.removed ? 0 : p.count));
|
|
275
|
+
const removed = R.sumBy(parts, (p) => (p.removed ? p.count : 0));
|
|
276
|
+
return { kept, total: kept + removed };
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Whether a camera-ready started from the accepted source: it keeps at least half of its lines. A
|
|
280
|
+
* camera-ready is what was accepted plus the changes the reviewers asked for, so it differs — any
|
|
281
|
+
* difference cannot be the test. One that keeps less than half was built from something else.
|
|
282
|
+
*/
|
|
283
|
+
export const derivesFrom = (l) => l.kept * 2 >= l.total;
|
|
284
|
+
export const driftText = (d) => `${String(d.added)} line${d.added === 1 ? "" : "s"} added, ${String(d.removed)} removed`;
|
|
285
|
+
/** The venue of a cycle, as a person names it: the preset's spec, or the named venue. */
|
|
286
|
+
/** A cycle's venue as a person names it: a named venue's name, a preset's name without its package or path. */
|
|
287
|
+
export const venueName = (c) => c.venue.kind === "preset"
|
|
288
|
+
? c.venue.extends
|
|
289
|
+
.replace(/^paperlint:/, "")
|
|
290
|
+
.replace(/^.*\//, "")
|
|
291
|
+
.replace(/\.jsonc?$/, "")
|
|
292
|
+
: c.venue.name;
|
|
293
|
+
/**
|
|
294
|
+
* The frozen source the draft is measured against: the open cycle's `submitted.tex`, or — once the
|
|
295
|
+
* last cycle is accepted — its newest (`camera-ready.tex`, else `submitted.tex`); null between venues
|
|
296
|
+
* and before the first attempt. Pure over whether a file exists.
|
|
297
|
+
*/
|
|
298
|
+
export function driftBase(current, has) {
|
|
299
|
+
switch (current.kind) {
|
|
300
|
+
case "cycle":
|
|
301
|
+
return {
|
|
302
|
+
cycle: current.cycle,
|
|
303
|
+
key: frozenKey(current.cycle.id, "submitted", "tex"),
|
|
304
|
+
};
|
|
305
|
+
case "accepted": {
|
|
306
|
+
const camera = frozenKey(current.cycle.id, "camera-ready", "tex");
|
|
307
|
+
return {
|
|
308
|
+
cycle: current.cycle,
|
|
309
|
+
key: has(camera)
|
|
310
|
+
? camera
|
|
311
|
+
: frozenKey(current.cycle.id, "submitted", "tex"),
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
case "parked":
|
|
315
|
+
case "none":
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
/** What a frozen stage of a cycle IS, from the cycle's outcome. Pure. */
|
|
320
|
+
export function stateOf(c, stage) {
|
|
321
|
+
if (stage === "arxiv")
|
|
322
|
+
return "arXiv copy";
|
|
323
|
+
switch (c.outcome.kind) {
|
|
324
|
+
case "open":
|
|
325
|
+
return stage === "submitted"
|
|
326
|
+
? `CURRENT (under review at ${venueName(c)})`
|
|
327
|
+
: "camera-ready, cycle still open";
|
|
328
|
+
case "accepted":
|
|
329
|
+
return stage === "submitted" ? "camera-ready base" : "final";
|
|
330
|
+
case "rejected":
|
|
331
|
+
case "withdrawn":
|
|
332
|
+
return `historical (${c.outcome.kind})`;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
/** `2026-10-23T16:58:01.000Z` → `2026-10-23 16:58 UTC`; a day stays a day. */
|
|
336
|
+
const whenText = (frozen) => frozen.length === 10
|
|
337
|
+
? frozen
|
|
338
|
+
: `${frozen.slice(0, 10)} ${frozen.slice(11, 16)} UTC`;
|
|
339
|
+
/** A PDF's lock entry as the table shows it (only a source can be recorded lost). */
|
|
340
|
+
const entryDetail = (e) => e === undefined || e.kind === "lost"
|
|
341
|
+
? "not locked"
|
|
342
|
+
: `${String(e.bytes)} B frozen ${whenText(e.frozen)}`;
|
|
343
|
+
/** The stages a cycle has frozen: a PDF, a source, or a source the lock records as lost. */
|
|
344
|
+
const frozenStages = (c, has, lock) => VERSION_STAGES.filter((s) => has(frozenKey(c.id, s, "pdf")) ||
|
|
345
|
+
has(frozenKey(c.id, s, "tex")) ||
|
|
346
|
+
isLost(lock, frozenKey(c.id, s, "tex")));
|
|
347
|
+
/** The row of a cycle that froze nothing: what it is, from its outcome. */
|
|
348
|
+
const emptyRow = (c) => ({
|
|
349
|
+
version: c.id,
|
|
350
|
+
stage: "—",
|
|
351
|
+
state: c.outcome.kind === "open"
|
|
352
|
+
? "open, nothing sent yet"
|
|
353
|
+
: `${c.outcome.kind}, nothing frozen`,
|
|
354
|
+
detail: "",
|
|
355
|
+
});
|
|
356
|
+
/** A cycle's rows: one per stage frozen in its folder, or one saying what is missing or that nothing is. */
|
|
357
|
+
function cycleRows(c, has, lock) {
|
|
358
|
+
const stages = frozenStages(c, has, lock);
|
|
359
|
+
const unfrozen = c.submission !== null && !stages.includes("submitted")
|
|
360
|
+
? [
|
|
361
|
+
{
|
|
362
|
+
version: c.id,
|
|
363
|
+
stage: "submitted",
|
|
364
|
+
state: `NOT FROZEN — submission ${String(c.submission.id)} is on the portal`,
|
|
365
|
+
detail: "",
|
|
366
|
+
},
|
|
367
|
+
]
|
|
368
|
+
: [];
|
|
369
|
+
const frozen = stages.map((s) => ({
|
|
370
|
+
version: c.id,
|
|
371
|
+
stage: s,
|
|
372
|
+
state: stateOf(c, s),
|
|
373
|
+
detail: [
|
|
374
|
+
entryDetail(lock[frozenKey(c.id, s, "pdf")]),
|
|
375
|
+
...(isLost(lock, frozenKey(c.id, s, "tex")) ? ["source lost"] : []),
|
|
376
|
+
].join(", "),
|
|
377
|
+
}));
|
|
378
|
+
const rows = [...unfrozen, ...frozen];
|
|
379
|
+
return rows.length > 0 ? rows : [emptyRow(c)];
|
|
380
|
+
}
|
|
381
|
+
const draftDetail = (d) => {
|
|
382
|
+
switch (d.kind) {
|
|
383
|
+
case "none":
|
|
384
|
+
return "no attempt to compare with";
|
|
385
|
+
case "no-base":
|
|
386
|
+
return `nothing frozen at ${d.key} to compare with`;
|
|
387
|
+
case "same":
|
|
388
|
+
return `same as ${d.key}`;
|
|
389
|
+
case "drift":
|
|
390
|
+
return `differs from ${d.key}: ${driftText(d.drift)}`;
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
/** The table: each cycle, newest first, then the draft. Pure. */
|
|
394
|
+
export function versionRows(input) {
|
|
395
|
+
return [
|
|
396
|
+
...[...input.cycles]
|
|
397
|
+
.reverse()
|
|
398
|
+
.flatMap((c) => cycleRows(c, input.has, input.lock)),
|
|
399
|
+
{
|
|
400
|
+
version: "paper.tex",
|
|
401
|
+
stage: "draft",
|
|
402
|
+
state: "target not chosen",
|
|
403
|
+
detail: draftDetail(input.draft),
|
|
404
|
+
},
|
|
405
|
+
];
|
|
406
|
+
}
|
|
407
|
+
/** The rows as aligned columns, two spaces in front. Pure. */
|
|
408
|
+
export function versionTable(rows) {
|
|
409
|
+
const width = (pick) => Math.max(...rows.map((r) => pick(r).length));
|
|
410
|
+
const w = [
|
|
411
|
+
width((r) => r.version),
|
|
412
|
+
width((r) => r.stage),
|
|
413
|
+
width((r) => r.state),
|
|
414
|
+
];
|
|
415
|
+
return rows.map((r) => ` ${r.version.padEnd(w[0])} ${r.stage.padEnd(w[1])} ${r.state.padEnd(w[2])} ${r.detail}`.trimEnd());
|
|
416
|
+
}
|
|
417
|
+
/** What one found file does to the lock: kept as recorded, recorded anew, or recorded for the first time. */
|
|
418
|
+
function relocked(f, old, now) {
|
|
419
|
+
const fresh = { kind: "file", ...f.facts, frozen: now };
|
|
420
|
+
if (old?.kind !== "file")
|
|
421
|
+
return {
|
|
422
|
+
entry: fresh,
|
|
423
|
+
line: `locked versions/${f.key} (${String(f.facts.bytes)} bytes)`,
|
|
424
|
+
};
|
|
425
|
+
return old.sha256 === f.facts.sha256 && old.bytes === f.facts.bytes
|
|
426
|
+
? { entry: old, line: null }
|
|
427
|
+
: {
|
|
428
|
+
entry: fresh,
|
|
429
|
+
line: `re-locked versions/${f.key}: ${String(old.bytes)} → ${String(f.facts.bytes)} bytes — it changed after ${old.frozen}`,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* `paperlint versions lock`: the lock for the files on disk — each kept as recorded, recorded when it
|
|
434
|
+
* is new or changed, an entry whose file is gone dropped, a lost source kept while no file replaces
|
|
435
|
+
* it — and a line per change. Pure.
|
|
436
|
+
*/
|
|
437
|
+
export function relock(found, old, now) {
|
|
438
|
+
const keys = found.map((f) => f.key);
|
|
439
|
+
const each = found.map((f) => ({
|
|
440
|
+
key: f.key,
|
|
441
|
+
...relocked(f, old[f.key], now),
|
|
442
|
+
}));
|
|
443
|
+
const gone = Object.entries(old).filter(([k]) => !keys.includes(k));
|
|
444
|
+
return {
|
|
445
|
+
lock: {
|
|
446
|
+
...Object.fromEntries(gone.filter(([, e]) => e.kind === "lost")),
|
|
447
|
+
...Object.fromEntries(each.map((e) => [e.key, e.entry])),
|
|
448
|
+
},
|
|
449
|
+
lines: [
|
|
450
|
+
...each.flatMap((e) => (e.line === null ? [] : [e.line])),
|
|
451
|
+
...gone
|
|
452
|
+
.filter(([, e]) => e.kind === "file")
|
|
453
|
+
.map(([k]) => `dropped versions/${k}: the file is gone`),
|
|
454
|
+
],
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
//# sourceMappingURL=versions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"versions.js","sourceRoot":"","sources":["../../src/domain/versions.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,12 +17,17 @@ declare const _default: {
|
|
|
17
17
|
declaredNoFile: string;
|
|
18
18
|
bytesDiffer: string;
|
|
19
19
|
fileNotDeclared: string;
|
|
20
|
+
retired: string;
|
|
20
21
|
};
|
|
21
22
|
};
|
|
22
23
|
create(context: RuleContext): {
|
|
23
24
|
yaml(node: {
|
|
24
25
|
readonly value?: string;
|
|
25
26
|
}): void;
|
|
27
|
+
} | {
|
|
28
|
+
yaml(node: {
|
|
29
|
+
readonly value?: string;
|
|
30
|
+
}): void;
|
|
26
31
|
"root:exit"(node: object): void;
|
|
27
32
|
};
|
|
28
33
|
};
|
|
@@ -72,6 +77,8 @@ declare const _default: {
|
|
|
72
77
|
};
|
|
73
78
|
};
|
|
74
79
|
create(context: RuleContext): {
|
|
80
|
+
yaml?: never;
|
|
81
|
+
} | {
|
|
75
82
|
yaml(node: {
|
|
76
83
|
readonly value?: string;
|
|
77
84
|
}): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paper-stages.d.ts","sourceRoot":"","sources":["../../eslint-rules/paper-stages.ts"],"names":[],"mappings":"AAqDA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC
|
|
1
|
+
{"version":3,"file":"paper-stages.d.ts","sourceRoot":"","sources":["../../eslint-rules/paper-stages.ts"],"names":[],"mappings":"AAqDA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;;;;;;;;;;;;;;;;;;;;;;4BAiK/B,WAAW;2BAxHlB;oBAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;iBAAE;;2BA6HrB;oBAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;iBAAE;kCA8FpB,MAAM;;;QAa9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA8BG;;;;;;;;;;;;;;;4BAoBe,WAAW;;;2BAKZ;oBAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;iBAAE;;;;;AAnMhD,wBA4PE"}
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
* keyed by stage name holds one. The `versions/<date>-<stage>.pdf` convention already holds
|
|
47
47
|
* many, and the field must not be weaker than the filenames it describes.
|
|
48
48
|
*/
|
|
49
|
-
import { existsSync, statSync, readdirSync } from "node:fs";
|
|
50
|
-
import { join, dirname } from "node:path";
|
|
49
|
+
import { existsSync, readFileSync, statSync, readdirSync } from "node:fs";
|
|
50
|
+
import { join, dirname, relative } from "node:path";
|
|
51
51
|
import assert from "node:assert/strict";
|
|
52
52
|
import { load } from "js-yaml";
|
|
53
53
|
import { z } from "zod";
|
|
@@ -66,6 +66,56 @@ const StageRecord = z
|
|
|
66
66
|
sourceLost: z.unknown().optional(),
|
|
67
67
|
})
|
|
68
68
|
.catch({});
|
|
69
|
+
/** What these rules read of the paper's settings: whether it keeps `cycles` at all. */
|
|
70
|
+
const Settings = z
|
|
71
|
+
.looseObject({ cycles: z.array(z.unknown()).optional() })
|
|
72
|
+
.catch({});
|
|
73
|
+
/**
|
|
74
|
+
* Whether the paper keeps `cycles` in its paperlint.json. Its frozen versions are then
|
|
75
|
+
* `versions/<cycle>/<stage>.{pdf,tex}` recorded in `versions/lock.json`, judged by `versions/layout`,
|
|
76
|
+
* and the `stages:` list is retired: `paperlint versions migrate` turns it into folders.
|
|
77
|
+
*/
|
|
78
|
+
function keepsCycles(dir) {
|
|
79
|
+
try {
|
|
80
|
+
const raw = JSON.parse(readFileSync(join(dir, "paperlint.json"), "utf8"));
|
|
81
|
+
return Settings.parse(raw).cycles !== undefined;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** Under `cycles`: a non-empty `stages:` list is one finding, naming the command that retires it. */
|
|
88
|
+
function retired(context, dir) {
|
|
89
|
+
return {
|
|
90
|
+
yaml(node) {
|
|
91
|
+
const parsed = (() => {
|
|
92
|
+
try {
|
|
93
|
+
return { ok: true, data: load(node.value ?? "") };
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
assert.ok(e instanceof Error);
|
|
97
|
+
return { ok: false, reason: e.message };
|
|
98
|
+
}
|
|
99
|
+
})();
|
|
100
|
+
if (!parsed.ok) {
|
|
101
|
+
context.report({
|
|
102
|
+
node,
|
|
103
|
+
messageId: "badYaml",
|
|
104
|
+
data: { reason: parsed.reason },
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const raw = Frontmatter.parse(parsed.data).stages;
|
|
109
|
+
if (raw === undefined || (Array.isArray(raw) && raw.length === 0))
|
|
110
|
+
return;
|
|
111
|
+
context.report({
|
|
112
|
+
node,
|
|
113
|
+
messageId: "retired",
|
|
114
|
+
data: { paper: relative(context.cwd, dir) || "." },
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
69
119
|
const REQUIRED = ["date", "pdf", "bytes"];
|
|
70
120
|
const STAGES = ["submitted", "camera-ready", "arxiv"];
|
|
71
121
|
/**
|
|
@@ -113,6 +163,8 @@ function frozenPdfs(versionsDir) {
|
|
|
113
163
|
}
|
|
114
164
|
return out;
|
|
115
165
|
}
|
|
166
|
+
/** The frozen PDFs in `versions/` that no declared stage of the same day names. */
|
|
167
|
+
const undeclared = (dir, records) => frozenPdfs(join(dir, "versions")).filter((f) => !records.some((r) => r.stage === f.stage && r.date === f.date));
|
|
116
168
|
export default {
|
|
117
169
|
rules: {
|
|
118
170
|
stages: {
|
|
@@ -131,10 +183,13 @@ export default {
|
|
|
131
183
|
declaredNoFile: "stage «{{stage}}» ({{date}}) is declared, but `{{pdf}}` is not on disk",
|
|
132
184
|
bytesDiffer: "«{{stage}}» ({{date}}): {{want}} bytes declared, {{got}} on disk — this is NOT that file",
|
|
133
185
|
fileNotDeclared: "`versions/{{file}}` is frozen, but no «{{stage}}» stage on {{date}} is declared in `stages` — the artefact ran ahead of the declaration",
|
|
186
|
+
retired: "`stages` is retired for a paper that keeps `cycles` in paperlint.json: its frozen versions are versions/<cycle>/<stage>.{pdf,tex}, recorded in versions/lock.json — `npx paperlint versions migrate {{paper}}` moves the files there and takes the list out",
|
|
134
187
|
},
|
|
135
188
|
},
|
|
136
189
|
create(context) {
|
|
137
190
|
const dir = dirname(context.filename);
|
|
191
|
+
if (keepsCycles(dir))
|
|
192
|
+
return retired(context, dir);
|
|
138
193
|
let declared = null; // null = there was no frontmatter at all
|
|
139
194
|
return {
|
|
140
195
|
yaml(node) {
|
|
@@ -233,16 +288,13 @@ export default {
|
|
|
233
288
|
// 🔴 On `root:exit` rather than inside `yaml`, because the case worth catching is
|
|
234
289
|
// precisely the one with NO frontmatter at all — where the `yaml` visitor never runs.
|
|
235
290
|
"root:exit"(node) {
|
|
236
|
-
|
|
237
|
-
for (const f of frozenPdfs(join(dir, "versions"))) {
|
|
238
|
-
if (records.some((r) => r.stage === f.stage && r.date === f.date))
|
|
239
|
-
continue;
|
|
291
|
+
undeclared(dir, declared ?? []).forEach((f) => {
|
|
240
292
|
context.report({
|
|
241
293
|
node,
|
|
242
294
|
messageId: "fileNotDeclared",
|
|
243
295
|
data: { file: f.name, stage: f.stage, date: f.date },
|
|
244
296
|
});
|
|
245
|
-
}
|
|
297
|
+
});
|
|
246
298
|
},
|
|
247
299
|
};
|
|
248
300
|
},
|
|
@@ -294,6 +346,9 @@ export default {
|
|
|
294
346
|
},
|
|
295
347
|
create(context) {
|
|
296
348
|
const dir = dirname(context.filename);
|
|
349
|
+
// Under `cycles` the lock records each frozen source, and `versions/layout` checks it.
|
|
350
|
+
if (keepsCycles(dir))
|
|
351
|
+
return {};
|
|
297
352
|
return {
|
|
298
353
|
yaml(node) {
|
|
299
354
|
let data;
|