@descent-vtt/spec-brief 0.4.1 → 0.5.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 (48) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +26 -7
  3. package/dist/archive.js +31 -18
  4. package/dist/archive.js.map +1 -1
  5. package/dist/brief.js +8 -5
  6. package/dist/brief.js.map +1 -1
  7. package/dist/cli.js +21 -13
  8. package/dist/cli.js.map +1 -1
  9. package/dist/collisions.d.ts +9 -0
  10. package/dist/collisions.js +50 -40
  11. package/dist/collisions.js.map +1 -1
  12. package/dist/config.d.ts +6 -0
  13. package/dist/config.js +17 -6
  14. package/dist/config.js.map +1 -1
  15. package/dist/corpus.js +21 -15
  16. package/dist/corpus.js.map +1 -1
  17. package/dist/engine.d.ts +2 -1
  18. package/dist/engine.js +24 -15
  19. package/dist/engine.js.map +1 -1
  20. package/dist/frontmatter.js +1 -1
  21. package/dist/frontmatter.js.map +1 -1
  22. package/dist/fs.js +9 -5
  23. package/dist/fs.js.map +1 -1
  24. package/dist/git.js +7 -4
  25. package/dist/git.js.map +1 -1
  26. package/dist/glob.js +2 -0
  27. package/dist/glob.js.map +1 -1
  28. package/dist/links.js +2 -1
  29. package/dist/links.js.map +1 -1
  30. package/dist/lint.js +3 -4
  31. package/dist/lint.js.map +1 -1
  32. package/dist/plugins.js +10 -6
  33. package/dist/plugins.js.map +1 -1
  34. package/dist/report.d.ts +8 -1
  35. package/dist/report.js +22 -20
  36. package/dist/report.js.map +1 -1
  37. package/dist/rules.js +8 -4
  38. package/dist/rules.js.map +1 -1
  39. package/dist/scaffold.js +2 -2
  40. package/dist/scaffold.js.map +1 -1
  41. package/dist/schedule.js +10 -20
  42. package/dist/schedule.js.map +1 -1
  43. package/dist/schema.js +9 -13
  44. package/dist/schema.js.map +1 -1
  45. package/dist/text.d.ts +4 -1
  46. package/dist/text.js +10 -6
  47. package/dist/text.js.map +1 -1
  48. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -11,6 +11,59 @@ release that can turn a run red. This is the family's policy, recorded in
11
11
  releases up to 0.2.7 came before it, and several of their patches changed
12
12
  what a run reports.
13
13
 
