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.
Files changed (147) hide show
  1. package/CONTRIBUTING.md +2 -1
  2. package/dist/adapters/memory/index.d.ts +5 -0
  3. package/dist/adapters/memory/index.d.ts.map +1 -1
  4. package/dist/adapters/memory/index.js +16 -0
  5. package/dist/adapters/memory/index.js.map +1 -1
  6. package/dist/adapters/node/index.d.ts +1 -0
  7. package/dist/adapters/node/index.d.ts.map +1 -1
  8. package/dist/adapters/node/index.js +1 -0
  9. package/dist/adapters/node/index.js.map +1 -1
  10. package/dist/adapters/node/move-file.io.d.ts +4 -0
  11. package/dist/adapters/node/move-file.io.d.ts.map +1 -0
  12. package/dist/adapters/node/move-file.io.js +12 -0
  13. package/dist/adapters/node/move-file.io.js.map +1 -0
  14. package/dist/adapters/yaml/index.d.ts +3 -0
  15. package/dist/adapters/yaml/index.d.ts.map +1 -0
  16. package/dist/adapters/yaml/index.js +17 -0
  17. package/dist/adapters/yaml/index.js.map +1 -0
  18. package/dist/cli.d.ts.map +1 -1
  19. package/dist/cli.js +48 -7
  20. package/dist/cli.js.map +1 -1
  21. package/dist/cycle-rules.d.ts +3 -3
  22. package/dist/cycle-rules.d.ts.map +1 -1
  23. package/dist/cycle-rules.js +1 -2
  24. package/dist/cycle-rules.js.map +1 -1
  25. package/dist/domain/cycle.d.ts +16 -2
  26. package/dist/domain/cycle.d.ts.map +1 -1
  27. package/dist/domain/cycle.js +21 -42
  28. package/dist/domain/cycle.js.map +1 -1
  29. package/dist/domain/paper-layout.d.ts.map +1 -1
  30. package/dist/domain/paper-layout.js +5 -2
  31. package/dist/domain/paper-layout.js.map +1 -1
  32. package/dist/domain/paths.d.ts +12 -0
  33. package/dist/domain/paths.d.ts.map +1 -1
  34. package/dist/domain/paths.js +22 -1
  35. package/dist/domain/paths.js.map +1 -1
  36. package/dist/domain/scaffold-layout.js +1 -1
  37. package/dist/domain/versions-migration.d.ts +51 -0
  38. package/dist/domain/versions-migration.d.ts.map +1 -0
  39. package/dist/domain/versions-migration.js +252 -0
  40. package/dist/domain/versions-migration.js.map +1 -0
  41. package/dist/domain/versions.d.ts +167 -0
  42. package/dist/domain/versions.d.ts.map +1 -0
  43. package/dist/domain/versions.js +457 -0
  44. package/dist/domain/versions.js.map +1 -0
  45. package/dist/eslint-rules/paper-stages.d.ts +7 -0
  46. package/dist/eslint-rules/paper-stages.d.ts.map +1 -1
  47. package/dist/eslint-rules/paper-stages.js +62 -7
  48. package/dist/eslint-rules/paper-stages.js.map +1 -1
  49. package/dist/eslint-rules/rule-context.d.ts +2 -0
  50. package/dist/eslint-rules/rule-context.d.ts.map +1 -1
  51. package/dist/ports/move-file.d.ts +7 -0
  52. package/dist/ports/move-file.d.ts.map +1 -0
  53. package/dist/ports/move-file.js +2 -0
  54. package/dist/ports/move-file.js.map +1 -0
  55. package/dist/ports/yaml.d.ts +5 -0
  56. package/dist/ports/yaml.d.ts.map +1 -0
  57. package/dist/ports/yaml.js +2 -0
  58. package/dist/ports/yaml.js.map +1 -0
  59. package/dist/submission.d.ts +2 -0
  60. package/dist/submission.d.ts.map +1 -1
  61. package/dist/submission.js +49 -9
  62. package/dist/submission.js.map +1 -1
  63. package/dist/version-rules.d.ts +16 -0
  64. package/dist/version-rules.d.ts.map +1 -0
  65. package/dist/version-rules.js +201 -0
  66. package/dist/version-rules.js.map +1 -0
  67. package/dist/versions-command.d.ts +37 -0
  68. package/dist/versions-command.d.ts.map +1 -0
  69. package/dist/versions-command.js +209 -0
  70. package/dist/versions-command.js.map +1 -0
  71. package/dist/versions.d.ts +76 -0
  72. package/dist/versions.d.ts.map +1 -0
  73. package/dist/versions.js +164 -0
  74. package/dist/versions.js.map +1 -0
  75. package/docs/configuration.md +76 -2
  76. package/docs/layout.md +5 -5
  77. package/docs/rules/cycle/camera-ready-source.md +62 -0
  78. package/docs/rules/cycle/draft-drift.md +47 -0
  79. package/docs/rules/cycle/port-done.md +2 -2
  80. package/docs/rules/cycle/record.md +2 -1
  81. package/docs/rules/cycle/submitted-frozen.md +65 -0
  82. package/docs/rules/paper/allowed-files-only.md +1 -1
  83. package/docs/rules/versions/layout.md +88 -0
  84. package/docs/rules.md +25 -11
  85. package/docs/submission.md +21 -3
  86. package/eslint-rules/paper-stages.test.mjs +62 -0
  87. package/eslint-rules/paper-stages.ts +67 -8
  88. package/eslint-rules/prereg-coverage.test.ts +1 -0
  89. package/eslint-rules/rule-context.ts +2 -0
  90. package/package.json +2 -1
  91. package/presets/agent.jsonc +75 -0
  92. package/presets/agent.md +70 -0
  93. package/presets/agentverify.jsonc +72 -0
  94. package/presets/agentverify.md +85 -0
  95. package/presets/ieee-conference.md +1 -1
  96. package/presets/nlbse.jsonc +76 -0
  97. package/presets/nlbse.md +85 -0
  98. package/scripts/coverage-config.test.ts +3 -0
  99. package/skills/CLAUDE.md +2 -2
  100. package/skills/CLAUDE.md.spec.ts +1 -1
  101. package/skills/paper-pipeline/references/pipeline-status-template.md +4 -1
  102. package/skills/submit-paper/SKILL.md +18 -10
  103. package/skills/submit-paper/SKILL.md.spec.ts +17 -9
  104. package/skills/submit-paper/references/publishers/acm.md +5 -3
  105. package/src/adapters/memory/index.test.ts +22 -0
  106. package/src/adapters/memory/index.ts +21 -0
  107. package/src/adapters/node/index.ts +1 -0
  108. package/src/adapters/node/move-file.io.ts +21 -0
  109. package/src/adapters/node/move-file.test.ts +51 -0
  110. package/src/adapters/yaml/index.test.ts +16 -0
  111. package/src/adapters/yaml/index.ts +17 -0
  112. package/src/cli.test.ts +43 -0
  113. package/src/cli.ts +58 -6
  114. package/src/cycle-rules.ts +4 -5
  115. package/src/domain/cycle.test.ts +27 -0
  116. package/src/domain/cycle.ts +38 -6
  117. package/src/domain/paper-layout.ts +5 -2
  118. package/src/domain/paths.test.ts +28 -1
  119. package/src/domain/paths.ts +34 -1
  120. package/src/domain/scaffold-layout.ts +1 -1
  121. package/src/domain/versions-migration.test.ts +410 -0
  122. package/src/domain/versions-migration.ts +429 -0
  123. package/src/domain/versions.test.ts +564 -0
  124. package/src/domain/versions.ts +706 -0
  125. package/src/paper-settings-commands.test.ts +1 -1
  126. package/src/ports/move-file.ts +8 -0
  127. package/src/ports/yaml.ts +5 -0
  128. package/src/preset-card-rule.test.ts +3 -0
  129. package/src/presets.test.ts +55 -31
  130. package/src/rule-cases.test.ts +157 -8
  131. package/src/submission-freeze.test.ts +320 -0
  132. package/src/submission-new.test.ts +11 -1
  133. package/src/submission.ts +82 -11
  134. package/src/tex-venue-rules.test.ts +3 -0
  135. package/src/venue-rules.test.ts +2 -2
  136. package/src/version-rules.test.ts +222 -0
  137. package/src/version-rules.ts +282 -0
  138. package/src/versions-command.test.ts +594 -0
  139. package/src/versions-command.ts +360 -0
  140. package/src/versions.ts +286 -0
  141. package/dist/eslint-rules/cycle-stage.d.ts +0 -39
  142. package/dist/eslint-rules/cycle-stage.d.ts.map +0 -1
  143. package/dist/eslint-rules/cycle-stage.js +0 -119
  144. package/dist/eslint-rules/cycle-stage.js.map +0 -1
  145. package/docs/rules/cycle/stage.md +0 -85
  146. package/eslint-rules/cycle-stage.test.mjs +0 -131
  147. 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;;;;;;;;;;;;;;;;;;;;;4BAqG/B,WAAW;2BAKZ;oBAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;iBAAE;kCA8FpB,MAAM;;;QAgB9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA8BG;;;;;;;;;;;;;;;4BAoBe,WAAW;2BAGZ;oBAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;iBAAE;;;;;AAlMhD,wBA2PE"}
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
- const records = declared ?? [];
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;