paperlint 2.0.0 → 2.1.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 +4 -4
- package/CLAUDE.md +3 -3
- package/CONTRIBUTING.md +21 -0
- package/LICENSE +1 -1
- package/README.md +81 -24
- package/action.harness.mjs +10 -10
- package/action.mutations.mjs +3 -3
- package/action.yml +11 -11
- package/bin/{rpp.mjs → paperlint.mjs} +2 -2
- package/dist/adapters/banal/invocation.js +3 -3
- package/dist/adapters/banal/invocation.js.map +1 -1
- package/dist/adapters/banal/run.d.ts +1 -1
- package/dist/adapters/banal/run.js +1 -1
- package/dist/adapters/banal/run.js.map +1 -1
- package/dist/adapters/banal/settings.d.ts +4 -4
- package/dist/adapters/banal/settings.d.ts.map +1 -1
- package/dist/adapters/banal/settings.js +4 -4
- package/dist/adapters/banal/settings.js.map +1 -1
- package/dist/adapters/banal/xml.js +1 -1
- package/dist/adapters/banal/xml.js.map +1 -1
- package/dist/adapters/curl/download.io.d.ts.map +1 -1
- package/dist/adapters/curl/download.io.js +2 -2
- package/dist/adapters/curl/download.io.js.map +1 -1
- package/dist/build-engine.d.ts +1 -1
- package/dist/build-engine.d.ts.map +1 -1
- package/dist/build-engine.js +6 -3
- package/dist/build-engine.js.map +1 -1
- package/dist/build.d.ts +1 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +5 -4
- package/dist/build.js.map +1 -1
- package/dist/cli.d.ts +29 -30
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +174 -98
- package/dist/cli.js.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +21 -6
- package/dist/doctor.js.map +1 -1
- package/dist/engine.d.ts +2 -2
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +1 -1
- package/dist/facts-file.d.ts +33 -9
- package/dist/facts-file.d.ts.map +1 -1
- package/dist/facts-file.js +117 -17
- package/dist/facts-file.js.map +1 -1
- package/dist/hooks-settings.d.ts +0 -18
- package/dist/hooks-settings.d.ts.map +1 -1
- package/dist/hooks-settings.js +70 -42
- package/dist/hooks-settings.js.map +1 -1
- package/dist/init.d.ts +13 -10
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +74 -42
- package/dist/init.js.map +1 -1
- package/dist/link-skills.js +2 -2
- package/dist/link-skills.js.map +1 -1
- package/dist/new-paper.d.ts +2 -2
- package/dist/new-paper.d.ts.map +1 -1
- package/dist/new-paper.js +17 -2
- package/dist/new-paper.js.map +1 -1
- package/dist/paper-settings.d.ts +68 -0
- package/dist/paper-settings.d.ts.map +1 -0
- package/dist/paper-settings.js +144 -0
- package/dist/paper-settings.js.map +1 -0
- package/dist/presets.d.ts +84 -0
- package/dist/presets.d.ts.map +1 -0
- package/dist/presets.js +186 -0
- package/dist/presets.js.map +1 -0
- package/dist/rules-config.d.ts +8 -1
- package/dist/rules-config.d.ts.map +1 -1
- package/dist/rules-config.js +13 -8
- package/dist/rules-config.js.map +1 -1
- package/dist/structure.d.ts.map +1 -1
- package/dist/structure.js +2 -1
- package/dist/structure.js.map +1 -1
- package/dist/tex-requirements.d.ts +63 -7
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +91 -24
- package/dist/tex-requirements.js.map +1 -1
- package/dist/toolchain.d.ts +2 -2
- package/dist/toolchain.d.ts.map +1 -1
- package/dist/toolchain.js +8 -8
- package/dist/toolchain.js.map +1 -1
- package/dist/types.d.ts +4 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/venue-rules.d.ts +109 -0
- package/dist/venue-rules.d.ts.map +1 -0
- package/dist/venue-rules.js +387 -0
- package/dist/venue-rules.js.map +1 -0
- package/docs/configuration.md +73 -17
- package/docs/e2e.md +3 -4
- package/docs/install.md +11 -3
- package/docs/optional-rules.md +26 -25
- package/docs/rules.md +132 -3
- package/docs/toolchain.md +5 -5
- package/eslint-rules/paper-typography.mjs +1 -1
- package/eslint-rules/paper-typography.mutations.mjs +1 -1
- package/eslint-rules/papers.mjs +6 -2
- package/eslint-rules/pdf-last-page-balance.harness.mjs +5 -3
- package/eslint-rules/pdf-last-page-balance.mjs +23 -2
- package/fixtures/build-e2e/acmart/paperlint.json +1 -0
- package/fixtures/build-e2e/guards/paper.tex +1 -1
- package/fixtures/pdf-facts/README.md +1 -1
- package/fixtures/real-markdown-paper/baseline.json +1 -1
- package/fixtures/real-markdown-paper/baseline.mjs +2 -2
- package/fixtures/toolchain-mirror/install-tl +2 -2
- package/fixtures/toolchain-mirror/stub-pdflatex +1 -1
- package/fixtures/toolchain-mirror/stub-tlmgr +1 -1
- package/hooks/hooks.harness.mjs +1 -1
- package/lib/paper-config.d.mts +7 -0
- package/lib/paper-config.harness.mjs +3 -3
- package/lib/paper-config.mjs +34 -3
- package/lib/skill-trigger-cases.harness.mjs +1 -1
- package/package.json +4 -4
- package/plugin/hooks/hooks.json +3 -3
- package/scripts/check.mjs +1 -1
- package/scripts/harness-api.frozen.json +1 -1
- package/scripts/harness-api.test.ts +2 -2
- package/scripts/layer-legacy.frozen.json +1 -1
- package/scripts/mutation-batteries-frozen.harness.mjs +1 -1
- package/scripts/mutation-batteries.frozen.json +2 -2
- package/scripts/release-config.test.ts +1 -1
- package/skills/find-venue/SKILL.md +5 -1
- package/skills/find-venue/SKILL.md.spec.ts +4 -0
- package/skills/paper-pipeline/PIPELINE-MAP.md +14 -4
- package/skills/paper-pipeline/SKILL.md +4 -3
- package/skills/paper-pipeline/SKILL.md.spec.ts +3 -2
- package/skills/paper-pipeline/description-language.eval.mjs +1 -1
- package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +1 -1
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +2 -2
- package/skills/paper-pipeline/pipeline-firing.eval.mjs +1 -1
- package/skills/paper-pipeline/pipeline-language.eval.mjs +1 -1
- package/skills/paper-pipeline/scripts/consumer.d.mts +1 -0
- package/skills/paper-pipeline/scripts/consumer.harness.mjs +10 -4
- package/skills/paper-pipeline/scripts/consumer.mjs +8 -1
- package/skills/paper-pipeline/scripts/consumer.mutations.mjs +1 -1
- package/skills/render-paper/SKILL.md +3 -3
- package/skills/render-paper/SKILL.md.spec.ts +2 -2
- package/skills/render-paper/extract-pdf-facts.harness.mjs +6 -6
- package/skills/render-paper/extract-pdf-facts.mjs +4 -4
- package/skills/study-accepted-papers/SKILL.md +2 -1
- package/skills/study-accepted-papers/SKILL.md.spec.ts +1 -0
- package/skills/submit-paper/SKILL.md +9 -1
- package/skills/submit-paper/SKILL.md.spec.ts +8 -0
- package/skills/submit-paper/references/venues/acm-sigconf.jsonc +86 -0
- package/skills/submit-paper/references/venues/agenticdev.jsonc +34 -102
- package/skills/submit-paper/references/venues/aisec.jsonc +21 -82
- package/skills/submit-paper/references/venues/realm.jsonc +45 -43
- package/skills/submit-paper/references/venues/tex-base.jsonc +1 -1
- package/skills/submit-paper/references/venues/venue-profile.schema.json +120 -30
- package/src/adapters/banal/index.test.ts +1 -1
- package/src/adapters/banal/invocation.test.ts +3 -3
- package/src/adapters/banal/invocation.ts +3 -3
- package/src/adapters/banal/locate.test.ts +4 -4
- package/src/adapters/banal/run.ts +2 -2
- package/src/adapters/banal/settings.test.ts +12 -6
- package/src/adapters/banal/settings.ts +9 -5
- package/src/adapters/banal/xml.test.ts +1 -1
- package/src/adapters/banal/xml.ts +1 -1
- package/src/adapters/curl/download.io.ts +4 -2
- package/src/adapters/curl/download.test.ts +3 -1
- package/src/adapters/node/files.test.ts +1 -1
- package/src/adapters/node/process.test.ts +2 -2
- package/src/adapters/node/workspace.test.ts +4 -4
- package/src/build-engine.harness.mjs +2 -2
- package/src/build-engine.ts +11 -3
- package/src/build.harness.mjs +27 -22
- package/src/build.mutations.mjs +3 -2
- package/src/build.ts +6 -5
- package/src/cli.harness.mjs +72 -131
- package/src/cli.mutations.mjs +7 -32
- package/src/cli.ts +204 -121
- package/src/doctor.harness.mjs +9 -25
- package/src/doctor.ts +28 -5
- package/src/engine.harness.mjs +4 -2
- package/src/engine.ts +2 -2
- package/src/facts-file.test.ts +54 -7
- package/src/facts-file.ts +145 -24
- package/src/hooks-settings.harness.mjs +24 -15
- package/src/hooks-settings.mutations.mjs +2 -4
- package/src/hooks-settings.ts +92 -49
- package/src/init.ts +88 -52
- package/src/latex-log.harness.mjs +1 -1
- package/src/link-skills.harness.mjs +3 -1
- package/src/link-skills.mutations.mjs +1 -1
- package/src/link-skills.ts +2 -2
- package/src/new-paper.harness.mjs +10 -7
- package/src/new-paper.test.ts +145 -0
- package/src/new-paper.ts +22 -2
- package/src/paper-settings-commands.test.ts +311 -0
- package/src/paper-settings.test.ts +220 -0
- package/src/paper-settings.ts +206 -0
- package/src/pdf-facts.harness.mjs +1 -1
- package/src/presets.test.ts +236 -0
- package/src/presets.ts +307 -0
- package/src/rules-config.ts +14 -9
- package/src/structure.harness.mjs +4 -4
- package/src/structure.ts +2 -1
- package/src/tex-requirements.harness.mjs +12 -18
- package/src/tex-requirements.ts +179 -29
- package/src/toolchain.harness.mjs +34 -25
- package/src/toolchain.ts +14 -8
- package/src/types.ts +4 -4
- package/src/venue-rules.test.ts +545 -0
- package/src/venue-rules.ts +573 -0
- package/templates/paper/paperlint.json +4 -0
- package/fixtures/build-e2e/acmart/venue.json +0 -1
- package/plugin/.claude-plugin/plugin.json +0 -8
package/src/build.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* ── THE SHAPE: an ordered list of STEPS, composed, not configured ────────────
|
|
14
14
|
* Each step says whether it applies to THIS paper and why — decided from FACTS parsed out of the
|
|
15
|
-
* paper (its `\documentclass` and options, the venue named in `
|
|
15
|
+
* paper (its `\documentclass` and options, the venue named in `paperlint.json`), never from a config
|
|
16
16
|
* flag. `paperlint build` prints that plan before running anything, and `--dry-run` prints only the
|
|
17
17
|
* plan. Adding a step is one entry in `STEPS`.
|
|
18
18
|
*
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
* ── WHAT IS PURE AND WHAT IS NOT ─────────────────────────────────────────────
|
|
34
34
|
* The decision of the LaTeX loop is `latex-loop.ts`, reading the log is `latex-log.ts`; both are
|
|
35
35
|
* pure. This file is the shell: it runs processes through the injected `run` (the existing port —
|
|
36
|
-
* `spawnSync` by default) and reads the files a pass left behind. Where
|
|
36
|
+
* `spawnSync` by default) and reads the files a pass left behind. Where paperlint's own files live is
|
|
37
37
|
* answered by `consumer.mjs`, the one module allowed to know it (rule 10).
|
|
38
38
|
*/
|
|
39
39
|
// eslint-disable-next-line boundaries/dependencies -- legacy I/O, moves behind a port in #76
|
|
@@ -45,6 +45,7 @@ import { delimiter, join, relative } from "node:path";
|
|
|
45
45
|
// eslint-disable-next-line boundaries/dependencies -- legacy layer, moves behind a port in #76
|
|
46
46
|
import { getParser } from "@unified-latex/unified-latex-util-parse";
|
|
47
47
|
import { packageVenuesDir } from "../skills/paper-pipeline/scripts/consumer.mjs";
|
|
48
|
+
import { PAPER_SETTINGS_FILE } from "../lib/paper-config.mjs";
|
|
48
49
|
import {
|
|
49
50
|
declaredVenue,
|
|
50
51
|
factsPath,
|
|
@@ -86,7 +87,7 @@ export const PAPER_MARKERS = [
|
|
|
86
87
|
"PIPELINE-STATUS.md",
|
|
87
88
|
"paper.tex",
|
|
88
89
|
"paper.md",
|
|
89
|
-
|
|
90
|
+
PAPER_SETTINGS_FILE,
|
|
90
91
|
];
|
|
91
92
|
|
|
92
93
|
/** The source paperlint compiles, and the job name every output file carries. */
|
|
@@ -114,7 +115,7 @@ export interface PaperFacts {
|
|
|
114
115
|
readonly name: string;
|
|
115
116
|
readonly options: readonly string[];
|
|
116
117
|
} | null;
|
|
117
|
-
/** The
|
|
118
|
+
/** The label of the venue preset `paperlint.json` extends, or null. */
|
|
118
119
|
readonly venue: string | null;
|
|
119
120
|
/** Paper-supplied build scripts found on disk — reported as ignored. */
|
|
120
121
|
readonly ignoredScripts: readonly string[];
|
|
@@ -222,7 +223,7 @@ export function readFacts(paperDir: string): PaperFacts {
|
|
|
222
223
|
return {
|
|
223
224
|
main,
|
|
224
225
|
documentclass: documentclassOf(ast),
|
|
225
|
-
venue: venue?.
|
|
226
|
+
venue: venue?.label ?? null,
|
|
226
227
|
ignoredScripts: IGNORED_SCRIPTS.filter((s) =>
|
|
227
228
|
existsSync(join(paperDir, s)),
|
|
228
229
|
),
|
package/src/cli.harness.mjs
CHANGED
|
@@ -35,24 +35,10 @@ import {
|
|
|
35
35
|
} from "../lib/paper-config.mjs";
|
|
36
36
|
|
|
37
37
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
38
|
-
const {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
nextSteps,
|
|
43
|
-
findConfig,
|
|
44
|
-
findDeclaration,
|
|
45
|
-
toPaths,
|
|
46
|
-
runHook,
|
|
47
|
-
} = await import(join(HERE, "cli.ts"));
|
|
48
|
-
const {
|
|
49
|
-
init,
|
|
50
|
-
choosePapers,
|
|
51
|
-
offerWorkflow,
|
|
52
|
-
syncRppJson,
|
|
53
|
-
missingPrograms,
|
|
54
|
-
WORKFLOW_PATH,
|
|
55
|
-
} = await import(join(HERE, "init.ts"));
|
|
38
|
+
const { run, parseArgs, buildConfig, nextSteps, findConfig, toPaths, runHook } =
|
|
39
|
+
await import(join(HERE, "cli.ts"));
|
|
40
|
+
const { init, choosePapers, offerWorkflow, missingPrograms, WORKFLOW_PATH } =
|
|
41
|
+
await import(join(HERE, "init.ts"));
|
|
56
42
|
const { PROGRAMS } = await import(join(HERE, "doctor.ts"));
|
|
57
43
|
|
|
58
44
|
let n = 0;
|
|
@@ -192,11 +178,11 @@ check(
|
|
|
192
178
|
// 🔴 WITHOUT THIS BLOCK THE REWRITTEN `init` WOULD PRODUCE A BROKEN INSTALL. It writes one
|
|
193
179
|
// declaration — into `package.json`, the file the hooks know how to read (a hook does not
|
|
194
180
|
// import code and cannot walk up the tree; it can only read a path it is able to name). The
|
|
195
|
-
// utility, though, read ONLY
|
|
181
|
+
// utility, though, read ONLY its own config file, so right after `paperlint init` its `lint` would say
|
|
196
182
|
// "nothing to lint". I.e. the install command and the check command would be looking at
|
|
197
183
|
// different files — exactly the defect it exists to close, just from the other side.
|
|
198
184
|
{
|
|
199
|
-
const root = realpathSync(mkdtempSync(join(tmpdir(), "
|
|
185
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-carrier-")));
|
|
200
186
|
try {
|
|
201
187
|
const paper = join(root, "papers", "p1");
|
|
202
188
|
mkdirSync(join(paper, "versions"), { recursive: true });
|
|
@@ -327,26 +313,6 @@ check(
|
|
|
327
313
|
// 🔴 A package.json WITHOUT the key does not stop the walk upward. Otherwise the search
|
|
328
314
|
// would end at the first project going up the tree — and every project has a package.json —
|
|
329
315
|
// and would never find anything, ever.
|
|
330
|
-
writeFileSync(
|
|
331
|
-
join(root, "package.json"),
|
|
332
|
-
JSON.stringify({ name: "c", version: "1.0.0" }),
|
|
333
|
-
);
|
|
334
|
-
writeFileSync(
|
|
335
|
-
join(root, "rpp.json"),
|
|
336
|
-
JSON.stringify({ [PAPERS_DIR_FIELD]: "papers" }),
|
|
337
|
-
);
|
|
338
|
-
const viaRpp = await cli(["lint"], root);
|
|
339
|
-
check(
|
|
340
|
-
"a keyless package.json does not intercept the search — rpp.json is still found",
|
|
341
|
-
viaRpp.code === 0 && /config: rpp\.json/.test(viaRpp.out),
|
|
342
|
-
);
|
|
343
|
-
check(
|
|
344
|
-
"🔴 but a deprecated carrier is NAMED, not just silently read",
|
|
345
|
-
/rpp\.json is deprecated/.test(viaRpp.out) &&
|
|
346
|
-
/the hooks read only that file/.test(viaRpp.out),
|
|
347
|
-
);
|
|
348
|
-
|
|
349
|
-
// Both carriers side by side: the one everything ELSE reads wins.
|
|
350
316
|
writeFileSync(
|
|
351
317
|
join(root, "package.json"),
|
|
352
318
|
JSON.stringify({
|
|
@@ -355,14 +321,20 @@ check(
|
|
|
355
321
|
paperlint: { [PAPERS_DIR_FIELD]: "papers" },
|
|
356
322
|
}),
|
|
357
323
|
);
|
|
324
|
+
const inner = join(root, "tools");
|
|
325
|
+
mkdirSync(inner, { recursive: true });
|
|
326
|
+
writeFileSync(
|
|
327
|
+
join(inner, "package.json"),
|
|
328
|
+
JSON.stringify({ name: "tools", version: "1.0.0" }),
|
|
329
|
+
);
|
|
358
330
|
check(
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
findDeclaration(root)?.path === join(root, "package.json"),
|
|
331
|
+
"a keyless package.json does not intercept the search — the one above it is found",
|
|
332
|
+
findConfig(inner) === join(root, "package.json"),
|
|
362
333
|
);
|
|
334
|
+
const viaInner = await cli(["lint"], inner);
|
|
363
335
|
check(
|
|
364
|
-
|
|
365
|
-
|
|
336
|
+
"and `lint` run from under it uses that declaration",
|
|
337
|
+
viaInner.code === 0 && /config: \.\.\/package\.json/.test(viaInner.out),
|
|
366
338
|
);
|
|
367
339
|
} finally {
|
|
368
340
|
rmSync(root, { recursive: true, force: true });
|
|
@@ -372,7 +344,7 @@ check(
|
|
|
372
344
|
// ── `init` — THREE ACTIONS INSTEAD OF NINE ────────────────────────────────────────────────
|
|
373
345
|
//
|
|
374
346
|
// 🔴 THE DEFECT THIS COMMAND WAS REWRITTEN FOR (issue #33, measured 09-18): init wrote
|
|
375
|
-
//
|
|
347
|
+
// a separate config file with a GUESSED `"papers": "papers"` and never touched `package.json` — while the
|
|
376
348
|
// three hooks read the papers directory from exactly there. A consumer who did everything by
|
|
377
349
|
// the book got a `paper-edit-guard` guarding a directory that did not exist; from outside that
|
|
378
350
|
// is indistinguishable from a working guard, because silence is its success state.
|
|
@@ -381,7 +353,7 @@ check(
|
|
|
381
353
|
// stdin. A harness stuck at an input prompt is not a red test, it is the absence of any answer
|
|
382
354
|
// at all.
|
|
383
355
|
{
|
|
384
|
-
const workRoot = realpathSync(mkdtempSync(join(tmpdir(), "
|
|
356
|
+
const workRoot = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-init-")));
|
|
385
357
|
const project = (
|
|
386
358
|
name,
|
|
387
359
|
{ pkg = { name: "consumer", version: "1.0.0" }, papers = [] } = {},
|
|
@@ -441,10 +413,6 @@ check(
|
|
|
441
413
|
"and it says HOW it was measured — otherwise a guess reads as a fact",
|
|
442
414
|
/measured: its subdirectories carry/.test(out.text()),
|
|
443
415
|
);
|
|
444
|
-
check(
|
|
445
|
-
"🔴 `rpp.json` IS NO LONGER CREATED — a second declaration is what doctor exists to catch",
|
|
446
|
-
!existsSync(join(dir, "rpp.json")),
|
|
447
|
-
);
|
|
448
416
|
check(
|
|
449
417
|
"init ends with doctor's report: the install vouches for its OWN state",
|
|
450
418
|
/paperlint doctor — what is wired/.test(out.text()),
|
|
@@ -622,7 +590,7 @@ check(
|
|
|
622
590
|
);
|
|
623
591
|
check(
|
|
624
592
|
"and the CI step is still printed — it just gets pasted in by hand",
|
|
625
|
-
/uses: zernie\/
|
|
593
|
+
/uses: zernie\/paperlint@/.test(out.text()) &&
|
|
626
594
|
/paths: writing/.test(out.text()),
|
|
627
595
|
);
|
|
628
596
|
}
|
|
@@ -639,7 +607,7 @@ check(
|
|
|
639
607
|
});
|
|
640
608
|
check(
|
|
641
609
|
"🔴 a released version — the printed step is pinned to its tag, no <commit-sha>",
|
|
642
|
-
/uses: zernie\/
|
|
610
|
+
/uses: zernie\/paperlint@v1\.2\.3\b/.test(out.text()) &&
|
|
643
611
|
!/<commit-sha>/.test(out.text()),
|
|
644
612
|
);
|
|
645
613
|
}
|
|
@@ -691,7 +659,7 @@ check(
|
|
|
691
659
|
check(
|
|
692
660
|
"🔴 a released version — the written workflow is pinned to its tag, no <commit-sha> left",
|
|
693
661
|
wf === "written" &&
|
|
694
|
-
uses.includes("zernie/
|
|
662
|
+
uses.includes("zernie/paperlint@v1.2.3") &&
|
|
695
663
|
!yaml.includes("<commit-sha>"),
|
|
696
664
|
);
|
|
697
665
|
}
|
|
@@ -847,47 +815,7 @@ check(
|
|
|
847
815
|
);
|
|
848
816
|
}
|
|
849
817
|
|
|
850
|
-
// ── 8.
|
|
851
|
-
{
|
|
852
|
-
const dir = project("legacy", { papers: ["writing"] });
|
|
853
|
-
writeFileSync(
|
|
854
|
-
join(dir, "rpp.json"),
|
|
855
|
-
JSON.stringify({ minFindings: 5 }, null, 2) + "\n",
|
|
856
|
-
);
|
|
857
|
-
const out = say();
|
|
858
|
-
await init(dir, {
|
|
859
|
-
log: out.log,
|
|
860
|
-
err: out.log,
|
|
861
|
-
interactive: false,
|
|
862
|
-
run: haveAll,
|
|
863
|
-
});
|
|
864
|
-
check(
|
|
865
|
-
"an existing rpp.json gets the SAME value, rather than drifting silently",
|
|
866
|
-
JSON.parse(readFileSync(join(dir, "rpp.json"), "utf8"))[
|
|
867
|
-
PAPERS_DIR_FIELD
|
|
868
|
-
] === "writing",
|
|
869
|
-
);
|
|
870
|
-
check(
|
|
871
|
-
"and is named deprecated",
|
|
872
|
-
/rpp\.json was already here/.test(out.text()),
|
|
873
|
-
);
|
|
874
|
-
|
|
875
|
-
const own = join(workRoot, "legacy-own");
|
|
876
|
-
mkdirSync(own, { recursive: true });
|
|
877
|
-
writeFileSync(
|
|
878
|
-
join(own, "package.json"),
|
|
879
|
-
'{"name":"c","version":"1.0.0"}',
|
|
880
|
-
);
|
|
881
|
-
writeFileSync(join(own, "rpp.json"), `{"${PAPERS_DIR_FIELD}":"mine"}`);
|
|
882
|
-
check(
|
|
883
|
-
"and the papers-directory value it already declares stays byte for byte — this too is someone else's value",
|
|
884
|
-
syncRppJson(own, "writing") === "kept" &&
|
|
885
|
-
readFileSync(join(own, "rpp.json"), "utf8") ===
|
|
886
|
-
`{"${PAPERS_DIR_FIELD}":"mine"}`,
|
|
887
|
-
);
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
// ── 9. THE COMMAND IS WIRED TO `run`, NOT MERELY EXPORTED ────────────────────────
|
|
818
|
+
// ── 8. THE COMMAND IS WIRED TO `run`, NOT MERELY EXPORTED ────────────────────────
|
|
891
819
|
{
|
|
892
820
|
const dir = project("wired", { papers: ["writing"] });
|
|
893
821
|
const r = await cli(["init", dir]);
|
|
@@ -922,7 +850,7 @@ check(
|
|
|
922
850
|
// A "." default would give a green run over whatever happens to be lying around — the same
|
|
923
851
|
// contract as the action, and the same reason. An empty directory with no config is exactly
|
|
924
852
|
// that case.
|
|
925
|
-
const bare = realpathSync(mkdtempSync(join(tmpdir(), "
|
|
853
|
+
const bare = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-bare-")));
|
|
926
854
|
try {
|
|
927
855
|
const r = await cli(["lint"], bare);
|
|
928
856
|
check(
|
|
@@ -939,7 +867,7 @@ check(
|
|
|
939
867
|
|
|
940
868
|
// ── on live files: both halves ───────────────────────────────────────────────────────
|
|
941
869
|
{
|
|
942
|
-
const root = realpathSync(mkdtempSync(join(tmpdir(), "
|
|
870
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-cli-")));
|
|
943
871
|
try {
|
|
944
872
|
const paper = join(root, "papers", "p1");
|
|
945
873
|
mkdirSync(join(paper, "versions"), { recursive: true });
|
|
@@ -1021,12 +949,12 @@ check(
|
|
|
1021
949
|
|
|
1022
950
|
// ── THE CONFIG FINDS ITSELF, AND THE PAPERS DIRECTORY IS DECLARED IN IT ──────────────────
|
|
1023
951
|
//
|
|
1024
|
-
// 🔴 THE DEFECT THIS BLOCK EXISTS FOR: `paperlint init` wrote
|
|
952
|
+
// 🔴 THE DEFECT THIS BLOCK EXISTS FOR: `paperlint init` wrote a config file, and `check` only read
|
|
1025
953
|
// it via an explicit `--options`. I.e. the file the utility itself created had no effect on the
|
|
1026
954
|
// run — and there was no way to find that out: zero typography debt looks exactly like a
|
|
1027
955
|
// config that was never found.
|
|
1028
956
|
{
|
|
1029
|
-
const root = realpathSync(mkdtempSync(join(tmpdir(), "
|
|
957
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-cfg-")));
|
|
1030
958
|
try {
|
|
1031
959
|
const paper = join(root, "papers", "p1");
|
|
1032
960
|
mkdirSync(join(paper, "versions"), { recursive: true });
|
|
@@ -1042,21 +970,23 @@ check(
|
|
|
1042
970
|
|
|
1043
971
|
// findConfig — kept separate from the run so the failure is distinguishable
|
|
1044
972
|
writeFileSync(
|
|
1045
|
-
join(root, "
|
|
1046
|
-
JSON.stringify({ [PAPERS_DIR_FIELD]: "papers" }),
|
|
973
|
+
join(root, "package.json"),
|
|
974
|
+
JSON.stringify({ paperlint: { [PAPERS_DIR_FIELD]: "papers" } }),
|
|
1047
975
|
);
|
|
1048
976
|
check(
|
|
1049
977
|
"findConfig walks up from a subdirectory and finds the file at the root",
|
|
1050
|
-
findConfig(paper) === join(root, "
|
|
978
|
+
findConfig(paper) === join(root, "package.json"),
|
|
1051
979
|
);
|
|
1052
980
|
// There can be no tautology here: a neighboring tree must NOT pick up our file. Asserting
|
|
1053
|
-
// `=== null` against a live filesystem is not safe — someone else's
|
|
981
|
+
// `=== null` against a live filesystem is not safe — someone else's package.json may sit further
|
|
1054
982
|
// up the chain — so what is asserted is what is actually checkable: our config is not
|
|
1055
983
|
// visible from there.
|
|
1056
|
-
const sibling = realpathSync(
|
|
984
|
+
const sibling = realpathSync(
|
|
985
|
+
mkdtempSync(join(tmpdir(), "paperlint-other-")),
|
|
986
|
+
);
|
|
1057
987
|
check(
|
|
1058
988
|
"the config does NOT leak into a neighboring tree — the search goes up, not sideways",
|
|
1059
|
-
findConfig(sibling) !== join(root, "
|
|
989
|
+
findConfig(sibling) !== join(root, "package.json"),
|
|
1060
990
|
);
|
|
1061
991
|
rmSync(sibling, { recursive: true, force: true });
|
|
1062
992
|
|
|
@@ -1067,7 +997,7 @@ check(
|
|
|
1067
997
|
);
|
|
1068
998
|
check(
|
|
1069
999
|
"and the found file is NAMED out loud — swapping settings silently is not acceptable",
|
|
1070
|
-
/config:
|
|
1000
|
+
/config: package\.json/.test(found.out),
|
|
1071
1001
|
);
|
|
1072
1002
|
|
|
1073
1003
|
// 🔴 THE RESOLUTION DISCRIMINATOR. Run FROM the paper's directory: same config, and
|
|
@@ -1100,7 +1030,10 @@ check(
|
|
|
1100
1030
|
|
|
1101
1031
|
// 🔴 THE PAPERS DIRECTORY — A REQUIRED FIELD. A config without it is not an "empty config" but an
|
|
1102
1032
|
// unfinished one: silently falling back to "." means running the rules over the whole checkout.
|
|
1103
|
-
writeFileSync(
|
|
1033
|
+
writeFileSync(
|
|
1034
|
+
join(root, "package.json"),
|
|
1035
|
+
JSON.stringify({ paperlint: { minFindings: 3 } }),
|
|
1036
|
+
);
|
|
1104
1037
|
const noPapers = await cli(["lint"], root);
|
|
1105
1038
|
check(
|
|
1106
1039
|
"a config WITHOUT the papers-directory field — a failure, and the field is named by name",
|
|
@@ -1113,8 +1046,8 @@ check(
|
|
|
1113
1046
|
(
|
|
1114
1047
|
await (async () => {
|
|
1115
1048
|
writeFileSync(
|
|
1116
|
-
join(root, "
|
|
1117
|
-
JSON.stringify({ [PAPERS_DIR_FIELD]: "" }),
|
|
1049
|
+
join(root, "package.json"),
|
|
1050
|
+
JSON.stringify({ paperlint: { [PAPERS_DIR_FIELD]: "" } }),
|
|
1118
1051
|
);
|
|
1119
1052
|
return await cli(["lint"], root);
|
|
1120
1053
|
})()
|
|
@@ -1131,7 +1064,7 @@ check(
|
|
|
1131
1064
|
// the threshold had to show up here too — otherwise it would have been lost SILENTLY: the run
|
|
1132
1065
|
// would stay green, and the consumer's setting would stop meaning anything.
|
|
1133
1066
|
{
|
|
1134
|
-
const root = realpathSync(mkdtempSync(join(tmpdir(), "
|
|
1067
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-warn-")));
|
|
1135
1068
|
try {
|
|
1136
1069
|
const paper = join(root, "papers", "p1");
|
|
1137
1070
|
mkdirSync(join(paper, "versions"), { recursive: true });
|
|
@@ -1150,8 +1083,8 @@ check(
|
|
|
1150
1083
|
`---\nstages:\n - stage: submitted\n date: 2026-07-22\n pdf: versions/2026-07-22-submitted.pdf\n bytes: 100\n source: versions/s.tex\n sourceBytes: 4\nresearchQuestion: "does it hold?"\n---\n# S\n\n| id | note |\n|---|---|\n| cites | bib-authors run |\n`,
|
|
1151
1084
|
);
|
|
1152
1085
|
writeFileSync(
|
|
1153
|
-
join(root, "
|
|
1154
|
-
JSON.stringify({ [PAPERS_DIR_FIELD]: "papers" }),
|
|
1086
|
+
join(root, "package.json"),
|
|
1087
|
+
JSON.stringify({ paperlint: { [PAPERS_DIR_FIELD]: "papers" } }),
|
|
1155
1088
|
);
|
|
1156
1089
|
|
|
1157
1090
|
const lax = await cli(["lint"], root);
|
|
@@ -1184,15 +1117,15 @@ check(
|
|
|
1184
1117
|
// one fact that harness cannot know: that the module is WIRED IN. A correct module forgotten in
|
|
1185
1118
|
// `run()` gives zero findings and looks like a clean corpus.
|
|
1186
1119
|
{
|
|
1187
|
-
const root = realpathSync(mkdtempSync(join(tmpdir(), "
|
|
1120
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-wired-")));
|
|
1188
1121
|
try {
|
|
1189
1122
|
const paper = join(root, "papers", "orphan");
|
|
1190
1123
|
mkdirSync(paper, { recursive: true });
|
|
1191
1124
|
// The marker is present, the scorecard is not: NOT ONE pipeline rule runs over this directory.
|
|
1192
1125
|
writeFileSync(join(paper, "paper.tex"), "\\documentclass{article}\n");
|
|
1193
1126
|
writeFileSync(
|
|
1194
|
-
join(root, "
|
|
1195
|
-
JSON.stringify({ [PAPERS_DIR_FIELD]: "papers" }),
|
|
1127
|
+
join(root, "package.json"),
|
|
1128
|
+
JSON.stringify({ paperlint: { [PAPERS_DIR_FIELD]: "papers" } }),
|
|
1196
1129
|
);
|
|
1197
1130
|
|
|
1198
1131
|
const r = await cli(["lint"], root);
|
|
@@ -1225,7 +1158,7 @@ check(
|
|
|
1225
1158
|
);
|
|
1226
1159
|
check(
|
|
1227
1160
|
"and the line about the found config is not lost — it went to stderr",
|
|
1228
|
-
/config:
|
|
1161
|
+
/config: package\.json/.test(j.stderr) && !/config:/.test(j.stdout),
|
|
1229
1162
|
);
|
|
1230
1163
|
check(
|
|
1231
1164
|
"`--json` gives back ONE array, where the missing-file finding sits next to the rule findings",
|
|
@@ -1277,8 +1210,10 @@ check(
|
|
|
1277
1210
|
return [`UNPARSABLE: ${r.out}`];
|
|
1278
1211
|
}
|
|
1279
1212
|
};
|
|
1280
|
-
const tree = realpathSync(mkdtempSync(join(tmpdir(), "
|
|
1281
|
-
const elsewhere = realpathSync(
|
|
1213
|
+
const tree = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-outside-")));
|
|
1214
|
+
const elsewhere = realpathSync(
|
|
1215
|
+
mkdtempSync(join(tmpdir(), "paperlint-elsewhere-")),
|
|
1216
|
+
);
|
|
1282
1217
|
try {
|
|
1283
1218
|
const paper = join(tree, "papers", "p");
|
|
1284
1219
|
mkdirSync(paper, { recursive: true });
|
|
@@ -1435,13 +1370,13 @@ check(
|
|
|
1435
1370
|
//
|
|
1436
1371
|
// 🔴 npm puts a SYMLINK in `node_modules/.bin/`. The first version compared `import.meta.url`
|
|
1437
1372
|
// against `file://${process.argv[1]}`: for a symlink those two paths are DIFFERENT, the
|
|
1438
|
-
// condition is false, and the utility silently exited zero. A direct `node bin/
|
|
1373
|
+
// condition is false, and the utility silently exited zero. A direct `node bin/paperlint.mjs` worked
|
|
1439
1374
|
// fine, meanwhile — i.e. the defect was invisible in exactly the way it is normally checked.
|
|
1440
1375
|
{
|
|
1441
|
-
const root = realpathSync(mkdtempSync(join(tmpdir(), "
|
|
1376
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-link-")));
|
|
1442
1377
|
try {
|
|
1443
|
-
const link = join(root, "
|
|
1444
|
-
symlinkSync(join(HERE, "..", "bin", "
|
|
1378
|
+
const link = join(root, "paperlint-shim");
|
|
1379
|
+
symlinkSync(join(HERE, "..", "bin", "paperlint.mjs"), link);
|
|
1445
1380
|
const paper = join(root, "papers", "p");
|
|
1446
1381
|
mkdirSync(join(paper, "versions"), { recursive: true });
|
|
1447
1382
|
writeFileSync(
|
|
@@ -1552,7 +1487,9 @@ console.log(
|
|
|
1552
1487
|
|
|
1553
1488
|
// ── init wires the hooks, and a human can say no ─────────────────────────────────────────
|
|
1554
1489
|
{
|
|
1555
|
-
const root = realpathSync(
|
|
1490
|
+
const root = realpathSync(
|
|
1491
|
+
mkdtempSync(join(tmpdir(), "paperlint-init-hooks-")),
|
|
1492
|
+
);
|
|
1556
1493
|
const mk = (name) => {
|
|
1557
1494
|
const dir = join(root, name);
|
|
1558
1495
|
mkdirSync(join(dir, "papers", "p1"), { recursive: true });
|
|
@@ -1642,7 +1579,9 @@ console.log(
|
|
|
1642
1579
|
|
|
1643
1580
|
// ── init offers a first paper only where there is none, and only to a human ─────────────
|
|
1644
1581
|
{
|
|
1645
|
-
const root = realpathSync(
|
|
1582
|
+
const root = realpathSync(
|
|
1583
|
+
mkdtempSync(join(tmpdir(), "paperlint-init-paper-")),
|
|
1584
|
+
);
|
|
1646
1585
|
const bare = (name) => {
|
|
1647
1586
|
const dir = join(root, name);
|
|
1648
1587
|
mkdirSync(dir, { recursive: true });
|
|
@@ -1801,7 +1740,7 @@ console.log(
|
|
|
1801
1740
|
|
|
1802
1741
|
// ── `paperlint lint` does not sweep the project's paper TEMPLATE as a paper ────────────────────
|
|
1803
1742
|
{
|
|
1804
|
-
const root = realpathSync(mkdtempSync(join(tmpdir(), "
|
|
1743
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-template-")));
|
|
1805
1744
|
try {
|
|
1806
1745
|
writeFileSync(
|
|
1807
1746
|
join(root, "package.json"),
|
|
@@ -1843,7 +1782,7 @@ console.log(
|
|
|
1843
1782
|
|
|
1844
1783
|
// ── `paperlint new` through the CLI: the papers directory comes from the one declaration ────────
|
|
1845
1784
|
{
|
|
1846
|
-
const root = realpathSync(mkdtempSync(join(tmpdir(), "
|
|
1785
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-new-cli-")));
|
|
1847
1786
|
try {
|
|
1848
1787
|
const none = await cli(["new", "demo"], root);
|
|
1849
1788
|
check(
|
|
@@ -1856,11 +1795,13 @@ console.log(
|
|
|
1856
1795
|
);
|
|
1857
1796
|
const r = await cli(["new", "demo"], root);
|
|
1858
1797
|
check(
|
|
1859
|
-
"🔴 `paperlint new demo` creates writing/demo/ and its lint is the verdict — exit 0, no
|
|
1798
|
+
"🔴 `paperlint new demo` creates writing/demo/ and its lint is the verdict — exit 0, and only the warning that no venue is chosen yet",
|
|
1860
1799
|
r.code === 0 &&
|
|
1861
1800
|
existsSync(join(root, "writing", "demo", "PIPELINE-STATUS.md")) &&
|
|
1862
1801
|
existsSync(join(root, "writing", "demo", "paper.tex")) &&
|
|
1863
|
-
|
|
1802
|
+
existsSync(join(root, "writing", "demo", "paperlint.json")) &&
|
|
1803
|
+
/names no venue preset yet/.test(r.out) &&
|
|
1804
|
+
/\(0 errors, 1 warning\)/.test(r.out),
|
|
1864
1805
|
);
|
|
1865
1806
|
const again = await cli(["new", "demo", "--format", "md"], root);
|
|
1866
1807
|
check(
|
|
@@ -1887,7 +1828,7 @@ console.log(
|
|
|
1887
1828
|
|
|
1888
1829
|
// ── the settings are parsed at the boundary: unknown keys, `rules` blocks ─────────────────
|
|
1889
1830
|
{
|
|
1890
|
-
const root = realpathSync(mkdtempSync(join(tmpdir(), "
|
|
1831
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-settings-")));
|
|
1891
1832
|
try {
|
|
1892
1833
|
const dir = join(root, "papers", "p");
|
|
1893
1834
|
mkdirSync(dir, { recursive: true });
|
|
@@ -1899,13 +1840,13 @@ console.log(
|
|
|
1899
1840
|
join(dir, "paper.tex"),
|
|
1900
1841
|
"\\documentclass{article}\n\\begin{document}x\\end{document}\n",
|
|
1901
1842
|
);
|
|
1902
|
-
const settings = (
|
|
1843
|
+
const settings = (extra) =>
|
|
1903
1844
|
writeFileSync(
|
|
1904
1845
|
join(root, "package.json"),
|
|
1905
1846
|
JSON.stringify({
|
|
1906
1847
|
name: "c",
|
|
1907
1848
|
version: "1.0.0",
|
|
1908
|
-
paperlint: { [PAPERS_DIR_FIELD]: "papers", ...
|
|
1849
|
+
paperlint: { [PAPERS_DIR_FIELD]: "papers", ...extra },
|
|
1909
1850
|
}),
|
|
1910
1851
|
);
|
|
1911
1852
|
const balanceOn = (files) => ({
|
package/src/cli.mutations.mjs
CHANGED
|
@@ -11,12 +11,12 @@ const HERE = dirname(fileURLToPath(import.meta.url));
|
|
|
11
11
|
const ROOT = resolve(HERE, "..");
|
|
12
12
|
const CLI = join(HERE, "cli.ts");
|
|
13
13
|
const HARNESS = join(HERE, "cli.harness.mjs");
|
|
14
|
-
// The `bin/
|
|
14
|
+
// The `bin/paperlint.mjs` shim — the package's EXECUTABLE file, and after the move to TypeScript the
|
|
15
15
|
// "was I run or was I imported" check lives right there: `dist/cli.js` is now always imported,
|
|
16
16
|
// so a mutation in it about the symlink proves nothing. The first run after the move showed
|
|
17
17
|
// this literally — the mutation SURVIVED, and it looked like a hole in the harness, when the
|
|
18
18
|
// hole was in what the mutation was aimed at.
|
|
19
|
-
const SHIM = join(HERE, "..", "bin", "
|
|
19
|
+
const SHIM = join(HERE, "..", "bin", "paperlint.mjs");
|
|
20
20
|
// `init` stopped being twenty lines inside `cli.ts` and became its own module: it has four
|
|
21
21
|
// decisions, and each must be able to break in a way that EXACTLY its own assertion notices.
|
|
22
22
|
const INIT = join(HERE, "init.ts");
|
|
@@ -35,7 +35,7 @@ process.exit(
|
|
|
35
35
|
"the only way a consumer ever calls the utility. npm puts a SYMLINK in .bin, for " +
|
|
36
36
|
"which process.argv[1] and import.meta.url are different paths; comparing strings " +
|
|
37
37
|
"makes the condition false and the utility SILENTLY exits zero. A direct " +
|
|
38
|
-
"`node bin/
|
|
38
|
+
"`node bin/paperlint.mjs` still works, meanwhile, so the defect is invisible in exactly the " +
|
|
39
39
|
"way it is normally checked",
|
|
40
40
|
edits: [
|
|
41
41
|
[
|
|
@@ -153,22 +153,6 @@ process.exit(
|
|
|
153
153
|
"value, because nobody told them it changed",
|
|
154
154
|
edits: [[INIT, " if (existing !== undefined) {", " if (false) {"]],
|
|
155
155
|
},
|
|
156
|
-
{
|
|
157
|
-
name: "init CREATES the second rpp.json carrier again",
|
|
158
|
-
harness: HARNESS,
|
|
159
|
-
expect:
|
|
160
|
-
"🔴 `rpp.json` IS NO LONGER CREATED — a second declaration is what doctor exists to catch",
|
|
161
|
-
disables:
|
|
162
|
-
'"one declaration". Two carriers drift apart silently — this is defect #33, ' +
|
|
163
|
-
"reintroduced by the very install command meant to fix it",
|
|
164
|
-
edits: [
|
|
165
|
-
[
|
|
166
|
-
INIT,
|
|
167
|
-
' if (!existsSync(path)) return "absent";',
|
|
168
|
-
' if (!existsSync(path)) writeFileSync(path, "{}\\n", "utf8");',
|
|
169
|
-
],
|
|
170
|
-
],
|
|
171
|
-
},
|
|
172
156
|
{
|
|
173
157
|
name: "the default that was taken stops being named",
|
|
174
158
|
harness: HARNESS,
|
|
@@ -286,31 +270,22 @@ process.exit(
|
|
|
286
270
|
],
|
|
287
271
|
},
|
|
288
272
|
{
|
|
289
|
-
name: "the utility
|
|
273
|
+
name: "the utility stops reading package.json",
|
|
290
274
|
harness: HARNESS,
|
|
291
275
|
expect:
|
|
292
276
|
"🔴 THE UTILITY READS THE DECLARATION FROM package.json — otherwise `paperlint init` sets up something `paperlint lint` cannot see",
|
|
293
277
|
disables:
|
|
294
278
|
"the link between the install command and the check command. `init` writes one " +
|
|
295
|
-
"declaration into package.json, while `lint` looks for it
|
|
279
|
+
"declaration into package.json, while `lint` looks for it somewhere else — right after " +
|
|
296
280
|
'install the run answers "nothing to lint" over a corpus that is right there',
|
|
297
281
|
edits: [
|
|
298
282
|
[
|
|
299
283
|
CLI,
|
|
300
|
-
|
|
301
|
-
"
|
|
284
|
+
" if (existsSync(pkg) && declaresSettings(pkg)) return pkg;",
|
|
285
|
+
" if (false) return pkg;",
|
|
302
286
|
],
|
|
303
287
|
],
|
|
304
288
|
},
|
|
305
|
-
{
|
|
306
|
-
name: "a deprecated carrier gets read silently",
|
|
307
|
-
harness: HARNESS,
|
|
308
|
-
expect: "🔴 but a deprecated carrier is NAMED, not just silently read",
|
|
309
|
-
disables:
|
|
310
|
-
"the warning that settings live where the hooks do not look. The run is green, one " +
|
|
311
|
-
"directory is linted, a different one is guarded — and both states look the same",
|
|
312
|
-
edits: [[CLI, ' if (decl.kind === "rpp.json")', " if (false)"]],
|
|
313
|
-
},
|
|
314
289
|
{
|
|
315
290
|
name: "consumer data stops reaching the rule",
|
|
316
291
|
harness: HARNESS,
|