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.
Files changed (261) hide show
  1. package/.github/workflows/ci.yml +3 -0
  2. package/.github/workflows/deadlines.yml +100 -0
  3. package/README.md +6 -0
  4. package/dist/adapters/hotcrp/deadlines.d.ts +8 -0
  5. package/dist/adapters/hotcrp/deadlines.d.ts.map +1 -0
  6. package/dist/adapters/hotcrp/deadlines.io.d.ts +6 -0
  7. package/dist/adapters/hotcrp/deadlines.io.d.ts.map +1 -0
  8. package/dist/adapters/hotcrp/deadlines.io.js +26 -0
  9. package/dist/adapters/hotcrp/deadlines.io.js.map +1 -0
  10. package/dist/adapters/hotcrp/deadlines.js +134 -0
  11. package/dist/adapters/hotcrp/deadlines.js.map +1 -0
  12. package/dist/adapters/hotcrp/index.d.ts +2 -0
  13. package/dist/adapters/hotcrp/index.d.ts.map +1 -1
  14. package/dist/adapters/hotcrp/index.js +2 -0
  15. package/dist/adapters/hotcrp/index.js.map +1 -1
  16. package/dist/build.d.ts.map +1 -1
  17. package/dist/build.js +1 -6
  18. package/dist/build.js.map +1 -1
  19. package/dist/cli.d.ts +3 -4
  20. package/dist/cli.d.ts.map +1 -1
  21. package/dist/cli.js +53 -47
  22. package/dist/cli.js.map +1 -1
  23. package/dist/cycle-rules.d.ts +24 -0
  24. package/dist/cycle-rules.d.ts.map +1 -0
  25. package/dist/cycle-rules.js +212 -0
  26. package/dist/cycle-rules.js.map +1 -0
  27. package/dist/domain/cycle.d.ts +148 -0
  28. package/dist/domain/cycle.d.ts.map +1 -0
  29. package/dist/domain/cycle.js +281 -0
  30. package/dist/domain/cycle.js.map +1 -0
  31. package/dist/domain/deadline.d.ts +128 -0
  32. package/dist/domain/deadline.d.ts.map +1 -0
  33. package/dist/domain/deadline.js +281 -0
  34. package/dist/domain/deadline.js.map +1 -0
  35. package/dist/eslint-rules/cycle-stage.d.ts +39 -0
  36. package/dist/eslint-rules/cycle-stage.d.ts.map +1 -0
  37. package/dist/eslint-rules/cycle-stage.js +119 -0
  38. package/dist/eslint-rules/cycle-stage.js.map +1 -0
  39. package/dist/eslint-rules/latex-language.d.ts.map +1 -1
  40. package/dist/eslint-rules/latex-language.js +10 -13
  41. package/dist/eslint-rules/latex-language.js.map +1 -1
  42. package/dist/eslint-rules/paper-context.js +2 -2
  43. package/dist/eslint-rules/paper-typography.d.ts.map +1 -1
  44. package/dist/eslint-rules/paper-typography.js +22 -91
  45. package/dist/eslint-rules/paper-typography.js.map +1 -1
  46. package/dist/eslint-rules/papers.d.ts +1 -3
  47. package/dist/eslint-rules/papers.d.ts.map +1 -1
  48. package/dist/eslint-rules/papers.js +2 -4
  49. package/dist/eslint-rules/papers.js.map +1 -1
  50. package/dist/eslint-rules/tex-build.d.ts.map +1 -1
  51. package/dist/eslint-rules/tex-build.js +8 -25
  52. package/dist/eslint-rules/tex-build.js.map +1 -1
  53. package/dist/init.d.ts +1 -4
  54. package/dist/init.d.ts.map +1 -1
  55. package/dist/init.js +3 -9
  56. package/dist/init.js.map +1 -1
  57. package/dist/lib/markdown.d.ts +5 -81
  58. package/dist/lib/markdown.d.ts.map +1 -1
  59. package/dist/lib/markdown.js +5 -116
  60. package/dist/lib/markdown.js.map +1 -1
  61. package/dist/lib/paper-config.d.ts.map +1 -1
  62. package/dist/lib/paper-config.js +2 -2
  63. package/dist/lib/paper-config.js.map +1 -1
  64. package/dist/lib/skill-eval-fixture.d.ts +6 -4
  65. package/dist/lib/skill-eval-fixture.d.ts.map +1 -1
  66. package/dist/lib/skill-eval-fixture.js +25 -18
  67. package/dist/lib/skill-eval-fixture.js.map +1 -1
  68. package/dist/new-paper.d.ts +5 -11
  69. package/dist/new-paper.d.ts.map +1 -1
  70. package/dist/new-paper.js +12 -23
  71. package/dist/new-paper.js.map +1 -1
  72. package/dist/paper-files.d.ts +4 -4
  73. package/dist/paper-prose.d.ts +17 -0
  74. package/dist/paper-prose.d.ts.map +1 -0
  75. package/dist/paper-prose.js +46 -0
  76. package/dist/paper-prose.js.map +1 -0
  77. package/dist/paper-settings.d.ts +7 -0
  78. package/dist/paper-settings.d.ts.map +1 -1
  79. package/dist/paper-settings.js +61 -8
  80. package/dist/paper-settings.js.map +1 -1
  81. package/dist/ports/venue-deadlines.d.ts +14 -0
  82. package/dist/ports/venue-deadlines.d.ts.map +1 -0
  83. package/dist/ports/venue-deadlines.js +2 -0
  84. package/dist/ports/venue-deadlines.js.map +1 -0
  85. package/dist/presets.d.ts +12 -0
  86. package/dist/presets.d.ts.map +1 -1
  87. package/dist/presets.js +19 -11
  88. package/dist/presets.js.map +1 -1
  89. package/dist/structure.d.ts.map +1 -1
  90. package/dist/structure.js +4 -3
  91. package/dist/structure.js.map +1 -1
  92. package/dist/submission.d.ts +8 -0
  93. package/dist/submission.d.ts.map +1 -1
  94. package/dist/submission.js +41 -10
  95. package/dist/submission.js.map +1 -1
  96. package/dist/tex-requirements.d.ts +3 -0
  97. package/dist/tex-requirements.d.ts.map +1 -1
  98. package/dist/tex-requirements.js +19 -0
  99. package/dist/tex-requirements.js.map +1 -1
  100. package/dist/tex-venue-rules.d.ts +19 -0
  101. package/dist/tex-venue-rules.d.ts.map +1 -1
  102. package/dist/tex-venue-rules.js +69 -0
  103. package/dist/tex-venue-rules.js.map +1 -1
  104. package/dist/types.d.ts +0 -2
  105. package/dist/types.d.ts.map +1 -1
  106. package/dist/venue-rules.d.ts +10 -0
  107. package/dist/venue-rules.d.ts.map +1 -1
  108. package/dist/venue-rules.js +26 -10
  109. package/dist/venue-rules.js.map +1 -1
  110. package/docs/configuration.md +146 -20
  111. package/docs/e2e.md +4 -6
  112. package/docs/prior-art/paper-folder-scaffolding.md +24 -29
  113. package/docs/rules/cycle/evidence.md +87 -0
  114. package/docs/rules/cycle/port-done.md +75 -0
  115. package/docs/rules/cycle/record.md +152 -0
  116. package/docs/rules/cycle/stage.md +85 -0
  117. package/docs/rules/tex/claim-provenance.md +0 -1
  118. package/docs/rules.md +48 -47
  119. package/docs/submission.md +5 -0
  120. package/eslint-rules/cycle-stage.test.mjs +131 -0
  121. package/eslint-rules/cycle-stage.ts +141 -0
  122. package/eslint-rules/latex-language.harness.mjs +3 -3
  123. package/eslint-rules/latex-language.ts +10 -13
  124. package/eslint-rules/paper-context.ts +2 -2
  125. package/eslint-rules/paper-typography.test.mjs +17 -85
  126. package/eslint-rules/paper-typography.ts +22 -98
  127. package/eslint-rules/papers.harness.mjs +1 -1
  128. package/eslint-rules/papers.ts +2 -4
  129. package/eslint-rules/tex-build.harness.mjs +2 -2
  130. package/eslint-rules/tex-build.ts +8 -25
  131. package/eslint.config.mjs +41 -2
  132. package/hooks/hooks.harness.ts +15 -15
  133. package/hooks/paper-edit-guard.hook.mjs +5 -8
  134. package/hooks/paper-edit-guard.test.ts +12 -3
  135. package/hooks/paper-skills-nudge.hook.mjs +2 -3
  136. package/hooks/paper-skills-nudge.test.ts +13 -3
  137. package/hooks/paper-status-gates.hook.mjs +2 -2
  138. package/hooks/paper-status-gates.test.ts +14 -2
  139. package/lib/markdown.test.mjs +5 -60
  140. package/lib/markdown.ts +9 -136
  141. package/lib/paper-config.ts +3 -2
  142. package/lib/skill-checks.test.mjs +4 -1
  143. package/lib/skill-eval-fixture.ts +25 -18
  144. package/package.json +1 -3
  145. package/presets/aidc.jsonc +24 -3
  146. package/presets/aidc.md +6 -3
  147. package/presets/msr.jsonc +25 -1
  148. package/presets/msr.md +5 -1
  149. package/presets/realm.jsonc +1 -1
  150. package/presets/venue-profile.schema.json +39 -0
  151. package/scripts/check.ts +4 -0
  152. package/scripts/harness-api.frozen.json +0 -5
  153. package/scripts/refresh-deadlines.test.ts +431 -0
  154. package/scripts/refresh-deadlines.ts +387 -0
  155. package/skills/cold-read-diff/SKILL.md +3 -3
  156. package/skills/cold-read-diff/SKILL.md.spec.ts +2 -2
  157. package/skills/grade-paper-writing/SKILL.md +14 -22
  158. package/skills/grade-paper-writing/SKILL.md.spec.ts +13 -21
  159. package/skills/grade-paper-writing/__snapshots__/prose-lint.test.ts.snap +40 -26
  160. package/skills/grade-paper-writing/prose-lint.mjs +108 -278
  161. package/skills/grade-paper-writing/prose-lint.test.ts +184 -81
  162. package/skills/paper-adversarial-review/SKILL.md +2 -2
  163. package/skills/paper-adversarial-review/SKILL.md.spec.ts +1 -1
  164. package/skills/paper-pipeline/SKILL.md +9 -12
  165. package/skills/paper-pipeline/SKILL.md.spec.ts +8 -11
  166. package/skills/paper-pipeline/description-language.eval.mjs +25 -15
  167. package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +25 -15
  168. package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +25 -15
  169. package/skills/paper-pipeline/pipeline-firing.eval.mjs +27 -16
  170. package/skills/paper-pipeline/pipeline-language.eval.mjs +25 -15
  171. package/skills/paper-pipeline/references/acceptance-gate.md +16 -20
  172. package/skills/paper-pipeline/references/artifact-checklist.md +4 -5
  173. package/skills/paper-pipeline/references/pipeline-status-template.md +1 -1
  174. package/skills/paper-pipeline/scripts/README.md +0 -4
  175. package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +8 -72
  176. package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +48 -156
  177. package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +81 -75
  178. package/skills/paper-pipeline/scripts/generated-code.harness.mjs +3 -3
  179. package/skills/paper-status/SKILL.md +2 -2
  180. package/skills/paper-status/SKILL.md.spec.ts +1 -1
  181. package/skills/render-paper/SKILL.md +2 -2
  182. package/skills/render-paper/SKILL.md.spec.ts +1 -1
  183. package/skills/render-paper/extract-pdf-facts.mjs +2 -2
  184. package/skills/tighten-paper/SKILL.md +29 -42
  185. package/skills/tighten-paper/SKILL.md.spec.ts +28 -41
  186. package/skills/verify-citations/scripts/bib-authors.check.test.mjs +0 -13
  187. package/skills/verify-citations/scripts/bib-authors.cli.test.ts +23 -11
  188. package/skills/verify-citations/scripts/bib-authors.mjs +8 -93
  189. package/skills/verify-citations/scripts/bib-authors.test.mjs +1 -44
  190. package/src/adapters/hotcrp/cassette/README.md +22 -0
  191. package/src/adapters/hotcrp/cassette/deadlines-aidc26.html +17 -0
  192. package/src/adapters/hotcrp/cassette/deadlines-fse2027.html +24 -0
  193. package/src/adapters/hotcrp/cassette/deadlines-icse2027.html +25 -0
  194. package/src/adapters/hotcrp/cassette/deadlines-msr2027.html +25 -0
  195. package/src/adapters/hotcrp/cassette/deadlines-nosuch.html +18 -0
  196. package/src/adapters/hotcrp/cassette/deadlines-sosp26.html +26 -0
  197. package/src/adapters/hotcrp/deadlines.io.test.ts +86 -0
  198. package/src/adapters/hotcrp/deadlines.io.ts +27 -0
  199. package/src/adapters/hotcrp/deadlines.test.ts +170 -0
  200. package/src/adapters/hotcrp/deadlines.ts +161 -0
  201. package/src/adapters/hotcrp/index.ts +2 -0
  202. package/src/build.harness.mjs +2 -2
  203. package/src/build.test.ts +1 -1
  204. package/src/build.ts +1 -6
  205. package/src/cli.harness.mjs +26 -46
  206. package/src/cli.test.ts +41 -40
  207. package/src/cli.ts +57 -58
  208. package/src/cycle-rules.test.ts +278 -0
  209. package/src/cycle-rules.ts +273 -0
  210. package/src/domain/cycle.test.ts +456 -0
  211. package/src/domain/cycle.ts +432 -0
  212. package/src/domain/deadline.test.ts +386 -0
  213. package/src/domain/deadline.ts +472 -0
  214. package/src/folder-venue-rule.test.ts +1 -1
  215. package/src/init.test.ts +6 -7
  216. package/src/init.ts +3 -22
  217. package/src/lint-fix.test.ts +10 -4
  218. package/src/new-paper.harness.mjs +9 -28
  219. package/src/new-paper.test.ts +7 -6
  220. package/src/new-paper.ts +11 -30
  221. package/src/paper-files.test.ts +5 -4
  222. package/src/paper-files.ts +4 -4
  223. package/src/paper-prose.test.ts +49 -0
  224. package/src/paper-prose.ts +67 -0
  225. package/src/paper-settings.test.ts +167 -0
  226. package/src/paper-settings.ts +85 -11
  227. package/src/parse-args.test.ts +0 -1
  228. package/src/ports/venue-deadlines.ts +14 -0
  229. package/src/presets.test.ts +105 -0
  230. package/src/presets.ts +43 -10
  231. package/src/rule-cases.test.ts +144 -0
  232. package/src/structure.harness.ts +5 -11
  233. package/src/structure.ts +4 -3
  234. package/src/submission.test.ts +105 -1
  235. package/src/submission.ts +73 -12
  236. package/src/tex-requirements.ts +31 -0
  237. package/src/tex-venue-rules.test.ts +76 -0
  238. package/src/tex-venue-rules.ts +85 -0
  239. package/src/types.ts +0 -2
  240. package/src/venue-rules.test.ts +82 -0
  241. package/src/venue-rules.ts +44 -12
  242. package/fixtures/real-markdown-paper/baseline.json +0 -22
  243. package/fixtures/real-markdown-paper/baseline.ts +0 -70
  244. package/skills/paper-pipeline/PIPELINE-MAP.md +0 -381
  245. package/skills/paper-pipeline/pipeline-firing.baseline.json +0 -270
  246. package/skills/paper-pipeline/references/sweep-2026-08-09-ideal-pipeline.md +0 -585
  247. package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +0 -492
  248. package/skills/paper-pipeline/scripts/artifact-coverage.mjs +0 -397
  249. package/skills/paper-pipeline/scripts/artifact-coverage.test.ts +0 -86
  250. package/skills/paper-pipeline/scripts/check-provenance.mjs +0 -184
  251. package/skills/paper-pipeline/scripts/population-map.harness.mjs +0 -135
  252. package/skills/paper-pipeline/scripts/population-map.mjs +0 -188
  253. package/skills/paper-pipeline/scripts/population-map.test.mjs +0 -154
  254. package/skills/paper-pipeline/scripts/provenance.harness.mjs +0 -266
  255. package/skills/paper-pipeline/scripts/round-diff.harness.ts +0 -907
  256. package/skills/paper-pipeline/scripts/round-diff.mjs +0 -583
  257. package/skills/paper-pipeline/scripts/round-diff.test.mjs +0 -159
  258. package/skills/tighten-paper/__snapshots__/structure.test.ts.snap +0 -133
  259. package/skills/tighten-paper/structure.mjs +0 -364
  260. package/skills/tighten-paper/structure.test.ts +0 -117
  261. package/templates/paper/paper.md +0 -4
