@descent-vtt/spec-brief 0.1.0 → 0.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/CHANGELOG.md +264 -0
- package/README.md +90 -20
- package/dist/archive.d.ts +52 -2
- package/dist/archive.js +205 -37
- package/dist/archive.js.map +1 -1
- package/dist/brief.d.ts +2 -0
- package/dist/brief.js +5 -1
- package/dist/brief.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +48 -14
- package/dist/cli.js.map +1 -1
- package/dist/collisions.d.ts +43 -12
- package/dist/collisions.js +90 -58
- package/dist/collisions.js.map +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.js +58 -10
- package/dist/config.js.map +1 -1
- package/dist/corpus.d.ts +21 -1
- package/dist/corpus.js +51 -2
- package/dist/corpus.js.map +1 -1
- package/dist/engine.d.ts +33 -3
- package/dist/engine.js +112 -9
- package/dist/engine.js.map +1 -1
- package/dist/frontmatter.d.ts +18 -61
- package/dist/frontmatter.js +31 -300
- package/dist/frontmatter.js.map +1 -1
- package/dist/glob.d.ts +77 -54
- package/dist/glob.js +104 -433
- package/dist/glob.js.map +1 -1
- package/dist/index.d.ts +10 -8
- package/dist/index.js +8 -6
- package/dist/index.js.map +1 -1
- package/dist/links.d.ts +1 -1
- package/dist/links.js +3 -4
- package/dist/links.js.map +1 -1
- package/dist/lint.d.ts +33 -2
- package/dist/lint.js +9 -2
- package/dist/lint.js.map +1 -1
- package/dist/markdown.d.ts +26 -23
- package/dist/markdown.js +60 -237
- package/dist/markdown.js.map +1 -1
- package/dist/plugins.d.ts +14 -0
- package/dist/plugins.js +142 -6
- package/dist/plugins.js.map +1 -1
- package/dist/report.d.ts +40 -3
- package/dist/report.js +227 -11
- package/dist/report.js.map +1 -1
- package/dist/rules.d.ts +22 -4
- package/dist/rules.js +175 -45
- package/dist/rules.js.map +1 -1
- package/dist/scaffold.d.ts +1 -1
- package/dist/scaffold.js +10 -9
- package/dist/scaffold.js.map +1 -1
- package/dist/schedule.d.ts +133 -0
- package/dist/schedule.js +265 -0
- package/dist/schedule.js.map +1 -0
- package/dist/scope.d.ts +52 -0
- package/dist/scope.js +56 -0
- package/dist/scope.js.map +1 -0
- package/dist/text.d.ts +13 -0
- package/dist/text.js +23 -0
- package/dist/text.js.map +1 -1
- package/dist/trigger.d.ts +17 -0
- package/dist/trigger.js +49 -0
- package/dist/trigger.js.map +1 -0
- package/dist/types.d.ts +14 -5
- package/dist/types.js.map +1 -1
- package/dist/vendor/spec-core/markdown/frontmatter.d.ts +103 -0
- package/dist/vendor/spec-core/markdown/frontmatter.js +459 -0
- package/dist/vendor/spec-core/markdown/frontmatter.js.map +1 -0
- package/dist/vendor/spec-core/markdown/headings.d.ts +45 -0
- package/dist/vendor/spec-core/markdown/headings.js +199 -0
- package/dist/vendor/spec-core/markdown/headings.js.map +1 -0
- package/dist/vendor/spec-core/markdown/index.d.ts +5 -0
- package/dist/vendor/spec-core/markdown/index.js +4 -0
- package/dist/vendor/spec-core/markdown/index.js.map +1 -0
- package/dist/vendor/spec-core/markdown/layout.d.ts +39 -0
- package/dist/vendor/spec-core/markdown/layout.js +28 -0
- package/dist/vendor/spec-core/markdown/layout.js.map +1 -0
- package/dist/vendor/spec-core/markdown/links.d.ts +22 -0
- package/dist/vendor/spec-core/markdown/links.js +364 -0
- package/dist/vendor/spec-core/markdown/links.js.map +1 -0
- package/dist/vendor/spec-core/markdown/lists.d.ts +22 -0
- package/dist/vendor/spec-core/markdown/lists.js +127 -0
- package/dist/vendor/spec-core/markdown/lists.js.map +1 -0
- package/dist/vendor/spec-core/markdown/scan.d.ts +36 -0
- package/dist/vendor/spec-core/markdown/scan.js +439 -0
- package/dist/vendor/spec-core/markdown/scan.js.map +1 -0
- package/dist/vendor/spec-core/markdown/syntax.d.ts +75 -0
- package/dist/vendor/spec-core/markdown/syntax.js +181 -0
- package/dist/vendor/spec-core/markdown/syntax.js.map +1 -0
- package/dist/vendor/spec-core/markdown/tables.d.ts +18 -0
- package/dist/vendor/spec-core/markdown/tables.js +86 -0
- package/dist/vendor/spec-core/markdown/tables.js.map +1 -0
- package/dist/vendor/spec-core/markdown/types.d.ts +287 -0
- package/dist/vendor/spec-core/markdown/types.js +9 -0
- package/dist/vendor/spec-core/markdown/types.js.map +1 -0
- package/dist/vendor/spec-core/path/index.d.ts +1 -0
- package/dist/vendor/spec-core/path/index.js +2 -0
- package/dist/vendor/spec-core/path/index.js.map +1 -0
- package/dist/vendor/spec-core/path/posix.d.ts +83 -0
- package/dist/vendor/spec-core/path/posix.js +150 -0
- package/dist/vendor/spec-core/path/posix.js.map +1 -0
- package/dist/vendor/spec-core/pattern/automaton.d.ts +129 -0
- package/dist/vendor/spec-core/pattern/automaton.js +340 -0
- package/dist/vendor/spec-core/pattern/automaton.js.map +1 -0
- package/dist/vendor/spec-core/pattern/charset.d.ts +58 -0
- package/dist/vendor/spec-core/pattern/charset.js +116 -0
- package/dist/vendor/spec-core/pattern/charset.js.map +1 -0
- package/dist/vendor/spec-core/pattern/glob.d.ts +121 -0
- package/dist/vendor/spec-core/pattern/glob.js +481 -0
- package/dist/vendor/spec-core/pattern/glob.js.map +1 -0
- package/dist/vendor/spec-core/pattern/index.d.ts +6 -0
- package/dist/vendor/spec-core/pattern/index.js +4 -0
- package/dist/vendor/spec-core/pattern/index.js.map +1 -0
- package/dist/vendor/spec-core/pattern/regex.d.ts +67 -0
- package/dist/vendor/spec-core/pattern/regex.js +813 -0
- package/dist/vendor/spec-core/pattern/regex.js.map +1 -0
- package/dist/vendor/spec-core/text/index.d.ts +2 -0
- package/dist/vendor/spec-core/text/index.js +2 -0
- package/dist/vendor/spec-core/text/index.js.map +1 -0
- package/dist/vendor/spec-core/text/lines.d.ts +72 -0
- package/dist/vendor/spec-core/text/lines.js +156 -0
- package/dist/vendor/spec-core/text/lines.js.map +1 -0
- package/package.json +3 -2
- package/schema.json +17 -0
- package/src/vendor/spec-core/LICENSE +21 -0
- package/src/vendor/spec-core/README.md +8 -0
package/dist/rules.d.ts
CHANGED
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
* archive are about identity and the freeze itself.
|
|
9
9
|
*/
|
|
10
10
|
import { type Brief } from './brief.js';
|
|
11
|
-
import type
|
|
11
|
+
import { type Config, type SectionRule } from './config.js';
|
|
12
12
|
import { type Corpus } from './corpus.js';
|
|
13
|
+
import { type Glob } from './glob.js';
|
|
13
14
|
import { type Section } from './markdown.js';
|
|
14
15
|
import type { Severity, SeveritySetting } from './types.js';
|
|
15
16
|
export interface RuleResult {
|
|
@@ -49,10 +50,27 @@ export declare function analyse(corpus: Corpus): SharedAnalysis;
|
|
|
49
50
|
export declare function sectionRules(brief: Brief, config: Config): SectionRule[];
|
|
50
51
|
/** The headings that fill a section rule. */
|
|
51
52
|
export declare function sectionsFilling(brief: Brief, rule: SectionRule): Section[];
|
|
53
|
+
/**
|
|
54
|
+
* The paths a pattern names with no glob syntax that the tree does not hold
|
|
55
|
+
* and whose names carry no extension. The tree reads each as a file, and the
|
|
56
|
+
* spelling cannot say whether a directory was meant.
|
|
57
|
+
*/
|
|
58
|
+
export declare function unheldLiterals(glob: Glob, files: readonly string[]): string[];
|
|
59
|
+
/**
|
|
60
|
+
* Affected patterns a brief's own protections cover entirely: one error for
|
|
61
|
+
* all of them, and a warning for those the search could not decide.
|
|
62
|
+
*/
|
|
63
|
+
export declare function scopeContradiction(brief: Brief, repoFiles: readonly string[] | null, budget?: number): RuleResult[];
|
|
52
64
|
export declare const RULES: readonly Rule[];
|
|
53
65
|
/**
|
|
54
|
-
* The rules the
|
|
55
|
-
* so they carry no check: they exist so configuration
|
|
56
|
-
* and reports can describe them.
|
|
66
|
+
* The rules the wave analyses - `matrix` and `schedule` - report under. They
|
|
67
|
+
* are not run per brief, so they carry no check: they exist so configuration
|
|
68
|
+
* can set their severity and reports can describe them.
|
|
57
69
|
*/
|
|
58
70
|
export declare const COLLISION_RULES: readonly RuleInfo[];
|
|
71
|
+
/**
|
|
72
|
+
* The rules archival reports under beside its refusals. Like the collision
|
|
73
|
+
* rules they carry no check: the planner raises them, and they exist so
|
|
74
|
+
* configuration can set their severity and reports can describe them.
|
|
75
|
+
*/
|
|
76
|
+
export declare const ARCHIVE_RULES: readonly RuleInfo[];
|
package/dist/rules.js
CHANGED
|
@@ -8,12 +8,16 @@
|
|
|
8
8
|
* archive are about identity and the freeze itself.
|
|
9
9
|
*/
|
|
10
10
|
import { BUILT_IN_FIELDS, knownFields, lineOfField } from './brief.js';
|
|
11
|
+
import { statusWords } from './config.js';
|
|
11
12
|
import { dependencyCycles, duplicateIds, idKey, resolveDependency } from './corpus.js';
|
|
13
|
+
import { findEntry } from './frontmatter.js';
|
|
12
14
|
import { integrityOf } from './integrity.js';
|
|
13
|
-
import {
|
|
15
|
+
import { hasExtension, held, isGlobSyntax, matchGlob, parseGlob, readingIn, treeOf, WITNESS_BUDGET } from './glob.js';
|
|
14
16
|
import { hasContent } from './markdown.js';
|
|
15
17
|
import { closest } from './schema.js';
|
|
16
|
-
import {
|
|
18
|
+
import { contradictions, patternsOf, scopeOf } from './scope.js';
|
|
19
|
+
import { inWords, labelMatches } from './text.js';
|
|
20
|
+
import { namesAnEvent } from './trigger.js';
|
|
17
21
|
export function analyse(corpus) {
|
|
18
22
|
return { cycles: dependencyCycles(corpus), duplicates: duplicateIds(corpus) };
|
|
19
23
|
}
|
|
@@ -59,12 +63,44 @@ function leniency(brief) {
|
|
|
59
63
|
function live(check) {
|
|
60
64
|
return (context) => (context.brief.phase === 'live' ? check(context) : []);
|
|
61
65
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
const SCOPE_FIELDS = ['affectedFiles', 'protectedFiles'];
|
|
67
|
+
const quoted = (items) => inWords(items.map((item) => `"${item}"`));
|
|
68
|
+
/**
|
|
69
|
+
* The paths a pattern names with no glob syntax that the tree does not hold
|
|
70
|
+
* and whose names carry no extension. The tree reads each as a file, and the
|
|
71
|
+
* spelling cannot say whether a directory was meant.
|
|
72
|
+
*/
|
|
73
|
+
export function unheldLiterals(glob, files) {
|
|
74
|
+
const tree = treeOf(files);
|
|
75
|
+
return glob.literals
|
|
76
|
+
.map((literal) => literal.path)
|
|
77
|
+
.filter((path) => held(tree, path) === null && !hasExtension(path.slice(path.lastIndexOf('/') + 1)));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Affected patterns a brief's own protections cover entirely: one error for
|
|
81
|
+
* all of them, and a warning for those the search could not decide.
|
|
82
|
+
*/
|
|
83
|
+
export function scopeContradiction(brief, repoFiles, budget = WITNESS_BUDGET) {
|
|
84
|
+
const { covered, undecided } = contradictions(scopeOf(brief, readingIn(repoFiles)), budget);
|
|
85
|
+
const line = at(lineOfField(brief, 'protectedFiles'));
|
|
86
|
+
const results = [];
|
|
87
|
+
if (covered.length > 0) {
|
|
88
|
+
const one = covered.length === 1;
|
|
89
|
+
results.push({
|
|
90
|
+
line,
|
|
91
|
+
message: `${quoted(covered)} in affectedFiles ${one ? 'is' : 'are'} entirely protected, so nothing of ${one ? 'it' : 'them'} is writable`,
|
|
92
|
+
hint: `drop ${one ? 'it' : 'them'} from affectedFiles, or narrow protectedFiles so that some of ${one ? 'it' : 'each'} is writable`,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (undecided.length > 0) {
|
|
96
|
+
results.push({
|
|
97
|
+
line,
|
|
98
|
+
message: `whether ${quoted(undecided)} in affectedFiles ${undecided.length === 1 ? 'is' : 'are'} entirely protected is undecided: the search met its budget`,
|
|
99
|
+
hint: 'simplify the patterns until the question can be answered',
|
|
100
|
+
severity: 'warning',
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return results;
|
|
68
104
|
}
|
|
69
105
|
export const RULES = [
|
|
70
106
|
{
|
|
@@ -123,7 +159,7 @@ export const RULES = [
|
|
|
123
159
|
];
|
|
124
160
|
}
|
|
125
161
|
if (brief.status === null) {
|
|
126
|
-
const words =
|
|
162
|
+
const words = statusWords(config);
|
|
127
163
|
return [
|
|
128
164
|
{
|
|
129
165
|
line,
|
|
@@ -220,13 +256,16 @@ export const RULES = [
|
|
|
220
256
|
description: 'Every required section is present.',
|
|
221
257
|
check: live(({ brief, config }) => sectionRules(brief, config)
|
|
222
258
|
.filter((rule) => !rule.optional && sectionsFilling(brief, rule).length === 0)
|
|
223
|
-
.map((rule) =>
|
|
224
|
-
|
|
225
|
-
message: `has no "${rule.name}" section`,
|
|
226
|
-
hint: rule.aliases.length === 0
|
|
259
|
+
.map((rule) => {
|
|
260
|
+
const heading = rule.aliases.length === 0
|
|
227
261
|
? `add a "## ${rule.name}" heading`
|
|
228
|
-
: `add a "## ${rule.name}" heading (also accepted: ${rule.aliases.map((a) => `"${a}"`).join(', ')})
|
|
229
|
-
|
|
262
|
+
: `add a "## ${rule.name}" heading (also accepted: ${rule.aliases.map((a) => `"${a}"`).join(', ')})`;
|
|
263
|
+
return {
|
|
264
|
+
line: at(brief.bodyStart),
|
|
265
|
+
message: `has no "${rule.name}" section`,
|
|
266
|
+
hint: rule.hint === undefined ? heading : `${heading}. ${rule.hint}`,
|
|
267
|
+
};
|
|
268
|
+
})),
|
|
230
269
|
},
|
|
231
270
|
{
|
|
232
271
|
id: 'duplicate-section',
|
|
@@ -243,7 +282,12 @@ export const RULES = [
|
|
|
243
282
|
check: live(({ brief, config }) => sectionRules(brief, config).flatMap((rule) => sectionsFilling(brief, rule)
|
|
244
283
|
.slice(0, 1)
|
|
245
284
|
.filter((s) => contentLines(brief, s).length === 0)
|
|
246
|
-
.map((s) => ({
|
|
285
|
+
.map((s) => ({
|
|
286
|
+
line: at(s.heading.line),
|
|
287
|
+
message: `the "${rule.name}" section is empty`,
|
|
288
|
+
hint: rule.hint ?? 'write it; a comment alone is not content',
|
|
289
|
+
severity: leniency(brief),
|
|
290
|
+
})))),
|
|
247
291
|
},
|
|
248
292
|
{
|
|
249
293
|
id: 'placeholder',
|
|
@@ -258,6 +302,7 @@ export const RULES = [
|
|
|
258
302
|
.map((s) => ({
|
|
259
303
|
line: at(s.heading.line),
|
|
260
304
|
message: `the "${rule.name}" section holds only a placeholder`,
|
|
305
|
+
hint: rule.hint ?? 'replace the placeholder with what the section must say',
|
|
261
306
|
severity: leniency(brief),
|
|
262
307
|
})))),
|
|
263
308
|
},
|
|
@@ -369,36 +414,59 @@ export const RULES = [
|
|
|
369
414
|
});
|
|
370
415
|
}),
|
|
371
416
|
},
|
|
417
|
+
{
|
|
418
|
+
id: 'deferral-trigger',
|
|
419
|
+
severity: 'error',
|
|
420
|
+
description: 'A deferred brief names in "trigger" the observable event that brings it back, not a date.',
|
|
421
|
+
check: live(({ brief, config }) => {
|
|
422
|
+
// A trigger of the wrong shape is the field rule's finding.
|
|
423
|
+
if (brief.status !== 'deferred' || brief.problems.some((p) => p.field === 'trigger'))
|
|
424
|
+
return [];
|
|
425
|
+
const example = 'such as "when the second tenant signs" or "when p95 exceeds 200 ms"';
|
|
426
|
+
const trigger = brief.trigger?.trim() ?? '';
|
|
427
|
+
if (trigger === '') {
|
|
428
|
+
const declared = findEntry(brief.frontMatter, 'trigger') !== undefined;
|
|
429
|
+
return [
|
|
430
|
+
{
|
|
431
|
+
line: at(lineOfField(brief, declared ? 'trigger' : config.status.field)),
|
|
432
|
+
message: 'is deferred and names no "trigger"',
|
|
433
|
+
hint: `add "trigger: <the event that brings it back>", ${example}`,
|
|
434
|
+
},
|
|
435
|
+
];
|
|
436
|
+
}
|
|
437
|
+
const line = at(lineOfField(brief, 'trigger'));
|
|
438
|
+
const hint = `name what must happen before the work resumes, ${example}`;
|
|
439
|
+
if (config.placeholders.some((p) => p.toLowerCase() === trigger.toLowerCase())) {
|
|
440
|
+
return [{ line, message: `the trigger "${trigger}" is a placeholder, not an event`, hint }];
|
|
441
|
+
}
|
|
442
|
+
if (!namesAnEvent(trigger)) {
|
|
443
|
+
return [{ line, message: `the trigger "${trigger}" is a date or a time, not an event`, hint: `${hint}; a date arrives whether or not the reason for the work has` }];
|
|
444
|
+
}
|
|
445
|
+
return [];
|
|
446
|
+
}),
|
|
447
|
+
},
|
|
372
448
|
{
|
|
373
449
|
id: 'glob',
|
|
374
450
|
severity: 'error',
|
|
375
451
|
description: 'Every scope pattern is a glob spec-brief can read.',
|
|
376
|
-
check: live(({ brief }) =>
|
|
377
|
-
|
|
378
|
-
|
|
452
|
+
check: live(({ brief }) => SCOPE_FIELDS.flatMap((field) => brief[field].flatMap((pattern) => {
|
|
453
|
+
const parsed = parseGlob(pattern);
|
|
454
|
+
if (parsed.ok)
|
|
455
|
+
return [];
|
|
456
|
+
return [
|
|
457
|
+
{
|
|
458
|
+
line: at(lineOfField(brief, field)),
|
|
459
|
+
message: `"${pattern}" in ${field}: ${parsed.error}`,
|
|
460
|
+
hint: 'a scope is a glob relative to the repository root: "src/auth/", "src/**/*.ts", "docs/{a,b}.md"',
|
|
461
|
+
},
|
|
462
|
+
];
|
|
463
|
+
}))),
|
|
379
464
|
},
|
|
380
465
|
{
|
|
381
466
|
id: 'scope-contradiction',
|
|
382
467
|
severity: 'error',
|
|
383
|
-
description: '
|
|
384
|
-
check: live(({ brief, repoFiles }) =>
|
|
385
|
-
const affected = globsOf(brief.affectedFiles, repoFiles).filter((g) => g.glob !== null);
|
|
386
|
-
const protectedGlobs = globsOf(brief.protectedFiles, repoFiles).filter((g) => g.glob !== null);
|
|
387
|
-
const results = [];
|
|
388
|
-
for (const a of affected) {
|
|
389
|
-
for (const p of protectedGlobs) {
|
|
390
|
-
const witness = intersectGlobs(a.glob, p.glob);
|
|
391
|
-
if (witness === null)
|
|
392
|
-
continue;
|
|
393
|
-
results.push({
|
|
394
|
-
line: at(lineOfField(brief, 'protectedFiles')),
|
|
395
|
-
message: `"${a.pattern}" is in scope and "${p.pattern}" is protected, and both cover ${witness}`,
|
|
396
|
-
hint: 'narrow the scope, or the protection, so that no file is both',
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
return results;
|
|
401
|
-
}),
|
|
468
|
+
description: 'Something of every affected pattern is writable: no pattern lies wholly inside protectedFiles.',
|
|
469
|
+
check: live(({ brief, repoFiles }) => scopeContradiction(brief, repoFiles)),
|
|
402
470
|
},
|
|
403
471
|
{
|
|
404
472
|
id: 'glob-matches-nothing',
|
|
@@ -407,15 +475,50 @@ export const RULES = [
|
|
|
407
475
|
check: live(({ brief, repoFiles }) => {
|
|
408
476
|
if (repoFiles === null)
|
|
409
477
|
return [];
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
478
|
+
const reading = readingIn(repoFiles);
|
|
479
|
+
return SCOPE_FIELDS.flatMap((field) => patternsOf(brief[field], reading)
|
|
480
|
+
// A literal the tree does not hold is literal-read-as-file's finding.
|
|
481
|
+
.filter((p) => unheldLiterals(p.glob, repoFiles).length === 0 && !repoFiles.some((file) => matchGlob(p.glob, file)))
|
|
482
|
+
.map((p) => ({
|
|
413
483
|
line: at(lineOfField(brief, field)),
|
|
414
|
-
message: `"${
|
|
484
|
+
message: `"${p.pattern}" in ${field} matches no file in the tree`,
|
|
415
485
|
hint: 'expected when the round creates it; otherwise check the spelling',
|
|
416
486
|
})));
|
|
417
487
|
}),
|
|
418
488
|
},
|
|
489
|
+
{
|
|
490
|
+
id: 'literal-read-as-file',
|
|
491
|
+
severity: 'note',
|
|
492
|
+
description: 'A path with no glob syntax that the tree does not hold, and whose name has no extension, says whether it is a directory.',
|
|
493
|
+
check: live(({ brief, repoFiles }) => {
|
|
494
|
+
if (repoFiles === null)
|
|
495
|
+
return [];
|
|
496
|
+
const reading = readingIn(repoFiles);
|
|
497
|
+
return SCOPE_FIELDS.flatMap((field) => patternsOf(brief[field], reading).flatMap(({ pattern, glob }) => {
|
|
498
|
+
const paths = unheldLiterals(glob, repoFiles);
|
|
499
|
+
if (paths.length === 0)
|
|
500
|
+
return [];
|
|
501
|
+
const line = at(lineOfField(brief, field));
|
|
502
|
+
if (!isGlobSyntax(pattern)) {
|
|
503
|
+
return [
|
|
504
|
+
{
|
|
505
|
+
line,
|
|
506
|
+
message: `"${pattern}" in ${field} is not in the tree and is read as a file`,
|
|
507
|
+
// The reader trims every list item, so the pattern is as meant.
|
|
508
|
+
hint: `write "${pattern}/" for a directory`,
|
|
509
|
+
},
|
|
510
|
+
];
|
|
511
|
+
}
|
|
512
|
+
return [
|
|
513
|
+
{
|
|
514
|
+
line,
|
|
515
|
+
message: `"${pattern}" in ${field} names ${inWords(paths)}, which ${paths.length === 1 ? 'is' : 'are'} not in the tree and ${paths.length === 1 ? 'is' : 'are'} read as ${paths.length === 1 ? 'a file' : 'files'}`,
|
|
516
|
+
hint: 'write a directory with a trailing "/", in an entry of its own',
|
|
517
|
+
},
|
|
518
|
+
];
|
|
519
|
+
}));
|
|
520
|
+
}),
|
|
521
|
+
},
|
|
419
522
|
{
|
|
420
523
|
id: 'archive-freeze',
|
|
421
524
|
severity: 'error',
|
|
@@ -436,13 +539,40 @@ export const RULES = [
|
|
|
436
539
|
},
|
|
437
540
|
];
|
|
438
541
|
/**
|
|
439
|
-
* The rules the
|
|
440
|
-
* so they carry no check: they exist so configuration
|
|
441
|
-
* and reports can describe them.
|
|
542
|
+
* The rules the wave analyses - `matrix` and `schedule` - report under. They
|
|
543
|
+
* are not run per brief, so they carry no check: they exist so configuration
|
|
544
|
+
* can set their severity and reports can describe them.
|
|
442
545
|
*/
|
|
443
546
|
export const COLLISION_RULES = [
|
|
444
547
|
{ id: 'collision', severity: 'error', description: 'Briefs in one wave do not write the same file.' },
|
|
548
|
+
{
|
|
549
|
+
id: 'collision-undecided',
|
|
550
|
+
severity: 'warning',
|
|
551
|
+
description: 'Whether two briefs in one wave can write the same file is decided within the search budget.',
|
|
552
|
+
},
|
|
445
553
|
{ id: 'unscoped', severity: 'note', description: 'A brief sharing a wave declares the files it writes.' },
|
|
446
554
|
{ id: 'shared-directory', severity: 'off', description: 'Briefs in one wave do not write into the same directory.' },
|
|
555
|
+
{
|
|
556
|
+
id: 'wave-schedule',
|
|
557
|
+
severity: 'error',
|
|
558
|
+
description: 'A brief declares a wave that holds: the one the schedule computes, or one lint and matrix accept, which is a note.',
|
|
559
|
+
},
|
|
560
|
+
];
|
|
561
|
+
/**
|
|
562
|
+
* The rules archival reports under beside its refusals. Like the collision
|
|
563
|
+
* rules they carry no check: the planner raises them, and they exist so
|
|
564
|
+
* configuration can set their severity and reports can describe them.
|
|
565
|
+
*/
|
|
566
|
+
export const ARCHIVE_RULES = [
|
|
567
|
+
{
|
|
568
|
+
id: 'scope-unmeasured',
|
|
569
|
+
severity: 'warning',
|
|
570
|
+
description: 'A brief that declares a scope is archived against the changes its round made.',
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
id: 'stale-link',
|
|
574
|
+
severity: 'warning',
|
|
575
|
+
description: 'No live brief is left linking to where a moved brief used to be.',
|
|
576
|
+
},
|
|
447
577
|
];
|
|
448
578
|
//# sourceMappingURL=rules.js.map
|
package/dist/rules.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The built-in rules. Each is a pure function of one brief and the corpus\n * around it, and returns what it found with 1-based lines.\n *\n * Structure is checked on live briefs only. An archived brief is a frozen\n * record of the tree before a round, and holding it to today's schema would\n * ask somebody to edit what must not be edited; the rules that do reach the\n * archive are about identity and the freeze itself.\n */\n\nimport { BUILT_IN_FIELDS, type Brief, knownFields, lineOfField } from './brief.js';\nimport type { Config, SectionRule } from './config.js';\nimport { type Corpus, dependencyCycles, duplicateIds, idKey, resolveDependency } from './corpus.js';\nimport { integrityOf } from './integrity.js';\nimport { type Glob, intersectGlobs, matchGlob, parseGlob } from './glob.js';\nimport { hasContent, type Section } from './markdown.js';\nimport { closest } from './schema.js';\nimport { labelMatches } from './text.js';\nimport type { Severity, SeveritySetting } from './types.js';\n\nexport interface RuleResult {\n /** 1-based. */\n readonly line: number;\n readonly message: string;\n readonly hint?: string | undefined;\n /** Lower than the rule's severity for this one finding, as a draft's unwritten sections are. */\n readonly severity?: Severity | undefined;\n}\n\nexport interface RuleContext {\n readonly brief: Brief;\n readonly corpus: Corpus;\n readonly config: Config;\n /** The files git sees, or every file without git; `null` when unknown. A rule that needs them stays quiet without them. */\n readonly repoFiles: readonly string[] | null;\n /** Analyses computed once per run and shared by every brief. */\n readonly shared: SharedAnalysis;\n /** A plugin's options from the configuration. */\n readonly options?: unknown;\n}\n\n/** What a rule is called, how loud it is by default, and what it checks. */\nexport interface RuleInfo {\n readonly id: string;\n readonly severity: SeveritySetting;\n readonly description: string;\n}\n\nexport interface Rule extends RuleInfo {\n check(context: RuleContext): readonly RuleResult[] | Promise<readonly RuleResult[]>;\n}\n\nexport interface SharedAnalysis {\n readonly cycles: readonly (readonly Brief[])[];\n readonly duplicates: ReadonlyMap<string, readonly Brief[]>;\n}\n\nexport function analyse(corpus: Corpus): SharedAnalysis {\n return { cycles: dependencyCycles(corpus), duplicates: duplicateIds(corpus) };\n}\n\nconst at = (line0: number): number => line0 + 1;\n\n/** The sections a brief must carry: the configured ones, then its type's. */\nexport function sectionRules(brief: Brief, config: Config): SectionRule[] {\n const typed = brief.type === null ? [] : (config.types[brief.type] ?? []);\n return [...config.sections, ...typed];\n}\n\n/** The headings that fill a section rule. */\nexport function sectionsFilling(brief: Brief, rule: SectionRule): Section[] {\n const names = [rule.name, ...rule.aliases];\n return brief.sections.filter((s) => names.some((name) => labelMatches(s.heading.text, name)));\n}\n\n/** A section's own lines that carry content: comments dropped, sub-headings not counted. */\nfunction contentLines(brief: Brief, section: Section): number[] {\n const headingLines = new Set(brief.scan.headings.map((h) => h.line));\n const lines: number[] = [];\n for (let i = section.heading.line + 1; i < section.end; i += 1) {\n if (!headingLines.has(i) && hasContent(brief.scan.prose[i] as string)) lines.push(i);\n }\n return lines;\n}\n\nfunction isPlaceholder(line: string, placeholders: readonly string[]): boolean {\n const text = line\n .replace(/^\\s*(?:[-*+]|\\d{1,9}[.)])\\s+/, '')\n .replace(/^\\[[ xX]\\]\\s*/, '')\n .replace(/[*_]/g, '')\n .trim();\n if (text === '') return true;\n const lower = text.toLowerCase();\n return placeholders.some((p) => {\n const word = p.toLowerCase();\n return lower === word || (lower.startsWith(word) && /^[\\s:.\\-\\u2014]/.test(lower.slice(word.length)));\n });\n}\n\n/** Completeness findings on a draft are reminders, not failures. */\nfunction leniency(brief: Brief): Severity | undefined {\n return brief.status === 'draft' ? 'warning' : undefined;\n}\n\nfunction live(check: (context: RuleContext) => RuleResult[]): (context: RuleContext) => RuleResult[] {\n return (context) => (context.brief.phase === 'live' ? check(context) : []);\n}\n\nfunction globsOf(\n patterns: readonly string[],\n repoFiles: readonly string[] | null,\n): { pattern: string; glob: Glob | null; error: string | null }[] {\n const files = repoFiles === null ? null : new Set(repoFiles);\n return patterns.map((pattern) => {\n const parsed = parseGlob(pattern, { isFile: files === null ? undefined : (path) => files.has(path) });\n return parsed.ok ? { pattern, glob: parsed.glob, error: null } : { pattern, glob: null, error: parsed.error };\n });\n}\n\nexport const RULES: readonly Rule[] = [\n {\n id: 'front-matter',\n severity: 'error',\n description: 'Front matter parses, with no duplicate keys and no YAML beyond the supported subset.',\n check: ({ brief }) =>\n (brief.frontMatter?.problems ?? []).map((p) => ({\n line: at(p.line),\n message: p.message,\n hint: 'front matter is flat \"key: value\" lines, with lists as [a, b] or \"- item\" lines',\n })),\n },\n {\n id: 'field',\n severity: 'error',\n description: 'Fields spec-brief reads hold values of the right shape.',\n check: ({ brief }) => brief.problems.map((p) => ({ line: at(p.line), message: p.message })),\n },\n {\n id: 'unknown-field',\n severity: 'warning',\n description: 'Front-matter keys are ones spec-brief or the configuration knows.',\n check: ({ brief, config }) => {\n const known = knownFields(config);\n return (brief.frontMatter?.entries ?? [])\n .filter((entry) => !known.has(entry.name))\n .map((entry) => {\n const guess = closest(entry.key, BUILT_IN_FIELDS);\n return {\n line: at(entry.line),\n message: `\"${entry.key}\" is not a key spec-brief knows`,\n hint:\n guess === undefined\n ? `if the repository uses it, list it under \"fields\" in the configuration`\n : `did you mean \"${guess}\"? If not, list \"${entry.key}\" under \"fields\" in the configuration`,\n };\n });\n },\n },\n {\n id: 'status',\n severity: 'error',\n description: 'The status agrees with where the brief lives.',\n check: ({ brief, config }) => {\n const field = config.status.field;\n if (field === null) return [];\n const line = at(lineOfField(brief, field));\n const expected = brief.phase === 'archived' ? config.status.archived : config.status.active;\n if (brief.statusWord === null) {\n return [\n {\n line,\n message: `declares no \"${field}\"`,\n hint: `add \"${field}: ${expected}\" to the front matter`,\n },\n ];\n }\n if (brief.status === null) {\n const words = [config.status.draft, config.status.active, config.status.archived].filter((w) => w !== null);\n return [\n {\n line,\n message: `\"${brief.statusWord}\" is not a status here`,\n hint: `use one of ${words.map((w) => `\"${w}\"`).join(', ')}`,\n },\n ];\n }\n if (brief.phase === 'live' && brief.status === 'archived') {\n return [\n {\n line,\n message: `is marked \"${brief.statusWord}\" but lives in ${config.briefs}/`,\n hint: `run \"spec-brief archive ${brief.id ?? brief.name}\", which moves it and writes its banner`,\n },\n ];\n }\n if (brief.phase === 'archived' && brief.status !== 'archived') {\n return [\n {\n line,\n message: `lives in ${config.archive}/ but is marked \"${brief.statusWord}\"`,\n hint: `run \"spec-brief unarchive ${brief.id ?? brief.name}\" to reopen it, or set \"${field}: ${config.status.archived}\"`,\n },\n ];\n }\n return [];\n },\n },\n {\n id: 'id',\n severity: 'error',\n description: 'Every brief has an id with no whitespace or slashes in it.',\n check: ({ brief, config }) => {\n if (brief.id === null) {\n return [\n {\n line: 1,\n message: config.id.source === 'frontmatter' ? 'declares no \"id\"' : 'has no id in its file name',\n hint: config.id.source === 'frontmatter' ? 'add \"id: <id>\" to the front matter' : undefined,\n },\n ];\n }\n if (/[\\s/\\\\]/.test(brief.id)) return [{ line: 1, message: `the id \"${brief.id}\" contains whitespace or a slash` }];\n return [];\n },\n },\n {\n id: 'duplicate-id',\n severity: 'error',\n description: 'No two briefs share an id, live or archived.',\n check: ({ brief, shared }) => {\n if (brief.id === null) return [];\n const group = shared.duplicates.get(idKey(brief.id));\n if (group === undefined || group[0] === brief) return [];\n return [\n {\n line: 1,\n message: `the id \"${brief.id}\" is also ${(group[0] as Brief).file}'s`,\n hint: 'ids are allocated once and never reused; give this brief the next free one',\n },\n ];\n },\n },\n {\n id: 'title',\n severity: 'warning',\n description: 'A brief has a title: a \"title\" field or a level-one heading.',\n check: live(({ brief }) =>\n brief.title === null ? [{ line: at(brief.bodyStart), message: 'has no title', hint: 'start the body with \"# <title>\"' }] : [],\n ),\n },\n {\n id: 'unknown-type',\n severity: 'error',\n description: 'A declared type is one the configuration defines.',\n check: live(({ brief, config }) => {\n if (brief.type === null || config.types[brief.type] !== undefined) return [];\n const names = Object.keys(config.types);\n return [\n {\n line: at(lineOfField(brief, 'type')),\n message: `\"${brief.type}\" is not a brief type here`,\n hint: names.length === 0 ? 'the configuration defines no types' : `use one of ${names.map((n) => `\"${n}\"`).join(', ')}`,\n },\n ];\n }),\n },\n {\n id: 'missing-section',\n severity: 'error',\n description: 'Every required section is present.',\n check: live(({ brief, config }) =>\n sectionRules(brief, config)\n .filter((rule) => !rule.optional && sectionsFilling(brief, rule).length === 0)\n .map((rule) => ({\n line: at(brief.bodyStart),\n message: `has no \"${rule.name}\" section`,\n hint:\n rule.aliases.length === 0\n ? `add a \"## ${rule.name}\" heading`\n : `add a \"## ${rule.name}\" heading (also accepted: ${rule.aliases.map((a) => `\"${a}\"`).join(', ')})`,\n })),\n ),\n },\n {\n id: 'duplicate-section',\n severity: 'warning',\n description: 'A section appears once.',\n check: live(({ brief, config }) =>\n sectionRules(brief, config).flatMap((rule) =>\n sectionsFilling(brief, rule)\n .slice(1)\n .map((s) => ({ line: at(s.heading.line), message: `a second \"${rule.name}\" section`, hint: 'merge the two' })),\n ),\n ),\n },\n {\n id: 'empty-section',\n severity: 'error',\n description: 'A section carries content; a comment alone is not content.',\n check: live(({ brief, config }) =>\n sectionRules(brief, config).flatMap((rule) =>\n sectionsFilling(brief, rule)\n .slice(0, 1)\n .filter((s) => contentLines(brief, s).length === 0)\n .map((s) => ({ line: at(s.heading.line), message: `the \"${rule.name}\" section is empty`, severity: leniency(brief) })),\n ),\n ),\n },\n {\n id: 'placeholder',\n severity: 'error',\n description: 'A section says something: not only \"TBD\", \"TODO\" or an empty box.',\n check: live(({ brief, config }) =>\n sectionRules(brief, config).flatMap((rule) =>\n sectionsFilling(brief, rule)\n .slice(0, 1)\n .filter((s) => {\n const lines = contentLines(brief, s);\n return lines.length > 0 && lines.every((i) => isPlaceholder(brief.scan.prose[i] as string, config.placeholders));\n })\n .map((s) => ({\n line: at(s.heading.line),\n message: `the \"${rule.name}\" section holds only a placeholder`,\n severity: leniency(brief),\n })),\n ),\n ),\n },\n {\n id: 'missing-checklist',\n severity: 'error',\n description: 'A checklist section holds at least one task item.',\n check: live(({ brief, config }) =>\n sectionRules(brief, config)\n .filter((rule) => rule.checklist)\n .flatMap((rule) =>\n sectionsFilling(brief, rule)\n .slice(0, 1)\n .filter((s) => !brief.tasks.some((t) => t.line > s.heading.line && t.line < s.end))\n .map((s) => ({\n line: at(s.heading.line),\n message: `the \"${rule.name}\" section has no \"- [ ]\" items`,\n hint: 'write each check as a task item, so it has a state that can be closed',\n severity: leniency(brief),\n })),\n ),\n ),\n },\n {\n id: 'must-contain',\n severity: 'error',\n description: 'A section contains the text the configuration requires of it.',\n check: live(({ brief, config }) =>\n sectionRules(brief, config).flatMap((rule) => {\n const section = sectionsFilling(brief, rule)[0];\n if (section === undefined) return [];\n const body = brief.scan.prose.slice(section.heading.line + 1, section.end).join('\\n');\n return rule.mustContain\n .filter((text) => !body.includes(text))\n .map((text) => ({ line: at(section.heading.line), message: `the \"${rule.name}\" section must contain \"${text}\"` }));\n }),\n ),\n },\n {\n id: 'section-order',\n severity: 'error',\n description: 'Sections appear in the configured order, when the configuration asks for one.',\n check: live(({ brief, config }) => {\n if (!config.sectionOrder) return [];\n const placed = sectionRules(brief, config)\n .map((rule) => ({ rule, section: sectionsFilling(brief, rule)[0] }))\n .filter((p): p is { rule: SectionRule; section: Section } => p.section !== undefined);\n for (let i = 1; i < placed.length; i += 1) {\n const before = placed[i - 1] as { rule: SectionRule; section: Section };\n const current = placed[i] as { rule: SectionRule; section: Section };\n if (current.section.heading.line < before.section.heading.line) {\n return [\n {\n line: at(current.section.heading.line),\n message: `\"${current.rule.name}\" comes before \"${before.rule.name}\"`,\n hint: `the order is ${sectionRules(brief, config)\n .map((r) => r.name)\n .join(', ')}`,\n },\n ];\n }\n }\n return [];\n }),\n },\n {\n id: 'dependency',\n severity: 'error',\n description: 'Every dependency names another brief that exists.',\n check: live(({ brief, corpus }) =>\n brief.dependsOn.flatMap((dependency) => {\n const line = at(lineOfField(brief, 'dependsOn'));\n if (brief.id !== null && idKey(dependency) === idKey(brief.id)) {\n return [{ line, message: 'depends on itself' }];\n }\n if (resolveDependency(corpus, dependency) === undefined) {\n return [{ line, message: `depends on \"${dependency}\", which is not a brief here` }];\n }\n return [];\n }),\n ),\n },\n {\n id: 'dependency-cycle',\n severity: 'error',\n description: 'Dependencies between live briefs form no cycle.',\n check: live(({ brief, shared }) =>\n shared.cycles\n .filter((cycle) => cycle[0] === brief)\n .map((cycle) => ({\n line: at(lineOfField(brief, 'dependsOn')),\n message: `dependencies form a cycle: ${cycle.map((b) => b.id ?? b.name).join(' -> ')}`,\n hint: 'a cycle can never become ready; remove the dependency that is not real',\n })),\n ),\n },\n {\n id: 'wave-order',\n severity: 'error',\n description: 'A dependency that is still live runs in an earlier wave.',\n check: live(({ brief, corpus }) => {\n const wave = brief.wave;\n if (wave === null) return [];\n return brief.dependsOn.flatMap((dependency) => {\n const target = resolveDependency(corpus, dependency);\n // A self-dependency is reported once, by the dependency rule.\n if (target === undefined || target === brief || target.phase !== 'live' || target.wave === null || target.wave < wave) return [];\n return [\n {\n line: at(lineOfField(brief, 'dependsOn')),\n message: `depends on ${target.id ?? target.name}, which is in wave ${target.wave}, not before wave ${wave}`,\n hint: `move this brief to a wave after ${target.wave}, or ${target.id ?? target.name} to one before ${wave}`,\n },\n ];\n });\n }),\n },\n {\n id: 'glob',\n severity: 'error',\n description: 'Every scope pattern is a glob spec-brief can read.',\n check: live(({ brief }) =>\n (['affectedFiles', 'protectedFiles'] as const).flatMap((field) =>\n globsOf(brief[field], null)\n .filter((g) => g.error !== null)\n .map((g) => ({ line: at(lineOfField(brief, field)), message: `\"${g.pattern}\" in ${field}: ${g.error as string}` })),\n ),\n ),\n },\n {\n id: 'scope-contradiction',\n severity: 'error',\n description: 'No file is both in scope and protected.',\n check: live(({ brief, repoFiles }) => {\n const affected = globsOf(brief.affectedFiles, repoFiles).filter((g) => g.glob !== null);\n const protectedGlobs = globsOf(brief.protectedFiles, repoFiles).filter((g) => g.glob !== null);\n const results: RuleResult[] = [];\n for (const a of affected) {\n for (const p of protectedGlobs) {\n const witness = intersectGlobs(a.glob as Glob, p.glob as Glob);\n if (witness === null) continue;\n results.push({\n line: at(lineOfField(brief, 'protectedFiles')),\n message: `\"${a.pattern}\" is in scope and \"${p.pattern}\" is protected, and both cover ${witness}`,\n hint: 'narrow the scope, or the protection, so that no file is both',\n });\n }\n }\n return results;\n }),\n },\n {\n id: 'glob-matches-nothing',\n severity: 'note',\n description: 'A scope pattern matches at least one file in the tree.',\n check: live(({ brief, repoFiles }) => {\n if (repoFiles === null) return [];\n return (['affectedFiles', 'protectedFiles'] as const).flatMap((field) =>\n globsOf(brief[field], repoFiles)\n .filter((g) => g.glob !== null && !repoFiles.some((file) => matchGlob(g.glob as Glob, file)))\n .map((g) => ({\n line: at(lineOfField(brief, field)),\n message: `\"${g.pattern}\" in ${field} matches no file in the tree`,\n hint: 'expected when the round creates it; otherwise check the spelling',\n })),\n );\n }),\n },\n {\n id: 'archive-freeze',\n severity: 'error',\n description: 'An archived brief has not changed since it was archived.',\n check: ({ brief }) => {\n if (brief.phase !== 'archived' || brief.integrity === null) return [];\n if (integrityOf(brief.text) === brief.integrity) return [];\n return [\n {\n line: at(lineOfField(brief, 'integrity')),\n message: 'has changed since it was archived',\n hint: 'an archived brief is frozen: restore it from git, or unarchive it to reopen the round',\n },\n ];\n },\n },\n];\n\n/**\n * The rules the collision analysis reports under. They are not run per brief,\n * so they carry no check: they exist so configuration can set their severity\n * and reports can describe them.\n */\nexport const COLLISION_RULES: readonly RuleInfo[] = [\n { id: 'collision', severity: 'error', description: 'Briefs in one wave do not write the same file.' },\n { id: 'unscoped', severity: 'note', description: 'A brief sharing a wave declares the files it writes.' },\n { id: 'shared-directory', severity: 'off', description: 'Briefs in one wave do not write into the same directory.' },\n];\n"]}
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The built-in rules. Each is a pure function of one brief and the corpus\n * around it, and returns what it found with 1-based lines.\n *\n * Structure is checked on live briefs only. An archived brief is a frozen\n * record of the tree before a round, and holding it to today's schema would\n * ask somebody to edit what must not be edited; the rules that do reach the\n * archive are about identity and the freeze itself.\n */\n\nimport { BUILT_IN_FIELDS, type Brief, knownFields, lineOfField } from './brief.js';\nimport { type Config, type SectionRule, statusWords } from './config.js';\nimport { type Corpus, dependencyCycles, duplicateIds, idKey, resolveDependency } from './corpus.js';\nimport { findEntry } from './frontmatter.js';\nimport { integrityOf } from './integrity.js';\nimport { type Glob, hasExtension, held, isGlobSyntax, matchGlob, parseGlob, readingIn, treeOf, WITNESS_BUDGET } from './glob.js';\nimport { hasContent, type Section } from './markdown.js';\nimport { closest } from './schema.js';\nimport { contradictions, patternsOf, scopeOf, type ScopePattern } from './scope.js';\nimport { inWords, labelMatches } from './text.js';\nimport { namesAnEvent } from './trigger.js';\nimport type { Severity, SeveritySetting } from './types.js';\n\nexport interface RuleResult {\n /** 1-based. */\n readonly line: number;\n readonly message: string;\n readonly hint?: string | undefined;\n /** Lower than the rule's severity for this one finding, as a draft's unwritten sections are. */\n readonly severity?: Severity | undefined;\n}\n\nexport interface RuleContext {\n readonly brief: Brief;\n readonly corpus: Corpus;\n readonly config: Config;\n /** The files git sees, or every file without git; `null` when unknown. A rule that needs them stays quiet without them. */\n readonly repoFiles: readonly string[] | null;\n /** Analyses computed once per run and shared by every brief. */\n readonly shared: SharedAnalysis;\n /** A plugin's options from the configuration. */\n readonly options?: unknown;\n}\n\n/** What a rule is called, how loud it is by default, and what it checks. */\nexport interface RuleInfo {\n readonly id: string;\n readonly severity: SeveritySetting;\n readonly description: string;\n}\n\nexport interface Rule extends RuleInfo {\n check(context: RuleContext): readonly RuleResult[] | Promise<readonly RuleResult[]>;\n}\n\nexport interface SharedAnalysis {\n readonly cycles: readonly (readonly Brief[])[];\n readonly duplicates: ReadonlyMap<string, readonly Brief[]>;\n}\n\nexport function analyse(corpus: Corpus): SharedAnalysis {\n return { cycles: dependencyCycles(corpus), duplicates: duplicateIds(corpus) };\n}\n\nconst at = (line0: number): number => line0 + 1;\n\n/** The sections a brief must carry: the configured ones, then its type's. */\nexport function sectionRules(brief: Brief, config: Config): SectionRule[] {\n const typed = brief.type === null ? [] : (config.types[brief.type] ?? []);\n return [...config.sections, ...typed];\n}\n\n/** The headings that fill a section rule. */\nexport function sectionsFilling(brief: Brief, rule: SectionRule): Section[] {\n const names = [rule.name, ...rule.aliases];\n return brief.sections.filter((s) => names.some((name) => labelMatches(s.heading.text, name)));\n}\n\n/** A section's own lines that carry content: comments dropped, sub-headings not counted. */\nfunction contentLines(brief: Brief, section: Section): number[] {\n const headingLines = new Set(brief.scan.headings.map((h) => h.line));\n const lines: number[] = [];\n for (let i = section.heading.line + 1; i < section.end; i += 1) {\n if (!headingLines.has(i) && hasContent(brief.scan.prose[i] as string)) lines.push(i);\n }\n return lines;\n}\n\nfunction isPlaceholder(line: string, placeholders: readonly string[]): boolean {\n const text = line\n .replace(/^\\s*(?:[-*+]|\\d{1,9}[.)])\\s+/, '')\n .replace(/^\\[[ xX]\\]\\s*/, '')\n .replace(/[*_]/g, '')\n .trim();\n if (text === '') return true;\n const lower = text.toLowerCase();\n return placeholders.some((p) => {\n const word = p.toLowerCase();\n return lower === word || (lower.startsWith(word) && /^[\\s:.\\-\\u2014]/.test(lower.slice(word.length)));\n });\n}\n\n/** Completeness findings on a draft are reminders, not failures. */\nfunction leniency(brief: Brief): Severity | undefined {\n return brief.status === 'draft' ? 'warning' : undefined;\n}\n\nfunction live(check: (context: RuleContext) => RuleResult[]): (context: RuleContext) => RuleResult[] {\n return (context) => (context.brief.phase === 'live' ? check(context) : []);\n}\n\nconst SCOPE_FIELDS = ['affectedFiles', 'protectedFiles'] as const;\n\nconst quoted = (items: readonly string[]): string => inWords(items.map((item) => `\"${item}\"`));\n\n/**\n * The paths a pattern names with no glob syntax that the tree does not hold\n * and whose names carry no extension. The tree reads each as a file, and the\n * spelling cannot say whether a directory was meant.\n */\nexport function unheldLiterals(glob: Glob, files: readonly string[]): string[] {\n const tree = treeOf(files);\n return glob.literals\n .map((literal) => literal.path)\n .filter((path) => held(tree, path) === null && !hasExtension(path.slice(path.lastIndexOf('/') + 1)));\n}\n\n/**\n * Affected patterns a brief's own protections cover entirely: one error for\n * all of them, and a warning for those the search could not decide.\n */\nexport function scopeContradiction(brief: Brief, repoFiles: readonly string[] | null, budget: number = WITNESS_BUDGET): RuleResult[] {\n const { covered, undecided } = contradictions(scopeOf(brief, readingIn(repoFiles)), budget);\n const line = at(lineOfField(brief, 'protectedFiles'));\n const results: RuleResult[] = [];\n if (covered.length > 0) {\n const one = covered.length === 1;\n results.push({\n line,\n message: `${quoted(covered)} in affectedFiles ${one ? 'is' : 'are'} entirely protected, so nothing of ${one ? 'it' : 'them'} is writable`,\n hint: `drop ${one ? 'it' : 'them'} from affectedFiles, or narrow protectedFiles so that some of ${one ? 'it' : 'each'} is writable`,\n });\n }\n if (undecided.length > 0) {\n results.push({\n line,\n message: `whether ${quoted(undecided)} in affectedFiles ${undecided.length === 1 ? 'is' : 'are'} entirely protected is undecided: the search met its budget`,\n hint: 'simplify the patterns until the question can be answered',\n severity: 'warning',\n });\n }\n return results;\n}\n\nexport const RULES: readonly Rule[] = [\n {\n id: 'front-matter',\n severity: 'error',\n description: 'Front matter parses, with no duplicate keys and no YAML beyond the supported subset.',\n check: ({ brief }) =>\n (brief.frontMatter?.problems ?? []).map((p) => ({\n line: at(p.line),\n message: p.message,\n hint: 'front matter is flat \"key: value\" lines, with lists as [a, b] or \"- item\" lines',\n })),\n },\n {\n id: 'field',\n severity: 'error',\n description: 'Fields spec-brief reads hold values of the right shape.',\n check: ({ brief }) => brief.problems.map((p) => ({ line: at(p.line), message: p.message })),\n },\n {\n id: 'unknown-field',\n severity: 'warning',\n description: 'Front-matter keys are ones spec-brief or the configuration knows.',\n check: ({ brief, config }) => {\n const known = knownFields(config);\n return (brief.frontMatter?.entries ?? [])\n .filter((entry) => !known.has(entry.name))\n .map((entry) => {\n const guess = closest(entry.key, BUILT_IN_FIELDS);\n return {\n line: at(entry.line),\n message: `\"${entry.key}\" is not a key spec-brief knows`,\n hint:\n guess === undefined\n ? `if the repository uses it, list it under \"fields\" in the configuration`\n : `did you mean \"${guess}\"? If not, list \"${entry.key}\" under \"fields\" in the configuration`,\n };\n });\n },\n },\n {\n id: 'status',\n severity: 'error',\n description: 'The status agrees with where the brief lives.',\n check: ({ brief, config }) => {\n const field = config.status.field;\n if (field === null) return [];\n const line = at(lineOfField(brief, field));\n const expected = brief.phase === 'archived' ? config.status.archived : config.status.active;\n if (brief.statusWord === null) {\n return [\n {\n line,\n message: `declares no \"${field}\"`,\n hint: `add \"${field}: ${expected}\" to the front matter`,\n },\n ];\n }\n if (brief.status === null) {\n const words = statusWords(config);\n return [\n {\n line,\n message: `\"${brief.statusWord}\" is not a status here`,\n hint: `use one of ${words.map((w) => `\"${w}\"`).join(', ')}`,\n },\n ];\n }\n if (brief.phase === 'live' && brief.status === 'archived') {\n return [\n {\n line,\n message: `is marked \"${brief.statusWord}\" but lives in ${config.briefs}/`,\n hint: `run \"spec-brief archive ${brief.id ?? brief.name}\", which moves it and writes its banner`,\n },\n ];\n }\n if (brief.phase === 'archived' && brief.status !== 'archived') {\n return [\n {\n line,\n message: `lives in ${config.archive}/ but is marked \"${brief.statusWord}\"`,\n hint: `run \"spec-brief unarchive ${brief.id ?? brief.name}\" to reopen it, or set \"${field}: ${config.status.archived}\"`,\n },\n ];\n }\n return [];\n },\n },\n {\n id: 'id',\n severity: 'error',\n description: 'Every brief has an id with no whitespace or slashes in it.',\n check: ({ brief, config }) => {\n if (brief.id === null) {\n return [\n {\n line: 1,\n message: config.id.source === 'frontmatter' ? 'declares no \"id\"' : 'has no id in its file name',\n hint: config.id.source === 'frontmatter' ? 'add \"id: <id>\" to the front matter' : undefined,\n },\n ];\n }\n if (/[\\s/\\\\]/.test(brief.id)) return [{ line: 1, message: `the id \"${brief.id}\" contains whitespace or a slash` }];\n return [];\n },\n },\n {\n id: 'duplicate-id',\n severity: 'error',\n description: 'No two briefs share an id, live or archived.',\n check: ({ brief, shared }) => {\n if (brief.id === null) return [];\n const group = shared.duplicates.get(idKey(brief.id));\n if (group === undefined || group[0] === brief) return [];\n return [\n {\n line: 1,\n message: `the id \"${brief.id}\" is also ${(group[0] as Brief).file}'s`,\n hint: 'ids are allocated once and never reused; give this brief the next free one',\n },\n ];\n },\n },\n {\n id: 'title',\n severity: 'warning',\n description: 'A brief has a title: a \"title\" field or a level-one heading.',\n check: live(({ brief }) =>\n brief.title === null ? [{ line: at(brief.bodyStart), message: 'has no title', hint: 'start the body with \"# <title>\"' }] : [],\n ),\n },\n {\n id: 'unknown-type',\n severity: 'error',\n description: 'A declared type is one the configuration defines.',\n check: live(({ brief, config }) => {\n if (brief.type === null || config.types[brief.type] !== undefined) return [];\n const names = Object.keys(config.types);\n return [\n {\n line: at(lineOfField(brief, 'type')),\n message: `\"${brief.type}\" is not a brief type here`,\n hint: names.length === 0 ? 'the configuration defines no types' : `use one of ${names.map((n) => `\"${n}\"`).join(', ')}`,\n },\n ];\n }),\n },\n {\n id: 'missing-section',\n severity: 'error',\n description: 'Every required section is present.',\n check: live(({ brief, config }) =>\n sectionRules(brief, config)\n .filter((rule) => !rule.optional && sectionsFilling(brief, rule).length === 0)\n .map((rule) => {\n const heading =\n rule.aliases.length === 0\n ? `add a \"## ${rule.name}\" heading`\n : `add a \"## ${rule.name}\" heading (also accepted: ${rule.aliases.map((a) => `\"${a}\"`).join(', ')})`;\n return {\n line: at(brief.bodyStart),\n message: `has no \"${rule.name}\" section`,\n hint: rule.hint === undefined ? heading : `${heading}. ${rule.hint}`,\n };\n }),\n ),\n },\n {\n id: 'duplicate-section',\n severity: 'warning',\n description: 'A section appears once.',\n check: live(({ brief, config }) =>\n sectionRules(brief, config).flatMap((rule) =>\n sectionsFilling(brief, rule)\n .slice(1)\n .map((s) => ({ line: at(s.heading.line), message: `a second \"${rule.name}\" section`, hint: 'merge the two' })),\n ),\n ),\n },\n {\n id: 'empty-section',\n severity: 'error',\n description: 'A section carries content; a comment alone is not content.',\n check: live(({ brief, config }) =>\n sectionRules(brief, config).flatMap((rule) =>\n sectionsFilling(brief, rule)\n .slice(0, 1)\n .filter((s) => contentLines(brief, s).length === 0)\n .map((s) => ({\n line: at(s.heading.line),\n message: `the \"${rule.name}\" section is empty`,\n hint: rule.hint ?? 'write it; a comment alone is not content',\n severity: leniency(brief),\n })),\n ),\n ),\n },\n {\n id: 'placeholder',\n severity: 'error',\n description: 'A section says something: not only \"TBD\", \"TODO\" or an empty box.',\n check: live(({ brief, config }) =>\n sectionRules(brief, config).flatMap((rule) =>\n sectionsFilling(brief, rule)\n .slice(0, 1)\n .filter((s) => {\n const lines = contentLines(brief, s);\n return lines.length > 0 && lines.every((i) => isPlaceholder(brief.scan.prose[i] as string, config.placeholders));\n })\n .map((s) => ({\n line: at(s.heading.line),\n message: `the \"${rule.name}\" section holds only a placeholder`,\n hint: rule.hint ?? 'replace the placeholder with what the section must say',\n severity: leniency(brief),\n })),\n ),\n ),\n },\n {\n id: 'missing-checklist',\n severity: 'error',\n description: 'A checklist section holds at least one task item.',\n check: live(({ brief, config }) =>\n sectionRules(brief, config)\n .filter((rule) => rule.checklist)\n .flatMap((rule) =>\n sectionsFilling(brief, rule)\n .slice(0, 1)\n .filter((s) => !brief.tasks.some((t) => t.line > s.heading.line && t.line < s.end))\n .map((s) => ({\n line: at(s.heading.line),\n message: `the \"${rule.name}\" section has no \"- [ ]\" items`,\n hint: 'write each check as a task item, so it has a state that can be closed',\n severity: leniency(brief),\n })),\n ),\n ),\n },\n {\n id: 'must-contain',\n severity: 'error',\n description: 'A section contains the text the configuration requires of it.',\n check: live(({ brief, config }) =>\n sectionRules(brief, config).flatMap((rule) => {\n const section = sectionsFilling(brief, rule)[0];\n if (section === undefined) return [];\n const body = brief.scan.prose.slice(section.heading.line + 1, section.end).join('\\n');\n return rule.mustContain\n .filter((text) => !body.includes(text))\n .map((text) => ({ line: at(section.heading.line), message: `the \"${rule.name}\" section must contain \"${text}\"` }));\n }),\n ),\n },\n {\n id: 'section-order',\n severity: 'error',\n description: 'Sections appear in the configured order, when the configuration asks for one.',\n check: live(({ brief, config }) => {\n if (!config.sectionOrder) return [];\n const placed = sectionRules(brief, config)\n .map((rule) => ({ rule, section: sectionsFilling(brief, rule)[0] }))\n .filter((p): p is { rule: SectionRule; section: Section } => p.section !== undefined);\n for (let i = 1; i < placed.length; i += 1) {\n const before = placed[i - 1] as { rule: SectionRule; section: Section };\n const current = placed[i] as { rule: SectionRule; section: Section };\n if (current.section.heading.line < before.section.heading.line) {\n return [\n {\n line: at(current.section.heading.line),\n message: `\"${current.rule.name}\" comes before \"${before.rule.name}\"`,\n hint: `the order is ${sectionRules(brief, config)\n .map((r) => r.name)\n .join(', ')}`,\n },\n ];\n }\n }\n return [];\n }),\n },\n {\n id: 'dependency',\n severity: 'error',\n description: 'Every dependency names another brief that exists.',\n check: live(({ brief, corpus }) =>\n brief.dependsOn.flatMap((dependency) => {\n const line = at(lineOfField(brief, 'dependsOn'));\n if (brief.id !== null && idKey(dependency) === idKey(brief.id)) {\n return [{ line, message: 'depends on itself' }];\n }\n if (resolveDependency(corpus, dependency) === undefined) {\n return [{ line, message: `depends on \"${dependency}\", which is not a brief here` }];\n }\n return [];\n }),\n ),\n },\n {\n id: 'dependency-cycle',\n severity: 'error',\n description: 'Dependencies between live briefs form no cycle.',\n check: live(({ brief, shared }) =>\n shared.cycles\n .filter((cycle) => cycle[0] === brief)\n .map((cycle) => ({\n line: at(lineOfField(brief, 'dependsOn')),\n message: `dependencies form a cycle: ${cycle.map((b) => b.id ?? b.name).join(' -> ')}`,\n hint: 'a cycle can never become ready; remove the dependency that is not real',\n })),\n ),\n },\n {\n id: 'wave-order',\n severity: 'error',\n description: 'A dependency that is still live runs in an earlier wave.',\n check: live(({ brief, corpus }) => {\n const wave = brief.wave;\n if (wave === null) return [];\n return brief.dependsOn.flatMap((dependency) => {\n const target = resolveDependency(corpus, dependency);\n // A self-dependency is reported once, by the dependency rule.\n if (target === undefined || target === brief || target.phase !== 'live' || target.wave === null || target.wave < wave) return [];\n return [\n {\n line: at(lineOfField(brief, 'dependsOn')),\n message: `depends on ${target.id ?? target.name}, which is in wave ${target.wave}, not before wave ${wave}`,\n hint: `move this brief to a wave after ${target.wave}, or ${target.id ?? target.name} to one before ${wave}`,\n },\n ];\n });\n }),\n },\n {\n id: 'deferral-trigger',\n severity: 'error',\n description: 'A deferred brief names in \"trigger\" the observable event that brings it back, not a date.',\n check: live(({ brief, config }) => {\n // A trigger of the wrong shape is the field rule's finding.\n if (brief.status !== 'deferred' || brief.problems.some((p) => p.field === 'trigger')) return [];\n const example = 'such as \"when the second tenant signs\" or \"when p95 exceeds 200 ms\"';\n const trigger = brief.trigger?.trim() ?? '';\n if (trigger === '') {\n const declared = findEntry(brief.frontMatter, 'trigger') !== undefined;\n return [\n {\n line: at(lineOfField(brief, declared ? 'trigger' : (config.status.field as string))),\n message: 'is deferred and names no \"trigger\"',\n hint: `add \"trigger: <the event that brings it back>\", ${example}`,\n },\n ];\n }\n const line = at(lineOfField(brief, 'trigger'));\n const hint = `name what must happen before the work resumes, ${example}`;\n if (config.placeholders.some((p) => p.toLowerCase() === trigger.toLowerCase())) {\n return [{ line, message: `the trigger \"${trigger}\" is a placeholder, not an event`, hint }];\n }\n if (!namesAnEvent(trigger)) {\n return [{ line, message: `the trigger \"${trigger}\" is a date or a time, not an event`, hint: `${hint}; a date arrives whether or not the reason for the work has` }];\n }\n return [];\n }),\n },\n {\n id: 'glob',\n severity: 'error',\n description: 'Every scope pattern is a glob spec-brief can read.',\n check: live(({ brief }) =>\n SCOPE_FIELDS.flatMap((field) =>\n brief[field].flatMap((pattern) => {\n const parsed = parseGlob(pattern);\n if (parsed.ok) return [];\n return [\n {\n line: at(lineOfField(brief, field)),\n message: `\"${pattern}\" in ${field}: ${parsed.error}`,\n hint: 'a scope is a glob relative to the repository root: \"src/auth/\", \"src/**/*.ts\", \"docs/{a,b}.md\"',\n },\n ];\n }),\n ),\n ),\n },\n {\n id: 'scope-contradiction',\n severity: 'error',\n description: 'Something of every affected pattern is writable: no pattern lies wholly inside protectedFiles.',\n check: live(({ brief, repoFiles }) => scopeContradiction(brief, repoFiles)),\n },\n {\n id: 'glob-matches-nothing',\n severity: 'note',\n description: 'A scope pattern matches at least one file in the tree.',\n check: live(({ brief, repoFiles }) => {\n if (repoFiles === null) return [];\n const reading = readingIn(repoFiles);\n return SCOPE_FIELDS.flatMap((field) =>\n patternsOf(brief[field], reading)\n // A literal the tree does not hold is literal-read-as-file's finding.\n .filter((p) => unheldLiterals(p.glob, repoFiles).length === 0 && !repoFiles.some((file) => matchGlob(p.glob, file)))\n .map((p) => ({\n line: at(lineOfField(brief, field)),\n message: `\"${p.pattern}\" in ${field} matches no file in the tree`,\n hint: 'expected when the round creates it; otherwise check the spelling',\n })),\n );\n }),\n },\n {\n id: 'literal-read-as-file',\n severity: 'note',\n description: 'A path with no glob syntax that the tree does not hold, and whose name has no extension, says whether it is a directory.',\n check: live(({ brief, repoFiles }) => {\n if (repoFiles === null) return [];\n const reading = readingIn(repoFiles);\n return SCOPE_FIELDS.flatMap((field) =>\n patternsOf(brief[field], reading).flatMap(({ pattern, glob }: ScopePattern) => {\n const paths = unheldLiterals(glob, repoFiles);\n if (paths.length === 0) return [];\n const line = at(lineOfField(brief, field));\n if (!isGlobSyntax(pattern)) {\n return [\n {\n line,\n message: `\"${pattern}\" in ${field} is not in the tree and is read as a file`,\n // The reader trims every list item, so the pattern is as meant.\n hint: `write \"${pattern}/\" for a directory`,\n },\n ];\n }\n return [\n {\n line,\n message: `\"${pattern}\" in ${field} names ${inWords(paths)}, which ${paths.length === 1 ? 'is' : 'are'} not in the tree and ${paths.length === 1 ? 'is' : 'are'} read as ${paths.length === 1 ? 'a file' : 'files'}`,\n hint: 'write a directory with a trailing \"/\", in an entry of its own',\n },\n ];\n }),\n );\n }),\n },\n {\n id: 'archive-freeze',\n severity: 'error',\n description: 'An archived brief has not changed since it was archived.',\n check: ({ brief }) => {\n if (brief.phase !== 'archived' || brief.integrity === null) return [];\n if (integrityOf(brief.text) === brief.integrity) return [];\n return [\n {\n line: at(lineOfField(brief, 'integrity')),\n message: 'has changed since it was archived',\n hint: 'an archived brief is frozen: restore it from git, or unarchive it to reopen the round',\n },\n ];\n },\n },\n];\n\n/**\n * The rules the wave analyses - `matrix` and `schedule` - report under. They\n * are not run per brief, so they carry no check: they exist so configuration\n * can set their severity and reports can describe them.\n */\nexport const COLLISION_RULES: readonly RuleInfo[] = [\n { id: 'collision', severity: 'error', description: 'Briefs in one wave do not write the same file.' },\n {\n id: 'collision-undecided',\n severity: 'warning',\n description: 'Whether two briefs in one wave can write the same file is decided within the search budget.',\n },\n { id: 'unscoped', severity: 'note', description: 'A brief sharing a wave declares the files it writes.' },\n { id: 'shared-directory', severity: 'off', description: 'Briefs in one wave do not write into the same directory.' },\n {\n id: 'wave-schedule',\n severity: 'error',\n description: 'A brief declares a wave that holds: the one the schedule computes, or one lint and matrix accept, which is a note.',\n },\n];\n\n/**\n * The rules archival reports under beside its refusals. Like the collision\n * rules they carry no check: the planner raises them, and they exist so\n * configuration can set their severity and reports can describe them.\n */\nexport const ARCHIVE_RULES: readonly RuleInfo[] = [\n {\n id: 'scope-unmeasured',\n severity: 'warning',\n description: 'A brief that declares a scope is archived against the changes its round made.',\n },\n {\n id: 'stale-link',\n severity: 'warning',\n description: 'No live brief is left linking to where a moved brief used to be.',\n },\n];\n"]}
|
package/dist/scaffold.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Comments are not content, so a freshly scaffolded brief reads as unwritten
|
|
5
5
|
* until somebody writes it.
|
|
6
6
|
*/
|
|
7
|
-
import type
|
|
7
|
+
import { type Config } from './config.js';
|
|
8
8
|
import type { Corpus } from './corpus.js';
|
|
9
9
|
export interface NewBrief {
|
|
10
10
|
readonly id: string;
|
package/dist/scaffold.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Comments are not content, so a freshly scaffolded brief reads as unwritten
|
|
5
5
|
* until somebody writes it.
|
|
6
6
|
*/
|
|
7
|
+
import { DEFAULT_CONFIG } from './config.js';
|
|
7
8
|
import { renderScalar } from './frontmatter.js';
|
|
8
9
|
import { fillTemplate, slugify } from './text.js';
|
|
9
10
|
/**
|
|
@@ -25,16 +26,16 @@ export function fileNameFor(config, id, title) {
|
|
|
25
26
|
return `${slug === '' ? id : slug}.md`;
|
|
26
27
|
return slug === '' ? `${id}.md` : `${id}${config.id.separator}${slug}.md`;
|
|
27
28
|
}
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
};
|
|
29
|
+
const DEFAULT_RULES = [...DEFAULT_CONFIG.sections, ...Object.values(DEFAULT_CONFIG.types).flat()];
|
|
30
|
+
/**
|
|
31
|
+
* What a new brief says under a heading: the section's configured hint, or the
|
|
32
|
+
* default section's of the same name - a configuration written before hints
|
|
33
|
+
* existed names its sections without one - or a plain instruction. A "-->"
|
|
34
|
+
* would end the comment early and leave the rest as content, so it is broken.
|
|
35
|
+
*/
|
|
36
36
|
function hint(rule) {
|
|
37
|
-
|
|
37
|
+
const text = rule.hint ?? DEFAULT_RULES.find((d) => d.name.toLowerCase() === rule.name.toLowerCase())?.hint ?? `Write the ${rule.name}.`;
|
|
38
|
+
return text.replaceAll('-->', '-- >');
|
|
38
39
|
}
|
|
39
40
|
function frontMatter(config, brief) {
|
|
40
41
|
const lines = ['---'];
|