@descent-vtt/spec-brief 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/CHANGELOG.md +301 -0
  2. package/README.md +92 -22
  3. package/dist/archive.d.ts +52 -2
  4. package/dist/archive.js +205 -37
  5. package/dist/archive.js.map +1 -1
  6. package/dist/brief.d.ts +2 -0
  7. package/dist/brief.js +5 -1
  8. package/dist/brief.js.map +1 -1
  9. package/dist/cli.d.ts +1 -1
  10. package/dist/cli.js +48 -14
  11. package/dist/cli.js.map +1 -1
  12. package/dist/collisions.d.ts +43 -12
  13. package/dist/collisions.js +90 -58
  14. package/dist/collisions.js.map +1 -1
  15. package/dist/config.d.ts +18 -0
  16. package/dist/config.js +66 -11
  17. package/dist/config.js.map +1 -1
  18. package/dist/corpus.d.ts +21 -1
  19. package/dist/corpus.js +51 -2
  20. package/dist/corpus.js.map +1 -1
  21. package/dist/engine.d.ts +33 -3
  22. package/dist/engine.js +112 -9
  23. package/dist/engine.js.map +1 -1
  24. package/dist/frontmatter.d.ts +18 -61
  25. package/dist/frontmatter.js +31 -300
  26. package/dist/frontmatter.js.map +1 -1
  27. package/dist/glob.d.ts +77 -54
  28. package/dist/glob.js +104 -433
  29. package/dist/glob.js.map +1 -1
  30. package/dist/index.d.ts +10 -8
  31. package/dist/index.js +8 -6
  32. package/dist/index.js.map +1 -1
  33. package/dist/links.d.ts +1 -1
  34. package/dist/links.js +3 -4
  35. package/dist/links.js.map +1 -1
  36. package/dist/lint.d.ts +33 -2
  37. package/dist/lint.js +9 -2
  38. package/dist/lint.js.map +1 -1
  39. package/dist/markdown.d.ts +27 -23
  40. package/dist/markdown.js +66 -235
  41. package/dist/markdown.js.map +1 -1
  42. package/dist/plugins.d.ts +14 -0
  43. package/dist/plugins.js +142 -6
  44. package/dist/plugins.js.map +1 -1
  45. package/dist/report.d.ts +40 -3
  46. package/dist/report.js +227 -11
  47. package/dist/report.js.map +1 -1
  48. package/dist/rules.d.ts +22 -4
  49. package/dist/rules.js +175 -45
  50. package/dist/rules.js.map +1 -1
  51. package/dist/scaffold.d.ts +1 -1
  52. package/dist/scaffold.js +10 -9
  53. package/dist/scaffold.js.map +1 -1
  54. package/dist/schedule.d.ts +133 -0
  55. package/dist/schedule.js +265 -0
  56. package/dist/schedule.js.map +1 -0
  57. package/dist/scope.d.ts +52 -0
  58. package/dist/scope.js +56 -0
  59. package/dist/scope.js.map +1 -0
  60. package/dist/text.d.ts +13 -0
  61. package/dist/text.js +23 -0
  62. package/dist/text.js.map +1 -1
  63. package/dist/trigger.d.ts +17 -0
  64. package/dist/trigger.js +49 -0
  65. package/dist/trigger.js.map +1 -0
  66. package/dist/types.d.ts +14 -5
  67. package/dist/types.js.map +1 -1
  68. package/dist/vendor/spec-core/markdown/frontmatter.d.ts +103 -0
  69. package/dist/vendor/spec-core/markdown/frontmatter.js +459 -0
  70. package/dist/vendor/spec-core/markdown/frontmatter.js.map +1 -0
  71. package/dist/vendor/spec-core/markdown/headings.d.ts +45 -0
  72. package/dist/vendor/spec-core/markdown/headings.js +199 -0
  73. package/dist/vendor/spec-core/markdown/headings.js.map +1 -0
  74. package/dist/vendor/spec-core/markdown/index.d.ts +5 -0
  75. package/dist/vendor/spec-core/markdown/index.js +4 -0
  76. package/dist/vendor/spec-core/markdown/index.js.map +1 -0
  77. package/dist/vendor/spec-core/markdown/layout.d.ts +39 -0
  78. package/dist/vendor/spec-core/markdown/layout.js +28 -0
  79. package/dist/vendor/spec-core/markdown/layout.js.map +1 -0
  80. package/dist/vendor/spec-core/markdown/links.d.ts +25 -0
  81. package/dist/vendor/spec-core/markdown/links.js +394 -0
  82. package/dist/vendor/spec-core/markdown/links.js.map +1 -0
  83. package/dist/vendor/spec-core/markdown/lists.d.ts +22 -0
  84. package/dist/vendor/spec-core/markdown/lists.js +127 -0
  85. package/dist/vendor/spec-core/markdown/lists.js.map +1 -0
  86. package/dist/vendor/spec-core/markdown/scan.d.ts +36 -0
  87. package/dist/vendor/spec-core/markdown/scan.js +463 -0
  88. package/dist/vendor/spec-core/markdown/scan.js.map +1 -0
  89. package/dist/vendor/spec-core/markdown/syntax.d.ts +75 -0
  90. package/dist/vendor/spec-core/markdown/syntax.js +181 -0
  91. package/dist/vendor/spec-core/markdown/syntax.js.map +1 -0
  92. package/dist/vendor/spec-core/markdown/tables.d.ts +18 -0
  93. package/dist/vendor/spec-core/markdown/tables.js +86 -0
  94. package/dist/vendor/spec-core/markdown/tables.js.map +1 -0
  95. package/dist/vendor/spec-core/markdown/types.d.ts +290 -0
  96. package/dist/vendor/spec-core/markdown/types.js +9 -0
  97. package/dist/vendor/spec-core/markdown/types.js.map +1 -0
  98. package/dist/vendor/spec-core/path/index.d.ts +1 -0
  99. package/dist/vendor/spec-core/path/index.js +2 -0
  100. package/dist/vendor/spec-core/path/index.js.map +1 -0
  101. package/dist/vendor/spec-core/path/posix.d.ts +83 -0
  102. package/dist/vendor/spec-core/path/posix.js +150 -0
  103. package/dist/vendor/spec-core/path/posix.js.map +1 -0
  104. package/dist/vendor/spec-core/pattern/automaton.d.ts +129 -0
  105. package/dist/vendor/spec-core/pattern/automaton.js +340 -0
  106. package/dist/vendor/spec-core/pattern/automaton.js.map +1 -0
  107. package/dist/vendor/spec-core/pattern/charset.d.ts +58 -0
  108. package/dist/vendor/spec-core/pattern/charset.js +116 -0
  109. package/dist/vendor/spec-core/pattern/charset.js.map +1 -0
  110. package/dist/vendor/spec-core/pattern/glob.d.ts +121 -0
  111. package/dist/vendor/spec-core/pattern/glob.js +481 -0
  112. package/dist/vendor/spec-core/pattern/glob.js.map +1 -0
  113. package/dist/vendor/spec-core/pattern/index.d.ts +6 -0
  114. package/dist/vendor/spec-core/pattern/index.js +4 -0
  115. package/dist/vendor/spec-core/pattern/index.js.map +1 -0
  116. package/dist/vendor/spec-core/pattern/regex.d.ts +67 -0
  117. package/dist/vendor/spec-core/pattern/regex.js +813 -0
  118. package/dist/vendor/spec-core/pattern/regex.js.map +1 -0
  119. package/dist/vendor/spec-core/text/index.d.ts +2 -0
  120. package/dist/vendor/spec-core/text/index.js +2 -0
  121. package/dist/vendor/spec-core/text/index.js.map +1 -0
  122. package/dist/vendor/spec-core/text/lines.d.ts +72 -0
  123. package/dist/vendor/spec-core/text/lines.js +156 -0
  124. package/dist/vendor/spec-core/text/lines.js.map +1 -0
  125. package/package.json +4 -3
  126. package/schema.json +17 -0
  127. package/src/vendor/spec-core/LICENSE +21 -0
  128. 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 { Config, SectionRule } from './config.js';
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 collision analysis reports under. They are not run per brief,
55
- * so they carry no check: they exist so configuration can set their severity
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 { intersectGlobs, matchGlob, parseGlob } from './glob.js';
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 { labelMatches } from './text.js';
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
- function globsOf(patterns, repoFiles) {
63
- const files = repoFiles === null ? null : new Set(repoFiles);
64
- return patterns.map((pattern) => {
65
- const parsed = parseGlob(pattern, { isFile: files === null ? undefined : (path) => files.has(path) });
66
- return parsed.ok ? { pattern, glob: parsed.glob, error: null } : { pattern, glob: null, error: parsed.error };
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 = [config.status.draft, config.status.active, config.status.archived].filter((w) => w !== null);
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
- line: at(brief.bodyStart),
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) => ({ line: at(s.heading.line), message: `the "${rule.name}" section is empty`, severity: leniency(brief) })))),
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 }) => ['affectedFiles', 'protectedFiles'].flatMap((field) => globsOf(brief[field], null)
377
- .filter((g) => g.error !== null)
378
- .map((g) => ({ line: at(lineOfField(brief, field)), message: `"${g.pattern}" in ${field}: ${g.error}` })))),
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: 'No file is both in scope and protected.',
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
- return ['affectedFiles', 'protectedFiles'].flatMap((field) => globsOf(brief[field], repoFiles)
411
- .filter((g) => g.glob !== null && !repoFiles.some((file) => matchGlob(g.glob, file)))
412
- .map((g) => ({
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: `"${g.pattern}" in ${field} matches no file in the tree`,
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 collision analysis reports under. They are not run per brief,
440
- * so they carry no check: they exist so configuration can set their severity
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"]}
@@ -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 { Config } from './config.js';
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 HINTS = {
29
- intent: 'The state of the tree when this round is done, and why it matters. One paragraph.',
30
- 'negative scope': 'What this round must not do, even where it would look helpful.',
31
- 'not empowered': 'Files, interfaces and decisions this round may not change.',
32
- invariants: 'Checks that must hold before the round is called done, each one a task item.',
33
- 'acceptance criteria': 'What a reviewer checks to accept the result, each one a task item.',
34
- 'the defect, measured': 'How to reproduce the defect, and the measurement that shows it.',
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
- return HINTS[rule.name.toLowerCase()] ?? `Write the ${rule.name}.`;
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 = ['---'];