@@ -4,7 +4,7 @@ description: "Use when a paper \"feels long / bloated / overly complex / hard to
4
4
  allowed-tools: [Read, Write, Edit, Grep, Glob, Agent]
5
5
  ---
6
6
 
7
- <!-- vigiles:sha256:fd00cde33ea4b81c compiled from skills/tighten-paper/SKILL.md.spec.ts -->
7
+ <!-- vigiles:sha256:c57d6da372b28acf compiled from skills/tighten-paper/SKILL.md.spec.ts -->
8
8
 
9
9
  # tighten-paper — the developmental edit
10
10
 
@@ -34,31 +34,12 @@ counter, is not this skill; it is the damage this skill exists to undo.
34
34
  If you are invoked while the author is still adding material, say so and decline the pass. The right
35
35
  answer is "not yet", not a smaller cut.
36
36
 
37
- ## 🏗 START WITH THE MECHANICAL LEG — run `structure.mjs` (added 2026-08-05)
37
+ ## 🏗 START WITH THE SHAPE — weigh every section before judging it
38
38
 
39
- ```
40
- node .claude/skills/tighten-paper/structure.mjs <paper.md> # whole paper
41
- node .claude/skills/tighten-paper/structure.mjs <paper.md> --section=3 # one section
42
- ```
43
-
44
- 🔴 **THE SCRIPT NO LONGER FINDS ANYTHING — it is an INVENTORY (2026-08-26).** All sixteen of its
45
- checks moved into ESLint rules (`eslint-rules/paper-structure.mjs`). The findings you used to read
46
- here now come from:
47
-
48
- ```
49
- npx eslint --no-config-lookup --config eslint.config.mjs <paper.md>
50
- ```
51
-
52
- ⚠️ Do NOT pass `--flags-only`. It is kept only because two harnesses assert on its silence, and it
53
- prints nothing and exits 0 whatever the paper looks like. Measured 26.08 on `the reference paper`:
54
- `--flags-only` printed 0 lines while ESLint on the same bytes reported **6 structural findings**
55
- (subsection-size ×2 · section-lead · free-section-size ×2 · block-ungraded). A mode that cannot
56
- speak is not a check, and reading its silence as "clean" is the defect this pipeline is named for.
57
-
58
- What the inventory prints, and why it stays a script: the outline **in order** — every heading, its
59
- own words, its total with subsections, its share of the body, and the `carries:`/`score:`/`verdict:`
60
- note the section wrote about itself, printed beside its weight. That comparison is the whole point
61
- and a linter cannot make it: a finding has nowhere to put a table.
39
+ Write the outline out **in order**: every heading, its own words before its first subsection, its
40
+ total with subsections, its share of the body — and beside each, the `carries:`/`score:`/`verdict:`
41
+ note the section wrote about itself. That comparison, a section's weight printed next to what it
42
+ claims about itself, is the whole point.
62
43
 