14
+ ## 0.5.1
15
+
16
+ The README's commands gave `npx` the command's bare name, which on npm is
17
+ not this project's package. spec-brief itself is unchanged.
18
+
19
+ ### Security
20
+
21
+ - Every command in the README gives `npx` the package's full name behind
22
+ `--no-install`, as in `npx --no-install @descent-vtt/spec-brief ...`.
23
+ `spec-brief` without the scope belongs to nobody on npm (2026-10-07), so
24
+ anyone can register it.
25
+ `npx` given a name the project has not installed fetches the package of
26
+ that name and runs it, unasked when no terminal is attached: a fresh
27
+ clone, a worktree before `npm ci`, a CI job with no install step.
28
+ The CI examples install first, and [Names](README.md#names) says whose the
29
+ names are; what was measured is in the family's
30
+ [adopting guide](https://github.com/DescentVTT/spec-core/blob/main/docs/adopting.md#names).
31
+ Upgrading: where a CI job or a script gives `npx` the command's name
32
+ alone, write `npx --no-install @descent-vtt/spec-brief`. `--no-install`
33
+ before the bare name is not enough: npm still runs a copy of that name's
34
+ package an earlier fetch left in its cache.
35
+
36
+ ## 0.5.0
37
+
38
+ Five defects a reading of every surviving mutant found, none of which a test
39
+ had held: names every JavaScript object answers to, a root directory named
40
+ with two leading dots, and three in what `matrix` and `archive` show.
41
+
42
+ ### Changed
43
+
44
+ - A root whose directory name starts with two dots, such as `..drafts/`,
45
+ keeps git: it was taken for a path outside the work tree, so `archive`
46
+ skipped its dirty-tree check there. Upgrading: an archival that passed can
47
+ be refused; commit the work or pass `--allow-dirty`, and name the round
48
+ with `--commit` or `--base`.
49
+
50
+ ### Fixed
51
+
52
+ - A command, brief type, configuration key or template placeholder named
53
+ `constructor`, `toString` or `__proto__` is the unknown name it is:
54
+ `spec-brief constructor` is "not a command" with exit 2 where it was a
55
+ stack trace with exit 1, `type: constructor` is an `unknown-type` finding
56
+ where it stopped `lint` for every brief, and `{constructor}` in a
57
+ template is left as written.
58
+ - `matrix` draws its grid by brief, so two briefs that share an id are two
59
+ rows marked each for itself; the findings and exit code were already right.
60
+ - `matrix` marks in its grid only what the run reports: with
61
+ `shared-directory` off, as by default, two briefs writing into one
62
+ directory are no longer marked `~`. `--format json` is unchanged.
63
+ - `archive` and `unarchive` refuse a brief whose banner on the first line
64
+ hides TOML or unclosed front matter, with the `front-matter` finding,
65
+ where they ended on an unexpected error.
66
+
14
67
  ## 0.4.1
15
68
 
16
69
  `lint`, `matrix`, `schedule` and `archive` over a large repository take time
package/README.md CHANGED
@@ -8,11 +8,23 @@ spec-brief is one of the [spec-\* tools](https://github.com/DescentVTT/spec-core
8
8
 
9
9
  ```bash
10
10
  npm install --save-dev @descent-vtt/spec-brief
11
- npx spec-brief init
12
- npx spec-brief new "Rotate session tokens on privilege change" --wave 1
13
- npx spec-brief lint
11
+ npx --no-install @descent-vtt/spec-brief init
12
+ npx --no-install @descent-vtt/spec-brief new "Rotate session tokens on privilege change" --wave 1
13
+ npx --no-install @descent-vtt/spec-brief lint
14
14
  ```
15
15
 
16
+ ## Names
17
+
18
+ The package is `@descent-vtt/spec-brief`, and the command it installs is `spec-brief`. The name without the scope is not this project: on npm, `spec-brief` belonged to nobody on 2026-10-07, and whoever registers it decides what it runs.
19
+
20
+ `npx` fetches and runs the package of whatever name it is given when the project has none installed - a fresh clone, a worktree before `npm ci`, a CI job without the install step - and without a terminal it does not ask first. So give `npx` the full name:
21
+
22
+ - `npx --no-install @descent-vtt/spec-brief` in a project that installed it: it runs that install, the version the lockfile pins, and where there is none it stops with an error that names this package.
23
+ - `npx @descent-vtt/spec-brief`, without `--no-install`, where nothing is installed: it fetches this package and runs it.
24
+
25
+ <!-- bare-name: the two forms in the next sentence are shown as what not to write -->
26
+ Never `npx spec-brief`, and not `npx --no-install spec-brief` either: `--no-install` stops a download, and npm still runs a copy of the bare name's package that an earlier fetch left in its cache. A `package.json` script names the command alone, as in `"briefs": "spec-brief lint"`, because npm fetches nothing for a script. The family's [adopting guide](https://github.com/DescentVTT/spec-core/blob/main/docs/adopting.md#names) has what was measured, under npm 10, 11 and 12.
27
+
16
28
  ## Why
17
29
 
18
30
  Repositories that run agents on briefs end up doing the same ceremony by hand, and getting it wrong in the same places:
@@ -136,6 +148,7 @@ Scopes are intersected as globs, not compared as strings: the two above share no
136
148
  - **One collision per pair of briefs**: two briefs whose scopes meet in several places are one collision, listing every pair of patterns that meets.
137
149
  - **Unscoped**: a brief with no scope is reported as unscoped rather than counted as safe.
138
150
  - **Undecided**: the search for a shared file has a budget; a pair it cannot decide within it is marked `?` and reported as `collision-undecided`, a warning, never as a collision and never as clean - unless the tree holds a file both patterns match and neither brief protects, which proves the collision.
151
+ - **A mark is a finding**: the grid marks a pair, and a line beneath it says why, only for what the run reports, so a [rule](#rules) that is off marks nothing. `shared-directory` is off unless the configuration turns it on; on, two briefs that write into one directory without sharing a file are marked `~`. `--format json` lists every pair the comparison found under `waves`, and the `findings` the run reports beside them.
139
152
 
140
153
  ### `spec-brief schedule`
141
154
 
@@ -356,12 +369,16 @@ How a heading fills a section, and what counts as a heading, a task or a link, i
356
369
 
357
370
  ## In CI
358
371
 
372
+ After the job has installed the project, with `npm ci`:
373
+
359
374
  ```yaml
360
- - run: npx spec-brief lint --format github
361
- - run: npx spec-brief matrix --format github
362
- - run: npx spec-brief schedule --format github
375
+ - run: npx --no-install @descent-vtt/spec-brief lint --format github
376
+ - run: npx --no-install @descent-vtt/spec-brief matrix --format github
377
+ - run: npx --no-install @descent-vtt/spec-brief schedule --format github
363
378
  ```
364
379
 
380
+ Each gives `npx` the package's [full name](#names) and `--no-install`: a job that lost its install step then stops, where the command's name alone would fetch whatever package has that name.
381
+
365
382
  | Format | What it writes |
366
383
  | --- | --- |
367
384
  | `github` | Workflow commands, which annotate the pull request with no upload and no permission. |
@@ -371,7 +388,9 @@ How a heading fills a section, and what counts as a heading, a task or a link, i
371
388
 
372
389
  ```yaml
373
390
  spec-brief:
374
- script: npx spec-brief lint --format gitlab > gl-code-quality.json
391
+ script:
392
+ - npm ci
393
+ - npx --no-install @descent-vtt/spec-brief lint --format gitlab > gl-code-quality.json
375
394
  artifacts:
376
395
  reports:
377
396
  codequality: gl-code-quality.json
package/dist/archive.js CHANGED
@@ -89,11 +89,15 @@ function globs(patterns) {
89
89
  return parsed.ok ? [parsed.glob] : [];
90
90
  });
91
91
  }
92
- /** A list marker opening a line, after its indentation. */
93
- const ITEM_START = /^[ \t]*(?:[-*+]|\d{1,9}[.)])[ \t]+/;
92
+ /** A list marker opening a line, after its indentation, and the space that makes it one. */
93
+ const ITEM_START = /^[ \t]*(?:[-*+]|\d{1,9}[.)])[ \t]/;
94
94
  /** A line that ends a sentence: a stop, then only closing marks. */
95
95
  const SENTENCE_END = /[.!?][)\]*_"'`]*[ \t]*$/;
96
- /** Where a note's text starts: after its indentation and a list marker, if it has one. */
96
+ /**
97
+ * Where a note's text starts: after its indentation and a list marker, if it
98
+ * has one. Every part is optional, so it matches at the start of any line,
99
+ * anchored or not.
100
+ */
97
101
  const NOTE_START = /^[ \t]*(?:(?:[-*+]|\d{1,9}[.)])[ \t]+)?/;
98
102
  /**
99
103
  * Whether an open box carries a note that closes it: a line under the box,
@@ -153,6 +157,7 @@ function isBriefPath(path, config) {
153
157
  /** Task items that must be closed before a brief can be archived. */
154
158
  export function openTasks(brief, corpus) {
155
159
  const { tasks: scope, dispositions } = corpus.config.archiving;
160
+ // A box is never on a heading's line or past the last one, so neither bound is ever met.
156
161
  const inScope = scope === 'all'
157
162
  ? brief.tasks
158
163
  : brief.tasks.filter((task) => brief.sections.some((s) => task.line > s.heading.line && task.line < s.end && scope.some((name) => labelMatches(s.heading.text, name))));
@@ -191,11 +196,13 @@ function withoutBanner(lines, banner) {
191
196
  }
192
197
  /**
193
198
  * Inserts a banner under the front matter, after the blank line that usually
194
- * follows it, and writes one blank line after the banner.
199
+ * follows it, and writes one blank line after the banner. A front matter never
200
+ * closed has no line to go under: its `close` is -1, which is the top, as for
201
+ * a brief with none.
195
202
  */
196
203
  function withBanner(lines, banner) {
197
204
  const frontMatter = readFrontMatter(lines);
198
- let at = frontMatter === null || frontMatter.close < 0 ? 0 : frontMatter.close + 1;
205
+ let at = frontMatter === null ? 0 : frontMatter.close + 1;
199
206
  if (at > 0 && lines[at]?.trim() === '')
200
207
  at += 1;
201
208
  return [...lines.slice(0, at), ...banner, '', ...lines.slice(at)];
@@ -203,7 +210,8 @@ function withBanner(lines, banner) {
203
210
  /**
204
211
  * Sets the integrity field to the hash of everything else. The field is put in
205
212
  * place before hashing, so the lines hashed are exactly the lines a later
206
- * check sees once it takes the field back out.
213
+ * check sees once it takes the field back out. The hash is taken without the
214
+ * field's line, so the word that holds its place is never read.
207
215
  */
208
216
  function withIntegrity(lines) {
209
217
  const placed = setEntry(lines, readFrontMatter(lines), INTEGRITY_FIELD, 'pending');
@@ -217,13 +225,19 @@ function withIntegrity(lines) {
217
225
  * matter and is not refused by it already - the rule lowered, or a reopening,
218
226
  * which lint does not gate - is refused here. It leaves the front matter as
219
227
  * written rather than half edited.
228
+ *
229
+ * The front matter judged is the brief's own, or for a brief read as having
230
+ * none, whatever block the `lines` about to be edited open with. They open
231
+ * with one where a banner stood at the very top of the file: it hid the block
232
+ * under it from every reader, and with the banner taken out that block is
233
+ * what an edit would go into.
220
234
  */
221
- function writable(brief, edits, blocking) {
222
- const frontMatter = brief.frontMatter;
235
+ function writable(brief, lines, edits, blocking) {
236
+ const frontMatter = brief.frontMatter ?? readFrontMatter(lines);
223
237
  if (frontMatter === null || editable(frontMatter))
224
238
  return true;
225
239
  if (edits && !blocking.some((f) => f.rule === 'front-matter')) {
226
- const open = frontMatter.close < 0;
240
+ const open = frontMatter.close === -1;
227
241
  blocking.push(problem(brief, 'front-matter', 'error', 1, open ? 'the front matter is never closed, so it cannot be written into' : 'the front matter is TOML, which spec-brief does not write', open ? 'close the front matter with a "---" line' : 'write the front matter as YAML between "---" lines'));
228
242
  }
229
243
  return false;
@@ -296,11 +310,8 @@ export function planArchive(corpus, brief, request) {
296
310
  const blocking = [];
297
311
  const warnings = [];
298
312
  // Under --strict a warning is an error, here as in lint.
299
- for (const finding of request.findings ?? []) {
300
- const refuses = finding.severity === 'error' || (request.strict === true && finding.severity === 'warning');
301
- if (finding.file === brief.file && refuses)
302
- blocking.push(finding);
303
- }
313
+ const refuses = (finding) => finding.file === brief.file && (finding.severity === 'error' || (request.strict === true && finding.severity === 'warning'));
314
+ blocking.push(...(request.findings?.filter(refuses) ?? []));
304
315
  // Only a status field can say "draft" or "deferred", so there is one to point at.
305
316
  const field = config.status.field;
306
317
  if (brief.status === 'draft') {
@@ -373,7 +384,7 @@ export function planArchive(corpus, brief, request) {
373
384
  const banner = renderBanner(config.archiving.banner, values);
374
385
  if (banner.length > 0)
375
386
  lines = withBanner(lines, banner);
376
- const writes = writable(brief, config.status.field !== null || config.archiving.freeze, blocking);
387
+ const writes = writable(brief, lines, config.status.field !== null || config.archiving.freeze, blocking);
377
388
  if (writes && config.status.field !== null) {
378
389
  lines = setEntry(lines, readFrontMatter(lines), config.status.field, renderScalar(config.status.archived));
379
390
  }
@@ -465,15 +476,17 @@ export function planUnarchive(corpus, brief, request = {}) {
465
476
  lines = withoutBanner(lines, brief.banner);
466
477
  lines = removeEntry(lines, readFrontMatter(lines), INTEGRITY_FIELD);
467
478
  const field = config.status.field;
468
- if (writable(brief, field !== null, blocking) && field !== null) {
479
+ if (writable(brief, lines, field !== null, blocking) && field !== null) {
469
480
  lines = setEntry(lines, readFrontMatter(lines), field, renderScalar(config.status.active));
470
481
  }
471
482
  // Archival gives a brief with no front matter a block to hold its hash.
472
483
  // A block this reopening emptied goes too, and the brief is the one that
473
484
  // was archived. One that was empty before it was archived reads the same
474
485
  // once the hash is in it, and goes with it; one empty in the archive stays.
475
- const left = readFrontMatter(lines);
476
- const emptied = left !== null && left.kind === 'yaml' && left.close === 1 && (brief.frontMatter?.close ?? 0) > 1;
486
+ // No edit above reaches the opening line, so a brief that had a block has
487
+ // one still, and only a YAML block has lost a line.
488
+ const had = brief.frontMatter;
489
+ const emptied = had !== null && had.close > 1 && readFrontMatter(lines).close === 1;
477
490
  if (emptied)
478
491
  lines = lines.slice(2);
479
492
  const incoming = inbound(corpus, brief, to);
@@ -1 +1 @@
1
- {"version":3,"file":"archive.js","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Archival and its reverse, planned.\n *\n * A plan is a pure function of the corpus and a request: what would be\n * refused and why, the text every touched file would hold afterwards, and the\n * operations that get there. Nothing is written here. `--dry-run` prints a\n * plan; `apply.ts` executes one as a transaction. Splitting the two is what\n * makes the ceremony testable without a disk and previewable without risk.\n */\n\nimport { BANNER_CLOSE, BANNER_OPEN, type Brief, lineOfField } from './brief.js';\nimport type { Config } from './config.js';\nimport { type Corpus, resolveDependency } from './corpus.js';\nimport { editable, readFrontMatter, removeEntry, renderScalar, setEntry } from './frontmatter.js';\nimport type { CommitInfo, FileChange } from './git.js';\nimport { type Glob, matchGlob, parseGlob } from './glob.js';\nimport { INTEGRITY_FIELD, integrityOf } from './integrity.js';\nimport { dirOf, linesLinkingTo, normalisePath, rewriteLinks } from './links.js';\nimport { configuredSeverity } from './lint.js';\nimport type { Scan } from './markdown.js';\nimport { encodeLike, fillTemplate, joinLines, labelMatches, templateHoles } from './text.js';\nimport type { Finding, Severity } from './types.js';\n\nexport interface PullRequest {\n readonly number: number;\n readonly url: string | null;\n}\n\n/**\n * Why the files a round changed are unknown. `git`: there was no git to ask.\n * `revision`: no commit was named and no base configured, so no diff was\n * read. `merged`: the commit is already in the base branch, so the diff from\n * their merge base is empty whatever the round changed - archiving on the base\n * branch after the merge.\n */\nexport type Unmeasured =\n | { readonly reason: 'git' }\n | { readonly reason: 'revision' }\n | { readonly reason: 'merged'; readonly base: string; readonly commit: string };\n\nexport interface ArchiveRequest {\n /** `YYYY-MM-DD`. */\n readonly date: string;\n readonly summary?: string | undefined;\n readonly pr?: PullRequest | undefined;\n readonly commit?: CommitInfo | undefined;\n /** The files the round changed, when they were read. */\n readonly changes?: readonly FileChange[] | undefined;\n /**\n * Why `changes` is missing, when the caller knows. Without changes the\n * scope checks have nothing to check, and a plan that said nothing would\n * read as a round that stayed in scope.\n */\n readonly unmeasured?: Unmeasured | undefined;\n /** Uncommitted paths, when the working tree was read. */\n readonly dirty?: readonly string[] | undefined;\n readonly allowDirty?: boolean | undefined;\n /** Out-of-scope changes refuse the archival instead of warning. */\n readonly strict?: boolean | undefined;\n /** The corpus's lint findings; errors on the brief refuse the archival. */\n readonly findings?: readonly Finding[] | undefined;\n}\n\nexport type FileOp =\n | {\n readonly kind: 'write';\n readonly path: string;\n readonly content: string;\n /** What the file holds when the plan is made; `null` for a file that must not exist yet. */\n readonly before: string | null;\n }\n | { readonly kind: 'remove'; readonly path: string; readonly before: string };\n\nexport interface Plan {\n readonly action: 'archive' | 'unarchive';\n readonly brief: Brief;\n readonly from: string;\n readonly to: string;\n /** The brief is already where this action would put it. */\n readonly done: boolean;\n readonly ops: readonly FileOp[];\n /** Why the action is refused; empty when it may proceed. */\n readonly blocking: readonly Finding[];\n readonly warnings: readonly Finding[];\n readonly linksRewritten: number;\n readonly inboundRewritten: readonly string[];\n /** Links archived briefs hold to the old path. They are frozen and left as they are. */\n readonly inboundFrozen: readonly { readonly file: string; readonly line: number }[];\n readonly banner: readonly string[];\n readonly changes: readonly FileChange[];\n}\n\nfunction problem(\n brief: Brief,\n rule: string,\n severity: Severity,\n line: number,\n message: string,\n hint?: string,\n): Finding {\n return { rule, severity, message, file: brief.file, line, brief: brief.id ?? undefined, hint };\n}\n\nfunction lineOf(brief: Brief, key: string): number {\n return lineOfField(brief, key) + 1;\n}\n\nfunction protectedFinding(brief: Brief, path: string): Finding {\n return {\n ...problem(brief, 'protected-file', 'error', lineOf(brief, 'protectedFiles'), `the round changed ${path}, which this brief protects`, 'revert the change, or record the departure in the brief before archiving it'),\n path,\n };\n}\n\n/**\n * The files a round changed outside its scope: one finding, since the scope\n * is one defect however many files show it - widened once, or explained once.\n */\nfunction outOfScopeFinding(brief: Brief, paths: readonly string[], strict: boolean): Finding {\n return {\n ...problem(\n brief,\n 'out-of-scope',\n strict ? 'error' : 'warning',\n lineOf(brief, 'affectedFiles'),\n `the round changed ${paths.length} file(s) outside affectedFiles: ${listed(paths)}`,\n 'widen affectedFiles if the scope was wrong, or say why in the summary',\n ),\n paths,\n };\n}\n\n/** Where an archive rule's finding goes: an error refuses, and under --strict a warning refuses as an error, as out-of-scope does. */\nfunction place(finding: Finding, strict: boolean, blocking: Finding[], warnings: Finding[]): void {\n if (finding.severity === 'error') blocking.push(finding);\n else if (strict && finding.severity === 'warning') blocking.push({ ...finding, severity: 'error' });\n else warnings.push(finding);\n}\n\nconst UNMEASURED: Readonly<Record<Unmeasured['reason'], { readonly why: string; readonly hint: string }>> = {\n git: {\n why: 'without git, the files the round changed cannot be read',\n hint: 'archive inside the git work tree without --no-git, and name the round with --commit <rev> or --base <rev>',\n },\n revision: {\n why: 'no commit or base was named, so the files the round changed were not read',\n hint: 'pass --commit <rev> for the commit the round landed as, or --base <rev> for the branch it started from, or set \"archiving.base\"',\n },\n merged: {\n why: 'the diff from their merge base is empty',\n hint: \"archive on the round's branch before it merges, or pass --base <rev> naming the commit the round started from\",\n },\n};\n\n/**\n * The finding for scope checks that had nothing to check. A brief with no\n * scope has nothing to check either way, so it gets none.\n */\nfunction unmeasuredScope(brief: Brief, request: ArchiveRequest, severity: Severity): Finding | null {\n const unmeasured = request.unmeasured ?? (request.changes === undefined ? { reason: 'revision' as const } : undefined);\n const fields = [\n ...(brief.protectedFiles.length > 0 ? ['protectedFiles'] : []),\n ...(brief.affectedFiles.length > 0 ? ['affectedFiles'] : []),\n ];\n if (unmeasured === undefined || fields.length === 0) return null;\n const { why, hint } = UNMEASURED[unmeasured.reason];\n const cause = unmeasured.reason === 'merged' ? `${unmeasured.commit.slice(0, 7)} is already in ${unmeasured.base}, so ${why}` : why;\n return problem(brief, 'scope-unmeasured', severity, lineOf(brief, fields[0] as string), `${fields.join(' and ')} went unchecked: ${cause}`, hint);\n}\n\n/**\n * Scope patterns, for matching the files a round changed. A changed path is a\n * file that exists or existed, so a literal pattern is read as either: its own\n * path, and everything beneath it. Nothing can lie beneath a file, so the\n * reading cannot invent a match, and the tree need not be asked - it has\n * changed since the scope was written, by the round itself.\n */\nfunction globs(patterns: readonly string[]): Glob[] {\n return patterns.flatMap((p) => {\n const parsed = parseGlob(p, { literal: 'either' });\n return parsed.ok ? [parsed.glob] : [];\n });\n}\n\n/** A list marker opening a line, after its indentation. */\nconst ITEM_START = /^[ \\t]*(?:[-*+]|\\d{1,9}[.)])[ \\t]+/;\n\n/** A line that ends a sentence: a stop, then only closing marks. */\nconst SENTENCE_END = /[.!?][)\\]*_\"'`]*[ \\t]*$/;\n\n/** Where a note's text starts: after its indentation and a list marker, if it has one. */\nconst NOTE_START = /^[ \\t]*(?:(?:[-*+]|\\d{1,9}[.)])[ \\t]+)?/;\n\n/**\n * Whether an open box carries a note that closes it: a line under the box,\n * within its item, that starts with a disposition marker and starts a note -\n * a bullet, \"- **Delegated to** 012\", a paragraph after a blank line, or a\n * line after one that ends a sentence, \"... did not.\" over \"**Rejected** by\n * ...\", which is how notes are written where they are written most. A line\n * that continues a sentence is not one: \"- [ ] Explain why the\" over\n * \"**Rejected** designs failed\" is the box's own sentence wrapping, and an\n * open task, as a marker in the middle of the box's line is a word in it. The note belongs to the box above\n * it, so the search stops at the first nested box: a child's note does not\n * close its parent.\n *\n * Where the note starts is read from the lines as written, and the marker from\n * the masked line, so a marker in code or a comment, or one after a code span,\n * closes nothing.\n */\nfunction dispositioned(scanned: Scan, line: number, end: number, markers: readonly string[]): boolean {\n const boxes = new Set(scanned.tasks.map((t) => t.line));\n for (let i = line + 1; i < end; i += 1) {\n if (boxes.has(i)) return false;\n const written = scanned.lines[i] as string;\n const above = scanned.lines[i - 1] as string;\n const startsNote = ITEM_START.test(written) || above.trim() === '' || SENTENCE_END.test(above);\n if (!startsNote) continue;\n const start = (NOTE_START.exec(written) as RegExpExecArray)[0].length;\n const text = scanned.masked[i] as string;\n if (markers.some((marker) => text.startsWith(marker, start))) return true;\n }\n return false;\n}\n\n/** A path inside a directory that may be the root itself. */\nfunction within(directory: string, name: string): string {\n return directory === '' ? name : `${directory}/${name}`;\n}\n\n/**\n * Whether a path is a brief: a file directly in the live or archive directory\n * whose name the configuration takes for a brief. Edits to briefs are the\n * ceremony, not the round, and are left out of the scope and tree checks.\n * Nothing else in those directories is: a README beside the briefs is work.\n */\nfunction isBriefPath(path: string, config: Config): boolean {\n const directory = dirOf(path);\n if (directory !== normalisePath(config.briefs) && directory !== normalisePath(config.archive)) return false;\n const name = path.slice(path.lastIndexOf('/') + 1);\n const accept = parseGlob(config.files);\n const excluded = config.exclude.some((e) => {\n const parsed = parseGlob(e);\n return parsed.ok && matchGlob(parsed.glob, name);\n });\n return accept.ok && matchGlob(accept.glob, name) && !excluded;\n}\n\n/** Task items that must be closed before a brief can be archived. */\nexport function openTasks(brief: Brief, corpus: Corpus): Brief['tasks'] {\n const { tasks: scope, dispositions } = corpus.config.archiving;\n const inScope =\n scope === 'all'\n ? brief.tasks\n : brief.tasks.filter((task) =>\n brief.sections.some(\n (s) => task.line > s.heading.line && task.line < s.end && scope.some((name) => labelMatches(s.heading.text, name)),\n ),\n );\n return inScope.filter((task) => !task.checked && !dispositioned(brief.scan, task.line, task.end, dispositions));\n}\n\nfunction diffstat(changes: readonly FileChange[]): string {\n const insertions = changes.reduce((sum, c) => sum + (c.insertions ?? 0), 0);\n const deletions = changes.reduce((sum, c) => sum + (c.deletions ?? 0), 0);\n const files = changes.length === 1 ? '1 file changed' : `${changes.length} files changed`;\n return `${files}, +${insertions} \\u2212${deletions}`;\n}\n\n/**\n * The banner's lines, markers included. A template line whose placeholders\n * are not all filled is left out, so a round with no pull request has no\n * pull-request sentence rather than a sentence with a hole in it. A template\n * with nothing left writes no banner at all, markers included.\n */\nexport function renderBanner(template: readonly string[], values: Readonly<Record<string, string>>): string[] {\n const kept = template\n .filter((line) => templateHoles(line).every((hole) => (values[hole] ?? '') !== ''))\n .map((line) => fillTemplate(line, values));\n if (kept.length === 0) return [];\n return [BANNER_OPEN, ...kept.map((line) => (line === '' ? '>' : `> ${line}`)), BANNER_CLOSE];\n}\n\n/**\n * Removes a banner block and the blank line written after it. The exact\n * reverse of {@link withBanner}, so reopening a brief leaves its blank lines\n * as they were.\n */\nfunction withoutBanner(lines: readonly string[], banner: Brief['banner']): string[] {\n if (banner === null) return [...lines];\n const after = lines[banner.end]?.trim() === '' ? banner.end + 1 : banner.end;\n return [...lines.slice(0, banner.start), ...lines.slice(after)];\n}\n\n/**\n * Inserts a banner under the front matter, after the blank line that usually\n * follows it, and writes one blank line after the banner.\n */\nfunction withBanner(lines: readonly string[], banner: readonly string[]): string[] {\n const frontMatter = readFrontMatter(lines);\n let at = frontMatter === null || frontMatter.close < 0 ? 0 : frontMatter.close + 1;\n if (at > 0 && lines[at]?.trim() === '') at += 1;\n return [...lines.slice(0, at), ...banner, '', ...lines.slice(at)];\n}\n\n/**\n * Sets the integrity field to the hash of everything else. The field is put in\n * place before hashing, so the lines hashed are exactly the lines a later\n * check sees once it takes the field back out.\n */\nfunction withIntegrity(lines: readonly string[]): string[] {\n const placed = setEntry(lines, readFrontMatter(lines), INTEGRITY_FIELD, 'pending');\n const hash = integrityOf(joinLines(placed, '\\n'));\n return setEntry(placed, readFrontMatter(placed), INTEGRITY_FIELD, hash);\n}\n\n/**\n * Whether a plan can write into the brief's front matter: it has none, or one\n * closed and written in YAML. One never closed, or TOML, cannot be written\n * into. The front-matter rule reports both, and a plan that `edits` the front\n * matter and is not refused by it already - the rule lowered, or a reopening,\n * which lint does not gate - is refused here. It leaves the front matter as\n * written rather than half edited.\n */\nfunction writable(brief: Brief, edits: boolean, blocking: Finding[]): boolean {\n const frontMatter = brief.frontMatter;\n if (frontMatter === null || editable(frontMatter)) return true;\n if (edits && !blocking.some((f) => f.rule === 'front-matter')) {\n const open = frontMatter.close < 0;\n blocking.push(\n problem(\n brief,\n 'front-matter',\n 'error',\n 1,\n open ? 'the front matter is never closed, so it cannot be written into' : 'the front matter is TOML, which spec-brief does not write',\n open ? 'close the front matter with a \"---\" line' : 'write the front matter as YAML between \"---\" lines',\n ),\n );\n }\n return false;\n}\n\ninterface Place {\n readonly file: string;\n readonly line: number;\n}\n\ninterface Inbound {\n readonly ops: FileOp[];\n readonly rewritten: string[];\n readonly frozen: Place[];\n /** Links live briefs hold to it, left as they are because rewriting is off. */\n readonly stale: Place[];\n}\n\n/**\n * Links other briefs hold to a file that is moving: rewritten in live briefs,\n * reported in frozen ones. `archiving.rewriteLinks` governs these as it does\n * the moving brief's own links, so with it off a live brief is not written,\n * and the links it is left holding are reported instead.\n */\nfunction inbound(corpus: Corpus, moving: Brief, to: string): Inbound {\n const result: Inbound = { ops: [], rewritten: [], frozen: [], stale: [] };\n for (const other of corpus.briefs) {\n if (other === moving) continue;\n const directory = dirOf(other.file);\n const lines = linesLinkingTo(other.scan, directory, moving.file);\n if (lines.length === 0) continue;\n if (other.phase === 'archived' || !corpus.config.archiving.rewriteLinks) {\n const left = other.phase === 'archived' ? result.frozen : result.stale;\n for (const line of lines) left.push({ file: other.file, line: line + 1 });\n continue;\n }\n const rewritten = rewriteLinks(other.scan, directory, directory, (p) => (p === moving.file ? to : p));\n result.ops.push({ kind: 'write', path: other.file, content: encodeLike(other.source, rewritten.lines), before: other.source });\n result.rewritten.push(other.file);\n }\n return result;\n}\n\n/** Five items, and a count of the rest. */\nfunction listed(items: readonly string[]): string {\n return items.slice(0, 5).join(', ') + (items.length > 5 ? `, and ${items.length - 5} more` : '');\n}\n\n/**\n * Links live briefs will hold to nothing once the brief moves, when rewriting\n * is off: the move is what breaks them, so the move says where they are.\n */\nfunction staleLinks(corpus: Corpus, brief: Brief, stale: readonly Place[], strict: boolean, blocking: Finding[], warnings: Finding[]): void {\n const severity = configuredSeverity(corpus, 'stale-link');\n if (severity === null || stale.length === 0) return;\n const finding = problem(\n brief,\n 'stale-link',\n severity,\n 1,\n `links on ${stale.length} line(s) of other live briefs will stop resolving when it moves: ${listed(stale.map((s) => `${s.file}:${s.line}`))}`,\n 'turn \"archiving.rewriteLinks\" on to have them rewritten, or fix them by hand',\n );\n place(finding, strict, blocking, warnings);\n}\n\nfunction donePlan(action: Plan['action'], brief: Brief): Plan {\n return {\n action,\n brief,\n from: brief.file,\n to: brief.file,\n done: true,\n ops: [],\n blocking: [],\n warnings: [],\n linksRewritten: 0,\n inboundRewritten: [],\n inboundFrozen: [],\n banner: [],\n changes: [],\n };\n}\n\nexport function planArchive(corpus: Corpus, brief: Brief, request: ArchiveRequest): Plan {\n if (brief.phase === 'archived') return donePlan('archive', brief);\n const { config } = corpus;\n const archiveDir = normalisePath(config.archive);\n const to = within(archiveDir, brief.name);\n const blocking: Finding[] = [];\n const warnings: Finding[] = [];\n\n // Under --strict a warning is an error, here as in lint.\n for (const finding of request.findings ?? []) {\n const refuses = finding.severity === 'error' || (request.strict === true && finding.severity === 'warning');\n if (finding.file === brief.file && refuses) blocking.push(finding);\n }\n // Only a status field can say \"draft\" or \"deferred\", so there is one to point at.\n const field = config.status.field as string;\n if (brief.status === 'draft') {\n blocking.push(\n problem(brief, 'archive-draft', 'error', lineOf(brief, field), 'is a draft, and a draft has not been executed', `set \"${field}: ${config.status.active}\" once the round runs`),\n );\n }\n if (brief.status === 'deferred') {\n blocking.push(\n problem(\n brief,\n 'archive-deferred',\n 'error',\n lineOf(brief, field),\n 'is deferred, and deferred work has not been executed',\n `set \"${field}: ${config.status.active}\" when its trigger fires and the round runs`,\n ),\n );\n }\n for (const task of openTasks(brief, corpus)) {\n blocking.push(\n problem(\n brief,\n 'open-task',\n 'error',\n task.line + 1,\n `\"${task.text === '' ? '(empty)' : task.text}\" is neither ticked nor dispositioned`,\n `tick it, or say why under it, as a bullet or as a paragraph after a blank line, starting ${config.archiving.dispositions.map((d) => `\"${d}\"`).join(', ')}`,\n ),\n );\n }\n for (const dependency of brief.dependsOn) {\n const target = resolveDependency(corpus, dependency);\n if (target !== undefined && target !== brief && target.phase === 'live') {\n blocking.push(\n problem(brief, 'dependency-open', 'error', lineOf(brief, 'dependsOn'), `depends on ${target.id ?? target.name}, which is not archived yet`, `archive ${target.id ?? target.name} first, or drop the dependency if it was never real`),\n );\n }\n }\n const bookkeeping = (path: string): boolean => isBriefPath(path, config);\n if (request.dirty !== undefined && request.allowDirty !== true) {\n const outside = request.dirty.filter((path) => !bookkeeping(path));\n if (outside.length > 0) {\n blocking.push(\n problem(brief, 'dirty-tree', 'error', 1, `the working tree has uncommitted changes outside the briefs: ${listed(outside)}`, 'commit them so the recorded commit holds the round, or pass --allow-dirty'),\n );\n }\n }\n\n const work = (request.changes ?? []).filter((change) => !bookkeeping(change.path));\n const protectedGlobs = globs(brief.protectedFiles);\n const affectedGlobs = globs(brief.affectedFiles);\n // Each protected file changed is its own defect - reverted, or ruled on,\n // one file at a time - so each is a finding that names its path.\n for (const change of work) {\n if (protectedGlobs.some((g) => matchGlob(g, change.path))) blocking.push(protectedFinding(brief, change.path));\n }\n // A protected file is reported once, as protected, and not again as out of scope.\n const unprotected = work.filter((c) => !protectedGlobs.some((g) => matchGlob(g, c.path)));\n const outside = affectedGlobs.length === 0 ? [] : unprotected.filter((c) => !affectedGlobs.some((g) => matchGlob(g, c.path)));\n if (outside.length > 0) {\n const finding = outOfScopeFinding(brief, outside.map((c) => c.path), request.strict === true);\n (request.strict === true ? blocking : warnings).push(finding);\n }\n const scopeSeverity = configuredSeverity(corpus, 'scope-unmeasured');\n const unmeasured = scopeSeverity === null ? null : unmeasuredScope(brief, request, scopeSeverity);\n if (unmeasured !== null) place(unmeasured, request.strict === true, blocking, warnings);\n if (corpus.briefs.some((b) => b.file === to)) {\n blocking.push(problem(brief, 'archive-exists', 'error', 1, `${to} already exists`, 'an archived brief is never overwritten'));\n }\n\n let lines: string[] = [...brief.lines];\n let linksRewritten = 0;\n if (config.archiving.rewriteLinks) {\n const rewritten = rewriteLinks(brief.scan, dirOf(brief.file), archiveDir, (p) => (p === brief.file ? to : p));\n lines = rewritten.lines;\n linksRewritten = rewritten.count;\n }\n lines = withoutBanner(lines, brief.banner);\n\n const pr = request.pr;\n const values: Record<string, string> = {\n date: request.date,\n summary: request.summary?.trim() ?? '',\n pr: pr === undefined ? '' : pr.url === null ? `#${pr.number}` : `[#${pr.number}](${pr.url})`,\n commit: request.commit?.sha.slice(0, 7) ?? '',\n diffstat: request.commit === undefined || request.changes === undefined ? '' : diffstat(request.changes),\n links:\n linksRewritten === 0\n ? ''\n : `Relative links were rewritten to resolve from \\`${archiveDir}/\\` (${linksRewritten}); no other word changed.`,\n id: brief.id ?? '',\n title: brief.title ?? '',\n author: request.commit?.author ?? '',\n };\n const banner = renderBanner(config.archiving.banner, values);\n if (banner.length > 0) lines = withBanner(lines, banner);\n const writes = writable(brief, config.status.field !== null || config.archiving.freeze, blocking);\n if (writes && config.status.field !== null) {\n lines = setEntry(lines, readFrontMatter(lines), config.status.field, renderScalar(config.status.archived));\n }\n lines = removeEntry(lines, readFrontMatter(lines), INTEGRITY_FIELD);\n if (writes && config.archiving.freeze) lines = withIntegrity(lines);\n\n const incoming = inbound(corpus, brief, to);\n staleLinks(corpus, brief, incoming.stale, request.strict === true, blocking, warnings);\n return {\n action: 'archive',\n brief,\n from: brief.file,\n to,\n done: false,\n ops: [\n { kind: 'write', path: to, content: encodeLike(brief.source, lines), before: null },\n ...incoming.ops,\n { kind: 'remove', path: brief.file, before: brief.source },\n ],\n blocking,\n warnings,\n linksRewritten,\n inboundRewritten: incoming.rewritten,\n inboundFrozen: incoming.frozen,\n banner,\n changes: request.changes ?? [],\n };\n}\n\n/** A plugin's answer to a refusal it may lift: which rule, for which path, and why. */\nexport interface Waiver {\n readonly rule: string;\n readonly path: string;\n readonly reason: string;\n}\n\n/**\n * The refusals a plugin may lift. A tool that verifies a ruling can say a\n * protected file was allowed to change, or that a file outside the scope was;\n * whether a round is done is spec-brief's to say.\n */\nexport const WAIVABLE: readonly string[] = ['protected-file', 'out-of-scope'];\n\n/**\n * The plan with the refusals plugins waived turned into notes. A waiver\n * matches a blocking finding by rule and path: a protected-file finding is\n * replaced by the note, and an out-of-scope finding loses the path and goes\n * when none is left. A waiver that matches nothing changes nothing, and one\n * for any other rule is ignored with a warning, which refuses under --strict\n * as any warning does.\n */\nexport function applyWaivers(plan: Plan, waivers: readonly { readonly plugin: string; readonly waiver: Waiver }[], strict = false): Plan {\n const { brief } = plan;\n const blocking = [...plan.blocking];\n const warnings = [...plan.warnings];\n for (const { plugin, waiver } of waivers) {\n if (!WAIVABLE.includes(waiver.rule)) {\n const ignored = problem(\n brief,\n 'waiver-ignored',\n 'warning',\n 1,\n `${plugin} asked to waive ${waiver.rule} for ${waiver.path}; a plugin may waive only ${WAIVABLE.join(' and ')}`,\n `report it to the plugin's authors: ${waiver.rule} is spec-brief's to decide`,\n );\n place(ignored, strict, blocking, warnings);\n continue;\n }\n const at = blocking.findIndex((f) => f.rule === waiver.rule && (f.path === waiver.path || f.paths?.includes(waiver.path) === true));\n if (at < 0) continue;\n const found = blocking[at] as Finding;\n const rest = (found.paths ?? []).filter((p) => p !== waiver.path);\n blocking.splice(at, 1, ...(rest.length > 0 ? [outOfScopeFinding(brief, rest, true)] : []));\n warnings.push({\n ...problem(brief, 'waived', 'note', found.line, `${plugin} waives ${waiver.rule} for ${waiver.path}: ${waiver.reason}`, 'review the waiver with the round; it stands where the refusal was'),\n path: waiver.path,\n });\n }\n return { ...plan, blocking, warnings };\n}\n\nexport interface UnarchiveRequest {\n /** Warnings refuse the reopening, as they fail lint. */\n readonly strict?: boolean | undefined;\n}\n\n/**\n * Reopens an archived brief: the banner and the freeze come off, the status\n * goes back to the live word, and the links are rewritten for the directory\n * it returns to. What the round wrote stays in history, where it belongs.\n */\nexport function planUnarchive(corpus: Corpus, brief: Brief, request: UnarchiveRequest = {}): Plan {\n if (brief.phase === 'live') return donePlan('unarchive', brief);\n const { config } = corpus;\n const briefsDir = normalisePath(config.briefs);\n const to = within(briefsDir, brief.name);\n const blocking: Finding[] = [];\n const warnings: Finding[] = [];\n if (corpus.briefs.some((b) => b.file === to)) {\n blocking.push(problem(brief, 'unarchive-exists', 'error', 1, `${to} already exists`, 'rename one of them first'));\n }\n\n let lines: string[] = [...brief.lines];\n let linksRewritten = 0;\n if (config.archiving.rewriteLinks) {\n const rewritten = rewriteLinks(brief.scan, dirOf(brief.file), briefsDir, (p) => (p === brief.file ? to : p));\n lines = rewritten.lines;\n linksRewritten = rewritten.count;\n }\n lines = withoutBanner(lines, brief.banner);\n lines = removeEntry(lines, readFrontMatter(lines), INTEGRITY_FIELD);\n const field = config.status.field;\n if (writable(brief, field !== null, blocking) && field !== null) {\n lines = setEntry(lines, readFrontMatter(lines), field, renderScalar(config.status.active));\n }\n // Archival gives a brief with no front matter a block to hold its hash.\n // A block this reopening emptied goes too, and the brief is the one that\n // was archived. One that was empty before it was archived reads the same\n // once the hash is in it, and goes with it; one empty in the archive stays.\n const left = readFrontMatter(lines);\n const emptied = left !== null && left.kind === 'yaml' && left.close === 1 && (brief.frontMatter?.close ?? 0) > 1;\n if (emptied) lines = lines.slice(2);\n\n const incoming = inbound(corpus, brief, to);\n staleLinks(corpus, brief, incoming.stale, request.strict === true, blocking, warnings);\n return {\n action: 'unarchive',\n brief,\n from: brief.file,\n to,\n done: false,\n ops: [\n { kind: 'write', path: to, content: encodeLike(brief.source, lines), before: null },\n ...incoming.ops,\n { kind: 'remove', path: brief.file, before: brief.source },\n ],\n blocking,\n warnings,\n linksRewritten,\n inboundRewritten: incoming.rewritten,\n inboundFrozen: incoming.frozen,\n banner: [],\n changes: [],\n };\n}\n"]}
1
+ {"version":3,"file":"archive.js","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Archival and its reverse, planned.\n *\n * A plan is a pure function of the corpus and a request: what would be\n * refused and why, the text every touched file would hold afterwards, and the\n * operations that get there. Nothing is written here. `--dry-run` prints a\n * plan; `apply.ts` executes one as a transaction. Splitting the two is what\n * makes the ceremony testable without a disk and previewable without risk.\n */\n\nimport { BANNER_CLOSE, BANNER_OPEN, type Brief, lineOfField } from './brief.js';\nimport type { Config } from './config.js';\nimport { type Corpus, resolveDependency } from './corpus.js';\nimport { editable, type FrontMatter, readFrontMatter, removeEntry, renderScalar, setEntry } from './frontmatter.js';\nimport type { CommitInfo, FileChange } from './git.js';\nimport { type Glob, matchGlob, parseGlob } from './glob.js';\nimport { INTEGRITY_FIELD, integrityOf } from './integrity.js';\nimport { dirOf, linesLinkingTo, normalisePath, rewriteLinks } from './links.js';\nimport { configuredSeverity } from './lint.js';\nimport type { Scan } from './markdown.js';\nimport { encodeLike, fillTemplate, joinLines, labelMatches, templateHoles } from './text.js';\nimport type { Finding, Severity } from './types.js';\n\nexport interface PullRequest {\n readonly number: number;\n readonly url: string | null;\n}\n\n/**\n * Why the files a round changed are unknown. `git`: there was no git to ask.\n * `revision`: no commit was named and no base configured, so no diff was\n * read. `merged`: the commit is already in the base branch, so the diff from\n * their merge base is empty whatever the round changed - archiving on the base\n * branch after the merge.\n */\nexport type Unmeasured =\n | { readonly reason: 'git' }\n | { readonly reason: 'revision' }\n | { readonly reason: 'merged'; readonly base: string; readonly commit: string };\n\nexport interface ArchiveRequest {\n /** `YYYY-MM-DD`. */\n readonly date: string;\n readonly summary?: string | undefined;\n readonly pr?: PullRequest | undefined;\n readonly commit?: CommitInfo | undefined;\n /** The files the round changed, when they were read. */\n readonly changes?: readonly FileChange[] | undefined;\n /**\n * Why `changes` is missing, when the caller knows. Without changes the\n * scope checks have nothing to check, and a plan that said nothing would\n * read as a round that stayed in scope.\n */\n readonly unmeasured?: Unmeasured | undefined;\n /** Uncommitted paths, when the working tree was read. */\n readonly dirty?: readonly string[] | undefined;\n readonly allowDirty?: boolean | undefined;\n /** Out-of-scope changes refuse the archival instead of warning. */\n readonly strict?: boolean | undefined;\n /** The corpus's lint findings; errors on the brief refuse the archival. */\n readonly findings?: readonly Finding[] | undefined;\n}\n\nexport type FileOp =\n | {\n readonly kind: 'write';\n readonly path: string;\n readonly content: string;\n /** What the file holds when the plan is made; `null` for a file that must not exist yet. */\n readonly before: string | null;\n }\n | { readonly kind: 'remove'; readonly path: string; readonly before: string };\n\nexport interface Plan {\n readonly action: 'archive' | 'unarchive';\n readonly brief: Brief;\n readonly from: string;\n readonly to: string;\n /** The brief is already where this action would put it. */\n readonly done: boolean;\n readonly ops: readonly FileOp[];\n /** Why the action is refused; empty when it may proceed. */\n readonly blocking: readonly Finding[];\n readonly warnings: readonly Finding[];\n readonly linksRewritten: number;\n readonly inboundRewritten: readonly string[];\n /** Links archived briefs hold to the old path. They are frozen and left as they are. */\n readonly inboundFrozen: readonly { readonly file: string; readonly line: number }[];\n readonly banner: readonly string[];\n readonly changes: readonly FileChange[];\n}\n\nfunction problem(\n brief: Brief,\n rule: string,\n severity: Severity,\n line: number,\n message: string,\n hint?: string,\n): Finding {\n return { rule, severity, message, file: brief.file, line, brief: brief.id ?? undefined, hint };\n}\n\nfunction lineOf(brief: Brief, key: string): number {\n return lineOfField(brief, key) + 1;\n}\n\nfunction protectedFinding(brief: Brief, path: string): Finding {\n return {\n ...problem(brief, 'protected-file', 'error', lineOf(brief, 'protectedFiles'), `the round changed ${path}, which this brief protects`, 'revert the change, or record the departure in the brief before archiving it'),\n path,\n };\n}\n\n/**\n * The files a round changed outside its scope: one finding, since the scope\n * is one defect however many files show it - widened once, or explained once.\n */\nfunction outOfScopeFinding(brief: Brief, paths: readonly string[], strict: boolean): Finding {\n return {\n ...problem(\n brief,\n 'out-of-scope',\n strict ? 'error' : 'warning',\n lineOf(brief, 'affectedFiles'),\n `the round changed ${paths.length} file(s) outside affectedFiles: ${listed(paths)}`,\n 'widen affectedFiles if the scope was wrong, or say why in the summary',\n ),\n paths,\n };\n}\n\n/** Where an archive rule's finding goes: an error refuses, and under --strict a warning refuses as an error, as out-of-scope does. */\nfunction place(finding: Finding, strict: boolean, blocking: Finding[], warnings: Finding[]): void {\n if (finding.severity === 'error') blocking.push(finding);\n else if (strict && finding.severity === 'warning') blocking.push({ ...finding, severity: 'error' });\n else warnings.push(finding);\n}\n\nconst UNMEASURED: Readonly<Record<Unmeasured['reason'], { readonly why: string; readonly hint: string }>> = {\n git: {\n why: 'without git, the files the round changed cannot be read',\n hint: 'archive inside the git work tree without --no-git, and name the round with --commit <rev> or --base <rev>',\n },\n revision: {\n why: 'no commit or base was named, so the files the round changed were not read',\n hint: 'pass --commit <rev> for the commit the round landed as, or --base <rev> for the branch it started from, or set \"archiving.base\"',\n },\n merged: {\n why: 'the diff from their merge base is empty',\n hint: \"archive on the round's branch before it merges, or pass --base <rev> naming the commit the round started from\",\n },\n};\n\n/**\n * The finding for scope checks that had nothing to check. A brief with no\n * scope has nothing to check either way, so it gets none.\n */\nfunction unmeasuredScope(brief: Brief, request: ArchiveRequest, severity: Severity): Finding | null {\n const unmeasured = request.unmeasured ?? (request.changes === undefined ? { reason: 'revision' as const } : undefined);\n const fields = [\n ...(brief.protectedFiles.length > 0 ? ['protectedFiles'] : []),\n ...(brief.affectedFiles.length > 0 ? ['affectedFiles'] : []),\n ];\n if (unmeasured === undefined || fields.length === 0) return null;\n const { why, hint } = UNMEASURED[unmeasured.reason];\n const cause = unmeasured.reason === 'merged' ? `${unmeasured.commit.slice(0, 7)} is already in ${unmeasured.base}, so ${why}` : why;\n return problem(brief, 'scope-unmeasured', severity, lineOf(brief, fields[0] as string), `${fields.join(' and ')} went unchecked: ${cause}`, hint);\n}\n\n/**\n * Scope patterns, for matching the files a round changed. A changed path is a\n * file that exists or existed, so a literal pattern is read as either: its own\n * path, and everything beneath it. Nothing can lie beneath a file, so the\n * reading cannot invent a match, and the tree need not be asked - it has\n * changed since the scope was written, by the round itself.\n */\nfunction globs(patterns: readonly string[]): Glob[] {\n return patterns.flatMap((p) => {\n const parsed = parseGlob(p, { literal: 'either' });\n return parsed.ok ? [parsed.glob] : [];\n });\n}\n\n/** A list marker opening a line, after its indentation, and the space that makes it one. */\nconst ITEM_START = /^[ \\t]*(?:[-*+]|\\d{1,9}[.)])[ \\t]/;\n\n/** A line that ends a sentence: a stop, then only closing marks. */\nconst SENTENCE_END = /[.!?][)\\]*_\"'`]*[ \\t]*$/;\n\n/**\n * Where a note's text starts: after its indentation and a list marker, if it\n * has one. Every part is optional, so it matches at the start of any line,\n * anchored or not.\n */\nconst NOTE_START = /^[ \\t]*(?:(?:[-*+]|\\d{1,9}[.)])[ \\t]+)?/;\n\n/**\n * Whether an open box carries a note that closes it: a line under the box,\n * within its item, that starts with a disposition marker and starts a note -\n * a bullet, \"- **Delegated to** 012\", a paragraph after a blank line, or a\n * line after one that ends a sentence, \"... did not.\" over \"**Rejected** by\n * ...\", which is how notes are written where they are written most. A line\n * that continues a sentence is not one: \"- [ ] Explain why the\" over\n * \"**Rejected** designs failed\" is the box's own sentence wrapping, and an\n * open task, as a marker in the middle of the box's line is a word in it. The note belongs to the box above\n * it, so the search stops at the first nested box: a child's note does not\n * close its parent.\n *\n * Where the note starts is read from the lines as written, and the marker from\n * the masked line, so a marker in code or a comment, or one after a code span,\n * closes nothing.\n */\nfunction dispositioned(scanned: Scan, line: number, end: number, markers: readonly string[]): boolean {\n const boxes = new Set(scanned.tasks.map((t) => t.line));\n for (let i = line + 1; i < end; i += 1) {\n if (boxes.has(i)) return false;\n const written = scanned.lines[i] as string;\n const above = scanned.lines[i - 1] as string;\n const startsNote = ITEM_START.test(written) || above.trim() === '' || SENTENCE_END.test(above);\n if (!startsNote) continue;\n const start = (NOTE_START.exec(written) as RegExpExecArray)[0].length;\n const text = scanned.masked[i] as string;\n if (markers.some((marker) => text.startsWith(marker, start))) return true;\n }\n return false;\n}\n\n/** A path inside a directory that may be the root itself. */\nfunction within(directory: string, name: string): string {\n return directory === '' ? name : `${directory}/${name}`;\n}\n\n/**\n * Whether a path is a brief: a file directly in the live or archive directory\n * whose name the configuration takes for a brief. Edits to briefs are the\n * ceremony, not the round, and are left out of the scope and tree checks.\n * Nothing else in those directories is: a README beside the briefs is work.\n */\nfunction isBriefPath(path: string, config: Config): boolean {\n const directory = dirOf(path);\n if (directory !== normalisePath(config.briefs) && directory !== normalisePath(config.archive)) return false;\n const name = path.slice(path.lastIndexOf('/') + 1);\n const accept = parseGlob(config.files);\n const excluded = config.exclude.some((e) => {\n const parsed = parseGlob(e);\n return parsed.ok && matchGlob(parsed.glob, name);\n });\n return accept.ok && matchGlob(accept.glob, name) && !excluded;\n}\n\n/** Task items that must be closed before a brief can be archived. */\nexport function openTasks(brief: Brief, corpus: Corpus): Brief['tasks'] {\n const { tasks: scope, dispositions } = corpus.config.archiving;\n // A box is never on a heading's line or past the last one, so neither bound is ever met.\n const inScope =\n scope === 'all'\n ? brief.tasks\n : brief.tasks.filter((task) =>\n brief.sections.some(\n (s) => task.line > s.heading.line && task.line < s.end && scope.some((name) => labelMatches(s.heading.text, name)),\n ),\n );\n return inScope.filter((task) => !task.checked && !dispositioned(brief.scan, task.line, task.end, dispositions));\n}\n\nfunction diffstat(changes: readonly FileChange[]): string {\n const insertions = changes.reduce((sum, c) => sum + (c.insertions ?? 0), 0);\n const deletions = changes.reduce((sum, c) => sum + (c.deletions ?? 0), 0);\n const files = changes.length === 1 ? '1 file changed' : `${changes.length} files changed`;\n return `${files}, +${insertions} \\u2212${deletions}`;\n}\n\n/**\n * The banner's lines, markers included. A template line whose placeholders\n * are not all filled is left out, so a round with no pull request has no\n * pull-request sentence rather than a sentence with a hole in it. A template\n * with nothing left writes no banner at all, markers included.\n */\nexport function renderBanner(template: readonly string[], values: Readonly<Record<string, string>>): string[] {\n const kept = template\n .filter((line) => templateHoles(line).every((hole) => (values[hole] ?? '') !== ''))\n .map((line) => fillTemplate(line, values));\n if (kept.length === 0) return [];\n return [BANNER_OPEN, ...kept.map((line) => (line === '' ? '>' : `> ${line}`)), BANNER_CLOSE];\n}\n\n/**\n * Removes a banner block and the blank line written after it. The exact\n * reverse of {@link withBanner}, so reopening a brief leaves its blank lines\n * as they were.\n */\nfunction withoutBanner(lines: readonly string[], banner: Brief['banner']): string[] {\n if (banner === null) return [...lines];\n const after = lines[banner.end]?.trim() === '' ? banner.end + 1 : banner.end;\n return [...lines.slice(0, banner.start), ...lines.slice(after)];\n}\n\n/**\n * Inserts a banner under the front matter, after the blank line that usually\n * follows it, and writes one blank line after the banner. A front matter never\n * closed has no line to go under: its `close` is -1, which is the top, as for\n * a brief with none.\n */\nfunction withBanner(lines: readonly string[], banner: readonly string[]): string[] {\n const frontMatter = readFrontMatter(lines);\n let at = frontMatter === null ? 0 : frontMatter.close + 1;\n if (at > 0 && lines[at]?.trim() === '') at += 1;\n return [...lines.slice(0, at), ...banner, '', ...lines.slice(at)];\n}\n\n/**\n * Sets the integrity field to the hash of everything else. The field is put in\n * place before hashing, so the lines hashed are exactly the lines a later\n * check sees once it takes the field back out. The hash is taken without the\n * field's line, so the word that holds its place is never read.\n */\nfunction withIntegrity(lines: readonly string[]): string[] {\n const placed = setEntry(lines, readFrontMatter(lines), INTEGRITY_FIELD, 'pending');\n const hash = integrityOf(joinLines(placed, '\\n'));\n return setEntry(placed, readFrontMatter(placed), INTEGRITY_FIELD, hash);\n}\n\n/**\n * Whether a plan can write into the brief's front matter: it has none, or one\n * closed and written in YAML. One never closed, or TOML, cannot be written\n * into. The front-matter rule reports both, and a plan that `edits` the front\n * matter and is not refused by it already - the rule lowered, or a reopening,\n * which lint does not gate - is refused here. It leaves the front matter as\n * written rather than half edited.\n *\n * The front matter judged is the brief's own, or for a brief read as having\n * none, whatever block the `lines` about to be edited open with. They open\n * with one where a banner stood at the very top of the file: it hid the block\n * under it from every reader, and with the banner taken out that block is\n * what an edit would go into.\n */\nfunction writable(brief: Brief, lines: readonly string[], edits: boolean, blocking: Finding[]): boolean {\n const frontMatter = brief.frontMatter ?? readFrontMatter(lines);\n if (frontMatter === null || editable(frontMatter)) return true;\n if (edits && !blocking.some((f) => f.rule === 'front-matter')) {\n const open = frontMatter.close === -1;\n blocking.push(\n problem(\n brief,\n 'front-matter',\n 'error',\n 1,\n open ? 'the front matter is never closed, so it cannot be written into' : 'the front matter is TOML, which spec-brief does not write',\n open ? 'close the front matter with a \"---\" line' : 'write the front matter as YAML between \"---\" lines',\n ),\n );\n }\n return false;\n}\n\ninterface Place {\n readonly file: string;\n readonly line: number;\n}\n\ninterface Inbound {\n readonly ops: FileOp[];\n readonly rewritten: string[];\n readonly frozen: Place[];\n /** Links live briefs hold to it, left as they are because rewriting is off. */\n readonly stale: Place[];\n}\n\n/**\n * Links other briefs hold to a file that is moving: rewritten in live briefs,\n * reported in frozen ones. `archiving.rewriteLinks` governs these as it does\n * the moving brief's own links, so with it off a live brief is not written,\n * and the links it is left holding are reported instead.\n */\nfunction inbound(corpus: Corpus, moving: Brief, to: string): Inbound {\n const result: Inbound = { ops: [], rewritten: [], frozen: [], stale: [] };\n for (const other of corpus.briefs) {\n if (other === moving) continue;\n const directory = dirOf(other.file);\n const lines = linesLinkingTo(other.scan, directory, moving.file);\n if (lines.length === 0) continue;\n if (other.phase === 'archived' || !corpus.config.archiving.rewriteLinks) {\n const left = other.phase === 'archived' ? result.frozen : result.stale;\n for (const line of lines) left.push({ file: other.file, line: line + 1 });\n continue;\n }\n const rewritten = rewriteLinks(other.scan, directory, directory, (p) => (p === moving.file ? to : p));\n result.ops.push({ kind: 'write', path: other.file, content: encodeLike(other.source, rewritten.lines), before: other.source });\n result.rewritten.push(other.file);\n }\n return result;\n}\n\n/** Five items, and a count of the rest. */\nfunction listed(items: readonly string[]): string {\n return items.slice(0, 5).join(', ') + (items.length > 5 ? `, and ${items.length - 5} more` : '');\n}\n\n/**\n * Links live briefs will hold to nothing once the brief moves, when rewriting\n * is off: the move is what breaks them, so the move says where they are.\n */\nfunction staleLinks(corpus: Corpus, brief: Brief, stale: readonly Place[], strict: boolean, blocking: Finding[], warnings: Finding[]): void {\n const severity = configuredSeverity(corpus, 'stale-link');\n if (severity === null || stale.length === 0) return;\n const finding = problem(\n brief,\n 'stale-link',\n severity,\n 1,\n `links on ${stale.length} line(s) of other live briefs will stop resolving when it moves: ${listed(stale.map((s) => `${s.file}:${s.line}`))}`,\n 'turn \"archiving.rewriteLinks\" on to have them rewritten, or fix them by hand',\n );\n place(finding, strict, blocking, warnings);\n}\n\nfunction donePlan(action: Plan['action'], brief: Brief): Plan {\n return {\n action,\n brief,\n from: brief.file,\n to: brief.file,\n done: true,\n ops: [],\n blocking: [],\n warnings: [],\n linksRewritten: 0,\n inboundRewritten: [],\n inboundFrozen: [],\n banner: [],\n changes: [],\n };\n}\n\nexport function planArchive(corpus: Corpus, brief: Brief, request: ArchiveRequest): Plan {\n if (brief.phase === 'archived') return donePlan('archive', brief);\n const { config } = corpus;\n const archiveDir = normalisePath(config.archive);\n const to = within(archiveDir, brief.name);\n const blocking: Finding[] = [];\n const warnings: Finding[] = [];\n\n // Under --strict a warning is an error, here as in lint.\n const refuses = (finding: Finding): boolean =>\n finding.file === brief.file && (finding.severity === 'error' || (request.strict === true && finding.severity === 'warning'));\n blocking.push(...(request.findings?.filter(refuses) ?? []));\n // Only a status field can say \"draft\" or \"deferred\", so there is one to point at.\n const field = config.status.field as string;\n if (brief.status === 'draft') {\n blocking.push(\n problem(brief, 'archive-draft', 'error', lineOf(brief, field), 'is a draft, and a draft has not been executed', `set \"${field}: ${config.status.active}\" once the round runs`),\n );\n }\n if (brief.status === 'deferred') {\n blocking.push(\n problem(\n brief,\n 'archive-deferred',\n 'error',\n lineOf(brief, field),\n 'is deferred, and deferred work has not been executed',\n `set \"${field}: ${config.status.active}\" when its trigger fires and the round runs`,\n ),\n );\n }\n for (const task of openTasks(brief, corpus)) {\n blocking.push(\n problem(\n brief,\n 'open-task',\n 'error',\n task.line + 1,\n `\"${task.text === '' ? '(empty)' : task.text}\" is neither ticked nor dispositioned`,\n `tick it, or say why under it, as a bullet or as a paragraph after a blank line, starting ${config.archiving.dispositions.map((d) => `\"${d}\"`).join(', ')}`,\n ),\n );\n }\n for (const dependency of brief.dependsOn) {\n const target = resolveDependency(corpus, dependency);\n if (target !== undefined && target !== brief && target.phase === 'live') {\n blocking.push(\n problem(brief, 'dependency-open', 'error', lineOf(brief, 'dependsOn'), `depends on ${target.id ?? target.name}, which is not archived yet`, `archive ${target.id ?? target.name} first, or drop the dependency if it was never real`),\n );\n }\n }\n const bookkeeping = (path: string): boolean => isBriefPath(path, config);\n if (request.dirty !== undefined && request.allowDirty !== true) {\n const outside = request.dirty.filter((path) => !bookkeeping(path));\n if (outside.length > 0) {\n blocking.push(\n problem(brief, 'dirty-tree', 'error', 1, `the working tree has uncommitted changes outside the briefs: ${listed(outside)}`, 'commit them so the recorded commit holds the round, or pass --allow-dirty'),\n );\n }\n }\n\n const work = (request.changes ?? []).filter((change) => !bookkeeping(change.path));\n const protectedGlobs = globs(brief.protectedFiles);\n const affectedGlobs = globs(brief.affectedFiles);\n // Each protected file changed is its own defect - reverted, or ruled on,\n // one file at a time - so each is a finding that names its path.\n for (const change of work) {\n if (protectedGlobs.some((g) => matchGlob(g, change.path))) blocking.push(protectedFinding(brief, change.path));\n }\n // A protected file is reported once, as protected, and not again as out of scope.\n const unprotected = work.filter((c) => !protectedGlobs.some((g) => matchGlob(g, c.path)));\n const outside = affectedGlobs.length === 0 ? [] : unprotected.filter((c) => !affectedGlobs.some((g) => matchGlob(g, c.path)));\n if (outside.length > 0) {\n const finding = outOfScopeFinding(brief, outside.map((c) => c.path), request.strict === true);\n (request.strict === true ? blocking : warnings).push(finding);\n }\n const scopeSeverity = configuredSeverity(corpus, 'scope-unmeasured');\n const unmeasured = scopeSeverity === null ? null : unmeasuredScope(brief, request, scopeSeverity);\n if (unmeasured !== null) place(unmeasured, request.strict === true, blocking, warnings);\n if (corpus.briefs.some((b) => b.file === to)) {\n blocking.push(problem(brief, 'archive-exists', 'error', 1, `${to} already exists`, 'an archived brief is never overwritten'));\n }\n\n let lines: string[] = [...brief.lines];\n let linksRewritten = 0;\n if (config.archiving.rewriteLinks) {\n const rewritten = rewriteLinks(brief.scan, dirOf(brief.file), archiveDir, (p) => (p === brief.file ? to : p));\n lines = rewritten.lines;\n linksRewritten = rewritten.count;\n }\n lines = withoutBanner(lines, brief.banner);\n\n const pr = request.pr;\n const values: Record<string, string> = {\n date: request.date,\n summary: request.summary?.trim() ?? '',\n pr: pr === undefined ? '' : pr.url === null ? `#${pr.number}` : `[#${pr.number}](${pr.url})`,\n commit: request.commit?.sha.slice(0, 7) ?? '',\n diffstat: request.commit === undefined || request.changes === undefined ? '' : diffstat(request.changes),\n links:\n linksRewritten === 0\n ? ''\n : `Relative links were rewritten to resolve from \\`${archiveDir}/\\` (${linksRewritten}); no other word changed.`,\n id: brief.id ?? '',\n title: brief.title ?? '',\n author: request.commit?.author ?? '',\n };\n const banner = renderBanner(config.archiving.banner, values);\n if (banner.length > 0) lines = withBanner(lines, banner);\n const writes = writable(brief, lines, config.status.field !== null || config.archiving.freeze, blocking);\n if (writes && config.status.field !== null) {\n lines = setEntry(lines, readFrontMatter(lines), config.status.field, renderScalar(config.status.archived));\n }\n lines = removeEntry(lines, readFrontMatter(lines), INTEGRITY_FIELD);\n if (writes && config.archiving.freeze) lines = withIntegrity(lines);\n\n const incoming = inbound(corpus, brief, to);\n staleLinks(corpus, brief, incoming.stale, request.strict === true, blocking, warnings);\n return {\n action: 'archive',\n brief,\n from: brief.file,\n to,\n done: false,\n ops: [\n { kind: 'write', path: to, content: encodeLike(brief.source, lines), before: null },\n ...incoming.ops,\n { kind: 'remove', path: brief.file, before: brief.source },\n ],\n blocking,\n warnings,\n linksRewritten,\n inboundRewritten: incoming.rewritten,\n inboundFrozen: incoming.frozen,\n banner,\n changes: request.changes ?? [],\n };\n}\n\n/** A plugin's answer to a refusal it may lift: which rule, for which path, and why. */\nexport interface Waiver {\n readonly rule: string;\n readonly path: string;\n readonly reason: string;\n}\n\n/**\n * The refusals a plugin may lift. A tool that verifies a ruling can say a\n * protected file was allowed to change, or that a file outside the scope was;\n * whether a round is done is spec-brief's to say.\n */\nexport const WAIVABLE: readonly string[] = ['protected-file', 'out-of-scope'];\n\n/**\n * The plan with the refusals plugins waived turned into notes. A waiver\n * matches a blocking finding by rule and path: a protected-file finding is\n * replaced by the note, and an out-of-scope finding loses the path and goes\n * when none is left. A waiver that matches nothing changes nothing, and one\n * for any other rule is ignored with a warning, which refuses under --strict\n * as any warning does.\n */\nexport function applyWaivers(plan: Plan, waivers: readonly { readonly plugin: string; readonly waiver: Waiver }[], strict = false): Plan {\n const { brief } = plan;\n const blocking = [...plan.blocking];\n const warnings = [...plan.warnings];\n for (const { plugin, waiver } of waivers) {\n if (!WAIVABLE.includes(waiver.rule)) {\n const ignored = problem(\n brief,\n 'waiver-ignored',\n 'warning',\n 1,\n `${plugin} asked to waive ${waiver.rule} for ${waiver.path}; a plugin may waive only ${WAIVABLE.join(' and ')}`,\n `report it to the plugin's authors: ${waiver.rule} is spec-brief's to decide`,\n );\n place(ignored, strict, blocking, warnings);\n continue;\n }\n const at = blocking.findIndex((f) => f.rule === waiver.rule && (f.path === waiver.path || f.paths?.includes(waiver.path) === true));\n if (at < 0) continue;\n const found = blocking[at] as Finding;\n const rest = (found.paths ?? []).filter((p) => p !== waiver.path);\n blocking.splice(at, 1, ...(rest.length > 0 ? [outOfScopeFinding(brief, rest, true)] : []));\n warnings.push({\n ...problem(brief, 'waived', 'note', found.line, `${plugin} waives ${waiver.rule} for ${waiver.path}: ${waiver.reason}`, 'review the waiver with the round; it stands where the refusal was'),\n path: waiver.path,\n });\n }\n return { ...plan, blocking, warnings };\n}\n\nexport interface UnarchiveRequest {\n /** Warnings refuse the reopening, as they fail lint. */\n readonly strict?: boolean | undefined;\n}\n\n/**\n * Reopens an archived brief: the banner and the freeze come off, the status\n * goes back to the live word, and the links are rewritten for the directory\n * it returns to. What the round wrote stays in history, where it belongs.\n */\nexport function planUnarchive(corpus: Corpus, brief: Brief, request: UnarchiveRequest = {}): Plan {\n if (brief.phase === 'live') return donePlan('unarchive', brief);\n const { config } = corpus;\n const briefsDir = normalisePath(config.briefs);\n const to = within(briefsDir, brief.name);\n const blocking: Finding[] = [];\n const warnings: Finding[] = [];\n if (corpus.briefs.some((b) => b.file === to)) {\n blocking.push(problem(brief, 'unarchive-exists', 'error', 1, `${to} already exists`, 'rename one of them first'));\n }\n\n let lines: string[] = [...brief.lines];\n let linksRewritten = 0;\n if (config.archiving.rewriteLinks) {\n const rewritten = rewriteLinks(brief.scan, dirOf(brief.file), briefsDir, (p) => (p === brief.file ? to : p));\n lines = rewritten.lines;\n linksRewritten = rewritten.count;\n }\n lines = withoutBanner(lines, brief.banner);\n lines = removeEntry(lines, readFrontMatter(lines), INTEGRITY_FIELD);\n const field = config.status.field;\n if (writable(brief, lines, field !== null, blocking) && field !== null) {\n lines = setEntry(lines, readFrontMatter(lines), field, renderScalar(config.status.active));\n }\n // Archival gives a brief with no front matter a block to hold its hash.\n // A block this reopening emptied goes too, and the brief is the one that\n // was archived. One that was empty before it was archived reads the same\n // once the hash is in it, and goes with it; one empty in the archive stays.\n // No edit above reaches the opening line, so a brief that had a block has\n // one still, and only a YAML block has lost a line.\n const had = brief.frontMatter;\n const emptied = had !== null && had.close > 1 && (readFrontMatter(lines) as FrontMatter).close === 1;\n if (emptied) lines = lines.slice(2);\n\n const incoming = inbound(corpus, brief, to);\n staleLinks(corpus, brief, incoming.stale, request.strict === true, blocking, warnings);\n return {\n action: 'unarchive',\n brief,\n from: brief.file,\n to,\n done: false,\n ops: [\n { kind: 'write', path: to, content: encodeLike(brief.source, lines), before: null },\n ...incoming.ops,\n { kind: 'remove', path: brief.file, before: brief.source },\n ],\n blocking,\n warnings,\n linksRewritten,\n inboundRewritten: incoming.rewritten,\n inboundFrozen: incoming.frozen,\n banner: [],\n changes: [],\n };\n}\n"]}
package/dist/brief.js CHANGED
@@ -64,7 +64,8 @@ export function parseBrief(file, text, config, phase) {
64
64
  const content = stripBom(text);
65
65
  const lines = splitLines(content);
66
66
  const frontMatter = readFrontMatter(lines);
67
- const bodyStart = frontMatter === null || frontMatter.close < 0 ? 0 : frontMatter.close + 1;
67
+ // A block never closed has `close` -1: the body starts at the top, as with no block.
68
+ const bodyStart = frontMatter === null ? 0 : frontMatter.close + 1;
68
69
  const scanned = scan(lines);
69
70
  const problems = [];
70
71
  const name = file.slice(file.lastIndexOf('/') + 1);
@@ -92,9 +93,10 @@ export function parseBrief(file, text, config, phase) {
92
93
  };
93
94
  const waveText = text1('wave');
94
95
  let wave = null;
95
- const waveEntry = findEntry(frontMatter, 'wave');
96
- if (waveText !== null && waveEntry !== undefined) {
97
- const quoted = waveEntry.value.kind === 'scalar' && waveEntry.value.scalar.quoted;
96
+ if (waveText !== null) {
97
+ // Text was read, so there is an entry, and its value is a scalar.
98
+ const waveEntry = findEntry(frontMatter, 'wave');
99
+ const { quoted } = waveEntry.value.scalar;
98
100
  if (!quoted && /^\d{1,9}$/.test(waveText))
99
101
  wave = Number(waveText);
100
102
  else
@@ -111,8 +113,9 @@ export function parseBrief(file, text, config, phase) {
111
113
  let banner = null;
112
114
  const open = lines.findIndex((_, i) => i >= bodyStart && marker(i, BANNER_OPEN));
113
115
  if (open >= 0) {
116
+ // The opening line is no closing marker, so the search could start on it and find the same.
114
117
  const close = lines.findIndex((_, i) => i > open && marker(i, BANNER_CLOSE));
115
- if (close > open)
118
+ if (close !== -1)
116
119
  banner = { start: open, end: close + 1 };
117
120
  }
118
121
  return {
package/dist/brief.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"brief.js","sourceRoot":"","sources":["../src/brief.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One brief, read: its identity, its front matter, its sections and its task\n * items. A pure function of a file's path and text.\n */\n\nimport type { Config } from './config.js';\nimport { findEntry, isNull, keyName, readFrontMatter, type FrontMatter, type YamlValue } from './frontmatter.js';\nimport { scan, sectionsOf, titleOf, type Scan, type Section, type TaskItem } from './markdown.js';\nimport { splitLines, stripBom } from './text.js';\nimport type { Phase, Status } from './types.js';\n\nexport const BANNER_OPEN = '<!-- spec-brief:banner -->';\nexport const BANNER_CLOSE = '<!-- /spec-brief:banner -->';\n\n/** Front-matter keys spec-brief itself reads, in the spelling it documents. */\nexport const BUILT_IN_FIELDS: readonly string[] = [\n 'id',\n 'title',\n 'type',\n 'status',\n 'date',\n 'wave',\n 'dependsOn',\n 'affectedFiles',\n 'protectedFiles',\n 'trigger',\n 'integrity',\n];\n\nexport interface FieldProblem {\n readonly field: string;\n /** 0-based line. */\n readonly line: number;\n readonly message: string;\n}\n\nexport interface Brief {\n /** `null` when no id can be read, which a rule reports. */\n readonly id: string | null;\n /** Repository-relative path. */\n readonly file: string;\n /** The file name. */\n readonly name: string;\n readonly phase: Phase;\n /** The file exactly as read, byte-order mark and line endings included. */\n readonly source: string;\n /** The text parsed: the source without a byte-order mark. */\n readonly text: string;\n readonly lines: readonly string[];\n readonly frontMatter: FrontMatter | null;\n /** 0-based first line after the front matter. */\n readonly bodyStart: number;\n readonly scan: Scan;\n readonly sections: readonly Section[];\n readonly tasks: readonly TaskItem[];\n readonly title: string | null;\n readonly status: Status | null;\n readonly statusWord: string | null;\n readonly type: string | null;\n readonly wave: number | null;\n readonly dependsOn: readonly string[];\n readonly affectedFiles: readonly string[];\n readonly protectedFiles: readonly string[];\n /** For deferred work: the observable event that brings it back. */\n readonly trigger: string | null;\n readonly integrity: string | null;\n /** The frozen banner's lines, markers included, end exclusive. */\n readonly banner: { readonly start: number; readonly end: number } | null;\n readonly problems: readonly FieldProblem[];\n}\n\nfunction valueText(value: YamlValue): string | null | { readonly problem: string } {\n if (value.kind === 'unsupported') return { problem: value.reason };\n if (value.kind === 'list') return { problem: 'must be a single value, not a list' };\n return isNull(value.scalar) ? null : value.scalar.text;\n}\n\nfunction valueList(value: YamlValue): string[] | { readonly problem: string } {\n if (value.kind === 'unsupported') return { problem: value.reason };\n if (value.kind === 'scalar') return isNull(value.scalar) ? [] : [value.scalar.text];\n const empty = value.items.find((item) => isNull(item) || item.text.trim() === '');\n if (empty !== undefined) return { problem: 'must not contain an empty item' };\n return value.items.map((item) => item.text.trim());\n}\n\n/** The id a file name carries: everything before the separator, or the whole stem. */\nexport function idFromName(name: string, separator: string): string {\n const stem = name.replace(/\\.md$/i, '');\n const at = stem.indexOf(separator);\n return at > 0 ? stem.slice(0, at) : stem;\n}\n\nfunction statusFrom(word: string | null, config: Config, phase: Phase): Status | null {\n if (config.status.field === null) return phase === 'archived' ? 'archived' : 'active';\n if (word === null) return null;\n const lower = word.toLowerCase();\n if (lower === config.status.archived.toLowerCase()) return 'archived';\n if (lower === config.status.active.toLowerCase()) return 'active';\n if (config.status.draft !== null && lower === config.status.draft.toLowerCase()) return 'draft';\n if (config.status.deferred !== null && lower === config.status.deferred.toLowerCase()) return 'deferred';\n return null;\n}\n\nexport function parseBrief(file: string, text: string, config: Config, phase: Phase): Brief {\n const content = stripBom(text);\n const lines = splitLines(content);\n const frontMatter = readFrontMatter(lines);\n const bodyStart = frontMatter === null || frontMatter.close < 0 ? 0 : frontMatter.close + 1;\n const scanned = scan(lines);\n const problems: FieldProblem[] = [];\n const name = file.slice(file.lastIndexOf('/') + 1);\n\n const text1 = (field: string): string | null => {\n const entry = findEntry(frontMatter, field);\n if (entry === undefined) return null;\n const value = valueText(entry.value);\n if (value !== null && typeof value === 'object') {\n problems.push({ field, line: entry.line, message: `\"${entry.key}\" ${value.problem}` });\n return null;\n }\n return value;\n };\n const list = (field: string): string[] => {\n const entry = findEntry(frontMatter, field);\n if (entry === undefined) return [];\n const value = valueList(entry.value);\n if (!Array.isArray(value)) {\n problems.push({ field, line: entry.line, message: `\"${entry.key}\" ${value.problem}` });\n return [];\n }\n return value;\n };\n\n const waveText = text1('wave');\n let wave: number | null = null;\n const waveEntry = findEntry(frontMatter, 'wave');\n if (waveText !== null && waveEntry !== undefined) {\n const quoted = waveEntry.value.kind === 'scalar' && waveEntry.value.scalar.quoted;\n if (!quoted && /^\\d{1,9}$/.test(waveText)) wave = Number(waveText);\n else problems.push({ field: 'wave', line: waveEntry.line, message: `\"wave\" must be a whole number, not \"${waveText}\"` });\n }\n\n const statusWord = config.status.field === null ? null : text1(config.status.field);\n const frontTitle = text1('title');\n const heading = titleOf(scanned);\n const id = config.id.source === 'filename' ? idFromName(name, config.id.separator) : text1('id');\n\n // A marker is a comment on a line of its own. Quoted in a code block it is\n // text: the prose mask keeps code and blanks comments, so a real marker is\n // a line whose prose is empty.\n const marker = (line: number, text: string): boolean =>\n (lines[line] as string).trim() === text && (scanned.prose[line] as string).trim() === '';\n let banner: Brief['banner'] = null;\n const open = lines.findIndex((_, i) => i >= bodyStart && marker(i, BANNER_OPEN));\n if (open >= 0) {\n const close = lines.findIndex((_, i) => i > open && marker(i, BANNER_CLOSE));\n if (close > open) banner = { start: open, end: close + 1 };\n }\n\n return {\n id: id === null || id.trim() === '' ? null : id.trim(),\n file,\n name,\n phase,\n source: text,\n text: content,\n lines,\n frontMatter,\n bodyStart,\n scan: scanned,\n sections: sectionsOf(scanned),\n tasks: scanned.tasks,\n title: frontTitle ?? (heading === undefined || heading.text === '' ? null : heading.text),\n status: statusFrom(statusWord, config, phase),\n statusWord,\n type: text1('type'),\n wave,\n dependsOn: list('dependsOn'),\n affectedFiles: list('affectedFiles'),\n protectedFiles: list('protectedFiles'),\n trigger: text1('trigger'),\n integrity: text1('integrity'),\n banner,\n problems,\n };\n}\n\n/** The 0-based line a front-matter key is declared on, or the first line when it is absent. */\nexport function lineOfField(brief: Brief, field: string): number {\n return findEntry(brief.frontMatter, field)?.line ?? 0;\n}\n\n/** Keys spec-brief knows, compared the way front matter compares them. */\nexport function knownFields(config: Config): Set<string> {\n const names = [...BUILT_IN_FIELDS, ...config.fields];\n if (config.status.field !== null) names.push(config.status.field);\n return new Set(names.map(keyName));\n}\n"]}
1
+ {"version":3,"file":"brief.js","sourceRoot":"","sources":["../src/brief.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One brief, read: its identity, its front matter, its sections and its task\n * items. A pure function of a file's path and text.\n */\n\nimport type { Config } from './config.js';\nimport { findEntry, isNull, keyName, readFrontMatter, type FrontMatter, type FrontMatterEntry, type YamlValue } from './frontmatter.js';\nimport { scan, sectionsOf, titleOf, type Scan, type Section, type TaskItem } from './markdown.js';\nimport { splitLines, stripBom } from './text.js';\nimport type { Phase, Status } from './types.js';\n\nexport const BANNER_OPEN = '<!-- spec-brief:banner -->';\nexport const BANNER_CLOSE = '<!-- /spec-brief:banner -->';\n\n/** Front-matter keys spec-brief itself reads, in the spelling it documents. */\nexport const BUILT_IN_FIELDS: readonly string[] = [\n 'id',\n 'title',\n 'type',\n 'status',\n 'date',\n 'wave',\n 'dependsOn',\n 'affectedFiles',\n 'protectedFiles',\n 'trigger',\n 'integrity',\n];\n\nexport interface FieldProblem {\n readonly field: string;\n /** 0-based line. */\n readonly line: number;\n readonly message: string;\n}\n\nexport interface Brief {\n /** `null` when no id can be read, which a rule reports. */\n readonly id: string | null;\n /** Repository-relative path. */\n readonly file: string;\n /** The file name. */\n readonly name: string;\n readonly phase: Phase;\n /** The file exactly as read, byte-order mark and line endings included. */\n readonly source: string;\n /** The text parsed: the source without a byte-order mark. */\n readonly text: string;\n readonly lines: readonly string[];\n readonly frontMatter: FrontMatter | null;\n /** 0-based first line after the front matter. */\n readonly bodyStart: number;\n readonly scan: Scan;\n readonly sections: readonly Section[];\n readonly tasks: readonly TaskItem[];\n readonly title: string | null;\n readonly status: Status | null;\n readonly statusWord: string | null;\n readonly type: string | null;\n readonly wave: number | null;\n readonly dependsOn: readonly string[];\n readonly affectedFiles: readonly string[];\n readonly protectedFiles: readonly string[];\n /** For deferred work: the observable event that brings it back. */\n readonly trigger: string | null;\n readonly integrity: string | null;\n /** The frozen banner's lines, markers included, end exclusive. */\n readonly banner: { readonly start: number; readonly end: number } | null;\n readonly problems: readonly FieldProblem[];\n}\n\nfunction valueText(value: YamlValue): string | null | { readonly problem: string } {\n if (value.kind === 'unsupported') return { problem: value.reason };\n if (value.kind === 'list') return { problem: 'must be a single value, not a list' };\n return isNull(value.scalar) ? null : value.scalar.text;\n}\n\nfunction valueList(value: YamlValue): string[] | { readonly problem: string } {\n if (value.kind === 'unsupported') return { problem: value.reason };\n if (value.kind === 'scalar') return isNull(value.scalar) ? [] : [value.scalar.text];\n const empty = value.items.find((item) => isNull(item) || item.text.trim() === '');\n if (empty !== undefined) return { problem: 'must not contain an empty item' };\n return value.items.map((item) => item.text.trim());\n}\n\n/** The id a file name carries: everything before the separator, or the whole stem. */\nexport function idFromName(name: string, separator: string): string {\n const stem = name.replace(/\\.md$/i, '');\n const at = stem.indexOf(separator);\n return at > 0 ? stem.slice(0, at) : stem;\n}\n\nfunction statusFrom(word: string | null, config: Config, phase: Phase): Status | null {\n if (config.status.field === null) return phase === 'archived' ? 'archived' : 'active';\n if (word === null) return null;\n const lower = word.toLowerCase();\n if (lower === config.status.archived.toLowerCase()) return 'archived';\n if (lower === config.status.active.toLowerCase()) return 'active';\n if (config.status.draft !== null && lower === config.status.draft.toLowerCase()) return 'draft';\n if (config.status.deferred !== null && lower === config.status.deferred.toLowerCase()) return 'deferred';\n return null;\n}\n\nexport function parseBrief(file: string, text: string, config: Config, phase: Phase): Brief {\n const content = stripBom(text);\n const lines = splitLines(content);\n const frontMatter = readFrontMatter(lines);\n // A block never closed has `close` -1: the body starts at the top, as with no block.\n const bodyStart = frontMatter === null ? 0 : frontMatter.close + 1;\n const scanned = scan(lines);\n const problems: FieldProblem[] = [];\n const name = file.slice(file.lastIndexOf('/') + 1);\n\n const text1 = (field: string): string | null => {\n const entry = findEntry(frontMatter, field);\n if (entry === undefined) return null;\n const value = valueText(entry.value);\n if (value !== null && typeof value === 'object') {\n problems.push({ field, line: entry.line, message: `\"${entry.key}\" ${value.problem}` });\n return null;\n }\n return value;\n };\n const list = (field: string): string[] => {\n const entry = findEntry(frontMatter, field);\n if (entry === undefined) return [];\n const value = valueList(entry.value);\n if (!Array.isArray(value)) {\n problems.push({ field, line: entry.line, message: `\"${entry.key}\" ${value.problem}` });\n return [];\n }\n return value;\n };\n\n const waveText = text1('wave');\n let wave: number | null = null;\n if (waveText !== null) {\n // Text was read, so there is an entry, and its value is a scalar.\n const waveEntry = findEntry(frontMatter, 'wave') as FrontMatterEntry;\n const { quoted } = (waveEntry.value as Extract<YamlValue, { kind: 'scalar' }>).scalar;\n if (!quoted && /^\\d{1,9}$/.test(waveText)) wave = Number(waveText);\n else problems.push({ field: 'wave', line: waveEntry.line, message: `\"wave\" must be a whole number, not \"${waveText}\"` });\n }\n\n const statusWord = config.status.field === null ? null : text1(config.status.field);\n const frontTitle = text1('title');\n const heading = titleOf(scanned);\n const id = config.id.source === 'filename' ? idFromName(name, config.id.separator) : text1('id');\n\n // A marker is a comment on a line of its own. Quoted in a code block it is\n // text: the prose mask keeps code and blanks comments, so a real marker is\n // a line whose prose is empty.\n const marker = (line: number, text: string): boolean =>\n (lines[line] as string).trim() === text && (scanned.prose[line] as string).trim() === '';\n let banner: Brief['banner'] = null;\n const open = lines.findIndex((_, i) => i >= bodyStart && marker(i, BANNER_OPEN));\n if (open >= 0) {\n // The opening line is no closing marker, so the search could start on it and find the same.\n const close = lines.findIndex((_, i) => i > open && marker(i, BANNER_CLOSE));\n if (close !== -1) banner = { start: open, end: close + 1 };\n }\n\n return {\n id: id === null || id.trim() === '' ? null : id.trim(),\n file,\n name,\n phase,\n source: text,\n text: content,\n lines,\n frontMatter,\n bodyStart,\n scan: scanned,\n sections: sectionsOf(scanned),\n tasks: scanned.tasks,\n title: frontTitle ?? (heading === undefined || heading.text === '' ? null : heading.text),\n status: statusFrom(statusWord, config, phase),\n statusWord,\n type: text1('type'),\n wave,\n dependsOn: list('dependsOn'),\n affectedFiles: list('affectedFiles'),\n protectedFiles: list('protectedFiles'),\n trigger: text1('trigger'),\n integrity: text1('integrity'),\n banner,\n problems,\n };\n}\n\n/** The 0-based line a front-matter key is declared on, or the first line when it is absent. */\nexport function lineOfField(brief: Brief, field: string): number {\n return findEntry(brief.frontMatter, field)?.line ?? 0;\n}\n\n/** Keys spec-brief knows, compared the way front matter compares them. */\nexport function knownFields(config: Config): Set<string> {\n const names = [...BUILT_IN_FIELDS, ...config.fields];\n if (config.status.field !== null) names.push(config.status.field);\n return new Set(names.map(keyName));\n}\n"]}