paperlint 7.0.0 → 7.2.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 +24 -11
- 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/cli.d.ts.map +1 -1
- package/dist/cli.js +42 -7
- 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/body-pages.d.ts +29 -0
- package/dist/domain/body-pages.d.ts.map +1 -1
- package/dist/domain/body-pages.js +25 -0
- package/dist/domain/body-pages.js.map +1 -1
- 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/lib/paper-config.d.ts.map +1 -1
- package/dist/lib/paper-config.js +2 -0
- package/dist/lib/paper-config.js.map +1 -1
- package/dist/paper-includes.d.ts +3 -0
- package/dist/paper-includes.d.ts.map +1 -1
- package/dist/paper-includes.js +17 -2
- package/dist/paper-includes.js.map +1 -1
- 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 +22 -3
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +26 -17
- package/dist/presets.js.map +1 -1
- package/dist/submission.d.ts +16 -0
- package/dist/submission.d.ts.map +1 -1
- package/dist/submission.js +59 -16
- package/dist/submission.js.map +1 -1
- package/dist/tex-requirements.d.ts +12 -0
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +26 -3
- package/dist/tex-requirements.js.map +1 -1
- package/dist/tex-venue-rules.d.ts +24 -2
- package/dist/tex-venue-rules.d.ts.map +1 -1
- package/dist/tex-venue-rules.js +98 -10
- package/dist/tex-venue-rules.js.map +1 -1
- package/dist/venue-rules.d.ts +12 -1
- package/dist/venue-rules.d.ts.map +1 -1
- package/dist/venue-rules.js +81 -17
- package/dist/venue-rules.js.map +1 -1
- package/docs/configuration.md +140 -12
- package/docs/e2e.md +14 -12
- 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/format/page-limit.md +18 -1
- package/docs/rules/tex/template.md +25 -5
- package/docs/rules/tex/venue-leftover.md +13 -0
- package/docs/rules.md +68 -55
- package/docs/submission.md +10 -1
- package/eslint-rules/cycle-stage.test.mjs +131 -0
- package/eslint-rules/cycle-stage.ts +141 -0
- package/eslint.config.mjs +41 -1
- package/fixtures/build-e2e/msr/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/msr/expect.json +18 -0
- package/fixtures/build-e2e/msr/paper.tex +49 -0
- package/fixtures/build-e2e/msr/paperlint.json +1 -0
- package/fixtures/build-e2e/msr-appendix-after/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/msr-appendix-after/expect.json +20 -0
- package/fixtures/build-e2e/msr-appendix-after/paper.tex +48 -0
- package/fixtures/build-e2e/msr-appendix-after/paperlint.json +1 -0
- package/lib/paper-config.ts +3 -0
- package/package.json +1 -1
- package/presets/aidc.jsonc +24 -3
- package/presets/aidc.md +6 -3
- package/presets/ieee-conference.md +1 -1
- package/presets/msr.jsonc +95 -0
- package/presets/msr.md +98 -0
- package/presets/venue-profile.schema.json +64 -0
- package/scripts/check.ts +4 -0
- package/scripts/refresh-deadlines.test.ts +431 -0
- package/scripts/refresh-deadlines.ts +387 -0
- package/skills/camera-ready/SKILL.md +8 -1
- package/skills/camera-ready/SKILL.md.spec.ts +7 -0
- package/skills/submission-portal/SKILL.md +2 -2
- package/skills/submission-portal/SKILL.md.spec.ts +1 -1
- package/skills/submit-paper/SKILL.md +9 -1
- package/skills/submit-paper/SKILL.md.spec.ts +8 -0
- 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/cli.test.ts +9 -0
- package/src/cli.ts +50 -6
- package/src/cycle-rules.test.ts +278 -0
- package/src/cycle-rules.ts +273 -0
- package/src/domain/body-pages.test.ts +144 -0
- package/src/domain/body-pages.ts +61 -1
- 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/new-paper.test.ts +28 -0
- package/src/paper-includes.test.ts +26 -0
- package/src/paper-includes.ts +20 -2
- package/src/paper-settings-commands.test.ts +1 -1
- package/src/paper-settings.test.ts +167 -0
- package/src/paper-settings.ts +85 -11
- package/src/ports/venue-deadlines.ts +14 -0
- package/src/preset-card-rule.test.ts +1 -0
- package/src/presets.test.ts +171 -1
- package/src/presets.ts +65 -15
- package/src/rule-cases.test.ts +144 -0
- package/src/submission.test.ts +147 -4
- package/src/submission.ts +100 -18
- package/src/tex-requirements.ts +52 -4
- package/src/tex-venue-rules.test.ts +237 -0
- package/src/tex-venue-rules.ts +130 -15
- package/src/venue-rules.test.ts +255 -2
- package/src/venue-rules.ts +115 -22
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* THE SHIPPED PRESETS' DEADLINES, RE-READ FROM THEIR PORTALS — run once a day by
|
|
4
|
+
* `.github/workflows/deadlines.yml`, which opens or updates one pull request when a value changed.
|
|
5
|
+
*
|
|
6
|
+
* node scripts/refresh-deadlines.ts [--presets <dir>] [--summary <file.md>]
|
|
7
|
+
*
|
|
8
|
+
* For every preset in the directory whose `portal` has an adapter (HotCRP today), the portal's public
|
|
9
|
+
* deadlines page is read and compared with the preset's `deadlines`:
|
|
10
|
+
*
|
|
11
|
+
* a kind the page lists at the instant the preset holds kept as it is, its `read` day too
|
|
12
|
+
* a kind the page lists at another instant rewritten, `read` = today
|
|
13
|
+
* a kind the page lists that the preset does not hold added, `read` = today
|
|
14
|
+
* a kind the preset holds that the page no longer lists KEPT and noted — a portal may drop a
|
|
15
|
+
* passed deadline; the reading stays true
|
|
16
|
+
* a reading from the call never touched: it is not the portal's
|
|
17
|
+
*
|
|
18
|
+
* Only a changed value rewrites the file, so an unchanged portal produces no diff and no pull
|
|
19
|
+
* request: a `read` day says when the value was last READ DIFFERENT, not when the job last ran.
|
|
20
|
+
*
|
|
21
|
+
* ── IT REPORTS, IT DOES NOT BLOCK ─────────────────────────────────────────────────────────────
|
|
22
|
+
* A portal that is down, gone or no longer a HotCRP page is a `::warning::` line and a line in the
|
|
23
|
+
* summary, and the run exits 0 — the venue's outage is not this repository's defect, and the next
|
|
24
|
+
* day's run reads it again. A preset that does not parse IS this repository's defect: `::error::`,
|
|
25
|
+
* exit 1. The main CI never runs this script against the network; its tests replay the pages
|
|
26
|
+
* recorded in `src/adapters/hotcrp/cassette/`.
|
|
27
|
+
*
|
|
28
|
+
* The block is found on the JSONC's syntax tree (TypeScript's JSON parser, the one the presets are
|
|
29
|
+
* read with) and replaced; everything else in the file — its comments above all — is left as it
|
|
30
|
+
* is, and Prettier formats the result as `npm run fmt` would.
|
|
31
|
+
*/
|
|
32
|
+
import { readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
33
|
+
import { dirname, join } from "node:path";
|
|
34
|
+
import { fileURLToPath } from "node:url";
|
|
35
|
+
import { format } from "prettier";
|
|
36
|
+
import ts from "typescript";
|
|
37
|
+
import { isMain } from "../skills/paper-pipeline/scripts/consumer.mjs";
|
|
38
|
+
import { hotcrpDeadlines } from "../src/adapters/hotcrp/index.ts";
|
|
39
|
+
import {
|
|
40
|
+
DEADLINE_WHATS,
|
|
41
|
+
type DeadlineWhat,
|
|
42
|
+
type IsoDate,
|
|
43
|
+
type PortalDeadlines,
|
|
44
|
+
type Reading,
|
|
45
|
+
} from "../src/domain/deadline.ts";
|
|
46
|
+
import type { Result } from "../src/domain/result.ts";
|
|
47
|
+
import {
|
|
48
|
+
supportedPortal,
|
|
49
|
+
type PortalFailure,
|
|
50
|
+
} from "../src/domain/submission.ts";
|
|
51
|
+
import { messageOf } from "../src/domain/text.ts";
|
|
52
|
+
import { presetsDir } from "../src/package-dirs.ts";
|
|
53
|
+
import { parsePreset } from "../src/tex-requirements.ts";
|
|
54
|
+
|
|
55
|
+
const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
56
|
+
|
|
57
|
+
/** Reads the deadlines page of the portal site at `site` (no trailing slash). */
|
|
58
|
+
export type ReadPortal = (
|
|
59
|
+
site: string,
|
|
60
|
+
) => Promise<Result<PortalDeadlines, PortalFailure>>;
|
|
61
|
+
|
|
62
|
+
/** What happened to one preset. */
|
|
63
|
+
export type Outcome =
|
|
64
|
+
| { readonly kind: "skipped"; readonly why: string }
|
|
65
|
+
| { readonly kind: "broken"; readonly why: string }
|
|
66
|
+
| { readonly kind: "unreadable"; readonly url: string; readonly why: string }
|
|
67
|
+
| {
|
|
68
|
+
readonly kind: "unchanged";
|
|
69
|
+
readonly url: string;
|
|
70
|
+
readonly notes: readonly string[];
|
|
71
|
+
}
|
|
72
|
+
| {
|
|
73
|
+
readonly kind: "changed";
|
|
74
|
+
readonly url: string;
|
|
75
|
+
readonly text: string;
|
|
76
|
+
readonly changes: readonly string[];
|
|
77
|
+
readonly notes: readonly string[];
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/** The preset's readings after the page's answer, what changed, and what is worth a note. */
|
|
81
|
+
export interface Refreshed {
|
|
82
|
+
readonly readings: readonly Reading[];
|
|
83
|
+
readonly changes: readonly string[];
|
|
84
|
+
readonly notes: readonly string[];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The page's instants for one kind, earliest first. */
|
|
88
|
+
const pageAts = (page: PortalDeadlines, what: DeadlineWhat): string[] =>
|
|
89
|
+
page.deadlines
|
|
90
|
+
.filter((d) => d.what === what)
|
|
91
|
+
.map((d) => d.at)
|
|
92
|
+
.toSorted();
|
|
93
|
+
|
|
94
|
+
/** A kind the page does not list: the preset's reading, if it holds one, is kept and noted. */
|
|
95
|
+
const offThePage = (
|
|
96
|
+
what: DeadlineWhat,
|
|
97
|
+
held: Reading | undefined,
|
|
98
|
+
): Refreshed =>
|
|
99
|
+
held === undefined
|
|
100
|
+
? { readings: [], changes: [], notes: [] }
|
|
101
|
+
: {
|
|
102
|
+
readings: [held],
|
|
103
|
+
changes: [],
|
|
104
|
+
notes: [`${what}: no longer on the page — kept as read ${held.read}`],
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/** One kind: the preset's portal reading against the page's. */
|
|
108
|
+
function oneKind(
|
|
109
|
+
what: DeadlineWhat,
|
|
110
|
+
held: Reading | undefined,
|
|
111
|
+
ats: readonly string[],
|
|
112
|
+
o: { readonly url: string; readonly today: IsoDate },
|
|
113
|
+
): Refreshed {
|
|
114
|
+
const notes =
|
|
115
|
+
ats.length > 1
|
|
116
|
+
? [`${what}: the page lists ${ats.join(", ")} — the earliest is recorded`]
|
|
117
|
+
: [];
|
|
118
|
+
const at = ats[0];
|
|
119
|
+
if (at === undefined) return offThePage(what, held);
|
|
120
|
+
if (held?.at === at) return { readings: [held], changes: [], notes };
|
|
121
|
+
const fresh: Reading = {
|
|
122
|
+
what,
|
|
123
|
+
at,
|
|
124
|
+
source: "portal",
|
|
125
|
+
url: o.url,
|
|
126
|
+
read: o.today,
|
|
127
|
+
};
|
|
128
|
+
const change =
|
|
129
|
+
held === undefined ? `${what}: added ${at}` : `${what}: ${held.at} → ${at}`;
|
|
130
|
+
return { readings: [fresh], changes: [change], notes };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The preset's readings after the portal answered `page` at `url` on `today`. The call's readings
|
|
135
|
+
* are kept as they are; the portal's are kept, rewritten or added kind by kind. Pure.
|
|
136
|
+
*/
|
|
137
|
+
export function refreshReadings(
|
|
138
|
+
current: readonly Reading[],
|
|
139
|
+
page: PortalDeadlines,
|
|
140
|
+
url: string,
|
|
141
|
+
today: IsoDate,
|
|
142
|
+
): Refreshed {
|
|
143
|
+
const perKind = DEADLINE_WHATS.map((what) => {
|
|
144
|
+
const portal = oneKind(
|
|
145
|
+
what,
|
|
146
|
+
current.find((r) => r.what === what && r.source === "portal"),
|
|
147
|
+
pageAts(page, what),
|
|
148
|
+
{ url, today },
|
|
149
|
+
);
|
|
150
|
+
const call = current.filter((r) => r.what === what && r.source === "call");
|
|
151
|
+
return { ...portal, readings: [...portal.readings, ...call] };
|
|
152
|
+
});
|
|
153
|
+
return {
|
|
154
|
+
readings: perKind.flatMap((k) => k.readings),
|
|
155
|
+
changes: perKind.flatMap((k) => k.changes),
|
|
156
|
+
notes: perKind.flatMap((k) => k.notes),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** The top-level property `name` of a JSONC object, on its syntax tree; undefined when absent. */
|
|
161
|
+
function property(
|
|
162
|
+
file: ts.JsonSourceFile,
|
|
163
|
+
name: string,
|
|
164
|
+
): ts.PropertyAssignment | undefined {
|
|
165
|
+
const top = file.statements[0]?.expression;
|
|
166
|
+
if (top === undefined || !ts.isObjectLiteralExpression(top)) return undefined;
|
|
167
|
+
return top.properties
|
|
168
|
+
.filter(ts.isPropertyAssignment)
|
|
169
|
+
.find((p) => ts.isStringLiteral(p.name) && p.name.text === name);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* The preset's text with `deadlines` set to `readings`: the value replaced where the key is, or the
|
|
174
|
+
* key inserted after `portal`. Formatted as the repository formats a preset.
|
|
175
|
+
*/
|
|
176
|
+
export async function withDeadlines(
|
|
177
|
+
text: string,
|
|
178
|
+
file: string,
|
|
179
|
+
readings: readonly Reading[],
|
|
180
|
+
): Promise<string> {
|
|
181
|
+
const tree = ts.parseJsonText(file, text);
|
|
182
|
+
const json = JSON.stringify(readings, null, 2);
|
|
183
|
+
const held = property(tree, "deadlines");
|
|
184
|
+
const spliced =
|
|
185
|
+
held === undefined
|
|
186
|
+
? insertAfterPortal(tree, text, json)
|
|
187
|
+
: text.slice(0, held.initializer.getStart(tree)) +
|
|
188
|
+
json +
|
|
189
|
+
text.slice(held.initializer.end);
|
|
190
|
+
return format(spliced, { filepath: file });
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** `"deadlines": …` after the `portal` property — only a preset with a portal is ever refreshed. */
|
|
194
|
+
function insertAfterPortal(
|
|
195
|
+
tree: ts.JsonSourceFile,
|
|
196
|
+
text: string,
|
|
197
|
+
json: string,
|
|
198
|
+
): string {
|
|
199
|
+
const portal = property(tree, "portal");
|
|
200
|
+
if (portal === undefined)
|
|
201
|
+
throw new Error(`${tree.fileName}: no "portal" to put "deadlines" after`);
|
|
202
|
+
return `${text.slice(0, portal.end)},\n\n "deadlines": ${json}${text.slice(portal.end)}`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** A failure to read the page, in one line. */
|
|
206
|
+
function failureText(f: PortalFailure): string {
|
|
207
|
+
switch (f.kind) {
|
|
208
|
+
case "unreachable":
|
|
209
|
+
return `unreachable: ${f.detail}`;
|
|
210
|
+
case "refused":
|
|
211
|
+
return `HTTP ${String(f.httpStatus)}: ${f.messages.map((m) => m.message).join("; ")}`;
|
|
212
|
+
case "malformed":
|
|
213
|
+
return `not the page expected (HTTP ${String(f.httpStatus)}): ${f.detail}`;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** One preset: its portal read, its readings compared, its new text when one changed. */
|
|
218
|
+
export async function refreshPreset(o: {
|
|
219
|
+
readonly file: string;
|
|
220
|
+
readonly text: string;
|
|
221
|
+
readonly read: ReadPortal;
|
|
222
|
+
readonly today: IsoDate;
|
|
223
|
+
}): Promise<Outcome> {
|
|
224
|
+
const parsed = ((): ReturnType<typeof parsePreset> | string => {
|
|
225
|
+
try {
|
|
226
|
+
return parsePreset(o.text, o.file, presetsDir());
|
|
227
|
+
} catch (e) {
|
|
228
|
+
return messageOf(e);
|
|
229
|
+
}
|
|
230
|
+
})();
|
|
231
|
+
if (typeof parsed === "string") return { kind: "broken", why: parsed };
|
|
232
|
+
if (parsed.portal === null) return { kind: "skipped", why: "no portal" };
|
|
233
|
+
const portal = supportedPortal(parsed.portal);
|
|
234
|
+
if (!portal.ok) return { kind: "skipped", why: portal.error };
|
|
235
|
+
const url = `${portal.value.url}/deadlines`;
|
|
236
|
+
const page = await o.read(portal.value.url);
|
|
237
|
+
if (!page.ok)
|
|
238
|
+
return { kind: "unreadable", url, why: failureText(page.error) };
|
|
239
|
+
const r = refreshReadings(parsed.deadlines ?? [], page.value, url, o.today);
|
|
240
|
+
if (r.changes.length === 0) return { kind: "unchanged", url, notes: r.notes };
|
|
241
|
+
const text = await withDeadlines(o.text, o.file, r.readings);
|
|
242
|
+
return { kind: "changed", url, text, changes: r.changes, notes: r.notes };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** The lines one preset's outcome contributes: to the log (annotations included) and to the summary. */
|
|
246
|
+
function report(
|
|
247
|
+
name: string,
|
|
248
|
+
o: Outcome,
|
|
249
|
+
): { readonly log: readonly string[]; readonly md: readonly string[] } {
|
|
250
|
+
switch (o.kind) {
|
|
251
|
+
case "skipped":
|
|
252
|
+
return { log: [], md: [] };
|
|
253
|
+
case "broken":
|
|
254
|
+
return {
|
|
255
|
+
log: [`::error title=deadlines::${name} does not parse: ${o.why}`],
|
|
256
|
+
md: [`- 🔴 \`${name}\` does not parse: ${o.why}`],
|
|
257
|
+
};
|
|
258
|
+
case "unreadable":
|
|
259
|
+
return {
|
|
260
|
+
log: [
|
|
261
|
+
`::warning title=deadlines::${name}: could not read ${o.url} — ${o.why}`,
|
|
262
|
+
],
|
|
263
|
+
md: [`- ⚠️ \`${name}\`: could not read ${o.url} — ${o.why}`],
|
|
264
|
+
};
|
|
265
|
+
case "unchanged":
|
|
266
|
+
return { log: o.notes.map((n) => `${name}: ${n}`), md: [] };
|
|
267
|
+
case "changed":
|
|
268
|
+
return changedReport(name, o);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** A changed preset: each change and note, in the log and as a list in the summary. */
|
|
273
|
+
const changedReport = (
|
|
274
|
+
name: string,
|
|
275
|
+
o: Extract<Outcome, { kind: "changed" }>,
|
|
276
|
+
): { readonly log: readonly string[]; readonly md: readonly string[] } => {
|
|
277
|
+
const lines = [...o.changes, ...o.notes];
|
|
278
|
+
return {
|
|
279
|
+
log: lines.map((c) => `${name}: ${c}`),
|
|
280
|
+
md: [`- \`${name}\` (${o.url})`, ...lines.map((c) => ` - ${c}`)],
|
|
281
|
+
};
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
export interface MainOptions {
|
|
285
|
+
readonly presetsDir: string;
|
|
286
|
+
readonly read: ReadPortal;
|
|
287
|
+
readonly today: IsoDate;
|
|
288
|
+
readonly log: (line: string) => void;
|
|
289
|
+
readonly write: (file: string, text: string) => void;
|
|
290
|
+
/** Where the pull request's body goes; null: nowhere. */
|
|
291
|
+
readonly summary: string | null;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** Every preset of the directory, refreshed — a changed one written back. */
|
|
295
|
+
async function outcomesOf(
|
|
296
|
+
o: MainOptions,
|
|
297
|
+
): Promise<readonly { readonly name: string; readonly out: Outcome }[]> {
|
|
298
|
+
const names = readdirSync(o.presetsDir)
|
|
299
|
+
.filter((f) => f.endsWith(".jsonc"))
|
|
300
|
+
.toSorted();
|
|
301
|
+
return Promise.all(
|
|
302
|
+
names.map(async (name) => {
|
|
303
|
+
const file = join(o.presetsDir, name);
|
|
304
|
+
const text = readFileSync(file, "utf8");
|
|
305
|
+
const out = await refreshPreset({
|
|
306
|
+
file,
|
|
307
|
+
text,
|
|
308
|
+
read: o.read,
|
|
309
|
+
today: o.today,
|
|
310
|
+
});
|
|
311
|
+
if (out.kind === "changed") o.write(file, out.text);
|
|
312
|
+
return { name, out };
|
|
313
|
+
}),
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/** Every preset of the directory, refreshed. 0 unless a preset does not parse. */
|
|
318
|
+
export async function main(o: MainOptions): Promise<number> {
|
|
319
|
+
const outcomes = await outcomesOf(o);
|
|
320
|
+
const reports = outcomes.map(({ name, out }) => report(name, out));
|
|
321
|
+
reports
|
|
322
|
+
.flatMap((r) => r.log)
|
|
323
|
+
.forEach((l) => {
|
|
324
|
+
o.log(l);
|
|
325
|
+
});
|
|
326
|
+
const count = (k: Outcome["kind"]) =>
|
|
327
|
+
outcomes.filter((x) => x.out.kind === k).length;
|
|
328
|
+
const read = count("changed") + count("unchanged");
|
|
329
|
+
o.log(
|
|
330
|
+
count("changed") === 0
|
|
331
|
+
? `✓ no deadline changed: ${String(read)} preset(s) with a portal read, ${String(count("skipped"))} skipped`
|
|
332
|
+
: `${String(count("changed"))} preset(s) changed`,
|
|
333
|
+
);
|
|
334
|
+
if (o.summary !== null)
|
|
335
|
+
o.write(
|
|
336
|
+
o.summary,
|
|
337
|
+
summaryOf(
|
|
338
|
+
o.today,
|
|
339
|
+
reports.flatMap((r) => r.md),
|
|
340
|
+
),
|
|
341
|
+
);
|
|
342
|
+
return count("broken") > 0 ? 1 : 0;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** The pull request's body. */
|
|
346
|
+
const summaryOf = (today: IsoDate, lines: readonly string[]): string =>
|
|
347
|
+
[
|
|
348
|
+
`The venue presets' deadlines, re-read on ${today} from each preset's portal by \`.github/workflows/deadlines.yml\` (\`scripts/refresh-deadlines.ts\`). A reading whose value changed carries today's \`read\` day; the others keep theirs.`,
|
|
349
|
+
"",
|
|
350
|
+
...lines,
|
|
351
|
+
"",
|
|
352
|
+
"Merging releases a patch; papers get the new dates through their normal update of paperlint.",
|
|
353
|
+
"",
|
|
354
|
+
].join("\n");
|
|
355
|
+
|
|
356
|
+
/** `--presets <dir>` and `--summary <file>` from the command line. */
|
|
357
|
+
export function argsOf(argv: readonly string[]): {
|
|
358
|
+
readonly presets: string;
|
|
359
|
+
readonly summary: string | null;
|
|
360
|
+
} {
|
|
361
|
+
const value = (flag: string): string | null => {
|
|
362
|
+
const i = argv.indexOf(flag);
|
|
363
|
+
return i === -1 ? null : (argv[i + 1] ?? null);
|
|
364
|
+
};
|
|
365
|
+
return {
|
|
366
|
+
presets: value("--presets") ?? join(ROOT, "presets"),
|
|
367
|
+
summary: value("--summary"),
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// `isMain`, not a comparison with `file://${argv[1]}`: that is false through a symlink (consumer.mjs).
|
|
372
|
+
if (isMain(import.meta.url)) {
|
|
373
|
+
const args = argsOf(process.argv.slice(2));
|
|
374
|
+
process.exit(
|
|
375
|
+
await main({
|
|
376
|
+
presetsDir: args.presets,
|
|
377
|
+
read: (site) => hotcrpDeadlines({ url: site }).read(),
|
|
378
|
+
// The clock is read here, at the edge: the day a reading was taken.
|
|
379
|
+
today: new Date().toISOString().slice(0, 10),
|
|
380
|
+
log: console.log,
|
|
381
|
+
write: (file, text) => {
|
|
382
|
+
writeFileSync(file, text);
|
|
383
|
+
},
|
|
384
|
+
summary: args.summary,
|
|
385
|
+
}),
|
|
386
|
+
);
|
|
387
|
+
}
|
|
@@ -4,7 +4,7 @@ description: On acceptance, turn the anonymized-for-review paper into the de-ano
|
|
|
4
4
|
allowed-tools: [Read, Write, Edit, Grep, Glob, Bash, WebSearch, WebFetch]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
<!-- vigiles:sha256:
|
|
7
|
+
<!-- vigiles:sha256:1c9ad89e55c3e89a compiled from skills/camera-ready/SKILL.md.spec.ts -->
|
|
8
8
|
|
|
9
9
|
# camera-ready — de-anonymize an accepted paper into the final proceedings version
|
|
10
10
|
|
|
@@ -94,6 +94,13 @@ and going over is a hard reject at the proceedings stage. Recompile
|
|
|
94
94
|
If tight, ACM allows references to spill into the reference-only extra pages.
|
|
95
95
|
|
|
96
96
|
## 5. Ship
|
|
97
|
+
- **Before the final PDF goes up, hand the last check to the author, in plain words.** Tell them that
|
|
98
|
+
paperlint passing is not the venue or the publisher accepting the format, and ask them to open the
|
|
99
|
+
exact PDF they are about to upload and check it against the venue's call for papers and its
|
|
100
|
+
camera-ready instructions themselves — the page count and what counts toward the limit (the
|
|
101
|
+
camera-ready's extra page, if any), the template and its class options, the author block and
|
|
102
|
+
acknowledgements now de-anonymized, and that every figure, table and reference renders. Upload once
|
|
103
|
+
they say they have looked.
|
|
97
104
|
- Rebuild the final PDF, upload to the camera-ready system (often the same HotCRP, or an ACM eRights /
|
|
98
105
|
TAPS pipeline — follow the copyright email).
|
|
99
106
|
- Complete the **ACM/IEEE eRights** copyright form → paste the returned copyright block + DOI into the
|
|
@@ -109,6 +109,13 @@ and going over is a hard reject at the proceedings stage. Recompile
|
|
|
109
109
|
If tight, ACM allows references to spill into the reference-only extra pages.
|
|
110
110
|
|
|
111
111
|
## 5. Ship
|
|
112
|
+
- **Before the final PDF goes up, hand the last check to the author, in plain words.** Tell them that
|
|
113
|
+
paperlint passing is not the venue or the publisher accepting the format, and ask them to open the
|
|
114
|
+
exact PDF they are about to upload and check it against the venue's call for papers and its
|
|
115
|
+
camera-ready instructions themselves — the page count and what counts toward the limit (the
|
|
116
|
+
camera-ready's extra page, if any), the template and its class options, the author block and
|
|
117
|
+
acknowledgements now de-anonymized, and that every figure, table and reference renders. Upload once
|
|
118
|
+
they say they have looked.
|
|
112
119
|
- Rebuild the final PDF, upload to the camera-ready system (often the same HotCRP, or an ACM eRights /
|
|
113
120
|
TAPS pipeline — follow the copyright email).
|
|
114
121
|
- Complete the **ACM/IEEE eRights** copyright form → paste the returned copyright block + DOI into the
|
|
@@ -4,7 +4,7 @@ description: "Read or change a paper's submission on the venue's HotCRP portal v
|
|
|
4
4
|
allowed-tools: [Read, Grep, Glob, Bash]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
<!-- vigiles:sha256:
|
|
7
|
+
<!-- vigiles:sha256:4b3a8c0b51ebe206 compiled from skills/submission-portal/SKILL.md.spec.ts -->
|
|
8
8
|
|
|
9
9
|
# submission-portal — read and update the submission on the portal, then prove it landed
|
|
10
10
|
|
|
@@ -14,7 +14,7 @@ supported; for any other portal the command says so and the upload is done by ha
|
|
|
14
14
|
## What must be in place
|
|
15
15
|
|
|
16
16
|
- **The venue preset declares the portal**: `"portal": { "kind": "hotcrp", "url": "<the site>" }`
|
|
17
|
-
(`paperlint:aidc`
|
|
17
|
+
(`paperlint:aidc` and `paperlint:msr` have it). For your own venue preset, add it there.
|
|
18
18
|
- **The paper's `paperlint.json` names its submission**: `"submission": { "id": <number> }` — the
|
|
19
19
|
number HotCRP shows for the paper.
|
|
20
20
|
- **A token in `HOTCRP_TOKEN`**, created by the author on the HotCRP site under Account settings →
|
|
@@ -15,7 +15,7 @@ supported; for any other portal the command says so and the upload is done by ha
|
|
|
15
15
|
## What must be in place
|
|
16
16
|
|
|
17
17
|
- **The venue preset declares the portal**: \`"portal": { "kind": "hotcrp", "url": "<the site>" }\`
|
|
18
|
-
(\`paperlint:aidc\`
|
|
18
|
+
(\`paperlint:aidc\` and \`paperlint:msr\` have it). For your own venue preset, add it there.
|
|
19
19
|
- **The paper's \`paperlint.json\` names its submission**: \`"submission": { "id": <number> }\` — the
|
|
20
20
|
number HotCRP shows for the paper.
|
|
21
21
|
- **A token in \`HOTCRP_TOKEN\`**, created by the author on the HotCRP site under Account settings →
|
|
@@ -4,7 +4,7 @@ description: End-to-end playbook for submitting a peer-reviewed paper to a doubl
|
|
|
4
4
|
allowed-tools: [Read, Write, Edit, Grep, Glob, Bash, WebSearch, WebFetch, Skill]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
<!-- vigiles:sha256:
|
|
7
|
+
<!-- vigiles:sha256:81d5777962c2864a compiled from skills/submit-paper/SKILL.md.spec.ts -->
|
|
8
8
|
|
|
9
9
|
# submit-paper — get a reviewed paper from "done" to "ready for review"
|
|
10
10
|
|
|
@@ -20,6 +20,7 @@ paper against:
|
|
|
20
20
|
- `presets/agenticdev.md` — AgenticDev @ ASE (workshop; ASE workshop proceedings).
|
|
21
21
|
- `presets/aisec.md` — AISec @ ACM CCS (security workshop; ACM DL; harder bar).
|
|
22
22
|
- `presets/aidc.md` — AIDC @ IEEE ACSAC (workshop; IEEE template, LLM Usage Statement).
|
|
23
|
+
- `presets/msr.md` — MSR Technical Papers (conference; IEEE template from 2027, appendices count in the page limit).
|
|
23
24
|
- `presets/realm.md` — REALM @ EMNLP (ACL family, OpenReview).
|
|
24
25
|
|
|
25
26
|
**The card is prose; the machine-checked format is the PRESET beside it.** A paper declares its venue
|
|
@@ -110,6 +111,13 @@ Many workshop HotCRPs have **no supplementary-upload field** — check first; if
|
|
|
110
111
|
An independent researcher with no ties usually checks **none** — that's honest.
|
|
111
112
|
|
|
112
113
|
## 5. Submit
|
|
114
|
+
- **Before the PDF goes up — and before every Replace — hand the last check to the author, in plain
|
|
115
|
+
words.** Tell them that paperlint passing is not the venue accepting the format: the preset is
|
|
116
|
+
transcribed from a call that can change, and any check can miss something. Ask them to open the
|
|
117
|
+
exact PDF they are about to upload and check it against the venue's call for papers themselves —
|
|
118
|
+
the page count and what counts toward the limit (appendices, references), the template and its
|
|
119
|
+
class options, anonymity, and that every figure, table and reference renders. Upload once they say
|
|
120
|
+
they have looked. (`paperlint submission update` prints the same request before it sends.)
|
|
113
121
|
- **Save and submit**, then **mark it "ready for review."** A saved draft alone is NOT evaluated — this
|
|
114
122
|
is the step people miss.
|
|
115
123
|
- You can edit until the deadline: use **Replace** on the Submission field to swap the PDF for a late
|
|
@@ -36,6 +36,7 @@ paper against:
|
|
|
36
36
|
- \`presets/agenticdev.md\` — AgenticDev @ ASE (workshop; ASE workshop proceedings).
|
|
37
37
|
- \`presets/aisec.md\` — AISec @ ACM CCS (security workshop; ACM DL; harder bar).
|
|
38
38
|
- \`presets/aidc.md\` — AIDC @ IEEE ACSAC (workshop; IEEE template, LLM Usage Statement).
|
|
39
|
+
- \`presets/msr.md\` — MSR Technical Papers (conference; IEEE template from 2027, appendices count in the page limit).
|
|
39
40
|
- \`presets/realm.md\` — REALM @ EMNLP (ACL family, OpenReview).
|
|
40
41
|
|
|
41
42
|
**The card is prose; the machine-checked format is the PRESET beside it.** A paper declares its venue
|
|
@@ -126,6 +127,13 @@ Many workshop HotCRPs have **no supplementary-upload field** — check first; if
|
|
|
126
127
|
An independent researcher with no ties usually checks **none** — that's honest.
|
|
127
128
|
|
|
128
129
|
## 5. Submit
|
|
130
|
+
- **Before the PDF goes up — and before every Replace — hand the last check to the author, in plain
|
|
131
|
+
words.** Tell them that paperlint passing is not the venue accepting the format: the preset is
|
|
132
|
+
transcribed from a call that can change, and any check can miss something. Ask them to open the
|
|
133
|
+
exact PDF they are about to upload and check it against the venue's call for papers themselves —
|
|
134
|
+
the page count and what counts toward the limit (appendices, references), the template and its
|
|
135
|
+
class options, anonymity, and that every figure, table and reference renders. Upload once they say
|
|
136
|
+
they have looked. (\`paperlint submission update\` prints the same request before it sends.)
|
|
129
137
|
- **Save and submit**, then **mark it "ready for review."** A saved draft alone is NOT evaluated — this
|
|
130
138
|
is the step people miss.
|
|
131
139
|
- You can edit until the deadline: use **Replace** on the Submission field to swap the PDF for a late
|
|
@@ -22,3 +22,25 @@ To re-record: run the same four requests with `curl` against a HotCRP site where
|
|
|
22
22
|
submission and a token (`Authorization: bearer $HOTCRP_TOKEN`), always with `dry_run=1` on a `POST`,
|
|
23
23
|
save each body, then replace every identifying value with a fake of the same type before committing.
|
|
24
24
|
These files ship in the npm package with the rest of `src/`, like the tests beside them.
|
|
25
|
+
|
|
26
|
+
## The deadlines pages
|
|
27
|
+
|
|
28
|
+
`deadlines-<site>.html` are five live sites' `GET /deadlines` pages and one site that does not exist,
|
|
29
|
+
recorded whole on 2026-10-05 (`curl`, no account): the page is public and carries no personal data,
|
|
30
|
+
so nothing is replaced. `deadlines.test.ts` parses each through the adapter's real parser; the
|
|
31
|
+
expected values were read off the pages' text by hand first. `/api/deadlines` on the same sites
|
|
32
|
+
answers 401 «Missing credentials», so the HTML page is the source.
|
|
33
|
+
`scripts/refresh-deadlines.test.ts` replays the MSR and AIDC pages through the scheduled refresh of
|
|
34
|
+
the presets' `deadlines` (`.github/workflows/deadlines.yml`). Re-read live through the adapter at
|
|
35
|
+
14:38 UTC the same day, both pages differed from these files only in the script nonces and `now`.
|
|
36
|
+
|
|
37
|
+
| file | site | HTTP | what it shows |
|
|
38
|
+
| ------------------------- | --------------------- | ---: | ------------------------------------------------------------------ |
|
|
39
|
+
| `deadlines-msr2027.html` | msr2027.hotcrp.com | 200 | submission and resubmission; the resubmission only in the list |
|
|
40
|
+
| `deadlines-aidc26.html` | aidc.submit.acsac.org | 200 | submission, and a final-version deadline only in the status object |
|
|
41
|
+
| `deadlines-sosp26.html` | sosp26.hotcrp.com | 200 | registration, submission, a late resubmission, final version |
|
|
42
|
+
| `deadlines-icse2027.html` | icse2027.hotcrp.com | 200 | a round that is not open (`"open":false`); dates printed as AoE |
|
|
43
|
+
| `deadlines-fse2027.html` | fse2027.hotcrp.com | 200 | one submission deadline printed as AoE |
|
|
44
|
+
| `deadlines-nosuch.html` | acsac2026.hotcrp.com | 404 | «No such conference» |
|
|
45
|
+
|
|
46
|
+
To re-record: `curl -sS -D - -o deadlines-<site>.html https://<site>/deadlines`.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
|
5
|
+
<link href="stylesheets/style.css?mtime=1782300564" rel="stylesheet">
|
|
6
|
+
<link rel="icon" type="image/png" href="images/review48.png">
|
|
7
|
+
<title>Deadlines - AIDC'26</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body id="m-deadlines" data-upload-limit="33554432"><div id="p-page" class="need-banner-offset"><header id="p-header"><hr class="c"><div id="h-site" class="header-site-page"><a class="q" href="/"><span class="header-site-name">AIDC'26</span></a></div><div id="h-right"></div><div id="h-page"><h1 id="h-title">Deadlines</h1></div><nav id="p-quicklinks" aria-label="Submission search"></nav><hr class="c">
|
|
10
|
+
<div id="h-messages" class="want-mx-auto"></div></header>
|
|
11
|
+
<main id="p-body">
|
|
12
|
+
<h3>Past</h3><dl class="bsp"><dt><strong>Submission deadline</strong>: <span class="need-usertime" data-ts="1791028799">Saturday Oct 3, 2026, 11:59:59 UTC</span></dt><dd> submissions must be completed by this deadline to be reviewed.</dd></dl>
|
|
13
|
+
<hr class="c"></main></div><footer id="p-footer" class="need-banner-offset banner-bottom"><a class="noq" href="https://hotcrp.com/">HotCRP</a></footer><!--[if lt IE 9]><script src="scripts/jquery-1.12.4.min.js?mtime=1782300564" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ="></script><![endif]-->
|
|
14
|
+
<![if !IE|gte IE 9]><script src="scripts/jquery-3.7.1.min.js?mtime=1782300564" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="></script><![endif]>
|
|
15
|
+
<script>window.siteinfo={"site_relative":"","base":"\/","suffix":"","assets":"","cookie_params":"; SameSite=Lax","postvalue":".empty","snouns":["submission","submissions","Submission","Submissions"],"user":[]}</script><script src="scripts/script.js?mtime=1782300564"></script>
|
|
16
|
+
<script>hotcrp.onload.time(0,1);hotcrp.init_deadlines({"now":1791194489.473936,"email":null,"sub":{"open":true,"sub":1791028799,"blind":true},"final":{"open":true,"done":1793447999}})</script></body>
|
|
17
|
+
</html>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
|
5
|
+
<meta name="google" content="notranslate">
|
|
6
|
+
<meta name="theme-color" content="#ffb7ca">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
|
+
<link href="https://static.hotcrp.com/@1790985506/stylesheets/style.css" rel="stylesheet">
|
|
9
|
+
<link href="https://static.hotcrp.com/@1786633883/stylesheets/mobile.css" media="screen and (max-width: 1100px)" rel="stylesheet">
|
|
10
|
+
<link href="https://static.hotcrp.com/@1788721683/stylesheets/plugin.css" rel="stylesheet">
|
|
11
|
+
<link rel="icon" type="image/svg+xml" href="https://static.hotcrp.com/images/review48.svg">
|
|
12
|
+
<title>Deadlines - FSE 2027</title>
|
|
13
|
+
</head>
|
|
14
|
+
<body id="m-deadlines" data-upload-limit="104857600" data-document-max-size="600000000"><div id="p-page" class="need-banner-offset"><header id="p-header"><hr class="c"><div id="h-site" class="header-site-page"><a class="q" href="/"><span class="header-site-name">FSE 2027</span></a></div><div id="h-right"></div><div id="h-page"><h1 id="h-title">Deadlines</h1></div><nav id="p-quicklinks" aria-label="Submission search"></nav><hr class="c">
|
|
15
|
+
<div id="h-messages" class="want-mx-auto"></div></header>
|
|
16
|
+
<main id="p-body">
|
|
17
|
+
<h3>Past</h3><dl class="bsp"><dt><strong>Submission deadline</strong>: <span class="need-usertime" data-ts="1791115140">Saturday Oct 3, 2026, 11:59 PM AoE</span></dt><dd>Submissions must be completed by this deadline to be reviewed.</dd></dl>
|
|
18
|
+
<hr class="c"></main></div><footer id="p-footer" class="need-banner-offset banner-bottom"><a class="noq" href="https://hotcrp.com/">HotCRP</a><!-- Version 3.4 --></footer><!--[if lt IE 9]><script nonce="P6MeSP0WsZYikm82vBcVfQ==" src="https://static.hotcrp.com/@1749780400/scripts/jquery-1.12.4.min.js" crossorigin="anonymous" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ="></script><![endif]-->
|
|
19
|
+
<![if !IE|gte IE 9]><script nonce="P6MeSP0WsZYikm82vBcVfQ==" src="https://static.hotcrp.com/@1749780400/scripts/jquery-3.7.1.min.js" crossorigin="anonymous" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="></script><![endif]>
|
|
20
|
+
<script nonce="P6MeSP0WsZYikm82vBcVfQ==">window.siteinfo={"site_relative":"","base":"\/","suffix":"","assets":"https:\/\/static.hotcrp.com\/","icons_url":"@1787797517\/images\/icons.svg","cookie_params":"; Domain=.hotcrp.com; Secure; SameSite=Lax","postvalue":".empty","download_prefix":"fse2027-","snouns":["submission","submissions","Submission","Submissions"],"user":[]}</script><script nonce="P6MeSP0WsZYikm82vBcVfQ==" src="https://static.hotcrp.com/@1790985505/scripts/script.js" crossorigin="anonymous"></script>
|
|
21
|
+
<script nonce="P6MeSP0WsZYikm82vBcVfQ==" src="https://static.hotcrp.com/@1777496734/scripts/markdown-it.min.js" crossorigin="anonymous"></script>
|
|
22
|
+
<script nonce="P6MeSP0WsZYikm82vBcVfQ==" src="https://static.hotcrp.com/@1787955704/scripts/plugin.js" crossorigin="anonymous"></script>
|
|
23
|
+
<script nonce="P6MeSP0WsZYikm82vBcVfQ==">hotcrp.onload.time(720,0);hotcrp.init_deadlines({"now":1791194490.16279,"email":null,"sub":{"open":true,"sub":1791115140,"blind":true}})</script></body>
|
|
24
|
+
</html>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
|
5
|
+
<meta name="google" content="notranslate">
|
|
6
|
+
<meta name="theme-color" content="#ffb7ca">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
|
+
<link href="https://static.hotcrp.com/@1790985506/stylesheets/style.css" rel="stylesheet">
|
|
9
|
+
<link href="https://static.hotcrp.com/@1786633883/stylesheets/mobile.css" media="screen and (max-width: 1100px)" rel="stylesheet">
|
|
10
|
+
<link href="https://static.hotcrp.com/@1788721683/stylesheets/plugin.css" rel="stylesheet">
|
|
11
|
+
<link rel="icon" type="image/svg+xml" href="https://static.hotcrp.com/images/review48.svg">
|
|
12
|
+
<title>Deadlines - ICSE 2027</title>
|
|
13
|
+
</head>
|
|
14
|
+
<body id="m-deadlines" data-upload-limit="104857600" data-document-max-size="600000000"><div id="p-page" class="need-banner-offset"><header id="p-header"><hr class="c"><div id="h-site" class="header-site-page"><a class="q" href="/"><span class="header-site-name">ICSE 2027</span></a></div><div id="h-right"></div><div id="h-page"><h1 id="h-title">Deadlines</h1></div><nav id="p-quicklinks" aria-label="Submission search"></nav><hr class="c">
|
|
15
|
+
<div id="h-messages" class="want-mx-auto"></div></header>
|
|
16
|
+
<main id="p-body">
|
|
17
|
+
<h3>Past</h3><dl class="bsp"><dt><strong>Submission deadline</strong>: <span class="need-usertime" data-ts="1783598340">Wednesday Jul 8, 2026, 11:59 PM AoE</span></dt><dd>Submissions must be completed by this deadline to be reviewed.</dd>
|
|
18
|
+
<dt><strong>Registration deadline</strong>: <span class="need-usertime" data-ts="1782302399">Tuesday Jun 23, 2026, 11:59:59 PM AoE</span></dt><dd>You can register new submissions until this deadline.</dd></dl>
|
|
19
|
+
<hr class="c"></main></div><footer id="p-footer" class="need-banner-offset banner-bottom"><a class="noq" href="https://hotcrp.com/">HotCRP</a><!-- Version 3.4 --></footer><!--[if lt IE 9]><script nonce="FL4FfJ6tspuiYI3NwY6I0w==" src="https://static.hotcrp.com/@1749780400/scripts/jquery-1.12.4.min.js" crossorigin="anonymous" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ="></script><![endif]-->
|
|
20
|
+
<![if !IE|gte IE 9]><script nonce="FL4FfJ6tspuiYI3NwY6I0w==" src="https://static.hotcrp.com/@1749780400/scripts/jquery-3.7.1.min.js" crossorigin="anonymous" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="></script><![endif]>
|
|
21
|
+
<script nonce="FL4FfJ6tspuiYI3NwY6I0w==">window.siteinfo={"site_relative":"","base":"\/","suffix":"","assets":"https:\/\/static.hotcrp.com\/","icons_url":"@1787797517\/images\/icons.svg","cookie_params":"; Domain=.hotcrp.com; Secure; SameSite=Lax","postvalue":".empty","download_prefix":"icse2027-","snouns":["submission","submissions","Submission","Submissions"],"user":[]}</script><script nonce="FL4FfJ6tspuiYI3NwY6I0w==" src="https://static.hotcrp.com/@1790985505/scripts/script.js" crossorigin="anonymous"></script>
|
|
22
|
+
<script nonce="FL4FfJ6tspuiYI3NwY6I0w==" src="https://static.hotcrp.com/@1777496734/scripts/markdown-it.min.js" crossorigin="anonymous"></script>
|
|
23
|
+
<script nonce="FL4FfJ6tspuiYI3NwY6I0w==" src="https://static.hotcrp.com/@1787955704/scripts/plugin.js" crossorigin="anonymous"></script>
|
|
24
|
+
<script nonce="FL4FfJ6tspuiYI3NwY6I0w==">hotcrp.onload.time(720,0);hotcrp.init_deadlines({"now":1791194489.960642,"email":null,"sub":{"open":false,"sub":1783598340,"reg":1782302399,"blind":true}})</script></body>
|
|
25
|
+
</html>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
|
5
|
+
<meta name="google" content="notranslate">
|
|
6
|
+
<meta name="theme-color" content="#ffb7ca">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
|
+
<link href="https://static.hotcrp.com/@1790985506/stylesheets/style.css" rel="stylesheet">
|
|
9
|
+
<link href="https://static.hotcrp.com/@1786633883/stylesheets/mobile.css" media="screen and (max-width: 1100px)" rel="stylesheet">
|
|
10
|
+
<link href="https://static.hotcrp.com/@1788721683/stylesheets/plugin.css" rel="stylesheet">
|
|
11
|
+
<link rel="icon" type="image/svg+xml" href="https://static.hotcrp.com/images/review48.svg">
|
|
12
|
+
<title>Deadlines - MSR 2027</title>
|
|
13
|
+
</head>
|
|
14
|
+
<body id="m-deadlines" data-upload-limit="104857600" data-document-max-size="600000000"><div id="p-page" class="need-banner-offset"><header id="p-header"><hr class="c"><div id="h-site" class="header-site-page"><a class="q" href="/"><span class="header-site-name">MSR 2027</span></a></div><div id="h-right"></div><div id="h-page"><h1 id="h-title">Deadlines</h1></div><nav id="p-quicklinks" aria-label="Submission search"></nav><hr class="c">
|
|
15
|
+
<div id="h-messages" class="want-mx-auto"></div></header>
|
|
16
|
+
<main id="p-body">
|
|
17
|
+
<h3>Upcoming</h3><dl class="bsp"><dt><strong>Submission deadline</strong>: <span class="need-usertime" data-ts="1792468800">Tuesday Oct 20, 2026, 12 AM EDT</span></dt><dd>Submissions must be completed by this deadline to be reviewed.</dd>
|
|
18
|
+
<dt><strong>Resubmission deadline</strong>: <span class="need-usertime" data-ts="1792728000">Friday Oct 23, 2026, 12 AM EDT</span></dt><dd>Completed submissions may be updated until this deadline.</dd></dl>
|
|
19
|
+
<hr class="c"></main></div><footer id="p-footer" class="need-banner-offset banner-bottom"><a class="noq" href="https://hotcrp.com/">HotCRP</a><!-- Version 3.4 --></footer><!--[if lt IE 9]><script nonce="8XgRxNPYP5XebPvVSL6MyA==" src="https://static.hotcrp.com/@1749780400/scripts/jquery-1.12.4.min.js" crossorigin="anonymous" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ="></script><![endif]-->
|
|
20
|
+
<![if !IE|gte IE 9]><script nonce="8XgRxNPYP5XebPvVSL6MyA==" src="https://static.hotcrp.com/@1749780400/scripts/jquery-3.7.1.min.js" crossorigin="anonymous" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="></script><![endif]>
|
|
21
|
+
<script nonce="8XgRxNPYP5XebPvVSL6MyA==">window.siteinfo={"site_relative":"","base":"\/","suffix":"","assets":"https:\/\/static.hotcrp.com\/","icons_url":"@1787797517\/images\/icons.svg","cookie_params":"; Domain=.hotcrp.com; Secure; SameSite=Lax","postvalue":".empty","download_prefix":"msr2027-","snouns":["submission","submissions","Submission","Submissions"],"user":[]}</script><script nonce="8XgRxNPYP5XebPvVSL6MyA==" src="https://static.hotcrp.com/@1790985505/scripts/script.js" crossorigin="anonymous"></script>
|
|
22
|
+
<script nonce="8XgRxNPYP5XebPvVSL6MyA==" src="https://static.hotcrp.com/@1777496734/scripts/markdown-it.min.js" crossorigin="anonymous"></script>
|
|
23
|
+
<script nonce="8XgRxNPYP5XebPvVSL6MyA==" src="https://static.hotcrp.com/@1787955704/scripts/plugin.js" crossorigin="anonymous"></script>
|
|
24
|
+
<script nonce="8XgRxNPYP5XebPvVSL6MyA==">hotcrp.onload.time(240,0);hotcrp.init_deadlines({"now":1791194488.329003,"email":null,"sub":{"open":true,"sub":1792468800,"blind":true}})</script></body>
|
|
25
|
+
</html>
|