63
44
  🔴 **Why this exists, and why word counts alone mislead.** Asked whether a paper short-changed its
64
45
  contribution, per-section word counts said no — the contribution had 38% more words than related
@@ -67,7 +48,7 @@ owned **0, 28 and 38** words before their first subsection. Nothing in the toolc
67
48
  question, because every metric was per-sentence or per-document and none was per-section. The author's
68
49
  word for the result was a "salad" — a jumbled mix — and he was right while the numbers said fine.
69
50
 
70
- **What the script does NOT decide, and you must:** the ORDER, the NAMES, and whether a section earns
51
+ **What the count does NOT decide, and you must:** the ORDER, the NAMES, and whether a section earns
71
52
  its place. Those are judgement, and pretending otherwise would be the failure this whole pipeline
72
53
  exists to prevent.
73
54
 
@@ -188,18 +169,24 @@ called it "fat for no reason" after a pass that cut almost nothing and left it 1
188
169
 
189
170
  A verdict that lives in a chat log is gone by the next session, and the next session then re-derives
190
171
  it or ignores it. **Every section and subsection gets a one-line verdict comment immediately above its
191
- heading**, in the paper source, stamped with the date and the skill that produced it:
192
-
193
- ```
194
- <!-- TIGHTEN 2026-08-06 · score 8/10 · verdict: KEEP · 190w
195
- carries: tools get close but none resolves a NAMED rule against a committed config
196
- because: it is the only place the tool-ecosystem gap is closed; a reviewer who skips it
197
- asks "hasn't this been done" and gets no answer -->
198
- <!-- TIGHTEN 2026-08-06 · score 5/10 · verdict: SHORTEN -89w · 210w
199
- carries: why a linter for the rules file cannot work
200
- because: the point lands in two sentences; the rest re-establishes what §2 already did -->
201
- <!-- TIGHTEN 2026-08-06 · score 2/10 · verdict: MOVE→artifact · 45w
202
- carries: nothing the reader must hold to judge the contribution -->
172
+ heading**, in `paper.tex`, as LaTeX `%` comments, stamped with the date and the skill that
173
+ produced it:
174
+
175
+ ```latex
176
+ % TIGHTEN 2026-08-06 · score 8/10 · verdict: KEEP · 190w
177
+ % carries: tools get close but none resolves a NAMED rule against a committed config
178
+ % because: it is the only place the tool-ecosystem gap is closed; a reviewer who skips it
179
+ % asks "hasn't this been done" and gets no answer
180
+ \section{Related tools}
181
+
182
+ % TIGHTEN 2026-08-06 · score 5/10 · verdict: SHORTEN -89w · 210w
183
+ % carries: why a linter for the rules file cannot work
184
+ % because: the point lands in two sentences; the rest re-establishes what §2 already did
185
+ \subsection{Why not a linter}
186
+
187
+ % TIGHTEN 2026-08-06 · score 2/10 · verdict: MOVE→artifact · 45w
188
+ % carries: nothing the reader must hold to judge the contribution
189
+ \subsection{Implementation notes}
203
190
  ```
204
191
 
205
192
  🔴 **Before assigning any `MOVE→` verdict, read `paper-pipeline/references/body-vs-appendix.md`.**
@@ -224,7 +211,7 @@ written and **not one ever concluded "cut me"**. Author, 2026-08-06, reading the
224
211
  value on every page and compared nothing.
225
212
 
226
213
  The pairing is the mechanism: **a low score beside a KEEP verdict is a visible contradiction**, and
227
- `structure.mjs` reports it. Prose cannot contradict itself; a number can.
214
+ the outline above puts the two side by side. Prose cannot contradict itself; a number can.
228
215
 
229
216
  **Calibration, so the scale does not drift to "everything is an 8":**
230
217
  - **9–10** — remove it and a reviewer cannot assess the contribution. The claim, its evidence, its price.
@@ -279,10 +266,10 @@ Rules for these comments:
279
266
  unpriced prose accumulates, because nothing there costs a page.
280
267
  - **Stale beats absent, but not by much.** A comment whose date precedes the last substantive edit to
281
268
  its section is stale; re-grade it rather than trusting it.
282
- - **A verdict nobody executed is a note, not a decision.** `structure.mjs` reports blocks still
283
- carrying a non-KEEP verdict — a paper full of unexecuted CUTs is worse than one never graded,
269
+ - **A verdict nobody executed is a note, not a decision.** List the blocks still carrying a
270
+ non-KEEP verdict — a paper full of unexecuted CUTs is worse than one never graded,
284
271
  because it reads as decided.
285
- - They are working notes: stripped before typesetting, like every other `<!-- -->` block.
272
+ - They are working notes: a `%` comment never reaches the PDF.
286
273
  - The chat summary still gets written — but it is the *derivative*, and the paper is the record.
287
274
 
288
275
  ## 🧱 Enforce subsections — a section without them is a wall
@@ -40,31 +40,12 @@ counter, is not this skill; it is the damage this skill exists to undo.
40
40
  If you are invoked while the author is still adding material, say so and decline the pass. The right
41
41
  answer is "not yet", not a smaller cut.
42
42
 
43
- ## 🏗 START WITH THE MECHANICAL LEG — run \`structure.mjs\` (added 2026-08-05)
43
+ ## 🏗 START WITH THE SHAPE — weigh every section before judging it
44
44
 
45
- \`\`\`
46
- node .claude/skills/tighten-paper/structure.mjs <paper.md> # whole paper
47
- node .claude/skills/tighten-paper/structure.mjs <paper.md> --section=3 # one section
48
- \`\`\`
49
-
50
- 🔴 **THE SCRIPT NO LONGER FINDS ANYTHING — it is an INVENTORY (2026-08-26).** All sixteen of its
51
- checks moved into ESLint rules (\`eslint-rules/paper-structure.mjs\`). The findings you used to read
52
- here now come from:
53
-
54
- \`\`\`
55
- npx eslint --no-config-lookup --config eslint.config.mjs <paper.md>
56
- \`\`\`
57
-
58
- ⚠️ Do NOT pass \`--flags-only\`. It is kept only because two harnesses assert on its silence, and it
59
- prints nothing and exits 0 whatever the paper looks like. Measured 26.08 on \`the reference paper\`:
60
- \`--flags-only\` printed 0 lines while ESLint on the same bytes reported **6 structural findings**
61
- (subsection-size ×2 · section-lead · free-section-size ×2 · block-ungraded). A mode that cannot
62
- speak is not a check, and reading its silence as "clean" is the defect this pipeline is named for.
63
-
64
- What the inventory prints, and why it stays a script: the outline **in order** — every heading, its
65
- own words, its total with subsections, its share of the body, and the \`carries:\`/\`score:\`/\`verdict:\`
66
- note the section wrote about itself, printed beside its weight. That comparison is the whole point
67
- and a linter cannot make it: a finding has nowhere to put a table.
45
+ Write the outline out **in order**: every heading, its own words before its first subsection, its
46
+ total with subsections, its share of the body — and beside each, the \`carries:\`/\`score:\`/\`verdict:\`
47
+ note the section wrote about itself. That comparison, a section's weight printed next to what it
48
+ claims about itself, is the whole point.
68
49
 
69
50
  🔴 **Why this exists, and why word counts alone mislead.** Asked whether a paper short-changed its
70
51
  contribution, per-section word counts said no — the contribution had 38% more words than related
@@ -73,7 +54,7 @@ owned **0, 28 and 38** words before their first subsection. Nothing in the toolc
73
54
  question, because every metric was per-sentence or per-document and none was per-section. The author's
74
55
  word for the result was a "salad" — a jumbled mix — and he was right while the numbers said fine.
75
56
 
76
- **What the script does NOT decide, and you must:** the ORDER, the NAMES, and whether a section earns
57
+ **What the count does NOT decide, and you must:** the ORDER, the NAMES, and whether a section earns
77
58
  its place. Those are judgement, and pretending otherwise would be the failure this whole pipeline
78
59
  exists to prevent.
79
60
 
@@ -194,18 +175,24 @@ called it "fat for no reason" after a pass that cut almost nothing and left it 1
194
175
 
195
176
  A verdict that lives in a chat log is gone by the next session, and the next session then re-derives
196
177
  it or ignores it. **Every section and subsection gets a one-line verdict comment immediately above its
197
- heading**, in the paper source, stamped with the date and the skill that produced it:
198
-
199
- \`\`\`
200
- <!-- TIGHTEN 2026-08-06 · score 8/10 · verdict: KEEP · 190w
201
- carries: tools get close but none resolves a NAMED rule against a committed config
202
- because: it is the only place the tool-ecosystem gap is closed; a reviewer who skips it
203
- asks "hasn't this been done" and gets no answer -->
204
- <!-- TIGHTEN 2026-08-06 · score 5/10 · verdict: SHORTEN -89w · 210w
205
- carries: why a linter for the rules file cannot work
206
- because: the point lands in two sentences; the rest re-establishes what §2 already did -->
207
- <!-- TIGHTEN 2026-08-06 · score 2/10 · verdict: MOVE→artifact · 45w
208
- carries: nothing the reader must hold to judge the contribution -->
178
+ heading**, in \`paper.tex\`, as LaTeX \`%\` comments, stamped with the date and the skill that
179
+ produced it:
180
+
181
+ \`\`\`latex
182
+ % TIGHTEN 2026-08-06 · score 8/10 · verdict: KEEP · 190w
183
+ % carries: tools get close but none resolves a NAMED rule against a committed config
184
+ % because: it is the only place the tool-ecosystem gap is closed; a reviewer who skips it
185
+ % asks "hasn't this been done" and gets no answer
186
+ \\section{Related tools}
187
+
188
+ % TIGHTEN 2026-08-06 · score 5/10 · verdict: SHORTEN -89w · 210w
189
+ % carries: why a linter for the rules file cannot work
190
+ % because: the point lands in two sentences; the rest re-establishes what §2 already did
191
+ \\subsection{Why not a linter}
192
+
193
+ % TIGHTEN 2026-08-06 · score 2/10 · verdict: MOVE→artifact · 45w
194
+ % carries: nothing the reader must hold to judge the contribution
195
+ \\subsection{Implementation notes}
209
196
  \`\`\`
210
197
 
211
198
  🔴 **Before assigning any \`MOVE→\` verdict, read \`paper-pipeline/references/body-vs-appendix.md\`.**
@@ -230,7 +217,7 @@ written and **not one ever concluded "cut me"**. Author, 2026-08-06, reading the
230
217
  value on every page and compared nothing.
231
218
 
232
219
  The pairing is the mechanism: **a low score beside a KEEP verdict is a visible contradiction**, and
233
- \`structure.mjs\` reports it. Prose cannot contradict itself; a number can.
220
+ the outline above puts the two side by side. Prose cannot contradict itself; a number can.
234
221
 
235
222
  **Calibration, so the scale does not drift to "everything is an 8":**
236
223
  - **9–10** — remove it and a reviewer cannot assess the contribution. The claim, its evidence, its price.
@@ -285,10 +272,10 @@ Rules for these comments:
285
272
  unpriced prose accumulates, because nothing there costs a page.
286
273
  - **Stale beats absent, but not by much.** A comment whose date precedes the last substantive edit to
287
274
  its section is stale; re-grade it rather than trusting it.
288
- - **A verdict nobody executed is a note, not a decision.** \`structure.mjs\` reports blocks still
289
- carrying a non-KEEP verdict — a paper full of unexecuted CUTs is worse than one never graded,
275
+ - **A verdict nobody executed is a note, not a decision.** List the blocks still carrying a
276
+ non-KEEP verdict — a paper full of unexecuted CUTs is worse than one never graded,
290
277
  because it reads as decided.
291
- - They are working notes: stripped before typesetting, like every other \`<!-- -->\` block.
278
+ - They are working notes: a \`%\` comment never reaches the PDF.
292
279
  - The chat summary still gets written — but it is the *derivative*, and the paper is the record.
293
280
 
294
281
  ## 🧱 Enforce subsections — a section without them is a wall
@@ -50,15 +50,6 @@ test("checkAuthors sorts every kind of entry into its bucket", async () => {
50
50
  entry("nomatch", "Ada Lovelace", "Nobody Has This"),
51
51
  entry("onlycorr", "Ada Lovelace", "Only On CoRR"),
52
52
  entry("down", "Ada Lovelace", "Service Down"),
53
- {
54
- type: "mdref",
55
- key: "sw",
56
- unparsed: true,
57
- author: "",
58
- title: "",
59
- booktitle: "",
60
- journal: "",
61
- },
62
53
  ];
63
54
  const answers = {
64
55
  "Same Paper": [hit("Same Paper.", ["Ada Lovelace", "Alan Turing"])],
@@ -101,10 +92,6 @@ test("checkAuthors sorts every kind of entry into its bucket", async () => {
101
92
  },
102
93
  ],
103
94
  skipped: [
104
- {
105
- key: "sw",
106
- why: "no author/title — a reference to software or a dataset, DBLP does not apply",
107
- },
108
95
  {
109
96
  key: "etal",
110
97
  why: "author list ends in `and others` — completeness not checkable",
@@ -1,7 +1,7 @@
1
1
  /**
2
- * bib-authors.mjs as a process: which file it picks from a paper directory, the three
3
- * bibliography sources it reads (.bib, a .tex's filecontents or its sibling refs.bib, a markdown
4
- * reference list), and the report and exit code for each outcome. DBLP is a preload that answers
2
+ * bib-authors.mjs as a process: which file it picks from a paper directory, the bibliography
3
+ * sources it reads (.bib, a .tex's filecontents or its sibling refs.bib), the file it refuses, and
4
+ * the report and exit code for each outcome. DBLP is a preload that answers
5
5
  * by title from a table (the documented `result.hits.hit[].info` shape) — no network.
6
6
  */
7
7
  import assert from "node:assert/strict";
@@ -58,8 +58,10 @@ writeTree(root, {
58
58
  "sibling/paper.tex": "\\documentclass{article}\n",
59
59
  "sibling/refs.bib": bib("good", "Ada Lovelace", "All Good"),
60
60
  "nobib/paper.tex": "\\documentclass{article}\n",
61
- "md/paper.md":
62
- "# P\n\n## References\n\n1. A. Lovelace. *All Good.* Proc. X, 2024.\n2. some-npm-package, https://npm.example\n",
61
+ "etal/notes.txt": "not a bibliography\n",
62
+ "etal/refs.bib":
63
+ bib("good", "Ada Lovelace", "All Good") +
64
+ bib("etal", "Ada Lovelace and others", "Truncated"),
63
65
  "down/refs.bib": bib("down", "Ada Lovelace", "Service Down"),
64
66
  "empty/.keep": "",
65
67
  });
@@ -98,7 +100,7 @@ test("refusals: no argument, no such path, a directory with nothing to read, a .
98
100
  {
99
101
  status: 2,
100
102
  stdout: "",
101
- stderr: "bib-authors: no .bib, .tex or paper.md in <root>/empty\n",
103
+ stderr: "bib-authors: no .bib or .tex in <root>/empty\n",
102
104
  },
103
105
  {
104
106
  status: 2,
@@ -162,15 +164,25 @@ test("a .tex with no filecontents reads the refs.bib beside it — PASS, exit 0"
162
164
  });
163
165
  });
164
166
 
165
- test("a markdown reference list: a software entry is not applicable, not dropped", () => {
166
- const r = run(join(root, "md"));
167
+ test("a file that is neither a .bib nor a .tex is refused — not swapped for the refs.bib beside it", () => {
168
+ const r = run(join(root, "etal", "notes.txt"));
169
+ assert.deepEqual(r, {
170
+ status: 2,
171
+ stdout: "",
172
+ stderr:
173
+ "bib-authors: <root>/etal/notes.txt is not a .bib or a .tex — give the paper's .bib or paper.tex\n",
174
+ });
175
+ });
176
+
177
+ test("an `and others` entry is printed as not applicable, not dropped", () => {
178
+ const r = run(join(root, "etal"));
167
179
  assert.deepEqual(r, {
168
180
  status: 0,
169
181
  stderr: "",
170
182
  stdout:
171
- "== bib-authors: <root>/md/paper.md ==\n" +
172
- " · n/a ref2 — no author/title — a reference to software or a dataset, DBLP does not apply\n\n" +
173
- "-- 1 entries · 0 difference(s) · 1 not applicable · 0 NOT CHECKED\nPASS: no author-list disagreement.\n",
183
+ "== bib-authors: <root>/etal/refs.bib ==\n" +
184
+ " · n/a etal — author list ends in `and others` — completeness not checkable\n\n" +
185
+ "-- 2 entries · 0 difference(s) · 1 not applicable · 0 NOT CHECKED\nPASS: no author-list disagreement.\n",
174
186
  });
175
187
  });
176
188
 
@@ -45,90 +45,12 @@
45
45
  */
46
46
  import { readFileSync, existsSync, readdirSync } from "node:fs";
47
47
  import { join, extname } from "node:path";
48
- import { headings as mdHeadings, requireMarkdown } from "#lib/markdown";
49
48
  import { isMain } from "../../paper-pipeline/scripts/consumer.mjs";
50
49
 
51
50
  const DBLP = "https://dblp.org/search/publ/api";
52
51
 
53
52
  /* ---------- input: a .bib, a .tex with filecontents, or a paper dir ---------- */
54
53
 
55
- /**
56
- * Some papers carry no BibTeX at all. `compile-rules-2026` writes its 67 references as a
57
- * hand-numbered markdown list that `repro/md2acl.py` turns into ACL format at build time:
58
- *
59
- * 12. N. F. Liu, K. Lin, J. Hewitt. *Lost in the Middle: How Language Models Use Long
60
- * Contexts.* TACL, 2024. arXiv:2307.03172.
61
- *
62
- * 🔴 Exempting such a paper would be the wrong call, and measurably so: a hand-written author
63
- * list has NO machine-checkable source, so it is the MORE exposed of the two formats, not the
64
- * less. The class this script hunts lives exactly where an entry names a published venue and an
65
- * arXiv id in the same breath — which is most of this list.
66
- *
67
- * Authors are taken only from the text BEFORE the first `*`, on purpose: entries here sometimes
68
- * append a second work ("— and the reanalysis: S. B. Hossain, ...") after the title, and folding
69
- * those names in would invent co-authors that the first work does not have.
70
- */
71
- export function parseMarkdownRefs(text) {
72
- // 🔴 The heading comes FROM THE PARSER, not from a regex (the base's rule "parse markdown with a
73
- // parser"). The previous `text.search(/^#+\s*References\s*$/m)` opened the bibliography on a
74
- // `## References` line QUOTED inside a ``` fence — and the papers in this repo quote their own
75
- // markup in chunks. Exactly this assert already stands at a neighbour
76
- // (`paper-pipeline/scripts/extract-ref-facts.harness.mjs`: "a heading inside a ``` block is not a
77
- // heading"), that is, the class was known and was being reproduced here again.
78
- // `requireMarkdown()` — so that a missing markdown-it fails LOUDLY: with an empty parser
79
- // `headings()` returns [], and the gate would report "no references" instead of refusing. That is
80
- // the same trade of one silent failure for another that the rule was written against.
81
- requireMarkdown();
82
- const h = mdHeadings(text).find((x) => /^References\s*$/u.test(x.text));
83
- if (!h) return [];
84
- const body = text.slice(h.offset);
85
- const out = [];
86
- // 🔴 `$(?![\s\S])`, NOT `\Z`. JavaScript has no `\Z` anchor — outside a unicode-mode pattern it
87
- // is an identity escape meaning the LETTER Z, so the first version of this lookahead ended entry
88
- // 1 in the middle of "J. Zhou", at the Z. Measured: 48 of 67 references parsed, and the missing
89
- // 19 looked like ordinary gaps rather than a bug. Same family as the `\b`-over-Cyrillic defect
90
- // already recorded in this repo: an escape that means one thing in Perl/Python and another here.
91
- const re = /^(\d+)\.[ \t]+([\s\S]*?)(?=^\d+\.[ \t]|^#|$(?![\s\S]))/gm;
92
- let m;
93
- while ((m = re.exec(body))) {
94
- const entry = m[2].replace(/\s*\n\s*/g, " ").trim();
95
- const t = entry.match(/\*([^*]+)\*/);
96
- if (!t) {
97
- // No italic title and no author list — in this corpus these are SOFTWARE references
98
- // (npm packages, repos). Nothing for DBLP to disagree with. They are surfaced as
99
- // not-applicable rather than dropped: an entry that silently vanishes between the file
100
- // and the report is indistinguishable from an entry that passed.
101
- out.push({
102
- type: "mdref",
103
- key: `ref${m[1]}`,
104
- unparsed: true,
105
- author: "",
106
- title: "",
107
- booktitle: "",
108
- journal: "",
109
- });
110
- continue;
111
- }
112
- const authorPart = entry
113
- .slice(0, entry.indexOf("*"))
114
- .trim()
115
- .replace(/[.,;]\s*$/, "");
116
- const rest = entry.slice(entry.indexOf(t[0]) + t[0].length);
117
- out.push({
118
- type: "mdref",
119
- key: `ref${m[1]}`,
120
- author: authorPart
121
- .split(/,\s*|\s+and\s+/)
122
- .filter(Boolean)
123
- .join(" and "),
124
- title: t[1].replace(/\.$/, "").trim(),
125
- booktitle: rest.trim(),
126
- journal: "",
127
- });
128
- }
129
- return out;
130
- }
131
-
132
54
  function bibTextFrom(target) {
133
55
  let file = target;
134
56
  if (!existsSync(file)) die(`no such path: ${file}`);
@@ -161,16 +83,16 @@ function bibTextFrom(target) {
161
83
  }
162
84
  return undefined;
163
85
  };
164
- const found =
165
- pick(".bib") ||
166
- pick(".tex") ||
167
- names.find((f) => f === "paper.md" || f === "draft.md");
168
- if (!found) die(`no .bib, .tex or paper.md in ${file}`);
86
+ const found = pick(".bib") || pick(".tex");
87
+ if (!found) die(`no .bib or .tex in ${file}`);
169
88
  file = join(file, found);
170
89
  }
