paperlint 12.0.0 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +2 -1
- package/dist/adapters/memory/index.d.ts +5 -0
- package/dist/adapters/memory/index.d.ts.map +1 -1
- package/dist/adapters/memory/index.js +16 -0
- package/dist/adapters/memory/index.js.map +1 -1
- package/dist/adapters/node/index.d.ts +1 -0
- package/dist/adapters/node/index.d.ts.map +1 -1
- package/dist/adapters/node/index.js +1 -0
- package/dist/adapters/node/index.js.map +1 -1
- package/dist/adapters/node/move-file.io.d.ts +4 -0
- package/dist/adapters/node/move-file.io.d.ts.map +1 -0
- package/dist/adapters/node/move-file.io.js +12 -0
- package/dist/adapters/node/move-file.io.js.map +1 -0
- package/dist/adapters/yaml/index.d.ts +3 -0
- package/dist/adapters/yaml/index.d.ts.map +1 -0
- package/dist/adapters/yaml/index.js +17 -0
- package/dist/adapters/yaml/index.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +48 -7
- package/dist/cli.js.map +1 -1
- package/dist/cycle-rules.d.ts +3 -3
- package/dist/cycle-rules.d.ts.map +1 -1
- package/dist/cycle-rules.js +1 -2
- package/dist/cycle-rules.js.map +1 -1
- package/dist/domain/cycle.d.ts +16 -2
- package/dist/domain/cycle.d.ts.map +1 -1
- package/dist/domain/cycle.js +21 -42
- package/dist/domain/cycle.js.map +1 -1
- package/dist/domain/paper-layout.d.ts.map +1 -1
- package/dist/domain/paper-layout.js +5 -2
- package/dist/domain/paper-layout.js.map +1 -1
- package/dist/domain/paths.d.ts +12 -0
- package/dist/domain/paths.d.ts.map +1 -1
- package/dist/domain/paths.js +22 -1
- package/dist/domain/paths.js.map +1 -1
- package/dist/domain/scaffold-layout.js +1 -1
- package/dist/domain/versions-migration.d.ts +51 -0
- package/dist/domain/versions-migration.d.ts.map +1 -0
- package/dist/domain/versions-migration.js +252 -0
- package/dist/domain/versions-migration.js.map +1 -0
- package/dist/domain/versions.d.ts +167 -0
- package/dist/domain/versions.d.ts.map +1 -0
- package/dist/domain/versions.js +457 -0
- package/dist/domain/versions.js.map +1 -0
- package/dist/eslint-rules/paper-stages.d.ts +7 -0
- package/dist/eslint-rules/paper-stages.d.ts.map +1 -1
- package/dist/eslint-rules/paper-stages.js +62 -7
- package/dist/eslint-rules/paper-stages.js.map +1 -1
- package/dist/eslint-rules/rule-context.d.ts +2 -0
- package/dist/eslint-rules/rule-context.d.ts.map +1 -1
- package/dist/ports/move-file.d.ts +7 -0
- package/dist/ports/move-file.d.ts.map +1 -0
- package/dist/ports/move-file.js +2 -0
- package/dist/ports/move-file.js.map +1 -0
- package/dist/ports/yaml.d.ts +5 -0
- package/dist/ports/yaml.d.ts.map +1 -0
- package/dist/ports/yaml.js +2 -0
- package/dist/ports/yaml.js.map +1 -0
- package/dist/submission.d.ts +2 -0
- package/dist/submission.d.ts.map +1 -1
- package/dist/submission.js +49 -9
- package/dist/submission.js.map +1 -1
- package/dist/version-rules.d.ts +16 -0
- package/dist/version-rules.d.ts.map +1 -0
- package/dist/version-rules.js +201 -0
- package/dist/version-rules.js.map +1 -0
- package/dist/versions-command.d.ts +37 -0
- package/dist/versions-command.d.ts.map +1 -0
- package/dist/versions-command.js +209 -0
- package/dist/versions-command.js.map +1 -0
- package/dist/versions.d.ts +76 -0
- package/dist/versions.d.ts.map +1 -0
- package/dist/versions.js +164 -0
- package/dist/versions.js.map +1 -0
- package/docs/configuration.md +76 -2
- package/docs/layout.md +5 -5
- package/docs/rules/cycle/camera-ready-source.md +62 -0
- package/docs/rules/cycle/draft-drift.md +47 -0
- package/docs/rules/cycle/port-done.md +2 -2
- package/docs/rules/cycle/record.md +2 -1
- package/docs/rules/cycle/submitted-frozen.md +65 -0
- package/docs/rules/paper/allowed-files-only.md +1 -1
- package/docs/rules/versions/layout.md +88 -0
- package/docs/rules.md +25 -11
- package/docs/submission.md +21 -3
- package/eslint-rules/paper-stages.test.mjs +62 -0
- package/eslint-rules/paper-stages.ts +67 -8
- package/eslint-rules/prereg-coverage.test.ts +1 -0
- package/eslint-rules/rule-context.ts +2 -0
- package/package.json +2 -1
- package/presets/agent.jsonc +75 -0
- package/presets/agent.md +70 -0
- package/presets/agentverify.jsonc +72 -0
- package/presets/agentverify.md +85 -0
- package/presets/ieee-conference.md +1 -1
- package/presets/nlbse.jsonc +76 -0
- package/presets/nlbse.md +85 -0
- package/scripts/coverage-config.test.ts +3 -0
- package/skills/CLAUDE.md +2 -2
- package/skills/CLAUDE.md.spec.ts +1 -1
- package/skills/paper-pipeline/references/pipeline-status-template.md +4 -1
- package/skills/submit-paper/SKILL.md +18 -10
- package/skills/submit-paper/SKILL.md.spec.ts +17 -9
- package/skills/submit-paper/references/publishers/acm.md +5 -3
- package/src/adapters/memory/index.test.ts +22 -0
- package/src/adapters/memory/index.ts +21 -0
- package/src/adapters/node/index.ts +1 -0
- package/src/adapters/node/move-file.io.ts +21 -0
- package/src/adapters/node/move-file.test.ts +51 -0
- package/src/adapters/yaml/index.test.ts +16 -0
- package/src/adapters/yaml/index.ts +17 -0
- package/src/cli.test.ts +43 -0
- package/src/cli.ts +58 -6
- package/src/cycle-rules.ts +4 -5
- package/src/domain/cycle.test.ts +27 -0
- package/src/domain/cycle.ts +38 -6
- package/src/domain/paper-layout.ts +5 -2
- package/src/domain/paths.test.ts +28 -1
- package/src/domain/paths.ts +34 -1
- package/src/domain/scaffold-layout.ts +1 -1
- package/src/domain/versions-migration.test.ts +410 -0
- package/src/domain/versions-migration.ts +429 -0
- package/src/domain/versions.test.ts +564 -0
- package/src/domain/versions.ts +706 -0
- package/src/paper-settings-commands.test.ts +1 -1
- package/src/ports/move-file.ts +8 -0
- package/src/ports/yaml.ts +5 -0
- package/src/preset-card-rule.test.ts +3 -0
- package/src/presets.test.ts +55 -31
- package/src/rule-cases.test.ts +157 -8
- package/src/submission-freeze.test.ts +320 -0
- package/src/submission-new.test.ts +11 -1
- package/src/submission.ts +82 -11
- package/src/tex-venue-rules.test.ts +3 -0
- package/src/venue-rules.test.ts +2 -2
- package/src/version-rules.test.ts +222 -0
- package/src/version-rules.ts +282 -0
- package/src/versions-command.test.ts +594 -0
- package/src/versions-command.ts +360 -0
- package/src/versions.ts +286 -0
- package/dist/eslint-rules/cycle-stage.d.ts +0 -39
- package/dist/eslint-rules/cycle-stage.d.ts.map +0 -1
- package/dist/eslint-rules/cycle-stage.js +0 -119
- package/dist/eslint-rules/cycle-stage.js.map +0 -1
- package/docs/rules/cycle/stage.md +0 -85
- package/eslint-rules/cycle-stage.test.mjs +0 -131
- package/eslint-rules/cycle-stage.ts +0 -141
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "AGENTVERIFY @ ICSE — venue card"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# AGENTVERIFY @ ICSE
|
|
6
|
+
|
|
7
|
+
The card of the venue preset `paperlint:agentverify` ([`agentverify.jsonc`](agentverify.jsonc)):
|
|
8
|
+
what the venue asks for, in prose. The numbers a machine checks live in the preset; the section at
|
|
9
|
+
the end is generated from it. The `submit-paper` skill reads this card for the venue's specifics.
|
|
10
|
+
|
|
11
|
+
**Re-verify the call for papers every year**
|
|
12
|
+
([agentverify2027.github.io/cfp.html](https://agentverify2027.github.io/cfp.html)). Sources read
|
|
13
|
+
2026-10-26: that page, [the home page](https://agentverify2027.github.io/), the portal's
|
|
14
|
+
[deadlines page](https://icse2027-agentverify.hotcrp.com/deadlines), and the
|
|
15
|
+
[ICSE 2027 workshops page](https://conf.researchr.org/track/icse-2027/icse-2027-workshops). The
|
|
16
|
+
researchr page `conf.researchr.org/home/agentverify-2027` only redirects to the workshop's site.
|
|
17
|
+
|
|
18
|
+
## The venue (2027 edition)
|
|
19
|
+
|
|
20
|
+
- **Venue**: Workshop on Infrastructure for Trustworthy Software Agents, an ICSE 2027 workshop,
|
|
21
|
+
Sun 25 April 2027, Dublin.
|
|
22
|
+
- **Portal**: HotCRP, `https://icse2027-agentverify.hotcrp.com/` («Submit Your Paper →»).
|
|
23
|
+
- **Format and review**: «Papers will be submitted via HotCRP and reviewed double-blind.
|
|
24
|
+
Submissions should follow the IEEE format in line with ICSE 2027 and present an original
|
|
25
|
+
contribution.» The portal's status data carries `"blind":true`. No class line is given, so the
|
|
26
|
+
preset keeps the family's `\documentclass[conference]{IEEEtran}`.
|
|
27
|
+
- **Publisher**: «Accepted papers will appear in the ICSE 2027 workshop proceedings by default.»
|
|
28
|
+
The ICSE workshops page: «The workshop papers will be published by the IEEE.» The CFP does not
|
|
29
|
+
say what a non-default route would be, or whether it is archival.
|
|
30
|
+
- **Presenting**: «At least one author of each accepted paper must register and present.»
|
|
31
|
+
- **Deadlines (AoE)**: submission 13 Nov 2026, reviews due 5 Dec, final notification 11 Dec 2026,
|
|
32
|
+
camera-ready 29 Jan 2027.
|
|
33
|
+
|
|
34
|
+
## 🔴 The submission deadline moved
|
|
35
|
+
|
|
36
|
+
The organizers' email of 2026-10-23 (as received by an author; the email itself is not public):
|
|
37
|
+
|
|
38
|
+
> The submission deadline for AGENTVERIFY 2027 has been updated to November 13, 2026. We do not
|
|
39
|
+
> have a separate abstract submission deadline.
|
|
40
|
+
|
|
41
|
+
Source: **organizers' email, 2026-10-23**. On 2026-10-26 every public source agreed with it: the
|
|
42
|
+
CFP, the home page («Papers are due November 13th, 2026 (AoE)»), the ICSE workshops page («Workshop
|
|
43
|
+
Papers Submission Deadline for all Workshops: 13 November 2026») and the portal (`"sub":1794657599`,
|
|
44
|
+
2026-11-14T11:59:59Z). None of the pages read showed 6 November. If a cached copy of any of them
|
|
45
|
+
does, the email and the live pages win.
|
|
46
|
+
|
|
47
|
+
## Paper kinds and limits
|
|
48
|
+
|
|
49
|
+
| kind | pages | the call's words |
|
|
50
|
+
| ---------- | ----: | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
51
|
+
| `full` | 8 | «Full Papers: Maximum of 8 pages, including references.» |
|
|
52
|
+
| `short` | 4 | «Short & Demonstration Papers: Maximum of 4 pages, including references.» |
|
|
53
|
+
| `abstract` | 2 | «Extended Abstracts: Maximum of 2 pages, including references.» They «are free of APC charges and will be included in the proceedings.» |
|
|
54
|
+
|
|
55
|
+
🔴 **Every limit is a total, references included.** `format/page-limit` can only compare banal's
|
|
56
|
+
body count with the number, so references standing alone on a page after a full body pass the rule
|
|
57
|
+
and are over the limit. Count the PDF's pages yourself before submitting.
|
|
58
|
+
|
|
59
|
+
## What else the preset encodes
|
|
60
|
+
|
|
61
|
+
- **The IEEE template**, through `extends: paperlint:ieee-conference` ([`ieee-conference.md`](ieee-conference.md)).
|
|
62
|
+
- **Double-blind review** (`blind`), so `anonymity/identity` checks the PDF against the paper's
|
|
63
|
+
declared `identity`.
|
|
64
|
+
- **Portal and deadlines**: the submission reading is the portal's, refreshed by paperlint's
|
|
65
|
+
scheduled workflow; notification and camera-ready are the site's. No `registration`: there is no
|
|
66
|
+
abstract deadline.
|
|
67
|
+
|
|
68
|
+
## Not verified
|
|
69
|
+
|
|
70
|
+
- **Remote presentation**: not stated (only «must register and present»).
|
|
71
|
+
- **Archival / non-archival choice**: «by default» suggests an alternative; none is described.
|
|
72
|
+
- **AI / LLM usage policy and required sections**: none stated.
|
|
73
|
+
|
|
74
|
+
## What the preset resolves to
|
|
75
|
+
|
|
76
|
+
<!-- paperlint:preset-rules -->
|
|
77
|
+
<!-- prettier-ignore-start -->
|
|
78
|
+
<!-- Generated from agentverify.jsonc by `npx eslint --fix` (rule preset/card-rules). Edit the preset, not this section. -->
|
|
79
|
+
|
|
80
|
+
**`paperlint:agentverify`** — venue preset for AGENTVERIFY · call for papers: <https://agentverify2027.github.io/cfp.html> · extends `paperlint:ieee-conference`
|
|
81
|
+
|
|
82
|
+
No preset of this chain sets a rule: under it, every rule keeps paperlint's default.
|
|
83
|
+
|
|
84
|
+
<!-- prettier-ignore-end -->
|
|
85
|
+
<!-- /paperlint:preset-rules -->
|
|
@@ -30,7 +30,7 @@ versions. `compsoc` changes the text block and the headings, and nothing the `pd
|
|
|
30
30
|
<!-- prettier-ignore-start -->
|
|
31
31
|
<!-- Generated from ieee-conference.jsonc by `npx eslint --fix` (rule preset/card-rules). Edit the preset, not this section. -->
|
|
32
32
|
|
|
33
|
-
**`paperlint:ieee-conference`** — template family, no venue of its own · extended by `paperlint:aidc`, `paperlint:msr`
|
|
33
|
+
**`paperlint:ieee-conference`** — template family, no venue of its own · extended by `paperlint:agent`, `paperlint:agentverify`, `paperlint:aidc`, `paperlint:msr`, `paperlint:nlbse`
|
|
34
34
|
|
|
35
35
|
No preset of this chain sets a rule: under it, every rule keeps paperlint's default.
|
|
36
36
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// NLBSE 2027 (the 6th Natural Language-based Software Engineering Workshop, an ICSE 2027 workshop)
|
|
2
|
+
// — a VENUE preset over the IEEE conference family. Everything the IEEEtran template decides comes
|
|
3
|
+
// from ieee-conference.jsonc; this file adds what NLBSE's call sets. Its card, in prose:
|
|
4
|
+
// `nlbse.md` beside this file.
|
|
5
|
+
//
|
|
6
|
+
// Verbatim from https://conf.researchr.org/home/icse-2027/nlbse-2027 (fetched 2026-10-26):
|
|
7
|
+
// «Full papers (maximum of 8 pages, including references).» · «Education tools and materials
|
|
8
|
+
// (maximum of 8 pages, including references).» · «Replication Studies and Negative results
|
|
9
|
+
// (maximum of 8 pages, including references).» · «Short and demonstration papers (maximum of 4
|
|
10
|
+
// pages, including references).» · «Position papers (maximum of 2 pages, including
|
|
11
|
+
// references).» · «Tool Competition entries (maximum of 4 pages, including references).»
|
|
12
|
+
// «All submissions must conform to the ICSE’27 formatting and submission instructions. All
|
|
13
|
+
// submissions must be anonymized, in PDF format and should be performed electronically through
|
|
14
|
+
// HotCRP: https://icse2027-nlbse.hotcrp.com/.»
|
|
15
|
+
// Dates: «Fri 13 Nov 2026 (updated) Paper submission / Fri 27 Nov 2026 (new) Tool
|
|
16
|
+
// competition/Research challenge paper submission / Fri 11 Dec 2026 Author notification / Fri
|
|
17
|
+
// 29 Jan 2027 Camera ready», «All dates are Anywhere on Earth (AoE).»
|
|
18
|
+
{
|
|
19
|
+
"type": "venue",
|
|
20
|
+
"name": "NLBSE",
|
|
21
|
+
// The call quoted above.
|
|
22
|
+
"url": "https://conf.researchr.org/home/icse-2027/nlbse-2027",
|
|
23
|
+
"extends": "paperlint:ieee-conference",
|
|
24
|
+
|
|
25
|
+
// «All submissions must be anonymized» (the call above), and `"blind":true` in the portal's
|
|
26
|
+
// status data (https://icse2027-nlbse.hotcrp.com/deadlines, read 2026-10-26). The call never
|
|
27
|
+
// says «double-blind»; anonymity/identity then requires the paper's `identity`.
|
|
28
|
+
"blind": true,
|
|
29
|
+
|
|
30
|
+
// «should be performed electronically through HotCRP» (the call above).
|
|
31
|
+
"portal": { "kind": "hotcrp", "url": "https://icse2027-nlbse.hotcrp.com" },
|
|
32
|
+
|
|
33
|
+
// 🔴 THE PORTAL HOLDS NO DEADLINE YET: on 2026-10-26 its status data read `"sub":{"open":false}`
|
|
34
|
+
// with no date, while the call lists 13 Nov. So these are the call's readings, in UTC at the end
|
|
35
|
+
// of the AoE day; the refresh adds the portal's the day it publishes one (the earlier binds).
|
|
36
|
+
// The competition papers' 27 Nov is in the card only: one reading per kind and source.
|
|
37
|
+
"deadlines": [
|
|
38
|
+
{
|
|
39
|
+
"what": "submission",
|
|
40
|
+
"at": "2026-11-14T11:59:59Z",
|
|
41
|
+
"source": "call",
|
|
42
|
+
"url": "https://conf.researchr.org/home/icse-2027/nlbse-2027",
|
|
43
|
+
"read": "2026-10-26",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"what": "notification",
|
|
47
|
+
"at": "2026-12-12T11:59:59Z",
|
|
48
|
+
"source": "call",
|
|
49
|
+
"url": "https://conf.researchr.org/home/icse-2027/nlbse-2027",
|
|
50
|
+
"read": "2026-10-26",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"what": "camera-ready",
|
|
54
|
+
"at": "2027-01-30T11:59:59Z",
|
|
55
|
+
"source": "call",
|
|
56
|
+
"url": "https://conf.researchr.org/home/icse-2027/nlbse-2027",
|
|
57
|
+
"read": "2026-10-26",
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
|
|
61
|
+
"format": {
|
|
62
|
+
// 🔴 EVERY LIMIT INCLUDES THE REFERENCES, and no key gates a total: `body_pages_max` is banal's
|
|
63
|
+
// body count, never more than the total, so a paper over it is over the limit; references alone
|
|
64
|
+
// on a page after a full body pass this gate and are still over (nlbse.md). The competition
|
|
65
|
+
// papers' «2-4 pages» (Multitask Code Classification) does not say whether references count, so
|
|
66
|
+
// only the call's «Tool Competition entries» line is a kind; the research challenge has no limit.
|
|
67
|
+
"kinds": {
|
|
68
|
+
"full": { "body_pages_max": 8 },
|
|
69
|
+
"education": { "body_pages_max": 8 },
|
|
70
|
+
"replication": { "body_pages_max": 8 },
|
|
71
|
+
"short": { "body_pages_max": 4 },
|
|
72
|
+
"position": { "body_pages_max": 2 },
|
|
73
|
+
"tool-competition": { "body_pages_max": 4 },
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
}
|
package/presets/nlbse.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "NLBSE @ ICSE — venue card"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# NLBSE @ ICSE
|
|
6
|
+
|
|
7
|
+
The card of the venue preset `paperlint:nlbse` ([`nlbse.jsonc`](nlbse.jsonc)): what the venue asks
|
|
8
|
+
for, in prose. The numbers a machine checks live in the preset; the section at the end is generated
|
|
9
|
+
from it. The `submit-paper` skill reads this card for the venue's specifics.
|
|
10
|
+
|
|
11
|
+
**Re-verify the call for papers every year**
|
|
12
|
+
([conf.researchr.org/home/icse-2027/nlbse-2027](https://conf.researchr.org/home/icse-2027/nlbse-2027)).
|
|
13
|
+
Sources read 2026-10-26: that page, the portal's
|
|
14
|
+
[deadlines page](https://icse2027-nlbse.hotcrp.com/deadlines), and the
|
|
15
|
+
[ICSE 2027 workshops page](https://conf.researchr.org/track/icse-2027/icse-2027-workshops).
|
|
16
|
+
|
|
17
|
+
## The venue (2027 edition)
|
|
18
|
+
|
|
19
|
+
- **Venue**: The 6th Natural Language-based Software Engineering Workshop, an ICSE 2027 workshop,
|
|
20
|
+
Sun 25 April 2027, Dublin. «NLBSE 2027 is sponsored by ACM SIGSOFT and IEEE-CS TCSE.»
|
|
21
|
+
- **Portal**: HotCRP, `https://icse2027-nlbse.hotcrp.com/`.
|
|
22
|
+
- **Format**: «All submissions must conform to the ICSE’27 formatting and submission instructions.»
|
|
23
|
+
No template or class line is named on the page; the ICSE 2027 workshops page says «The workshop
|
|
24
|
+
papers will be published by the IEEE.» and links the IEEE templates only for workshop proposals.
|
|
25
|
+
The preset keeps the family's `\documentclass[conference]{IEEEtran}`, an inference, not a quote.
|
|
26
|
+
- **Review**: «All submissions must be anonymized, in PDF format». The call never says single- or
|
|
27
|
+
double-blind; the portal's status data carries `"blind":true`, which the preset follows.
|
|
28
|
+
- **Publisher**: the call says only «The accepted submissions will be published in the workshop
|
|
29
|
+
proceedings.» (tool competitions) and «Accepted papers will appear in the NLBSE ’27 proceedings.»
|
|
30
|
+
(research challenge, skill classification). IEEE comes from the ICSE page above. No archival /
|
|
31
|
+
non-archival choice is mentioned.
|
|
32
|
+
- **Deadlines (AoE)**: paper submission 13 Nov 2026 (marked «updated»), tool competition and
|
|
33
|
+
research challenge papers **27 Nov 2026** (marked «new»), notification 11 Dec 2026, camera-ready
|
|
34
|
+
29 Jan 2027. Only the 13 Nov line is in the preset.
|
|
35
|
+
- 🔴 **Disagreement**: the call lists 13 Nov, but the portal's status data on 2026-10-26 reads
|
|
36
|
+
`"sub":{"open":false}` with no date, i.e. it was closed and showed none. The preset therefore
|
|
37
|
+
carries the call's reading until the portal publishes one.
|
|
38
|
+
|
|
39
|
+
## Paper kinds and limits
|
|
40
|
+
|
|
41
|
+
Every limit says «including references».
|
|
42
|
+
|
|
43
|
+
| kind | pages | the call's words |
|
|
44
|
+
| ------------------ | ----: | -------------------------------------------------------------------------------------- |
|
|
45
|
+
| `full` | 8 | «Full papers (maximum of 8 pages, including references).» |
|
|
46
|
+
| `education` | 8 | «Education tools and materials (maximum of 8 pages, including references).» |
|
|
47
|
+
| `replication` | 8 | «Replication Studies and Negative results (maximum of 8 pages, including references).» |
|
|
48
|
+
| `short` | 4 | «Short and demonstration papers (maximum of 4 pages, including references).» |
|
|
49
|
+
| `position` | 2 | «Position papers (maximum of 2 pages, including references).» |
|
|
50
|
+
| `tool-competition` | 4 | «Tool Competition entries (maximum of 4 pages, including references).» |
|
|
51
|
+
|
|
52
|
+
Two competition papers have no kind. Multitask Code Classification: «you must write a paper (2-4
|
|
53
|
+
pages)», references not mentioned. The research challenge states no limit.
|
|
54
|
+
|
|
55
|
+
🔴 **The limits are totals.** `format/page-limit` can only compare banal's body count with the
|
|
56
|
+
number, so references standing alone on a page after a full body pass the rule and are over the
|
|
57
|
+
limit. Count the PDF's pages yourself before submitting.
|
|
58
|
+
|
|
59
|
+
## What else the preset encodes
|
|
60
|
+
|
|
61
|
+
- **The IEEE template**, through `extends: paperlint:ieee-conference` ([`ieee-conference.md`](ieee-conference.md)).
|
|
62
|
+
- **Anonymous review** (`blind`), so `anonymity/identity` checks the PDF against the paper's
|
|
63
|
+
declared `identity`.
|
|
64
|
+
- **Portal and deadlines**: all three readings are the call's for now (see Disagreement above).
|
|
65
|
+
|
|
66
|
+
## Not verified
|
|
67
|
+
|
|
68
|
+
- **Remote presentation**: not stated on the page.
|
|
69
|
+
- **AI / LLM usage policy**: not stated. Required content exists only for the competition papers
|
|
70
|
+
(model, pre-processing, tuning, validation results, code link; the challenge: research problem,
|
|
71
|
+
role of natural language, dataset version, methods, findings, reproducibility link).
|
|
72
|
+
- **The template's class line** and **whether a non-archival route exists**: not stated.
|
|
73
|
+
|
|
74
|
+
## What the preset resolves to
|
|
75
|
+
|
|
76
|
+
<!-- paperlint:preset-rules -->
|
|
77
|
+
<!-- prettier-ignore-start -->
|
|
78
|
+
<!-- Generated from nlbse.jsonc by `npx eslint --fix` (rule preset/card-rules). Edit the preset, not this section. -->
|
|
79
|
+
|
|
80
|
+
**`paperlint:nlbse`** — venue preset for NLBSE · call for papers: <https://conf.researchr.org/home/icse-2027/nlbse-2027> · extends `paperlint:ieee-conference`
|
|
81
|
+
|
|
82
|
+
No preset of this chain sets a rule: under it, every rule keeps paperlint's default.
|
|
83
|
+
|
|
84
|
+
<!-- prettier-ignore-end -->
|
|
85
|
+
<!-- /paperlint:preset-rules -->
|
|
@@ -403,6 +403,9 @@ test("the exclude list is exactly the justified set", () => {
|
|
|
403
403
|
// skill and CLAUDE.md sources vigiles compiles; a glob's `**` skips a dot directory
|
|
404
404
|
"**/*.spec.ts",
|
|
405
405
|
".github/*.spec.ts",
|
|
406
|
+
// the same two kinds in a maintainer skill, which does not ship (`.claude/skills/<name>/`)
|
|
407
|
+
".claude/skills/*/*.spec.ts",
|
|
408
|
+
".claude/skills/*/*.eval.*",
|
|
406
409
|
// type declarations
|
|
407
410
|
"**/*.d.ts",
|
|
408
411
|
"**/*.d.mts",
|
package/skills/CLAUDE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- vigiles:sha256:
|
|
1
|
+
<!-- vigiles:sha256:b9427668f4875ea5 compiled from skills/CLAUDE.md.spec.ts -->
|
|
2
2
|
|
|
3
3
|
# CLAUDE.md
|
|
4
4
|
|
|
@@ -12,7 +12,7 @@ package's location come from `skills/paper-pipeline/scripts/consumer.mjs`; the l
|
|
|
12
12
|
|
|
13
13
|
### Skills Ship In Skills
|
|
14
14
|
|
|
15
|
-
**Guidance only** — Skills ship in `skills/`, never `.claude/skills/`. `paperlint init` symlinks each one into the consumer's `.claude/skills/<name>`; the link's directory name is the skill's name. `skills/` is the npm layout: of 855 packages scanned, 336 shipped `skills/` against 15 `.claude/skills/`. Rejected: leaving the skills for Claude Code to find inside `node_modules` (`docs/install.md`); a symlink loads at startup, deterministically.
|
|
15
|
+
**Guidance only** — Skills ship in `skills/`, never `.claude/skills/`. `paperlint init` symlinks each one into the consumer's `.claude/skills/<name>`; the link's directory name is the skill's name. `skills/` is the npm layout: of 855 packages scanned, 336 shipped `skills/` against 15 `.claude/skills/`. Rejected: leaving the skills for Claude Code to find inside `node_modules` (`docs/install.md`); a symlink loads at startup, deterministically. A skill for working on paperlint itself (it edits this repository's presets, tests or source) does not ship: it lives in `.claude/skills/<name>/` as a real directory, outside `files`, and no shipped skill or user doc names it (`add-venue`).
|
|
16
16
|
|
|
17
17
|
### A Criterion That Judges Prose
|
|
18
18
|
|
package/skills/CLAUDE.md.spec.ts
CHANGED
|
@@ -9,7 +9,7 @@ ${file("src/link-skills.ts")}; install-specific paths are flagged by ${file("esl
|
|
|
9
9
|
},
|
|
10
10
|
rules: {
|
|
11
11
|
"skills-ship-in-skills": guidance(
|
|
12
|
-
"Skills ship in `skills/`, never `.claude/skills/`. `paperlint init` symlinks each one into the consumer's `.claude/skills/<name>`; the link's directory name is the skill's name. `skills/` is the npm layout: of 855 packages scanned, 336 shipped `skills/` against 15 `.claude/skills/`. Rejected: leaving the skills for Claude Code to find inside `node_modules` (`docs/install.md`); a symlink loads at startup, deterministically.",
|
|
12
|
+
"Skills ship in `skills/`, never `.claude/skills/`. `paperlint init` symlinks each one into the consumer's `.claude/skills/<name>`; the link's directory name is the skill's name. `skills/` is the npm layout: of 855 packages scanned, 336 shipped `skills/` against 15 `.claude/skills/`. Rejected: leaving the skills for Claude Code to find inside `node_modules` (`docs/install.md`); a symlink loads at startup, deterministically. A skill for working on paperlint itself (it edits this repository's presets, tests or source) does not ship: it lives in `.claude/skills/<name>/` as a real directory, outside `files`, and no shipped skill or user doc names it (`add-venue`).",
|
|
13
13
|
),
|
|
14
14
|
"a-criterion-that-judges-prose": guidance(
|
|
15
15
|
'It follows the five principles in `eslint-rules/CLAUDE.md` ("A rule that judges prose"). A skill does not grep prose for what a rule can count.',
|
|
@@ -11,7 +11,10 @@ the verdict line's wording and the `☐`/`☑` marks as they are, or the hook go
|
|
|
11
11
|
|
|
12
12
|
## The front matter
|
|
13
13
|
|
|
14
|
-
Above the sections, the file carries YAML.
|
|
14
|
+
Above the sections, the file carries YAML. In a paper WITHOUT `cycles` in its `paperlint.json`, the
|
|
15
|
+
rules read its `stages` field (a paper with `cycles` keeps no such list: its frozen versions are the
|
|
16
|
+
folders `versions/<cycle>/` and the generated `versions/lock.json`, and `paperlint versions migrate`
|
|
17
|
+
moves an old list there):
|
|
15
18
|
|
|
16
19
|
```yaml
|
|
17
20
|
---
|
|
@@ -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:567bfff9583abdf1 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
|
|
|
@@ -137,20 +137,28 @@ Many workshop HotCRPs have **no supplementary-upload field** — check first; if
|
|
|
137
137
|
|
|
138
138
|
## 7. Freeze the version you actually uploaded
|
|
139
139
|
|
|
140
|
-
🔴 **
|
|
141
|
-
|
|
140
|
+
🔴 **What the reviewers read is the one version git cannot rebuild for you later** — it was built
|
|
141
|
+
outside the repo and lives on the portal. Freeze it at the send.
|
|
142
|
+
|
|
143
|
+
In a paper that keeps `cycles` in `paperlint.json`, the send freezes it:
|
|
144
|
+
`paperlint submission update <paper> --save` (and `new --save`) writes the exact PDF it sent and the
|
|
145
|
+
source the build read into
|
|
142
146
|
|
|
143
147
|
```
|
|
144
|
-
<paper-dir>/versions/<
|
|
148
|
+
<paper-dir>/versions/<cycle id>/submitted.pdf
|
|
149
|
+
<paper-dir>/versions/<cycle id>/submitted.tex
|
|
145
150
|
```
|
|
146
151
|
|
|
147
|
-
and
|
|
148
|
-
|
|
149
|
-
|
|
152
|
+
and records their bytes and sha256 in `versions/lock.json` (generated; never edit it). After an
|
|
153
|
+
acceptance the same command freezes `camera-ready.{pdf,tex}`. An upload made on the portal by hand:
|
|
154
|
+
put that PDF and its source there under those names and run `npx paperlint versions lock <paper>`.
|
|
155
|
+
`npx paperlint versions` prints which version is under review and how far `paper.tex` has moved.
|
|
150
156
|
|
|
151
|
-
**This is enforced, not advised** — `
|
|
152
|
-
|
|
153
|
-
|
|
157
|
+
**This is enforced, not advised** — `cycle/submitted-frozen` fails a cycle that has a submission and
|
|
158
|
+
no frozen `submitted.pdf`, and `versions/layout` fails a frozen file that no longer matches the lock.
|
|
159
|
+
A paper without `cycles` records each stage in `PIPELINE-STATUS.md`'s `stages:` list instead,
|
|
160
|
+
checked by `paper/stages` and `paper/source`; `npx paperlint versions migrate` moves it to the
|
|
161
|
+
folders once it keeps `cycles`.
|
|
154
162
|
|
|
155
163
|
**Why it is a step and not hygiene:** `agenticdev-2026` kept a folder for exactly this for a month and
|
|
156
164
|
the file inside was the wrong build (340 952 B vs the real 352 357 B). It surfaced only when a reviewer
|
|
@@ -153,20 +153,28 @@ Many workshop HotCRPs have **no supplementary-upload field** — check first; if
|
|
|
153
153
|
|
|
154
154
|
## 7. Freeze the version you actually uploaded
|
|
155
155
|
|
|
156
|
-
🔴 **
|
|
157
|
-
|
|
156
|
+
🔴 **What the reviewers read is the one version git cannot rebuild for you later** — it was built
|
|
157
|
+
outside the repo and lives on the portal. Freeze it at the send.
|
|
158
|
+
|
|
159
|
+
In a paper that keeps \`cycles\` in \`paperlint.json\`, the send freezes it:
|
|
160
|
+
\`paperlint submission update <paper> --save\` (and \`new --save\`) writes the exact PDF it sent and the
|
|
161
|
+
source the build read into
|
|
158
162
|
|
|
159
163
|
\`\`\`
|
|
160
|
-
<paper-dir>/versions/<
|
|
164
|
+
<paper-dir>/versions/<cycle id>/submitted.pdf
|
|
165
|
+
<paper-dir>/versions/<cycle id>/submitted.tex
|
|
161
166
|
\`\`\`
|
|
162
167
|
|
|
163
|
-
and
|
|
164
|
-
|
|
165
|
-
|
|
168
|
+
and records their bytes and sha256 in \`versions/lock.json\` (generated; never edit it). After an
|
|
169
|
+
acceptance the same command freezes \`camera-ready.{pdf,tex}\`. An upload made on the portal by hand:
|
|
170
|
+
put that PDF and its source there under those names and run \`npx paperlint versions lock <paper>\`.
|
|
171
|
+
\`npx paperlint versions\` prints which version is under review and how far \`paper.tex\` has moved.
|
|
166
172
|
|
|
167
|
-
**This is enforced, not advised** — \`
|
|
168
|
-
|
|
169
|
-
|
|
173
|
+
**This is enforced, not advised** — \`cycle/submitted-frozen\` fails a cycle that has a submission and
|
|
174
|
+
no frozen \`submitted.pdf\`, and \`versions/layout\` fails a frozen file that no longer matches the lock.
|
|
175
|
+
A paper without \`cycles\` records each stage in \`PIPELINE-STATUS.md\`'s \`stages:\` list instead,
|
|
176
|
+
checked by \`paper/stages\` and \`paper/source\`; \`npx paperlint versions migrate\` moves it to the
|
|
177
|
+
folders once it keeps \`cycles\`.
|
|
170
178
|
|
|
171
179
|
**Why it is a step and not hygiene:** \`agenticdev-2026\` kept a folder for exactly this for a month and
|
|
172
180
|
the file inside was the wrong build (340 952 B vs the real 352 357 B). It surfaced only when a reviewer
|
|
@@ -81,9 +81,11 @@ need to wait for them or make them up.
|
|
|
81
81
|
|
|
82
82
|
## The frozen version
|
|
83
83
|
|
|
84
|
-
Camera-ready is a separate **stage** in the
|
|
85
|
-
`paper
|
|
86
|
-
|
|
84
|
+
Camera-ready is a separate **stage** in `versions/`: once the cycle's `outcome` is `accepted`,
|
|
85
|
+
`paperlint submission update <paper> --save` freezes the PDF it sends and its source as
|
|
86
|
+
`versions/<cycle>/camera-ready.{pdf,tex}` and records them in `versions/lock.json`. A camera-ready
|
|
87
|
+
uploaded through the publisher's own system (not the review portal) goes there by hand, followed by
|
|
88
|
+
`npx paperlint versions lock <paper>`.
|
|
87
89
|
|
|
88
90
|
**Why this isn't a formality:** the arXiv posting and the extension paper get diffed against
|
|
89
91
|
camera-ready later, and reviewer references to line numbers only resolve against the exact render
|
|
@@ -6,9 +6,31 @@ import {
|
|
|
6
6
|
fixedDownload,
|
|
7
7
|
memoryFiles,
|
|
8
8
|
memoryListEntries,
|
|
9
|
+
memoryCopyFile,
|
|
10
|
+
memoryMoveFile,
|
|
9
11
|
memoryWorkspace,
|
|
10
12
|
} from "./index.ts";
|
|
11
13
|
|
|
14
|
+
test("memoryMoveFile: the bytes move to the new path; a path holding nothing throws", () => {
|
|
15
|
+
const files = memoryFiles({ "/p/a.pdf": "x" });
|
|
16
|
+
const move = memoryMoveFile(files);
|
|
17
|
+
move(absolutePath("/p/a.pdf"), absolutePath("/p/v/b.pdf"));
|
|
18
|
+
assert.deepEqual([...files.map.keys()], ["/p/v/b.pdf"]);
|
|
19
|
+
assert.throws(() => {
|
|
20
|
+
move(absolutePath("/p/a.pdf"), absolutePath("/p/c.pdf"));
|
|
21
|
+
}, /ENOENT/);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("memoryCopyFile: the bytes under the new path too; a path holding nothing throws", () => {
|
|
25
|
+
const files = memoryFiles({ "/p/a.tex": "x" });
|
|
26
|
+
const copy = memoryCopyFile(files);
|
|
27
|
+
copy(absolutePath("/p/a.tex"), absolutePath("/p/v/b.tex"));
|
|
28
|
+
assert.deepEqual([...files.map.keys()], ["/p/a.tex", "/p/v/b.tex"]);
|
|
29
|
+
assert.throws(() => {
|
|
30
|
+
copy(absolutePath("/p/none"), absolutePath("/p/c.tex"));
|
|
31
|
+
}, /ENOENT/);
|
|
32
|
+
});
|
|
33
|
+
|
|
12
34
|
test("memoryWorkspace: a `within` that throws is recorded as `threw`, and the error goes on", () => {
|
|
13
35
|
const ws = memoryWorkspace();
|
|
14
36
|
assert.throws(
|
|
@@ -7,6 +7,7 @@ import type { Download } from "../../ports/download.ts";
|
|
|
7
7
|
import type { Files } from "../../ports/files.ts";
|
|
8
8
|
import type { ListDir } from "../../ports/talk-media.ts";
|
|
9
9
|
import type { ListEntries } from "../../ports/list-entries.ts";
|
|
10
|
+
import type { CopyFile, MoveFile } from "../../ports/move-file.ts";
|
|
10
11
|
import type { Command, ProcessExit, RunProcess } from "../../ports/process.ts";
|
|
11
12
|
import type { NotAPromise, Scratch, Workspace } from "../../ports/workspace.ts";
|
|
12
13
|
import { err, ok } from "../../domain/result.ts";
|
|
@@ -73,6 +74,26 @@ export const memoryListDir =
|
|
|
73
74
|
),
|
|
74
75
|
];
|
|
75
76
|
|
|
77
|
+
/** `MoveFile` over `memoryFiles`: the bytes under the new path, none under the old; nothing there throws. */
|
|
78
|
+
/** `CopyFile` over `memoryFiles`: the bytes under the new path too; nothing there throws. */
|
|
79
|
+
export const memoryCopyFile =
|
|
80
|
+
(files: MemoryFiles): CopyFile =>
|
|
81
|
+
(from, to) => {
|
|
82
|
+
const b = files.map.get(from);
|
|
83
|
+
if (b === undefined) throw new Error(`ENOENT: no file to copy at ${from}`);
|
|
84
|
+
files.writeAtomic(to, b);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const memoryMoveFile =
|
|
88
|
+
(files: MemoryFiles): MoveFile =>
|
|
89
|
+
(from, to) => {
|
|
90
|
+
const b = files.map.get(from);
|
|
91
|
+
if (b === undefined) throw new Error(`ENOENT: no file to move at ${from}`);
|
|
92
|
+
files.writeAtomic(to, b);
|
|
93
|
+
// eslint-disable-next-line functional/immutable-data -- a test double's disk is the mutable map memoryFiles keeps
|
|
94
|
+
files.map.delete(from);
|
|
95
|
+
};
|
|
96
|
+
|
|
76
97
|
/**
|
|
77
98
|
* `ListEntries` over `memoryFiles`: each name directly under a directory, a file when it is one, else a
|
|
78
99
|
* directory. Memory holds no links: the walk's refusal to follow one is the node adapter's to show.
|
|
@@ -36,3 +36,4 @@ export { spawnProcess, type SpawnSync } from "./process.io.ts";
|
|
|
36
36
|
export { hostDirs } from "./host.io.ts";
|
|
37
37
|
export { nodeListDir } from "./list-dir.io.ts";
|
|
38
38
|
export { nodeListEntries } from "./list-entries.io.ts";
|
|
39
|
+
export { nodeCopyFile, nodeMoveFile } from "./move-file.io.ts";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** `MoveFile` and `CopyFile` over `node:fs`: a rename or a copy, the directory made first. */
|
|
2
|
+
import { copyFileSync, mkdirSync, renameSync } from "node:fs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import type { AbsolutePath } from "../../domain/paths.ts";
|
|
5
|
+
import type { CopyFile, MoveFile } from "../../ports/move-file.ts";
|
|
6
|
+
|
|
7
|
+
export const nodeMoveFile: MoveFile = (
|
|
8
|
+
from: AbsolutePath,
|
|
9
|
+
to: AbsolutePath,
|
|
10
|
+
): void => {
|
|
11
|
+
mkdirSync(dirname(to), { recursive: true });
|
|
12
|
+
renameSync(from, to);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const nodeCopyFile: CopyFile = (
|
|
16
|
+
from: AbsolutePath,
|
|
17
|
+
to: AbsolutePath,
|
|
18
|
+
): void => {
|
|
19
|
+
mkdirSync(dirname(to), { recursive: true });
|
|
20
|
+
copyFileSync(from, to);
|
|
21
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** `nodeMoveFile`: the file arrives with its bytes, into a folder made for it, and is gone from where it was. */
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import { absolutePath } from "../../domain/paths.ts";
|
|
6
|
+
import { useTempDir } from "../../../test/support.ts";
|
|
7
|
+
import { nodeCopyFile, nodeMoveFile } from "./move-file.io.ts";
|
|
8
|
+
|
|
9
|
+
const root = useTempDir("paperlint-move-file-");
|
|
10
|
+
|
|
11
|
+
describe("nodeMoveFile", () => {
|
|
12
|
+
it("moves a file into a folder that does not exist yet", () => {
|
|
13
|
+
writeFileSync(join(root, "a.pdf"), "bytes");
|
|
14
|
+
nodeMoveFile(
|
|
15
|
+
absolutePath(join(root, "a.pdf")),
|
|
16
|
+
absolutePath(join(root, "x", "y", "b.pdf")),
|
|
17
|
+
);
|
|
18
|
+
expect(existsSync(join(root, "a.pdf"))).toBe(false);
|
|
19
|
+
expect(readFileSync(join(root, "x", "y", "b.pdf"), "utf8")).toBe("bytes");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("throws when there is nothing to move", () => {
|
|
23
|
+
expect(() => {
|
|
24
|
+
nodeMoveFile(
|
|
25
|
+
absolutePath(join(root, "none")),
|
|
26
|
+
absolutePath(join(root, "z")),
|
|
27
|
+
);
|
|
28
|
+
}).toThrow();
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe("nodeCopyFile", () => {
|
|
33
|
+
it("copies a file into a folder that does not exist yet, and leaves it where it was", () => {
|
|
34
|
+
writeFileSync(join(root, "s.tex"), "source");
|
|
35
|
+
nodeCopyFile(
|
|
36
|
+
absolutePath(join(root, "s.tex")),
|
|
37
|
+
absolutePath(join(root, "c", "d", "s.tex")),
|
|
38
|
+
);
|
|
39
|
+
expect(readFileSync(join(root, "s.tex"), "utf8")).toBe("source");
|
|
40
|
+
expect(readFileSync(join(root, "c", "d", "s.tex"), "utf8")).toBe("source");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("throws when there is nothing to copy", () => {
|
|
44
|
+
expect(() => {
|
|
45
|
+
nodeCopyFile(
|
|
46
|
+
absolutePath(join(root, "none")),
|
|
47
|
+
absolutePath(join(root, "w")),
|
|
48
|
+
);
|
|
49
|
+
}).toThrow();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** `parseYaml`: the value, a bare date as a `Date`, or the parser's own message. */
|
|
2
|
+
import { expect, test } from "vitest";
|
|
3
|
+
import { parseYaml } from "./index.ts";
|
|
4
|
+
|
|
5
|
+
test("parses a mapping, a date without quotes as a Date; refuses what is not YAML with the reason", () => {
|
|
6
|
+
expect(parseYaml("a: 1\nd: 2026-07-22\n")).toStrictEqual({
|
|
7
|
+
ok: true,
|
|
8
|
+
value: { a: 1, d: new Date("2026-07-22T00:00:00.000Z") },
|
|
9
|
+
});
|
|
10
|
+
const bad = parseYaml("a: [");
|
|
11
|
+
expect(bad.ok).toBe(false);
|
|
12
|
+
expect(bad).toStrictEqual({
|
|
13
|
+
ok: false,
|
|
14
|
+
error: "unexpected end of the stream within a flow collection (2:1)",
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ParseYaml` over js-yaml: the one YAML reader of `src/`. A date without quotes reads as a `Date`.
|
|
3
|
+
* A refusal is the parser's first line — the reason and its line:column — without the excerpt it
|
|
4
|
+
* prints under it, so it fits in a one-line message.
|
|
5
|
+
*/
|
|
6
|
+
import { load } from "js-yaml";
|
|
7
|
+
import { messageOf } from "../../domain/text.ts";
|
|
8
|
+
import { err, ok } from "../../domain/result.ts";
|
|
9
|
+
import type { ParseYaml } from "../../ports/yaml.ts";
|
|
10
|
+
|
|
11
|
+
export const parseYaml: ParseYaml = (text) => {
|
|
12
|
+
try {
|
|
13
|
+
return ok(load(text));
|
|
14
|
+
} catch (e) {
|
|
15
|
+
return err(messageOf(e).split("\n", 1).join(""));
|
|
16
|
+
}
|
|
17
|
+
};
|