paperlint 7.1.0 → 8.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/workflows/ci.yml +3 -0
- package/.github/workflows/deadlines.yml +100 -0
- package/README.md +6 -0
- package/dist/adapters/hotcrp/deadlines.d.ts +8 -0
- package/dist/adapters/hotcrp/deadlines.d.ts.map +1 -0
- package/dist/adapters/hotcrp/deadlines.io.d.ts +6 -0
- package/dist/adapters/hotcrp/deadlines.io.d.ts.map +1 -0
- package/dist/adapters/hotcrp/deadlines.io.js +26 -0
- package/dist/adapters/hotcrp/deadlines.io.js.map +1 -0
- package/dist/adapters/hotcrp/deadlines.js +134 -0
- package/dist/adapters/hotcrp/deadlines.js.map +1 -0
- package/dist/adapters/hotcrp/index.d.ts +2 -0
- package/dist/adapters/hotcrp/index.d.ts.map +1 -1
- package/dist/adapters/hotcrp/index.js +2 -0
- package/dist/adapters/hotcrp/index.js.map +1 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +1 -6
- package/dist/build.js.map +1 -1
- package/dist/cli.d.ts +3 -4
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +53 -47
- package/dist/cli.js.map +1 -1
- package/dist/cycle-rules.d.ts +24 -0
- package/dist/cycle-rules.d.ts.map +1 -0
- package/dist/cycle-rules.js +212 -0
- package/dist/cycle-rules.js.map +1 -0
- package/dist/domain/cycle.d.ts +148 -0
- package/dist/domain/cycle.d.ts.map +1 -0
- package/dist/domain/cycle.js +281 -0
- package/dist/domain/cycle.js.map +1 -0
- package/dist/domain/deadline.d.ts +128 -0
- package/dist/domain/deadline.d.ts.map +1 -0
- package/dist/domain/deadline.js +281 -0
- package/dist/domain/deadline.js.map +1 -0
- package/dist/eslint-rules/cycle-stage.d.ts +39 -0
- package/dist/eslint-rules/cycle-stage.d.ts.map +1 -0
- package/dist/eslint-rules/cycle-stage.js +119 -0
- package/dist/eslint-rules/cycle-stage.js.map +1 -0
- package/dist/eslint-rules/latex-language.d.ts.map +1 -1
- package/dist/eslint-rules/latex-language.js +10 -13
- package/dist/eslint-rules/latex-language.js.map +1 -1
- package/dist/eslint-rules/paper-context.js +2 -2
- package/dist/eslint-rules/paper-typography.d.ts.map +1 -1
- package/dist/eslint-rules/paper-typography.js +22 -91
- package/dist/eslint-rules/paper-typography.js.map +1 -1
- package/dist/eslint-rules/papers.d.ts +1 -3
- package/dist/eslint-rules/papers.d.ts.map +1 -1
- package/dist/eslint-rules/papers.js +2 -4
- package/dist/eslint-rules/papers.js.map +1 -1
- package/dist/eslint-rules/tex-build.d.ts.map +1 -1
- package/dist/eslint-rules/tex-build.js +8 -25
- package/dist/eslint-rules/tex-build.js.map +1 -1
- package/dist/init.d.ts +1 -4
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +3 -9
- package/dist/init.js.map +1 -1
- package/dist/lib/markdown.d.ts +5 -81
- package/dist/lib/markdown.d.ts.map +1 -1
- package/dist/lib/markdown.js +5 -116
- package/dist/lib/markdown.js.map +1 -1
- package/dist/lib/paper-config.d.ts.map +1 -1
- package/dist/lib/paper-config.js +2 -2
- package/dist/lib/paper-config.js.map +1 -1
- package/dist/lib/skill-eval-fixture.d.ts +6 -4
- package/dist/lib/skill-eval-fixture.d.ts.map +1 -1
- package/dist/lib/skill-eval-fixture.js +25 -18
- package/dist/lib/skill-eval-fixture.js.map +1 -1
- package/dist/new-paper.d.ts +5 -11
- package/dist/new-paper.d.ts.map +1 -1
- package/dist/new-paper.js +12 -23
- package/dist/new-paper.js.map +1 -1
- package/dist/paper-files.d.ts +4 -4
- package/dist/paper-prose.d.ts +17 -0
- package/dist/paper-prose.d.ts.map +1 -0
- package/dist/paper-prose.js +46 -0
- package/dist/paper-prose.js.map +1 -0
- package/dist/paper-settings.d.ts +7 -0
- package/dist/paper-settings.d.ts.map +1 -1
- package/dist/paper-settings.js +61 -8
- package/dist/paper-settings.js.map +1 -1
- package/dist/ports/venue-deadlines.d.ts +14 -0
- package/dist/ports/venue-deadlines.d.ts.map +1 -0
- package/dist/ports/venue-deadlines.js +2 -0
- package/dist/ports/venue-deadlines.js.map +1 -0
- package/dist/presets.d.ts +12 -0
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +19 -11
- package/dist/presets.js.map +1 -1
- package/dist/structure.d.ts.map +1 -1
- package/dist/structure.js +4 -3
- package/dist/structure.js.map +1 -1
- package/dist/submission.d.ts +8 -0
- package/dist/submission.d.ts.map +1 -1
- package/dist/submission.js +41 -10
- package/dist/submission.js.map +1 -1
- package/dist/tex-requirements.d.ts +3 -0
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +19 -0
- package/dist/tex-requirements.js.map +1 -1
- package/dist/tex-venue-rules.d.ts +19 -0
- package/dist/tex-venue-rules.d.ts.map +1 -1
- package/dist/tex-venue-rules.js +69 -0
- package/dist/tex-venue-rules.js.map +1 -1
- package/dist/types.d.ts +0 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/venue-rules.d.ts +10 -0
- package/dist/venue-rules.d.ts.map +1 -1
- package/dist/venue-rules.js +26 -10
- package/dist/venue-rules.js.map +1 -1
- package/docs/configuration.md +146 -20
- package/docs/e2e.md +4 -6
- package/docs/prior-art/paper-folder-scaffolding.md +24 -29
- package/docs/rules/cycle/evidence.md +87 -0
- package/docs/rules/cycle/port-done.md +75 -0
- package/docs/rules/cycle/record.md +152 -0
- package/docs/rules/cycle/stage.md +85 -0
- package/docs/rules/tex/claim-provenance.md +0 -1
- package/docs/rules.md +48 -47
- package/docs/submission.md +5 -0
- package/eslint-rules/cycle-stage.test.mjs +131 -0
- package/eslint-rules/cycle-stage.ts +141 -0
- package/eslint-rules/latex-language.harness.mjs +3 -3
- package/eslint-rules/latex-language.ts +10 -13
- package/eslint-rules/paper-context.ts +2 -2
- package/eslint-rules/paper-typography.test.mjs +17 -85
- package/eslint-rules/paper-typography.ts +22 -98
- package/eslint-rules/papers.harness.mjs +1 -1
- package/eslint-rules/papers.ts +2 -4
- package/eslint-rules/tex-build.harness.mjs +2 -2
- package/eslint-rules/tex-build.ts +8 -25
- package/eslint.config.mjs +41 -2
- package/hooks/hooks.harness.ts +15 -15
- package/hooks/paper-edit-guard.hook.mjs +5 -8
- package/hooks/paper-edit-guard.test.ts +12 -3
- package/hooks/paper-skills-nudge.hook.mjs +2 -3
- package/hooks/paper-skills-nudge.test.ts +13 -3
- package/hooks/paper-status-gates.hook.mjs +2 -2
- package/hooks/paper-status-gates.test.ts +14 -2
- package/lib/markdown.test.mjs +5 -60
- package/lib/markdown.ts +9 -136
- package/lib/paper-config.ts +3 -2
- package/lib/skill-checks.test.mjs +4 -1
- package/lib/skill-eval-fixture.ts +25 -18
- package/package.json +1 -3
- package/presets/aidc.jsonc +24 -3
- package/presets/aidc.md +6 -3
- package/presets/msr.jsonc +25 -1
- package/presets/msr.md +5 -1
- package/presets/realm.jsonc +1 -1
- package/presets/venue-profile.schema.json +39 -0
- package/scripts/check.ts +4 -0
- package/scripts/harness-api.frozen.json +0 -5
- package/scripts/refresh-deadlines.test.ts +431 -0
- package/scripts/refresh-deadlines.ts +387 -0
- package/skills/cold-read-diff/SKILL.md +3 -3
- package/skills/cold-read-diff/SKILL.md.spec.ts +2 -2
- package/skills/grade-paper-writing/SKILL.md +14 -22
- package/skills/grade-paper-writing/SKILL.md.spec.ts +13 -21
- package/skills/grade-paper-writing/__snapshots__/prose-lint.test.ts.snap +40 -26
- package/skills/grade-paper-writing/prose-lint.mjs +108 -278
- package/skills/grade-paper-writing/prose-lint.test.ts +184 -81
- package/skills/paper-adversarial-review/SKILL.md +2 -2
- package/skills/paper-adversarial-review/SKILL.md.spec.ts +1 -1
- package/skills/paper-pipeline/SKILL.md +9 -12
- package/skills/paper-pipeline/SKILL.md.spec.ts +8 -11
- package/skills/paper-pipeline/description-language.eval.mjs +25 -15
- package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +25 -15
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +25 -15
- package/skills/paper-pipeline/pipeline-firing.eval.mjs +27 -16
- package/skills/paper-pipeline/pipeline-language.eval.mjs +25 -15
- package/skills/paper-pipeline/references/acceptance-gate.md +16 -20
- package/skills/paper-pipeline/references/artifact-checklist.md +4 -5
- package/skills/paper-pipeline/references/pipeline-status-template.md +1 -1
- package/skills/paper-pipeline/scripts/README.md +0 -4
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +8 -72
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +48 -156
- package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +81 -75
- package/skills/paper-pipeline/scripts/generated-code.harness.mjs +3 -3
- package/skills/paper-status/SKILL.md +2 -2
- package/skills/paper-status/SKILL.md.spec.ts +1 -1
- package/skills/render-paper/SKILL.md +2 -2
- package/skills/render-paper/SKILL.md.spec.ts +1 -1
- package/skills/render-paper/extract-pdf-facts.mjs +2 -2
- package/skills/tighten-paper/SKILL.md +29 -42
- package/skills/tighten-paper/SKILL.md.spec.ts +28 -41
- package/skills/verify-citations/scripts/bib-authors.check.test.mjs +0 -13
- package/skills/verify-citations/scripts/bib-authors.cli.test.ts +23 -11
- package/skills/verify-citations/scripts/bib-authors.mjs +8 -93
- package/skills/verify-citations/scripts/bib-authors.test.mjs +1 -44
- package/src/adapters/hotcrp/cassette/README.md +22 -0
- package/src/adapters/hotcrp/cassette/deadlines-aidc26.html +17 -0
- package/src/adapters/hotcrp/cassette/deadlines-fse2027.html +24 -0
- package/src/adapters/hotcrp/cassette/deadlines-icse2027.html +25 -0
- package/src/adapters/hotcrp/cassette/deadlines-msr2027.html +25 -0
- package/src/adapters/hotcrp/cassette/deadlines-nosuch.html +18 -0
- package/src/adapters/hotcrp/cassette/deadlines-sosp26.html +26 -0
- package/src/adapters/hotcrp/deadlines.io.test.ts +86 -0
- package/src/adapters/hotcrp/deadlines.io.ts +27 -0
- package/src/adapters/hotcrp/deadlines.test.ts +170 -0
- package/src/adapters/hotcrp/deadlines.ts +161 -0
- package/src/adapters/hotcrp/index.ts +2 -0
- package/src/build.harness.mjs +2 -2
- package/src/build.test.ts +1 -1
- package/src/build.ts +1 -6
- package/src/cli.harness.mjs +26 -46
- package/src/cli.test.ts +41 -40
- package/src/cli.ts +57 -58
- package/src/cycle-rules.test.ts +278 -0
- package/src/cycle-rules.ts +273 -0
- package/src/domain/cycle.test.ts +456 -0
- package/src/domain/cycle.ts +432 -0
- package/src/domain/deadline.test.ts +386 -0
- package/src/domain/deadline.ts +472 -0
- package/src/folder-venue-rule.test.ts +1 -1
- package/src/init.test.ts +6 -7
- package/src/init.ts +3 -22
- package/src/lint-fix.test.ts +10 -4
- package/src/new-paper.harness.mjs +9 -28
- package/src/new-paper.test.ts +7 -6
- package/src/new-paper.ts +11 -30
- package/src/paper-files.test.ts +5 -4
- package/src/paper-files.ts +4 -4
- package/src/paper-prose.test.ts +49 -0
- package/src/paper-prose.ts +67 -0
- package/src/paper-settings.test.ts +167 -0
- package/src/paper-settings.ts +85 -11
- package/src/parse-args.test.ts +0 -1
- package/src/ports/venue-deadlines.ts +14 -0
- package/src/presets.test.ts +105 -0
- package/src/presets.ts +43 -10
- package/src/rule-cases.test.ts +144 -0
- package/src/structure.harness.ts +5 -11
- package/src/structure.ts +4 -3
- package/src/submission.test.ts +105 -1
- package/src/submission.ts +73 -12
- package/src/tex-requirements.ts +31 -0
- package/src/tex-venue-rules.test.ts +76 -0
- package/src/tex-venue-rules.ts +85 -0
- package/src/types.ts +0 -2
- package/src/venue-rules.test.ts +82 -0
- package/src/venue-rules.ts +44 -12
- package/fixtures/real-markdown-paper/baseline.json +0 -22
- package/fixtures/real-markdown-paper/baseline.ts +0 -70
- package/skills/paper-pipeline/PIPELINE-MAP.md +0 -381
- package/skills/paper-pipeline/pipeline-firing.baseline.json +0 -270
- package/skills/paper-pipeline/references/sweep-2026-08-09-ideal-pipeline.md +0 -585
- package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +0 -492
- package/skills/paper-pipeline/scripts/artifact-coverage.mjs +0 -397
- package/skills/paper-pipeline/scripts/artifact-coverage.test.ts +0 -86
- package/skills/paper-pipeline/scripts/check-provenance.mjs +0 -184
- package/skills/paper-pipeline/scripts/population-map.harness.mjs +0 -135
- package/skills/paper-pipeline/scripts/population-map.mjs +0 -188
- package/skills/paper-pipeline/scripts/population-map.test.mjs +0 -154
- package/skills/paper-pipeline/scripts/provenance.harness.mjs +0 -266
- package/skills/paper-pipeline/scripts/round-diff.harness.ts +0 -907
- package/skills/paper-pipeline/scripts/round-diff.mjs +0 -583
- package/skills/paper-pipeline/scripts/round-diff.test.mjs +0 -159
- package/skills/tighten-paper/__snapshots__/structure.test.ts.snap +0 -133
- package/skills/tighten-paper/structure.mjs +0 -364
- package/skills/tighten-paper/structure.test.ts +0 -117
- package/templates/paper/paper.md +0 -4
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE CYCLE RULES over a paper's settings file — the `cycle` plugin, judged once per paper on its
|
|
3
|
+
* `PIPELINE-STATUS.md` (every paper folder has one, whatever its source format; `paper/folder-venue-leftover`
|
|
4
|
+
* reports there for the same reason):
|
|
5
|
+
*
|
|
6
|
+
* cycle/record error `cycles` in paperlint.json parses and names one current attempt: no two
|
|
7
|
+
* open cycles, the open one last, ids unique, every deadline an instant
|
|
8
|
+
* with its zone; each source's deadlines in order; no portal reading the
|
|
9
|
+
* venue's preset already carries
|
|
10
|
+
* cycle/evidence error every closed cycle's `outcome.evidence`, and every deadline override's
|
|
11
|
+
* `evidence`, is a file in the paper folder
|
|
12
|
+
*
|
|
13
|
+
* ── WHY ERROR, NOT WARN ───────────────────────────────────────────────────────────
|
|
14
|
+
* A warning is read once and never again. Each of these names a state that cannot be worked in:
|
|
15
|
+
* two open cycles is dual submission or a decision nobody recorded; a deadline without its zone is
|
|
16
|
+
* the mistake the record exists to prevent; a decision or an override whose evidence is not in the
|
|
17
|
+
* folder is a claim with nothing behind it. Nothing here depends on a clock or the network — the
|
|
18
|
+
* record is judged against itself, the installed venue preset and the disk, like `paper/stages`.
|
|
19
|
+
*
|
|
20
|
+
* ── THE PRESET'S READINGS ARE DERIVED, NOT COPIED ──────────────────────────────────
|
|
21
|
+
* A cycle whose venue is a preset gets that preset's `deadlines` (the portal's readings, refreshed in
|
|
22
|
+
* the package); the paper adds the call's readings and its overrides. A paper's own portal reading
|
|
23
|
+
* of a deadline the preset carries is a second copy of one fact, and it drifts the day the preset is
|
|
24
|
+
* updated — so it is reported.
|
|
25
|
+
*
|
|
26
|
+
* ── WHO SPEAKS WHEN THERE IS NOTHING TO JUDGE ──────────────────────────────────────
|
|
27
|
+
* No `paperlint.json`, one that is not JSON, or one without `cycles` (the flat form): silent — the
|
|
28
|
+
* flat form is still valid, and `pdf/profile` says when the whole file is broken. A `cycles` that
|
|
29
|
+
* does not parse, or names no current attempt (two open cycles), never reaches this rule through
|
|
30
|
+
* `paperlint lint`: `paperRuleBlocks` (src/cli.ts) refuses the settings file before ESLint runs, one
|
|
31
|
+
* line naming the file and the problem, exit 2 — the same strictness as an unknown key (measured
|
|
32
|
+
* 2026-10-05). The `broken` message below is what the rule says when a host runs it without that
|
|
33
|
+
* refusal; under the CLI only `order` is reachable.
|
|
34
|
+
*/
|
|
35
|
+
import { basename, dirname, join } from "node:path";
|
|
36
|
+
import { callerPath } from "./caller-path.ts";
|
|
37
|
+
import {
|
|
38
|
+
cycleProblemText,
|
|
39
|
+
cyclesOf,
|
|
40
|
+
parseCycles,
|
|
41
|
+
type Cycle,
|
|
42
|
+
type Cycles,
|
|
43
|
+
} from "./domain/cycle.ts";
|
|
44
|
+
import { deadlineOrderProblems, type Reading } from "./domain/deadline.ts";
|
|
45
|
+
import { resolvePreset } from "./presets.ts";
|
|
46
|
+
import { fieldOf } from "./domain/record.ts";
|
|
47
|
+
import { err, ok, type Result } from "./domain/result.ts";
|
|
48
|
+
import type {
|
|
49
|
+
FolderRuleContext,
|
|
50
|
+
FolderRuleModule,
|
|
51
|
+
} from "./folder-venue-rule.ts";
|
|
52
|
+
import type { Finding, VenueRuleDeps } from "./venue-rules.ts";
|
|
53
|
+
import { rulePageUrl } from "./venue-rules.ts";
|
|
54
|
+
import { CONFIG_FILE } from "#lib/paper-config";
|
|
55
|
+
|
|
56
|
+
const STATUS_FILE = "PIPELINE-STATUS.md";
|
|
57
|
+
|
|
58
|
+
export type CycleRuleName = "record" | "evidence";
|
|
59
|
+
|
|
60
|
+
/** The `cycles` value of the paper's settings file; undefined when there is no file, no JSON, or no key. */
|
|
61
|
+
function cyclesValue(dir: string, deps: VenueRuleDeps): unknown {
|
|
62
|
+
const bytes = deps.files.readBytes(callerPath(join(dir, CONFIG_FILE)));
|
|
63
|
+
if (bytes === null) return undefined;
|
|
64
|
+
try {
|
|
65
|
+
return fieldOf(JSON.parse(new TextDecoder().decode(bytes)), "cycles");
|
|
66
|
+
} catch {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The paper's cycles, parsed and consistent, or the one line that says what is wrong. */
|
|
72
|
+
export function readCycles(
|
|
73
|
+
dir: string,
|
|
74
|
+
deps: VenueRuleDeps,
|
|
75
|
+
): Result<Cycles | null, string> {
|
|
76
|
+
const list = parseCycles(cyclesValue(dir, deps));
|
|
77
|
+
if (!list.ok) return list;
|
|
78
|
+
if (list.value === null) return ok(null);
|
|
79
|
+
const cycles = cyclesOf(list.value);
|
|
80
|
+
return cycles.ok ? cycles : err(cycleProblemText(cycles.error));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** A cycle's venue preset — its spec, as the cycle names it — and the readings it carries. */
|
|
84
|
+
export interface PresetOfCycle {
|
|
85
|
+
readonly spec: string;
|
|
86
|
+
readonly readings: readonly Reading[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The preset of a cycle; null for a named venue or a preset that does not resolve. */
|
|
90
|
+
export type PresetReadings = (c: Cycle) => PresetOfCycle | null;
|
|
91
|
+
|
|
92
|
+
/** A paper's portal readings of deadlines its venue's preset already carries. */
|
|
93
|
+
function presetCopies(c: Cycle, preset: PresetOfCycle): readonly Finding[] {
|
|
94
|
+
return c.deadlines.flatMap((d) => {
|
|
95
|
+
const theirs = preset.readings.find(
|
|
96
|
+
(r) => r.what === d.what && r.source === "portal",
|
|
97
|
+
);
|
|
98
|
+
const ours = d.observed.some((o) => o.source === "portal");
|
|
99
|
+
return theirs !== undefined && ours
|
|
100
|
+
? [
|
|
101
|
+
{
|
|
102
|
+
messageId: "presetCopy",
|
|
103
|
+
data: {
|
|
104
|
+
id: c.id,
|
|
105
|
+
what: d.what,
|
|
106
|
+
preset: preset.spec,
|
|
107
|
+
at: theirs.at,
|
|
108
|
+
read: theirs.read,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
]
|
|
112
|
+
: [];
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** `cycle/record`: the record's own problems. Pure over the read and the presets' readings. */
|
|
117
|
+
export function judgeRecord(
|
|
118
|
+
read: Result<Cycles | null, string>,
|
|
119
|
+
presetReadings: PresetReadings,
|
|
120
|
+
): readonly Finding[] {
|
|
121
|
+
if (!read.ok) return [{ messageId: "broken", data: { why: read.error } }];
|
|
122
|
+
if (read.value === null) return [];
|
|
123
|
+
return read.value.list.flatMap((c) => {
|
|
124
|
+
const preset = presetReadings(c);
|
|
125
|
+
return [
|
|
126
|
+
...deadlineOrderProblems(c.deadlines, preset?.readings ?? []).map(
|
|
127
|
+
(why) => ({ messageId: "order", data: { id: c.id, why } }),
|
|
128
|
+
),
|
|
129
|
+
...(preset === null ? [] : presetCopies(c, preset)),
|
|
130
|
+
];
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** The overrides whose evidence is not on disk. */
|
|
135
|
+
const overrideEvidence = (
|
|
136
|
+
c: Cycle,
|
|
137
|
+
isFile: (relative: string) => boolean,
|
|
138
|
+
): readonly Finding[] =>
|
|
139
|
+
c.deadlines.flatMap((d) =>
|
|
140
|
+
d.override !== null && !isFile(d.override.evidence)
|
|
141
|
+
? [
|
|
142
|
+
{
|
|
143
|
+
messageId: "overrideMissing",
|
|
144
|
+
data: {
|
|
145
|
+
id: c.id,
|
|
146
|
+
what: d.what,
|
|
147
|
+
at: d.override.at,
|
|
148
|
+
evidence: d.override.evidence,
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
]
|
|
152
|
+
: [],
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
/** The closed cycles, and the overrides, whose evidence is not on disk. */
|
|
156
|
+
export function judgeEvidence(
|
|
157
|
+
cycles: readonly Cycle[],
|
|
158
|
+
isFile: (relative: string) => boolean,
|
|
159
|
+
): readonly Finding[] {
|
|
160
|
+
return cycles.flatMap((c) => {
|
|
161
|
+
const o = c.outcome;
|
|
162
|
+
return [
|
|
163
|
+
...(o.kind !== "open" && !isFile(o.evidence)
|
|
164
|
+
? [
|
|
165
|
+
{
|
|
166
|
+
messageId: "missing",
|
|
167
|
+
data: {
|
|
168
|
+
id: c.id,
|
|
169
|
+
outcome: o.kind,
|
|
170
|
+
date: o.date,
|
|
171
|
+
evidence: o.evidence,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
]
|
|
175
|
+
: []),
|
|
176
|
+
...overrideEvidence(c, isFile),
|
|
177
|
+
];
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const META: Readonly<Record<CycleRuleName, FolderRuleModule["meta"]>> = {
|
|
182
|
+
record: {
|
|
183
|
+
type: "suggestion",
|
|
184
|
+
docs: {
|
|
185
|
+
description:
|
|
186
|
+
"the paper's `cycles` record parses and names one current attempt, each source's deadlines in order, no copy of the preset's portal readings",
|
|
187
|
+
url: rulePageUrl("cycle/record"),
|
|
188
|
+
},
|
|
189
|
+
schema: [],
|
|
190
|
+
messages: {
|
|
191
|
+
broken: `${CONFIG_FILE}: {{why}}`,
|
|
192
|
+
order: `${CONFIG_FILE}, cycle «{{id}}»: {{why}} — a date typed wrong, or an AoE day miscounted (write a call's date as "YYYY-MM-DD AoE" and paperlint converts it)`,
|
|
193
|
+
presetCopy: `${CONFIG_FILE}, cycle «{{id}}»: the "{{what}}" deadline records a portal reading, and the preset {{preset}} carries the portal's reading of it ({{at}}, read {{read}}) — a second copy drifts when paperlint updates the preset: delete this one (the call's reading and an override stay)`,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
evidence: {
|
|
197
|
+
type: "suggestion",
|
|
198
|
+
docs: {
|
|
199
|
+
description:
|
|
200
|
+
"every closed cycle's decision, and every deadline override, points at a file in the paper folder — the mail, the reviews, the note",
|
|
201
|
+
url: rulePageUrl("cycle/evidence"),
|
|
202
|
+
},
|
|
203
|
+
schema: [],
|
|
204
|
+
messages: {
|
|
205
|
+
missing:
|
|
206
|
+
"cycle «{{id}}» is {{outcome}} ({{date}}) with evidence `{{evidence}}`, which is not in the paper folder — save the decision mail, the reviews or the note that records the decision there, or correct the path",
|
|
207
|
+
overrideMissing:
|
|
208
|
+
'cycle «{{id}}»: the override of the "{{what}}" deadline ({{at}}) rests on `{{evidence}}`, which is not in the paper folder — save the mail or the page that grants it there, or correct the path',
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
/** Each cycle's venue preset's readings, resolved from the paper's settings file like `extends`. */
|
|
214
|
+
const presetReadingsOf =
|
|
215
|
+
(dir: string, deps: VenueRuleDeps): PresetReadings =>
|
|
216
|
+
(c) => {
|
|
217
|
+
if (c.venue.kind !== "preset") return null;
|
|
218
|
+
const spec = c.venue.extends;
|
|
219
|
+
const p = resolvePreset(spec, join(dir, CONFIG_FILE), deps);
|
|
220
|
+
return p.ok ? { spec, readings: p.value.deadlines } : null;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
/** The findings of one rule for the paper folder `dir`. */
|
|
224
|
+
function findingsFor(
|
|
225
|
+
name: CycleRuleName,
|
|
226
|
+
dir: string,
|
|
227
|
+
deps: VenueRuleDeps,
|
|
228
|
+
): readonly Finding[] {
|
|
229
|
+
const read = readCycles(dir, deps);
|
|
230
|
+
switch (name) {
|
|
231
|
+
case "record":
|
|
232
|
+
return judgeRecord(read, presetReadingsOf(dir, deps));
|
|
233
|
+
case "evidence":
|
|
234
|
+
return read.ok && read.value !== null
|
|
235
|
+
? judgeEvidence(read.value.list, (rel) =>
|
|
236
|
+
deps.files.isFile(callerPath(join(dir, rel))),
|
|
237
|
+
)
|
|
238
|
+
: [];
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function rule(name: CycleRuleName, deps: VenueRuleDeps): FolderRuleModule {
|
|
243
|
+
return {
|
|
244
|
+
meta: META[name],
|
|
245
|
+
create(context: FolderRuleContext) {
|
|
246
|
+
if (basename(context.filename) !== STATUS_FILE) return {};
|
|
247
|
+
return {
|
|
248
|
+
root() {
|
|
249
|
+
const at = context.sourceCode.getLocFromIndex(0);
|
|
250
|
+
findingsFor(name, dirname(context.filename), deps).forEach((f) => {
|
|
251
|
+
context.report({ loc: { start: at, end: at }, ...f });
|
|
252
|
+
});
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** The `cycle` plugin's rules over the settings file, for every paper's `PIPELINE-STATUS.md`. */
|
|
260
|
+
export function cycleRules(
|
|
261
|
+
deps: VenueRuleDeps,
|
|
262
|
+
): Readonly<Record<CycleRuleName, FolderRuleModule>> {
|
|
263
|
+
return { record: rule("record", deps), evidence: rule("evidence", deps) };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** The level each `cycle/*` rule on PIPELINE-STATUS.md is on at in paperlint's own config — `stage` too (eslint-rules/cycle-stage.ts). */
|
|
267
|
+
export const CYCLE_RULE_LEVELS: Readonly<
|
|
268
|
+
Record<`cycle/${CycleRuleName | "stage"}`, "error">
|
|
269
|
+
> = {
|
|
270
|
+
"cycle/record": "error",
|
|
271
|
+
"cycle/evidence": "error",
|
|
272
|
+
"cycle/stage": "error",
|
|
273
|
+
};
|