171
90
  const text = readFileSync(file, "utf-8");
172
- if (file.endsWith(".md")) return { text, file, markdown: true };
173
91
  if (file.endsWith(".bib")) return { text, file };
92
+ // Only a .bib and a .tex are read: any other file would fall through to the refs.bib beside it,
93
+ // and the report would name a file nobody asked about.
94
+ if (!file.endsWith(".tex"))
95
+ die(`${file} is not a .bib or a .tex — give the paper's .bib or paper.tex`);
174
96
  // A .tex may carry the bibliography inline via filecontents — that is how our papers do it.
175
97
  const m = text.match(
176
98
  /\\begin\{filecontents\*?\}(?:\[[^\]]*\])?\{[^}]*\.bib\}\r?\n([\s\S]*?)\\end\{filecontents\*?\}/,
@@ -358,13 +280,6 @@ export async function checkAuthors(
358
280
  const matched = []; // compared against the published record, and equal
359
281
  const entries = parsed.filter((e) => e.title && e.author);
360
282
 
361
- for (const e of parsed.filter((x) => x.unparsed)) {
362
- skipped.push({
363
- key: e.key,
364
- why: "no author/title — a reference to software or a dataset, DBLP does not apply",
365
- });
366
- }
367
-
368
283
  for (const e of entries) {
369
284
  if (!claimsPublished(e)) continue; // a preprint entry is allowed to carry preprint metadata
370
285
  if (truncated(e.author)) {
@@ -438,8 +353,8 @@ async function main() {
438
353
  if (!args[0])
439
354
  die("usage: bib-authors.mjs <paper-dir|file.bib|file.tex> [--json]");
440
355
 
441
- const { text, file, markdown } = bibTextFrom(args[0]);
442
- const parsed = markdown ? parseMarkdownRefs(text) : parseBib(text);
356
+ const { text, file } = bibTextFrom(args[0]);
357
+ const parsed = parseBib(text);
443
358
  const entries = parsed.filter((e) => e.title && e.author);
444
359
  const { findings, skipped, unchecked } = await checkAuthors(parsed);
445
360
  if (asJson) {
@@ -14,12 +14,7 @@
14
14
  */
15
15
  import assert from "node:assert/strict";
16
16
  import { test } from "vitest";
17
- import {
18
- surnames,
19
- compare,
20
- claimsPublished,
21
- parseMarkdownRefs,
22
- } from "./bib-authors.mjs";
17
+ import { surnames, compare, claimsPublished } from "./bib-authors.mjs";
23
18
 
24
19
  const t = test;
25
20
 
@@ -135,41 +130,3 @@ t("a proceedings entry IS in scope", () => {
135
130
  true,
136
131
  );
137
132
  });
138
-
139
- console.log("bib-authors — where the bibliography begins");
140
-
141
- // Both halves. "Silent on a fence" without "finds the real one" is indistinguishable from a broken parse.
142
- t("a real ## References is parsed", () => {
143
- const md = [
144
- "# Paper",
145
- "text",
146
- "",
147
- "## References",
148
- "",
149
- "1. A. Author. *A title*. In NeurIPS, 2023.",
150
- ].join("\n");
151
- const refs = parseMarkdownRefs(md);
152
- assert.equal(refs.length, 1);
153
- assert.match(refs[0].title, /A title/);
154
- });
155
-
156
- t("a ## References inside a ``` fence does NOT open the bibliography", () => {
157
- const md = [
158
- "# Paper",
159
- "",
160
- "The section is written like this:",
161
- "",
162
- "```markdown",
163
- "## References",
164
- "",
165
- "1. Z. Ghost. *Not a reference, but a markup example*. In Nowhere, 2020.",
166
- "```",
167
- "",
168
- "The end.",
169
- ].join("\n");
170
- assert.deepEqual(
171
- parseMarkdownRefs(md),
172
- [],
173
- "quoted markup is not a bibliography — the ^#+ regex counted it as a heading",
174
- );
175
- });
@@ -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&#039;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&#039;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>