paperlint 4.0.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +16 -23
- package/CLAUDE.md +55 -11
- package/CONTRIBUTING.md +30 -7
- package/README.md +11 -8
- package/dist/adapters/banal/invocation.d.ts +1 -1
- package/dist/adapters/latex/document-class.d.ts +23 -0
- package/dist/adapters/latex/document-class.d.ts.map +1 -0
- package/dist/adapters/latex/document-class.js +110 -0
- package/dist/adapters/latex/document-class.js.map +1 -0
- package/dist/adapters/latex/hidden.d.ts +16 -0
- package/dist/adapters/latex/hidden.d.ts.map +1 -0
- package/dist/adapters/latex/hidden.js +49 -0
- package/dist/adapters/latex/hidden.js.map +1 -0
- package/dist/adapters/latex/includes.d.ts +12 -0
- package/dist/adapters/latex/includes.d.ts.map +1 -0
- package/dist/adapters/latex/includes.js +42 -0
- package/dist/adapters/latex/includes.js.map +1 -0
- package/dist/adapters/latex/index.d.ts +33 -0
- package/dist/adapters/latex/index.d.ts.map +1 -0
- package/dist/adapters/latex/index.js +25 -0
- package/dist/adapters/latex/index.js.map +1 -0
- package/dist/adapters/latex/nodes.d.ts +58 -0
- package/dist/adapters/latex/nodes.d.ts.map +1 -0
- package/dist/adapters/latex/nodes.js +97 -0
- package/dist/adapters/latex/nodes.js.map +1 -0
- package/dist/adapters/latex/outline.d.ts +10 -0
- package/dist/adapters/latex/outline.d.ts.map +1 -0
- package/dist/adapters/latex/outline.js +39 -0
- package/dist/adapters/latex/outline.js.map +1 -0
- package/dist/adapters/latex/parse.d.ts +42 -0
- package/dist/adapters/latex/parse.d.ts.map +1 -0
- package/dist/adapters/latex/parse.js +95 -0
- package/dist/adapters/latex/parse.js.map +1 -0
- package/dist/adapters/latex/prose.d.ts +8 -0
- package/dist/adapters/latex/prose.d.ts.map +1 -0
- package/dist/adapters/latex/prose.js +353 -0
- package/dist/adapters/latex/prose.js.map +1 -0
- package/dist/adapters/latex/rendered.d.ts +10 -0
- package/dist/adapters/latex/rendered.d.ts.map +1 -0
- package/dist/adapters/latex/rendered.js +39 -0
- package/dist/adapters/latex/rendered.js.map +1 -0
- package/dist/adapters/pdfjs/fill.d.ts.map +1 -1
- package/dist/adapters/pdfjs/fill.js +12 -12
- package/dist/adapters/pdfjs/fill.js.map +1 -1
- package/dist/build.js +4 -4
- package/dist/build.js.map +1 -1
- package/dist/claim-provenance.d.ts +34 -0
- package/dist/claim-provenance.d.ts.map +1 -0
- package/dist/claim-provenance.js +146 -0
- package/dist/claim-provenance.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +49 -5
- package/dist/cli.js.map +1 -1
- package/dist/domain/paper-source.d.ts +58 -0
- package/dist/domain/paper-source.d.ts.map +1 -0
- package/dist/domain/paper-source.js +109 -0
- package/dist/domain/paper-source.js.map +1 -0
- package/dist/domain/sentences.d.ts +26 -0
- package/dist/domain/sentences.d.ts.map +1 -0
- package/dist/domain/sentences.js +48 -0
- package/dist/domain/sentences.js.map +1 -0
- package/dist/domain/tex-document.d.ts +119 -0
- package/dist/domain/tex-document.d.ts.map +1 -0
- package/dist/domain/tex-document.js +43 -0
- package/dist/domain/tex-document.js.map +1 -0
- package/dist/eslint-rules/paper-typography.d.ts +7 -0
- package/dist/eslint-rules/paper-typography.d.ts.map +1 -1
- package/dist/eslint-rules/paper-typography.js +53 -1
- package/dist/eslint-rules/paper-typography.js.map +1 -1
- package/dist/facts-file.d.ts.map +1 -1
- package/dist/facts-file.js +2 -2
- package/dist/facts-file.js.map +1 -1
- package/dist/new-paper.d.ts +5 -0
- package/dist/new-paper.d.ts.map +1 -1
- package/dist/new-paper.js +3 -0
- package/dist/new-paper.js.map +1 -1
- package/dist/package-dirs.d.ts +5 -0
- package/dist/package-dirs.d.ts.map +1 -0
- package/dist/package-dirs.js +14 -0
- package/dist/package-dirs.js.map +1 -0
- package/dist/ports/latex.d.ts +35 -0
- package/dist/ports/latex.d.ts.map +1 -0
- package/dist/ports/latex.js +2 -0
- package/dist/ports/latex.js.map +1 -0
- package/dist/presets.d.ts +15 -2
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +19 -6
- package/dist/presets.js.map +1 -1
- package/dist/register.d.ts +40 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +134 -0
- package/dist/register.js.map +1 -0
- package/dist/tex-paper.d.ts +19 -0
- package/dist/tex-paper.d.ts.map +1 -0
- package/dist/tex-paper.js +105 -0
- package/dist/tex-paper.js.map +1 -0
- package/dist/tex-requirements.d.ts +11 -0
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +14 -8
- package/dist/tex-requirements.js.map +1 -1
- package/dist/tex-venue-rules.d.ts +88 -0
- package/dist/tex-venue-rules.d.ts.map +1 -0
- package/dist/tex-venue-rules.js +268 -0
- package/dist/tex-venue-rules.js.map +1 -0
- package/dist/venue-rules.d.ts +1 -1
- package/dist/venue-rules.d.ts.map +1 -1
- package/docs/configuration.md +3 -2
- package/docs/e2e.md +69 -40
- package/docs/install.md +1 -1
- package/docs/prior-art/README.md +1 -1
- package/docs/prior-art/package-location.md +5 -5
- package/docs/prior-art/repro/README.md +1 -1
- package/docs/prior-art/test-tooling.md +21 -10
- package/docs/rules/tex/claim-provenance.md +83 -0
- package/docs/rules/tex/missing-input.md +58 -0
- package/docs/rules/tex/register.md +77 -0
- package/docs/rules/tex/required-section.md +75 -0
- package/docs/rules/tex/template.md +74 -0
- package/docs/rules/tex/venue-leftover.md +73 -0
- package/docs/rules.md +73 -48
- package/docs/testing.md +17 -4
- package/docs/texlive-install-decision.md +1 -1
- package/docs/toolchain.md +2 -2
- package/eslint-rules/paper-typography.test.mjs +48 -1
- package/eslint-rules/paper-typography.ts +72 -1
- package/eslint.config.mjs +156 -15
- package/fixtures/build-e2e/acmart/expect.json +26 -0
- package/fixtures/build-e2e/aidc/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/aidc/expect.json +14 -0
- package/fixtures/build-e2e/aidc/paper.tex +46 -0
- package/fixtures/build-e2e/aidc/paperlint.json +1 -0
- package/fixtures/build-e2e/broken/expect.json +11 -0
- package/fixtures/build-e2e/cite/expect.json +27 -0
- package/fixtures/build-e2e/empty/expect.json +12 -0
- package/fixtures/build-e2e/fallback/expect.json +53 -0
- package/fixtures/build-e2e/guards/expect.json +6 -0
- package/fixtures/build-e2e/no-source/expect.json +10 -0
- package/fixtures/build-e2e/unbalanced/expect.json +60 -0
- package/fixtures/pdf-facts/README.md +1 -1
- package/fixtures/real-markdown-paper/baseline.ts +8 -7
- package/fixtures/render-paper/build-clean.sh +3 -5
- package/package.json +14 -2
- package/{skills/submit-paper/references/venues → presets}/acm-sigconf.jsonc +4 -1
- package/{skills/submit-paper/references/venues → presets}/agenticdev.jsonc +4 -0
- package/presets/aidc.jsonc +73 -0
- package/{skills/submit-paper/references/venues → presets}/aisec.jsonc +4 -0
- package/presets/ieee-conference.jsonc +66 -0
- package/{skills/submit-paper/references/venues → presets}/realm.jsonc +8 -1
- package/{skills/submit-paper/references/venues → presets/tex}/paper-guards.tex +1 -1
- package/{skills/submit-paper/references/venues → presets}/venue-profile.schema.json +30 -2
- package/scripts/check.harness.ts +64 -10
- package/scripts/check.test.ts +60 -13
- package/scripts/check.ts +83 -30
- package/scripts/test.test.ts +1 -1
- package/scripts/test.ts +1 -1
- package/skills/cold-read-diff/SKILL.md +31 -10
- package/skills/cold-read-diff/SKILL.md.spec.ts +30 -9
- package/skills/draft-paper/SKILL.md +2 -2
- package/skills/draft-paper/SKILL.md.spec.ts +1 -1
- package/skills/grade-paper-writing/SKILL.md +109 -44
- package/skills/grade-paper-writing/SKILL.md.spec.ts +108 -43
- package/skills/harden-paper/SKILL.md +35 -36
- package/skills/harden-paper/SKILL.md.spec.ts +34 -35
- package/skills/paper-adversarial-review/SKILL.md +2 -2
- package/skills/paper-adversarial-review/SKILL.md.spec.ts +1 -1
- package/skills/paper-pipeline/PIPELINE-MAP.md +2 -2
- package/skills/paper-pipeline/SKILL.md +4 -4
- package/skills/paper-pipeline/SKILL.md.spec.ts +3 -3
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +6 -6
- package/skills/paper-pipeline/references/writing-craft.md +53 -18
- package/skills/paper-pipeline/scripts/consumer.d.mts +0 -2
- package/skills/paper-pipeline/scripts/consumer.mjs +1 -15
- package/skills/pc-panel-review/SKILL.md +36 -11
- package/skills/pc-panel-review/SKILL.md.spec.ts +35 -10
- package/skills/render-paper/SKILL.md +2 -2
- package/skills/render-paper/SKILL.md.spec.ts +1 -1
- package/skills/render-paper/check-render.sh +16 -63
- package/skills/render-paper/render-paper.harness.mjs +17 -59
- package/src/adapters/banal/index.test.ts +1 -1
- package/src/adapters/banal/invocation.ts +1 -1
- package/src/adapters/banal/output.test.ts +1 -1
- package/src/adapters/banal/run.test.ts +1 -1
- package/src/adapters/banal/xml.test.ts +1 -1
- package/src/adapters/latex/document-class.ts +155 -0
- package/src/adapters/latex/hidden.ts +70 -0
- package/src/adapters/latex/includes.ts +66 -0
- package/src/adapters/latex/index.test.ts +535 -0
- package/src/adapters/latex/index.ts +65 -0
- package/src/adapters/latex/nodes.ts +150 -0
- package/src/adapters/latex/outline.ts +64 -0
- package/src/adapters/latex/parse.ts +137 -0
- package/src/adapters/latex/prose.ts +484 -0
- package/src/adapters/latex/rendered.ts +59 -0
- package/src/adapters/pdfjs/fill.test.ts +1 -1
- package/src/adapters/pdfjs/fill.ts +18 -10
- package/src/build.harness.mjs +5 -7
- package/src/build.ts +4 -4
- package/src/claim-provenance.test.ts +352 -0
- package/src/claim-provenance.ts +217 -0
- package/src/cli.harness.mjs +1 -1
- package/src/cli.test.ts +2 -8
- package/src/cli.ts +53 -8
- package/src/domain/paper-source.test.ts +156 -0
- package/src/domain/paper-source.ts +233 -0
- package/src/domain/sentences.ts +67 -0
- package/src/domain/tex-document.test.ts +80 -0
- package/src/domain/tex-document.ts +143 -0
- package/src/engine.harness.mjs +1 -1
- package/src/facts-file.test.ts +4 -7
- package/src/facts-file.ts +2 -2
- package/src/latex-loop.harness.ts +1 -1
- package/src/new-paper.test.ts +28 -3
- package/src/new-paper.ts +8 -0
- package/src/package-dirs.ts +17 -0
- package/src/paper-settings-commands.test.ts +6 -1
- package/src/ports/latex.ts +42 -0
- package/src/presets.test.ts +65 -4
- package/src/presets.ts +38 -15
- package/src/register.test.ts +165 -0
- package/src/register.ts +177 -0
- package/src/rule-cases.test.ts +524 -0
- package/src/rule-docs.test.ts +159 -0
- package/src/tex-paper.test.ts +131 -0
- package/src/tex-paper.ts +152 -0
- package/src/tex-requirements.harness.ts +13 -23
- package/src/tex-requirements.ts +35 -8
- package/src/tex-venue-rules.test.ts +741 -0
- package/src/tex-venue-rules.ts +461 -0
- package/src/toolchain.harness.mjs +1 -1
- package/src/venue-rules.test.ts +96 -4
- package/src/venue-rules.ts +1 -1
- package/templates/paper/paper.tex +2 -1
- package/skills/harden-paper/fixtures/uncited-assertions-sample.md +0 -43
- package/skills/harden-paper/fixtures/uncited-assertions-sample.tex +0 -77
- /package/{skills/submit-paper/references/venues → presets/tex}/agenticdev.tex +0 -0
- /package/{skills/submit-paper/references/venues → presets}/tex-base.jsonc +0 -0
package/scripts/check.ts
CHANGED
|
@@ -34,9 +34,11 @@
|
|
|
34
34
|
* (no TeX, no network), and the tail names every skip. "I ran check" then means one thing.
|
|
35
35
|
*/
|
|
36
36
|
import { spawnSync } from "node:child_process";
|
|
37
|
-
import { existsSync } from "node:fs";
|
|
37
|
+
import { existsSync, readFileSync, rmSync } from "node:fs";
|
|
38
|
+
import { tmpdir } from "node:os";
|
|
38
39
|
import { delimiter, dirname, join } from "node:path";
|
|
39
40
|
import { fileURLToPath } from "node:url";
|
|
41
|
+
import { z } from "zod";
|
|
40
42
|
import { isMain } from "../skills/paper-pipeline/scripts/consumer.mjs";
|
|
41
43
|
|
|
42
44
|
const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
@@ -72,11 +74,17 @@ type GateCommand =
|
|
|
72
74
|
readonly script: string;
|
|
73
75
|
readonly run?: undefined;
|
|
74
76
|
readonly inCi?: RunInCi;
|
|
77
|
+
/**
|
|
78
|
+
* The script is a vitest run, so its JSON report says how many tests were SKIPPED: a run
|
|
79
|
+
* whose exit is 0 and whose report counts a skip is a skip, not a pass.
|
|
80
|
+
*/
|
|
81
|
+
readonly vitest?: true;
|
|
75
82
|
}
|
|
76
83
|
| {
|
|
77
84
|
readonly run: readonly [string, ...string[]];
|
|
78
85
|
readonly script?: undefined;
|
|
79
86
|
readonly inCi?: undefined;
|
|
87
|
+
readonly vitest?: undefined;
|
|
80
88
|
};
|
|
81
89
|
|
|
82
90
|
/**
|
|
@@ -160,22 +168,14 @@ export const GATES: readonly Gate[] = [
|
|
|
160
168
|
{
|
|
161
169
|
name: "install e2e — pack, install under npm and pnpm, run the binary",
|
|
162
170
|
job: "gates",
|
|
163
|
-
|
|
171
|
+
script: "test:e2e:install",
|
|
172
|
+
vitest: true,
|
|
164
173
|
},
|
|
165
174
|
{
|
|
166
|
-
name: "
|
|
175
|
+
name: "TeX e2e — real TeX Live: paperlint toolchain, a real pdflatex build of every fixture, the real banal",
|
|
167
176
|
job: "build-e2e",
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
{
|
|
171
|
-
name: "banal e2e — the real banal on pdf.js-written XML gives banal-on-pdftohtml's numbers",
|
|
172
|
-
job: "build-e2e",
|
|
173
|
-
run: ["node", "test/e2e/banal.ts"],
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
name: "toolchain e2e — real TeX Live into $PAPERLINT_TEXLIVE_DIR, then a build with only it on PATH",
|
|
177
|
-
job: "build-e2e",
|
|
178
|
-
run: ["node", "test/e2e/toolchain.ts"],
|
|
177
|
+
script: "test:e2e:tex",
|
|
178
|
+
vitest: true,
|
|
179
179
|
},
|
|
180
180
|
];
|
|
181
181
|
|
|
@@ -205,18 +205,41 @@ export const NOT_COVERED: Readonly<Record<string, string>> = {
|
|
|
205
205
|
};
|
|
206
206
|
|
|
207
207
|
/**
|
|
208
|
-
*
|
|
209
|
-
* absent and --strict is off. Anything else nonzero is a failure.
|
|
208
|
+
* vitest's JSON report, as far as the verdict reads it: skipped (`pending`) and `todo` tests.
|
|
210
209
|
*
|
|
211
|
-
* 🔴 A SKIP IS A THIRD OUTCOME. Until the Codex review on #45 the e2e
|
|
212
|
-
* this loop counted that as a pass
|
|
213
|
-
*
|
|
210
|
+
* 🔴 A SKIP IS A THIRD OUTCOME. Until the Codex review on #45 the e2e runs exited 0 on a skip and
|
|
211
|
+
* this loop counted that as a pass: a machine without pnpm or TeX printed "all gates passed" for
|
|
212
|
+
* runs that never happened. The count comes from the runner's own report, not from an exit code
|
|
213
|
+
* the tests would have to agree on.
|
|
214
214
|
*/
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
215
|
+
const VitestReport = z.looseObject({
|
|
216
|
+
numPendingTests: z.number().int().nonnegative(),
|
|
217
|
+
numTodoTests: z.number().int().nonnegative(),
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
/** How many tests a vitest JSON report says were not run, or null when it is not one. */
|
|
221
|
+
export function skippedIn(report: string | null): number | null {
|
|
222
|
+
if (report === null) return null;
|
|
223
|
+
try {
|
|
224
|
+
const r = VitestReport.safeParse(JSON.parse(report));
|
|
225
|
+
return r.success ? r.data.numPendingTests + r.data.numTodoTests : null;
|
|
226
|
+
} catch {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* A gate's outcome from its exit status and, for a vitest gate, the skip count its report gave
|
|
233
|
+
* (`null`: the report is missing or unreadable, which cannot be read as a pass).
|
|
234
|
+
*/
|
|
235
|
+
export function outcome(
|
|
236
|
+
status: number | null,
|
|
237
|
+
vitest?: { readonly skipped: number | null },
|
|
238
|
+
): Outcome {
|
|
239
|
+
if (status !== 0) return "fail";
|
|
240
|
+
if (vitest === undefined) return "pass";
|
|
241
|
+
if (vitest.skipped === null) return "fail";
|
|
242
|
+
return vitest.skipped > 0 ? "skip" : "pass";
|
|
220
243
|
}
|
|
221
244
|
|
|
222
245
|
/** The gates that did not pass, each as the line the verdict prints, split by outcome. */
|
|
@@ -225,11 +248,29 @@ interface NotPassed {
|
|
|
225
248
|
skipped: string[];
|
|
226
249
|
}
|
|
227
250
|
|
|
251
|
+
/** A report file's text, read once and removed; null when the run wrote none. */
|
|
252
|
+
export type ReadReport = (file: string) => string | null;
|
|
253
|
+
const readReport: ReadReport = (file) => {
|
|
254
|
+
if (!existsSync(file)) return null;
|
|
255
|
+
const text = readFileSync(file, "utf8");
|
|
256
|
+
rmSync(file, { force: true });
|
|
257
|
+
return text;
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
/** The arguments that make a vitest gate write its JSON report to `file`, beside the usual output. */
|
|
261
|
+
export const reportArgs = (file: string): string[] => [
|
|
262
|
+
"--",
|
|
263
|
+
"--reporter=default",
|
|
264
|
+
"--reporter=json",
|
|
265
|
+
`--outputFile.json=${file}`,
|
|
266
|
+
];
|
|
267
|
+
|
|
228
268
|
/** Run each gate in order; `write` announces it before it starts. */
|
|
229
269
|
function runEach(
|
|
230
270
|
gates: readonly Gate[],
|
|
231
271
|
spawn: Spawn,
|
|
232
272
|
write: (text: string) => unknown,
|
|
273
|
+
reportOf: ReadReport,
|
|
233
274
|
): NotPassed {
|
|
234
275
|
const BIN_FIRST_PATH = [join(ROOT, "node_modules", ".bin"), process.env.PATH]
|
|
235
276
|
.filter(Boolean)
|
|
@@ -237,10 +278,14 @@ function runEach(
|
|
|
237
278
|
const failed: string[] = [];
|
|
238
279
|
const skipped: string[] = [];
|
|
239
280
|
|
|
240
|
-
for (const g of gates) {
|
|
281
|
+
for (const [i, g] of gates.entries()) {
|
|
241
282
|
write(`── ${g.name}\n`);
|
|
242
283
|
const [cmd, ...args] = commandOf(g);
|
|
243
|
-
const
|
|
284
|
+
const file = join(
|
|
285
|
+
tmpdir(),
|
|
286
|
+
`paperlint-check-${String(process.pid)}-${String(i)}.json`,
|
|
287
|
+
);
|
|
288
|
+
const r = spawn(cmd, g.vitest ? [...args, ...reportArgs(file)] : args, {
|
|
244
289
|
cwd: ROOT,
|
|
245
290
|
stdio: "inherit",
|
|
246
291
|
encoding: "utf8",
|
|
@@ -249,12 +294,18 @@ function runEach(
|
|
|
249
294
|
// 🔴 The exit code is read from the command itself, never from a pipe. `cmd | tail && …`
|
|
250
295
|
// reports the FILTER's status, which is almost always zero — that is how a red harness
|
|
251
296
|
// shipped on 2026-09-16.
|
|
252
|
-
const
|
|
297
|
+
const skips = g.vitest ? { skipped: skippedIn(reportOf(file)) } : undefined;
|
|
298
|
+
const o = outcome(r.status, skips);
|
|
253
299
|
if (o === "pass") continue;
|
|
254
300
|
const shown = commandOf(g).join(" ");
|
|
255
301
|
if (o === "skip")
|
|
256
|
-
skipped.push(
|
|
257
|
-
|
|
302
|
+
skipped.push(
|
|
303
|
+
`${g.name} (${shown} → ${String(skips?.skipped)} test(s) skipped)`,
|
|
304
|
+
);
|
|
305
|
+
else
|
|
306
|
+
failed.push(
|
|
307
|
+
`${g.name} (${shown} → ${r.status === 0 ? "exit 0, but no vitest JSON report to read skips from" : String(r.status)})`,
|
|
308
|
+
);
|
|
258
309
|
}
|
|
259
310
|
return { failed, skipped };
|
|
260
311
|
}
|
|
@@ -299,14 +350,16 @@ export function runGates({
|
|
|
299
350
|
log = console.log,
|
|
300
351
|
err = console.error,
|
|
301
352
|
write = (s: string) => process.stdout.write(s),
|
|
353
|
+
reportOf = readReport,
|
|
302
354
|
}: {
|
|
303
355
|
gates?: readonly Gate[];
|
|
304
356
|
spawn?: Spawn;
|
|
305
357
|
log?: (line: string) => void;
|
|
306
358
|
err?: (line: string) => void;
|
|
307
359
|
write?: (text: string) => unknown;
|
|
360
|
+
reportOf?: ReadReport;
|
|
308
361
|
} = {}): number {
|
|
309
|
-
const notPassed = runEach(gates, spawn, write);
|
|
362
|
+
const notPassed = runEach(gates, spawn, write, reportOf);
|
|
310
363
|
report(gates, notPassed, { log, err });
|
|
311
364
|
return notPassed.failed.length ? 1 : 0;
|
|
312
365
|
}
|
package/scripts/test.test.ts
CHANGED
|
@@ -24,7 +24,7 @@ test("a failing vitest does not skip the harnesses, and is named as the failure"
|
|
|
24
24
|
1,
|
|
25
25
|
);
|
|
26
26
|
assert.deepEqual(calls, [
|
|
27
|
-
["vitest", "run"],
|
|
27
|
+
["vitest", "run", "--project", "unit"],
|
|
28
28
|
["vigiles", "test", "--min=1", "--no-skip"],
|
|
29
29
|
]);
|
|
30
30
|
assert.deepEqual(said, [
|
package/scripts/test.ts
CHANGED
|
@@ -46,7 +46,7 @@ export function withSources(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export const SUITES: readonly Suite[] = [
|
|
49
|
-
{ name: "vitest", command: ["vitest", "run"] },
|
|
49
|
+
{ name: "vitest", command: ["vitest", "run", "--project", "unit"] },
|
|
50
50
|
{ name: "harnesses", command: ["vigiles", "test", "--min=1"] },
|
|
51
51
|
];
|
|
52
52
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cold-read-diff
|
|
3
|
-
description: Send just-changed paper prose to
|
|
3
|
+
description: Send just-changed paper prose to two readers with NO context — an engineer who asks what each sentence claims, and a skimming workshop reviewer who asks whose result it states and whether it reads like a paper — the only check that catches a sentence which is short, true, jargon-free and still meaningless to anyone who does not already know the idea. Run after every prose edit to a paper, before calling the edit done, and before reporting a section as fixed. Scoped to the diff, so it is cheap enough to run every time. Not a writing grade (grade-paper-writing), not a structural pass (tighten-paper), not a defect review.
|
|
4
4
|
context: fork
|
|
5
5
|
allowed-tools: [Bash, Read, Agent]
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
<!-- vigiles:sha256:
|
|
8
|
+
<!-- vigiles:sha256:293f0627e6873774 compiled from skills/cold-read-diff/SKILL.md.spec.ts -->
|
|
9
9
|
|
|
10
10
|
# cold-read-diff — the reader who cannot fake understanding
|
|
11
11
|
|
|
@@ -57,21 +57,41 @@ one, and its confusion is the finding.
|
|
|
57
57
|
|
|
58
58
|
1. **Take the diff.** `git diff -U0 -- <paper.md>` since the last commit, or the `new_string` of the
|
|
59
59
|
edits just applied. Prose only — skip comments, tables, bibliography.
|
|
60
|
-
2. **Spawn
|
|
61
|
-
prompt**, not as a file path. A path is an invitation to read
|
|
62
|
-
neighbours is how the check fails silently. `sonnet` is the right
|
|
63
|
-
and not the one that wrote the sentence.
|
|
64
|
-
|
|
60
|
+
2. **Spawn TWO agents with no repository context, one per persona, in parallel.** Give each the
|
|
61
|
+
changed paragraphs **as text in the prompt**, not as a file path. A path is an invitation to read
|
|
62
|
+
the neighbours, and reading the neighbours is how the check fails silently. `sonnet` is the right
|
|
63
|
+
model: competent, cheap, and not the one that wrote the sentence.
|
|
64
|
+
|
|
65
|
+
| persona | who | what only it can report |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| **the engineer** | a strong practitioner outside academia, reading closely | a sentence that parses only for a reader who already holds the idea |
|
|
68
|
+
| **the workshop reviewer** | a PC member at the target venue with 40 minutes for the whole paper, skimming the abstract, the introduction and the conclusion, Confidence 3 | a sentence whose result has no owner, a relation between claims left to infer, a sentence that does not read like a paper at this venue |
|
|
69
|
+
|
|
70
|
+
The engineer cannot see the second column: a sentence with no stated owner reads to him as
|
|
71
|
+
confident, and he does not know the venue's register. Both run, every time; neither replaces the
|
|
72
|
+
other. The reviewer is told the venue's name and nothing else. When the diff touches none of the
|
|
73
|
+
abstract, the introduction and the conclusion, the reviewer still reads it — as the paragraph a
|
|
74
|
+
skimming reviewer lands on.
|
|
75
|
+
3. **Ask the engineer exactly two questions per sentence:**
|
|
65
76
|
- *What does this sentence claim? Restate it in your own words.* If restating requires guessing,
|
|
66
77
|
the answer is `CANNOT PARSE` plus what stopped them — an undefined term, a pronoun with no
|
|
67
78
|
referent, a number with no denominator, two clauses that do not connect.
|
|
68
79
|
- *Would a reader who stops here understand it?* Yes / No / Only-if-they-know-X.
|
|
80
|
+
|
|
81
|
+
**Ask the reviewer two different ones:**
|
|
82
|
+
- *Whose result does this sentence state — the authors' measurement, a cited work, or a property
|
|
83
|
+
of the world?* If it cannot tell, the answer is `WHOSE?` with the sentence quoted.
|
|
84
|
+
- *Does this read like a paper at this venue?* If not, `REGISTER` with the sentence quoted and
|
|
85
|
+
what a paper there would write instead — an aphorism, a fragment, a colloquial turn, or two
|
|
86
|
+
claims side by side with the relation between them unstated.
|
|
69
87
|
4. **Ask for the dangerous class explicitly:** *which sentences sound clever but say less than they
|
|
70
88
|
appear to?* Those read fine and carry nothing, and they are the ones a knowing reader waves
|
|
71
89
|
through.
|
|
72
90
|
5. **Apply the fixes — as rewrites of the whole thought, never as excisions.** See the rule below.
|
|
73
|
-
6. **Re-run on the fix.** An edit is not done until the
|
|
74
|
-
|
|
91
|
+
6. **Re-run on the fix, with both personas.** An edit is not done until the engineer parses the
|
|
92
|
+
replacement AND the reviewer finds its owner and its register. A fix for the engineer that goes
|
|
93
|
+
colloquial is a new `REGISTER` finding, and the reverse holds too. One confirming round; do not
|
|
94
|
+
loop past it.
|
|
75
95
|
|
|
76
96
|
## 🔴 Hand the reader WHOLE PARAGRAPHS, never `+` lines
|
|
77
97
|
|
|
@@ -115,7 +135,8 @@ findings:
|
|
|
115
135
|
|
|
116
136
|
- 🔴 **No context in the prompt. Ever.** Not the title, not the abstract, not "this is a paper about
|
|
117
137
|
agent rules files". Every sentence of background handed to the reader is a sentence of confusion
|
|
118
|
-
they will no longer report.
|
|
138
|
+
they will no longer report. The one exception is the venue's name, given to the reviewer persona:
|
|
139
|
+
judging register without knowing the venue is guessing.
|
|
119
140
|
- 🔴 **A fix that removes the flagged construction and keeps the compression is not a fix.** Proven
|
|
120
141
|
twice in one hour: *"not through any defect in how we write it, but because that is what English
|
|
121
142
|
is"* → *"that is what English is for"* (tic gone, meaning inverted into nonsense); *"the agent
|
|
@@ -8,7 +8,7 @@ import { experimental_skill } from "vigiles/spec";
|
|
|
8
8
|
export default experimental_skill({
|
|
9
9
|
name: "cold-read-diff",
|
|
10
10
|
description:
|
|
11
|
-
"Send just-changed paper prose to
|
|
11
|
+
"Send just-changed paper prose to two readers with NO context — an engineer who asks what each sentence claims, and a skimming workshop reviewer who asks whose result it states and whether it reads like a paper — the only check that catches a sentence which is short, true, jargon-free and still meaningless to anyone who does not already know the idea. Run after every prose edit to a paper, before calling the edit done, and before reporting a section as fixed. Scoped to the diff, so it is cheap enough to run every time. Not a writing grade (grade-paper-writing), not a structural pass (tighten-paper), not a defect review.",
|
|
12
12
|
context: "fork",
|
|
13
13
|
tools: ["Bash", "Read", "Agent"],
|
|
14
14
|
body: `
|
|
@@ -62,21 +62,41 @@ one, and its confusion is the finding.
|
|
|
62
62
|
|
|
63
63
|
1. **Take the diff.** \`git diff -U0 -- <paper.md>\` since the last commit, or the \`new_string\` of the
|
|
64
64
|
edits just applied. Prose only — skip comments, tables, bibliography.
|
|
65
|
-
2. **Spawn
|
|
66
|
-
prompt**, not as a file path. A path is an invitation to read
|
|
67
|
-
neighbours is how the check fails silently. \`sonnet\` is the right
|
|
68
|
-
and not the one that wrote the sentence.
|
|
69
|
-
|
|
65
|
+
2. **Spawn TWO agents with no repository context, one per persona, in parallel.** Give each the
|
|
66
|
+
changed paragraphs **as text in the prompt**, not as a file path. A path is an invitation to read
|
|
67
|
+
the neighbours, and reading the neighbours is how the check fails silently. \`sonnet\` is the right
|
|
68
|
+
model: competent, cheap, and not the one that wrote the sentence.
|
|
69
|
+
|
|
70
|
+
| persona | who | what only it can report |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| **the engineer** | a strong practitioner outside academia, reading closely | a sentence that parses only for a reader who already holds the idea |
|
|
73
|
+
| **the workshop reviewer** | a PC member at the target venue with 40 minutes for the whole paper, skimming the abstract, the introduction and the conclusion, Confidence 3 | a sentence whose result has no owner, a relation between claims left to infer, a sentence that does not read like a paper at this venue |
|
|
74
|
+
|
|
75
|
+
The engineer cannot see the second column: a sentence with no stated owner reads to him as
|
|
76
|
+
confident, and he does not know the venue's register. Both run, every time; neither replaces the
|
|
77
|
+
other. The reviewer is told the venue's name and nothing else. When the diff touches none of the
|
|
78
|
+
abstract, the introduction and the conclusion, the reviewer still reads it — as the paragraph a
|
|
79
|
+
skimming reviewer lands on.
|
|
80
|
+
3. **Ask the engineer exactly two questions per sentence:**
|
|
70
81
|
- *What does this sentence claim? Restate it in your own words.* If restating requires guessing,
|
|
71
82
|
the answer is \`CANNOT PARSE\` plus what stopped them — an undefined term, a pronoun with no
|
|
72
83
|
referent, a number with no denominator, two clauses that do not connect.
|
|
73
84
|
- *Would a reader who stops here understand it?* Yes / No / Only-if-they-know-X.
|
|
85
|
+
|
|
86
|
+
**Ask the reviewer two different ones:**
|
|
87
|
+
- *Whose result does this sentence state — the authors' measurement, a cited work, or a property
|
|
88
|
+
of the world?* If it cannot tell, the answer is \`WHOSE?\` with the sentence quoted.
|
|
89
|
+
- *Does this read like a paper at this venue?* If not, \`REGISTER\` with the sentence quoted and
|
|
90
|
+
what a paper there would write instead — an aphorism, a fragment, a colloquial turn, or two
|
|
91
|
+
claims side by side with the relation between them unstated.
|
|
74
92
|
4. **Ask for the dangerous class explicitly:** *which sentences sound clever but say less than they
|
|
75
93
|
appear to?* Those read fine and carry nothing, and they are the ones a knowing reader waves
|
|
76
94
|
through.
|
|
77
95
|
5. **Apply the fixes — as rewrites of the whole thought, never as excisions.** See the rule below.
|
|
78
|
-
6. **Re-run on the fix.** An edit is not done until the
|
|
79
|
-
|
|
96
|
+
6. **Re-run on the fix, with both personas.** An edit is not done until the engineer parses the
|
|
97
|
+
replacement AND the reviewer finds its owner and its register. A fix for the engineer that goes
|
|
98
|
+
colloquial is a new \`REGISTER\` finding, and the reverse holds too. One confirming round; do not
|
|
99
|
+
loop past it.
|
|
80
100
|
|
|
81
101
|
## 🔴 Hand the reader WHOLE PARAGRAPHS, never \`+\` lines
|
|
82
102
|
|
|
@@ -120,7 +140,8 @@ findings:
|
|
|
120
140
|
|
|
121
141
|
- 🔴 **No context in the prompt. Ever.** Not the title, not the abstract, not "this is a paper about
|
|
122
142
|
agent rules files". Every sentence of background handed to the reader is a sentence of confusion
|
|
123
|
-
they will no longer report.
|
|
143
|
+
they will no longer report. The one exception is the venue's name, given to the reviewer persona:
|
|
144
|
+
judging register without knowing the venue is guessing.
|
|
124
145
|
- 🔴 **A fix that removes the flagged construction and keeps the compression is not a fix.** Proven
|
|
125
146
|
twice in one hour: *"not through any defect in how we write it, but because that is what English
|
|
126
147
|
is"* → *"that is what English is for"* (tic gone, meaning inverted into nonsense); *"the agent
|
|
@@ -4,7 +4,7 @@ description: Use when the numbers exist and it's time to WRITE the paper — "dr
|
|
|
4
4
|
allowed-tools: [Read, Write, Edit, Grep, Glob, Agent, Skill]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
<!-- vigiles:sha256:
|
|
7
|
+
<!-- vigiles:sha256:c8d25c21dc01a742 compiled from skills/draft-paper/SKILL.md.spec.ts -->
|
|
8
8
|
|
|
9
9
|
# draft-paper — prose that survives review and gets cited
|
|
10
10
|
|
|
@@ -78,7 +78,7 @@ grade.
|
|
|
78
78
|
|
|
79
79
|
## Run the prose through the PAPER suite's QA (do not skip — and do not use the blog skills)
|
|
80
80
|
Before any review pass, run the draft through the paper-side prose QA — it catches what a self-read won't:
|
|
81
|
-
- **`grade-paper-writing`** — the
|
|
81
|
+
- **`grade-paper-writing`** — the 10-dim rubric + the PERSONA cold-read stall pass (a committed
|
|
82
82
|
non-academic persona, per-section — the localized "wait, what does that even mean?" axis authors are
|
|
83
83
|
blind to). Its avoid-list grep also strips the AI-writing tropes (hedge-stacking, "it's worth
|
|
84
84
|
noting", limp connectives, fake balance) that make a draft read unserious to a human PC.
|
|
@@ -84,7 +84,7 @@ grade.
|
|
|
84
84
|
|
|
85
85
|
## Run the prose through the PAPER suite's QA (do not skip — and do not use the blog skills)
|
|
86
86
|
Before any review pass, run the draft through the paper-side prose QA — it catches what a self-read won't:
|
|
87
|
-
- **\`grade-paper-writing\`** — the
|
|
87
|
+
- **\`grade-paper-writing\`** — the 10-dim rubric + the PERSONA cold-read stall pass (a committed
|
|
88
88
|
non-academic persona, per-section — the localized "wait, what does that even mean?" axis authors are
|
|
89
89
|
blind to). Its avoid-list grep also strips the AI-writing tropes (hedge-stacking, "it's worth
|
|
90
90
|
noting", limp connectives, fake balance) that make a draft read unserious to a human PC.
|