@descent-vtt/spec-guard 0.11.0 → 0.12.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 (157) hide show
  1. package/CHANGELOG.md +443 -31
  2. package/README.md +403 -41
  3. package/dist/cites.d.ts +192 -0
  4. package/dist/cites.d.ts.map +1 -0
  5. package/dist/cites.js +599 -0
  6. package/dist/cites.js.map +1 -0
  7. package/dist/cli.d.ts +14 -6
  8. package/dist/cli.d.ts.map +1 -1
  9. package/dist/cli.js +315 -22
  10. package/dist/cli.js.map +1 -1
  11. package/dist/comments.d.ts +1 -1
  12. package/dist/comments.js +1 -1
  13. package/dist/config.d.ts +3 -0
  14. package/dist/config.d.ts.map +1 -1
  15. package/dist/config.js +46 -5
  16. package/dist/config.js.map +1 -1
  17. package/dist/engine.d.ts.map +1 -1
  18. package/dist/engine.js +38 -14
  19. package/dist/engine.js.map +1 -1
  20. package/dist/glob.d.ts +128 -24
  21. package/dist/glob.d.ts.map +1 -1
  22. package/dist/glob.js +304 -61
  23. package/dist/glob.js.map +1 -1
  24. package/dist/impact.d.ts +179 -0
  25. package/dist/impact.d.ts.map +1 -0
  26. package/dist/impact.js +360 -0
  27. package/dist/impact.js.map +1 -0
  28. package/dist/index.d.ts +15 -7
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +8 -4
  31. package/dist/index.js.map +1 -1
  32. package/dist/mcp.d.ts +22 -75
  33. package/dist/mcp.d.ts.map +1 -1
  34. package/dist/mcp.js +206 -370
  35. package/dist/mcp.js.map +1 -1
  36. package/dist/overlay.d.ts +39 -0
  37. package/dist/overlay.d.ts.map +1 -0
  38. package/dist/overlay.js +133 -0
  39. package/dist/overlay.js.map +1 -0
  40. package/dist/parser.d.ts +41 -24
  41. package/dist/parser.d.ts.map +1 -1
  42. package/dist/parser.js +217 -160
  43. package/dist/parser.js.map +1 -1
  44. package/dist/prove.d.ts +46 -0
  45. package/dist/prove.d.ts.map +1 -0
  46. package/dist/prove.js +549 -0
  47. package/dist/prove.js.map +1 -0
  48. package/dist/query.d.ts +18 -1
  49. package/dist/query.d.ts.map +1 -1
  50. package/dist/query.js +9 -3
  51. package/dist/query.js.map +1 -1
  52. package/dist/reporter.d.ts +139 -1
  53. package/dist/reporter.d.ts.map +1 -1
  54. package/dist/reporter.js +593 -21
  55. package/dist/reporter.js.map +1 -1
  56. package/dist/rules.js +3 -3
  57. package/dist/rules.js.map +1 -1
  58. package/dist/runner.d.ts +31 -2
  59. package/dist/runner.d.ts.map +1 -1
  60. package/dist/runner.js +85 -21
  61. package/dist/runner.js.map +1 -1
  62. package/dist/specs.d.ts +27 -1
  63. package/dist/specs.d.ts.map +1 -1
  64. package/dist/specs.js +45 -10
  65. package/dist/specs.js.map +1 -1
  66. package/dist/structure.d.ts.map +1 -1
  67. package/dist/structure.js +7 -6
  68. package/dist/structure.js.map +1 -1
  69. package/dist/types.d.ts +193 -0
  70. package/dist/types.d.ts.map +1 -1
  71. package/dist/vendor/spec-core/jsonrpc/index.d.ts +3 -0
  72. package/dist/vendor/spec-core/jsonrpc/index.d.ts.map +1 -0
  73. package/dist/vendor/spec-core/jsonrpc/index.js +2 -0
  74. package/dist/vendor/spec-core/jsonrpc/index.js.map +1 -0
  75. package/dist/vendor/spec-core/jsonrpc/mcp.d.ts +158 -0
  76. package/dist/vendor/spec-core/jsonrpc/mcp.d.ts.map +1 -0
  77. package/dist/vendor/spec-core/jsonrpc/mcp.js +353 -0
  78. package/dist/vendor/spec-core/jsonrpc/mcp.js.map +1 -0
  79. package/dist/vendor/spec-core/markdown/frontmatter.d.ts +104 -0
  80. package/dist/vendor/spec-core/markdown/frontmatter.d.ts.map +1 -0
  81. package/dist/vendor/spec-core/markdown/frontmatter.js +459 -0
  82. package/dist/vendor/spec-core/markdown/frontmatter.js.map +1 -0
  83. package/dist/vendor/spec-core/markdown/headings.d.ts +46 -0
  84. package/dist/vendor/spec-core/markdown/headings.d.ts.map +1 -0
  85. package/dist/vendor/spec-core/markdown/headings.js +199 -0
  86. package/dist/vendor/spec-core/markdown/headings.js.map +1 -0
  87. package/dist/vendor/spec-core/markdown/index.d.ts +6 -0
  88. package/dist/vendor/spec-core/markdown/index.d.ts.map +1 -0
  89. package/dist/vendor/spec-core/markdown/index.js +4 -0
  90. package/dist/vendor/spec-core/markdown/index.js.map +1 -0
  91. package/dist/vendor/spec-core/markdown/layout.d.ts +40 -0
  92. package/dist/vendor/spec-core/markdown/layout.d.ts.map +1 -0
  93. package/dist/vendor/spec-core/markdown/layout.js +28 -0
  94. package/dist/vendor/spec-core/markdown/layout.js.map +1 -0
  95. package/dist/vendor/spec-core/markdown/links.d.ts +26 -0
  96. package/dist/vendor/spec-core/markdown/links.d.ts.map +1 -0
  97. package/dist/vendor/spec-core/markdown/links.js +394 -0
  98. package/dist/vendor/spec-core/markdown/links.js.map +1 -0
  99. package/dist/vendor/spec-core/markdown/lists.d.ts +23 -0
  100. package/dist/vendor/spec-core/markdown/lists.d.ts.map +1 -0
  101. package/dist/vendor/spec-core/markdown/lists.js +127 -0
  102. package/dist/vendor/spec-core/markdown/lists.js.map +1 -0
  103. package/dist/vendor/spec-core/markdown/scan.d.ts +37 -0
  104. package/dist/vendor/spec-core/markdown/scan.d.ts.map +1 -0
  105. package/dist/vendor/spec-core/markdown/scan.js +463 -0
  106. package/dist/vendor/spec-core/markdown/scan.js.map +1 -0
  107. package/dist/vendor/spec-core/markdown/syntax.d.ts +76 -0
  108. package/dist/vendor/spec-core/markdown/syntax.d.ts.map +1 -0
  109. package/dist/vendor/spec-core/markdown/syntax.js +181 -0
  110. package/dist/vendor/spec-core/markdown/syntax.js.map +1 -0
  111. package/dist/vendor/spec-core/markdown/tables.d.ts +19 -0
  112. package/dist/vendor/spec-core/markdown/tables.d.ts.map +1 -0
  113. package/dist/vendor/spec-core/markdown/tables.js +86 -0
  114. package/dist/vendor/spec-core/markdown/tables.js.map +1 -0
  115. package/dist/vendor/spec-core/markdown/types.d.ts +291 -0
  116. package/dist/vendor/spec-core/markdown/types.d.ts.map +1 -0
  117. package/dist/vendor/spec-core/markdown/types.js +9 -0
  118. package/dist/vendor/spec-core/markdown/types.js.map +1 -0
  119. package/dist/vendor/spec-core/path/index.d.ts +2 -0
  120. package/dist/vendor/spec-core/path/index.d.ts.map +1 -0
  121. package/dist/vendor/spec-core/path/index.js +2 -0
  122. package/dist/vendor/spec-core/path/index.js.map +1 -0
  123. package/dist/vendor/spec-core/path/posix.d.ts +84 -0
  124. package/dist/vendor/spec-core/path/posix.d.ts.map +1 -0
  125. package/dist/vendor/spec-core/path/posix.js +150 -0
  126. package/dist/vendor/spec-core/path/posix.js.map +1 -0
  127. package/dist/vendor/spec-core/pattern/automaton.d.ts +130 -0
  128. package/dist/vendor/spec-core/pattern/automaton.d.ts.map +1 -0
  129. package/dist/vendor/spec-core/pattern/automaton.js +340 -0
  130. package/dist/vendor/spec-core/pattern/automaton.js.map +1 -0
  131. package/dist/vendor/spec-core/pattern/charset.d.ts +59 -0
  132. package/dist/vendor/spec-core/pattern/charset.d.ts.map +1 -0
  133. package/dist/vendor/spec-core/pattern/charset.js +116 -0
  134. package/dist/vendor/spec-core/pattern/charset.js.map +1 -0
  135. package/dist/vendor/spec-core/pattern/glob.d.ts +122 -0
  136. package/dist/vendor/spec-core/pattern/glob.d.ts.map +1 -0
  137. package/dist/vendor/spec-core/pattern/glob.js +481 -0
  138. package/dist/vendor/spec-core/pattern/glob.js.map +1 -0
  139. package/dist/vendor/spec-core/pattern/index.d.ts +7 -0
  140. package/dist/vendor/spec-core/pattern/index.d.ts.map +1 -0
  141. package/dist/vendor/spec-core/pattern/index.js +4 -0
  142. package/dist/vendor/spec-core/pattern/index.js.map +1 -0
  143. package/dist/vendor/spec-core/pattern/regex.d.ts +68 -0
  144. package/dist/vendor/spec-core/pattern/regex.d.ts.map +1 -0
  145. package/dist/vendor/spec-core/pattern/regex.js +813 -0
  146. package/dist/vendor/spec-core/pattern/regex.js.map +1 -0
  147. package/dist/vendor/spec-core/text/index.d.ts +3 -0
  148. package/dist/vendor/spec-core/text/index.d.ts.map +1 -0
  149. package/dist/vendor/spec-core/text/index.js +2 -0
  150. package/dist/vendor/spec-core/text/index.js.map +1 -0
  151. package/dist/vendor/spec-core/text/lines.d.ts +73 -0
  152. package/dist/vendor/spec-core/text/lines.d.ts.map +1 -0
  153. package/dist/vendor/spec-core/text/lines.js +156 -0
  154. package/dist/vendor/spec-core/text/lines.js.map +1 -0
  155. package/package.json +9 -2
  156. package/src/vendor/spec-core/LICENSE +21 -0
  157. package/src/vendor/spec-core/README.md +8 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,416 @@ All notable changes to this project are documented here. Versions follow
5
5
  may change in a minor release — each such change is listed under **Changed**
6
6
  with the flag that restores the previous behaviour.
7
7
 
8
+ ## 0.12.1
9
+
10
+ The query is fast again. A warm `query` in the MCP server took 27 ms over this
11
+ repository's specs after 0.12.0 moved to spec-core's scanner, past the 20 ms
12
+ ADR-0012 set; it now takes about 6 ms, faster than 0.11.0. The scanner makes
13
+ only what it is asked for, and the server parses a spec again only when its
14
+ bytes change. The shipped README and changelog link to the ADRs by address,
15
+ so the links lead somewhere on npmjs.com and inside `node_modules`.
16
+
17
+ ### Added
18
+
19
+ - **`createDocumentMemo`, and `documents` on `runSpecGuard`, `loadRuleSet`,
20
+ `queryRules` and `impactOf`**: parsed spec documents kept from one call to
21
+ the next, for a process that reads the specs on every request. Every spec
22
+ is still found and read, so an edit is seen by the next call; a document is
23
+ parsed again only when its bytes change. It holds one parse of each
24
+ document the spec set read last names, and forgets one removed, left out or
25
+ edited at the next read. The MCP server keeps one for its lifetime; a
26
+ command run once takes none, and parses every document and hashes none, as
27
+ before.
28
+
29
+ ### Fixed
30
+
31
+ - **The README's and the changelog's links to the ADRs lead somewhere on
32
+ npmjs.com and in node_modules.** They were relative, and the package ships
33
+ neither `docs/` nor `.github/`, so outside the repository the 66 links to
34
+ ADRs and the one to the release workflow led nowhere. Each is an absolute
35
+ URL to the file on GitHub now, and a link to a file the package ships stays
36
+ relative. `tests/package.test.ts` holds every Markdown document the package
37
+ ships to that, and each absolute link to a file the repository holds.
38
+
39
+ ### Changed
40
+
41
+ - `npm publish` in a checkout refuses to run outside GitHub Actions, so a
42
+ version cannot reach npm from a workstation by mistake, without provenance;
43
+ spec-harness 0.1.0 did. The release never runs it: it stages a tarball it
44
+ packed.
45
+ - **spec-core is copied again, from `8840d36`**, `markdown` alone, and
46
+ verified by hash. Its scanner makes links, list items and the directives
47
+ mask the first time each is read, and keeps them, where every scan made all
48
+ three; spec-guard reads the directives mask and neither of the others. It
49
+ also lists an image inside a link's text - a badge wrapped in a link - as a
50
+ link of its own, after the link. Nothing here reads links: over the 145
51
+ Markdown files of the five spec-* repositories every directive, error,
52
+ status, title and warning reads as it did, and a run, `prove` and `cites` of
53
+ this repository answer as they did.
54
+ [ADR-0015](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0015-globs-from-spec-core.md).
55
+ - **A warm query in the MCP server meets its 20 ms budget again**, over
56
+ this repository's 19 specs as over its 18 ADRs. 0.12.0 had lost it to
57
+ spec-core's scanner, which costs about three times what the parser it
58
+ replaced did. Measured together in one process, a query of one path took a
59
+ median of 6.3 ms, against 23.8 ms with `8840d36`'s lazy scan alone,
60
+ 27.4 ms for 0.12.0 and 7.9 ms for 0.11.0; over the ADRs alone, 6.0 ms
61
+ against 18.7, 22.4 and 6.3 ms. The lazy scan takes 13 to 21% off, run to
62
+ run; the rest is the server parsing only a document whose bytes changed. A server's first
63
+ request, and one after an edit, still parse what they read: 24.3 ms for a
64
+ first request here. The 23 ms recorded for 0.12.0 was most likely measured
65
+ over the ADRs alone; over the specs this repository names it was 29 ms.
66
+ [ADR-0012](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0012-query-and-mcp.md)'s
67
+ amendment has both measurements.
68
+
69
+ ## 0.12.0
70
+
71
+ The family's core, and three commands. Patterns and Markdown are read through
72
+ spec-core, copied in and checked by hash: a pattern that cannot be read is
73
+ refused rather than matched as a literal, a glob no longer backtracks, and
74
+ directives are found as CommonMark finds code, comments and front matter.
75
+ `prove` shows every rule in force catching a violation, `cites` holds code
76
+ comments to the specs they cite, and `impact` names who depends on a path.
77
+ `archived` withholds a document.
78
+
79
+ ### Upgrading
80
+
81
+ Run this release beside the one it replaces, each with `--json`, and compare
82
+ `summary.total` before trusting a green run - and, if the totals differ, each
83
+ result's `spec` file and line, which say which rule moved. A rule that stops
84
+ running reads exactly like one that passes. On a consumer of 114 specs and 301
85
+ assertions, both releases ran the same 301 at the same places with the same
86
+ counts. What can differ, and where this changelog says why:
87
+
88
+ - **A run that exits 2**, or a directive that is invalid, where one ran: a
89
+ pattern that cannot be read - `**` inside a name (`docs/**.md`), an
90
+ extended glob, an unclosed `[` or `{` - in `specs`, `exclude`, `--spec`,
91
+ `--exclude` or a directive's attributes.
92
+ - **`summary.total` moving** because a directive moved between prose and code:
93
+ the shapes under Fixed. Every directive-shaped comment left in code, raw
94
+ HTML or front matter is on one line of the report and in `maskedDirectives`.
95
+ - **`summary.inactive` moving**: `archived` withholds a document; a
96
+ front-matter `status` the reader cannot read - text after a closing quote,
97
+ `: ` in a plain value - keeps its document in force with a warning, where
98
+ 0.11.0 took its first word, and nothing below the front matter is read in
99
+ its place; and an invalid directive in a draft is counted once, as invalid.
100
+ - **Warnings that fail nothing**, in `specWarnings`: a front-matter status
101
+ that cannot be read, a block never closed.
102
+ - **`impact` exits 2** when no spec matched, as `query` does.
103
+
104
+ ### Added
105
+
106
+ - **`spec-guard impact <paths...>`: who depends on this?** For each path, the
107
+ files that import it, transitively, each with its distance and the import
108
+ that takes it one step closer, and the rules in force that govern the path or
109
+ any of them, by `query`'s arithmetic. The import graph is ADR-0011's, read
110
+ backwards over the whole tree: relative JavaScript and TypeScript imports by
111
+ its resolution table, and relative Python imports, which name a module
112
+ beside the importing file. Go, Rust, C# and absolute Python references name
113
+ modules and are counted rather than followed; a path in one of those
114
+ languages says its dependents are not computed. An import naming no file, an
115
+ alias or a dynamic `import(name)` is listed, never guessed. `--depth <n>`,
116
+ `--ignore-status`, and `--json` with `formatVersion`. A path that does not
117
+ exist is exit 2, and so is a run in which no spec matched, as for `query`:
118
+ the dependents are written, and `--allow-empty` exits 0 with them. `impactOf`
119
+ is the API. [ADR-0018](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0018-impact.md).
120
+ - **`get_dependents` on the MCP server**, a third tool beside
121
+ `get_architectural_rules` and `check_architecture`: `impact` for a list of
122
+ paths, with `depth` and `include_inactive`. Its structured content is the
123
+ document `impact --json` writes and its text the command's report, and it is
124
+ annotated read-only, as the other two are. Its description, and the server's
125
+ instructions, tell an agent to call it before changing a file others import.
126
+ A path that does not exist is a tool error the model can read. A client
127
+ that pinned the tool list sees three. `impactDocument` is the API for the
128
+ document both answer with. ADR-0018's amendment.
129
+ - **`spec-guard cites`: do the comments cite decisions in force?** A comment
130
+ such as `// ADR-0011: the domain imports no infrastructure` is a claim, and it
131
+ goes stale when the document is never written or is superseded. `cites` reads
132
+ the comment text of every source file - by the classifier's reading, so never
133
+ a string - for the ids of the families a project names in a new `cites`
134
+ option, `[{ "id": "ADR-{n}", "files": "docs/adr/{n}-*.md" }]`, where `{n}` is
135
+ a number (`ADR-7`, `ADR-007` and `0007-x.md` are one document). Without it, a
136
+ directory of numbered specs whose titles all begin with one id and their own
137
+ number is read as a family; anything less is a note and exit 0, not a guess.
138
+ A **ghost** citation, of no document, is an error with the nearest ids as its
139
+ hint. A **stale** one, of a document superseded, deprecated, rejected or
140
+ archived, is a warning (an error under `--strict`) that names the successor
141
+ the document's status line gives, followed to the one in force. Ids another
142
+ project qualifies - `spec-core's ADR-0005` - are counted and not checked,
143
+ and so are ids in a link: after a `/`, `#`, `=` or `?`, or past a `://`,
144
+ as in `https://github.com/org/repo/blob/main/docs/adr/ADR-0042.md`.
145
+ Markdown, spec files, excluded paths and files in no known language are not
146
+ read, and are counted. Human, `--json` (with `formatVersion`), SARIF, GitHub
147
+ and GitLab output; exit 1 on a ghost, 2 for a family whose files match no
148
+ document. `findCitations` is the API.
149
+ [ADR-0017](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0017-citations-in-comments.md).
150
+ - **`spec-guard prove`: can each rule actually fail?** A passing rule cannot
151
+ say whether the code holds or the rule cannot see the code. `prove` shows
152
+ each rule in force a violation of itself - a file holding the forbidden text,
153
+ a file importing the forbidden module, a cycle, an import across layers, a
154
+ misnamed file, a required entry or a partner taken away, a required file
155
+ removed - and runs the rule over it, in memory, through the door every read
156
+ goes through: nothing is written to disk and nothing touches git. Each rule
157
+ is `killed`, `survived` (the finding, with the violation shown) or
158
+ `unprovable` (with the reason). Exit 1 when a rule survived, and under
159
+ `--strict` when one is unprovable; `--json` is versioned by `formatVersion`,
160
+ and `--format sarif` puts a survivor on its directive.
161
+ `proveSpecGuard` is the same for a caller of the API. The first proof of
162
+ this repository's own rules found one that could not fail as written, and
163
+ CI now proves every rule the repository states on each change.
164
+ [ADR-0016](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0016-rules-seen-to-fail.md).
165
+ - **`RunOptions.io`: a run over a tree that is not on disk.** `runSpecGuard`
166
+ reads through the `Io` it is given - finding and reading the specs, checking
167
+ targets exist, walking them, scanning files, reading imports and listing
168
+ directories - and through `nodeIo` when it is given none, as before. The run
169
+ had the door all along and handed the filesystem to every reader itself.
170
+ ripgrep reads the disk in a process of its own, where no door reaches, so
171
+ `engine: 'ripgrep'` with an `io` is refused with a message that says so, and
172
+ `auto` with one searches with the built-in scanner. A search that fails
173
+ outright through the door fails the run, rather than falling back to the
174
+ scanner that reads the disk. [ADR-0014](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0014-configuration-and-watch.md).
175
+ - **`--format gitlab` and `--format github`**, for the run, `prove` and
176
+ `cites`. `gitlab` writes a GitLab Code Quality report - `description`,
177
+ `check_name`, a `fingerprint`, a `severity` and a `location` - which a
178
+ merge request shows beside its changes. The fingerprint is the SHA-256 of
179
+ what a finding is about - a rule's document, kind, subject and targets, as
180
+ SARIF's is; a citation's file, family and id; a document's path - and never
181
+ of its message, which holds the count of matches and the directive's line:
182
+ one more match, or a line added above a directive, would otherwise have
183
+ closed every open issue and opened a new one. Findings that share an
184
+ identity, one ghost cited on two lines, are numbered in order. `github` writes one workflow command per finding, which a job's log
185
+ turns into an annotation on the pull request with no upload step. Both place
186
+ the findings SARIF places: a failing assertion or a surviving rule is
187
+ `critical`, a directive that could not be read `major`, a rule `prove` could
188
+ make no violation for `minor`, and a document not in force is named, as
189
+ `info`, on its first line. `formatGitlab`, `formatGithub`, `runAnnotations`,
190
+ `proveAnnotations` and `citesAnnotations` are the API.
191
+ - **What was not run is counted.** A comment shaped like a directive in code,
192
+ raw HTML or front matter is not run, and a report now says how many and
193
+ where: one dim line in the human report - "7 directive-shaped comments in
194
+ code, raw HTML or front matter were not run: README.md:120, ..." - naming
195
+ the first five, or every one under `--verbose`, and `maskedDirectives` in
196
+ the `--json` of a run and of `prove`, each with what hid it. Most are
197
+ examples; the ones that are not are rules 0.11.0 ran that this release does
198
+ not, such as a directive in an indented line or under a leading `---` read
199
+ as front matter. Warnings about how a document was read - a front-matter
200
+ status that cannot be read, a block never closed - are `specWarnings`.
201
+ Both fields are added, so no `formatVersion` moves.
202
+ [ADR-0002](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0002-directive-format.md)'s amendment.
203
+ - **`formatVersion: 1` on the run's `--json` and on `query --json`**, as
204
+ `prove --json` has had from the start: a field removed or renamed moves it,
205
+ a field added does not. Both documents are otherwise unchanged; this is one
206
+ field more, first in each.
207
+ - **`io` on `loadRuleSet` and `queryRules`, too.** A query found and read the
208
+ specs, and asked whether each path exists, through the filesystem whatever
209
+ the caller had; it now goes through the door it is given, as a run does, so
210
+ a query over a tree in memory sees that tree.
211
+
212
+ ### Fixed
213
+
214
+ - **An invalid directive in a document not in force was counted twice**, as
215
+ invalid and again as a rule not in force, so one bad directive in a draft
216
+ read "1 invalid · 1 not in force". It is counted once, as invalid, in the
217
+ summary, in the document's own line, and in the `inactive` of every JSON
218
+ report: it is no rule the document would run in force.
219
+ - **A glob could take a minute to fail one file name.** Every pattern was
220
+ compiled to a `RegExp`, and `*-*-*-*-*-*x` against a name of 121 dashes made
221
+ V8 try every way of dividing the name between six `[^/]*` groups: 55 seconds,
222
+ measured. Patterns are now read by the glob engine the spec-* tools share,
223
+ spec-core's, whose automaton keeps a set of live states and cannot backtrack:
224
+ the same match takes under a millisecond. spec-core is copied into
225
+ `src/vendor/spec-core` and verified by hash, so `dependencies` stays empty,
226
+ and its MIT notice ships in the package at `src/vendor/spec-core/LICENSE`.
227
+ [ADR-0015](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0015-globs-from-spec-core.md).
228
+ - **ripgrep and the scanner still read some patterns differently**, so the same
229
+ rule could count differently on either side of the tree size at which `auto`
230
+ changes engine. ripgrep anchored every alternative of `{src/*.ts,*.md}`
231
+ because one held a `/`, dropped the empty one in `{,src/}a.ts`, matched
232
+ nothing for `src/./a.ts`, and failed on a `}` that closes nothing. It is now
233
+ handed each alternative as a glob of its own, spelled as the scanner reads
234
+ it. And ripgrep applies no glob to a path named on its command line, so a
235
+ rule whose target is a file its `glob` does not match, or a directory its
236
+ `exclude` does, counted under ripgrep what the scanner left out; its list of
237
+ files is now held to the same filters. ADR-0015.
238
+ - **What is code in a spec is what CommonMark says it is.** Directives inside
239
+ code never execute, and what counted as code was decided by `maskCode`'s own
240
+ rules. It is now decided by spec-core's Markdown scanner, which every spec-*
241
+ tool reads documents with, copied into `src/vendor/spec-core` from `cbe2223`
242
+ and verified by hash; `maskCode` is still exported, and is the scanner's
243
+ mask. [ADR-0002](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0002-directive-format.md)'s amendment.
244
+
245
+ Directives that were hidden, and now execute:
246
+ - **A backtick fence's info string may not hold a backtick.** A line that
247
+ reads ```` ```js`x ```` is prose that opens with a code span. Read as a
248
+ fence, it hid every line under it, a real directive included, until
249
+ something closed it - and nothing said a rule had gone quiet.
250
+ - **A code span ends with its paragraph.** A backtick that closes nothing
251
+ there, as in ``press ` to open the console``, paired with the next
252
+ backtick anywhere in the document and hid every directive between the
253
+ two. An escaped backtick opens nothing.
254
+ - **A fence shown inside a comment opens nothing.** A template that showed
255
+ one hid every directive after it.
256
+ - **A fence line shown as code in a list item opens nothing.** Four columns
257
+ past an item's text is indented code, so a fence line written there - six
258
+ spaces under a `-` item, seven under `1.`, deeper in a nested item - is a
259
+ line of code. The first copy of the scanner read no indented code inside a
260
+ list, took the line for an opener that nothing closed, and hid every
261
+ directive after it.
262
+
263
+ And a code fence or a `<pre>`, `<script>`, `<style>` or `<textarea>`
264
+ block that is never closed, and runs to the end of the document, is a
265
+ warning on its opening line (`specWarnings`), since no directive after it
266
+ runs. It fails nothing: it is read as every renderer shows it, as code.
267
+
268
+ Directives that executed, and are now code:
269
+ - **A closing fence has no info string.** Inside a block opened by three
270
+ backticks, a line of three backticks followed by `js` is a line of the
271
+ block. It used to close it, and a directive shown after it executed.
272
+ - **A fence may be indented with the list item it sits in.** CommonMark
273
+ counts its three spaces from the item, so a fence in a `1.` item nested in
274
+ a `-` item is five or more spaces from the margin. It was not recognised,
275
+ and a directive shown inside it executed: always behind `~~~`, and behind
276
+ backticks whenever the prose above mentioned one, since the two fence lines
277
+ otherwise paired as a code span by accident. An opener may sit at any
278
+ indentation outside indented code, and a closer at most three columns
279
+ deeper than its opener.
280
+ - **Indented code, raw-text HTML and front matter are not read.** Four
281
+ columns past the margin, or past the text of the list item a line sits
282
+ in, after a blank line, are code in every renderer;
283
+ `<script>`, `<pre>`, `<style>` and `<textarea>` hold no Markdown; front
284
+ matter is metadata. `<details>` and `<div>` hold Markdown, and are read.
285
+
286
+ And a backtick inside a comment is a character, so a directive holding one
287
+ keeps it: ``symbol="`eval`"`` is searched for as written, where the two
288
+ backticks paired and the rule searched for six spaces.
289
+
290
+ This repository's own specs execute the same assertions, at the same lines,
291
+ and every directive in the 143 Markdown files of the five spec-* repositories
292
+ is read as before. A test holds the scanner to 0.11.0's masking on every
293
+ Markdown file here, and on random documents built from lines both read alike,
294
+ and shows it parting from 0.11.0 on each shape above.
295
+
296
+ ### Changed
297
+
298
+ - **Mutation testing in CI runs in eight shards**, not four. Three of the four
299
+ took 23 to 28 minutes on the sweep that merged this release's code, and the
300
+ fourth was cancelled at the 30-minute job limit. ADR-0003 has the per-file
301
+ minutes and the split; the longest of the eight took 16m16s.
302
+ - **A pattern that cannot be read is refused.** An unclosed `[` or `{`, an
303
+ extended glob such as `+(a|b)` (a group holding a `|`: `C++(notes).md` and
304
+ `*(2017).md` are names with parentheses), a `..`, or a range that runs
305
+ backwards, in `glob`, `exclude`, `module`, `order`, `pattern`, `dirs` or
306
+ `required`, makes the directive invalid; in `exclude` in the configuration or `--exclude`, or in
307
+ a spec pattern, it is exit 2. Each used to be read as a literal or as whatever
308
+ its `RegExp` happened to mean, which is a filter that matches nothing and
309
+ passes. So is an alternative that names no path, as in `{dist/**,}`, whose
310
+ empty alternative matched the empty string. A brace group with a comma in it
311
+ in a directive is one more case: a list attribute splits on commas, so
312
+ `glob="*.{ts,tsx}"` was always the two patterns `*.{ts` and `tsx}`, and the
313
+ message now says so. A configuration's `exclude` is a JSON array, where braces
314
+ keep their commas. Nothing restores the old readings.
315
+ [ADR-0015](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0015-globs-from-spec-core.md).
316
+ - **`**` inside a name is refused**: `docs/**.md`, `src/**.ts`, `**.ts`,
317
+ `a**b`. The message says what `**` means and gives the two ways to write
318
+ what was meant, `docs/**/*.md` for any depth and `*.md` for one level. It
319
+ used to cross directories under the scanner and not under ripgrep, so a rule
320
+ counted differently by engine; read as `*`, as `.gitignore` reads it,
321
+ `"specs": ["docs/**.md"]` would quietly have stopped finding every nested
322
+ document. In a directive the directive is invalid, in a draft as in a
323
+ document in force. A spec pattern is refused before any command starts: in
324
+ the configuration's `specs`, naming the file, and on the command line or in
325
+ `--spec`, for `query`, `impact`, `cites`, `prove` and `mcp` as for a run -
326
+ exit 2, as `exclude` in the configuration and `--exclude` are.
327
+ `specPatternError` is the check, for a caller of the API.
328
+ - **A `.` or empty segment is no segment** (`src/./a.ts` is `src/a.ts`), and
329
+ **a leading `/` anchors a `glob`** at the root, as it anchors an `exclude`.
330
+ Both matched nothing in the scanner before; ripgrep already anchored.
331
+ - **`archived` withholds a document**, a sixth word beside `draft`,
332
+ `proposed`, `rejected`, `deprecated` and `superseded`. `spec-brief` closes a
333
+ task brief's round by setting `status: archived` and moving the brief to an
334
+ archive directory. Its directives stated the round's premises and goals,
335
+ which are history once the round is closed, and executed anyway they failed.
336
+ An archived document is still parsed, validated and named in every output
337
+ format. `--ignore-status` executes it as before. The list stays closed:
338
+ `done`, `closed`, `archive` and every other near word stay in force, and
339
+ [ADR-0010](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0010-spec-status.md)'s amendment says why this word and
340
+ no other.
341
+ - **A status is found by the scanner's headings and front-matter reader.**
342
+ The three spellings, their order and the words are unchanged, and every
343
+ status in the five spec-* repositories reads as it did. Where the scanner
344
+ reads a document differently, the status follows it:
345
+ - A `## Status` kept in a comment - a template not yet filled in - no longer
346
+ withholds the document, and a `##` in a comment or in code no longer ends
347
+ the preamble where the `**Status:**` label is read.
348
+ - An underlined heading is a heading. An underlined `Status` is the section,
349
+ and an underlined section ends the preamble, so a `status: draft` line with
350
+ `---` directly under it is a heading, as every renderer shows it, and not a
351
+ label.
352
+ - Front matter is read by spec-core's reader, the family's: a quoted value
353
+ to its closing quote and a plain one to a comment, as before. A value it
354
+ refuses declares nothing - a `: ` in a plain value, text after a closing
355
+ quote, a value continued on the next line, `*Draft*`, which YAML reads as
356
+ an alias - and a `status` nested under another key is that key's. Front
357
+ matter behind a byte-order mark, or closed by `...`, is read as front
358
+ matter.
359
+ - A front-matter `status` decides even when it cannot be read. A value the
360
+ reader refuses, an empty one, one with no word or a list leaves the
361
+ status unrecognised and the document in force, and the `## Status`
362
+ section and the `Status:` label are not read in its place: under 0.11.0
363
+ `status: "accepted" (2024-05-01)` was accepted, and falling through to a
364
+ section still saying `Proposed` would have withheld its rules. Every
365
+ format says so, on the key's line: a warning in the human report, a
366
+ `specWarnings` entry in `--json` (run and `prove`), a SARIF notification
367
+ and a `spec-warning` annotation for GitHub and GitLab.
368
+
369
+ [ADR-0010](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0010-spec-status.md)'s second amendment.
370
+ - **A title is the first level-one heading as the scanner reads it.** An
371
+ underlined one (`===`) is a title and one kept in a comment is not; a code
372
+ span in it is kept as written, where it was blanked, and a comment in it is
373
+ dropped. A first level-one heading with no text leaves the document
374
+ untitled rather than promoting the next. In this repository one title
375
+ changed: ADR-0009's keeps its `` `--fix` ``.
376
+ - **`maskCode` keeps a carriage return inside code**, where it blanked one:
377
+ every line terminator stays where it was, a lone `\r` included.
378
+ - **Reading a spec costs about four times what it did**: 17 ms against 4 ms
379
+ over this repository's 18 ADRs, and 23 ms against 9 ms for a warm query of
380
+ one path, past the 20 ms ADR-0012 set out to meet. The scanner finds list
381
+ items, links and tables that nothing here reads.
382
+ [ADR-0002](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0002-directive-format.md)'s amendment says where the
383
+ remedy lies.
384
+ - **The MCP server speaks the protocol through spec-core.** Classifying each
385
+ request, the two eras, dispatch, result shapes and the stdio framing are
386
+ spec-core's `jsonrpc` module, copied from `f085f29` and verified by hash;
387
+ `src/mcp.ts` keeps the instructions, the tools and the resources. What a
388
+ client sees is unchanged, and every test of the server passes as it was
389
+ written. One edge moved: a last line cut inside a UTF-8 character when stdin
390
+ closes is read as far as it goes rather than dropped. The protocol's names
391
+ are still exported from the package. [ADR-0015](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0015-globs-from-spec-core.md).
392
+ - **API:**
393
+ - `createGlobMatcher`, `createExcludeMatcher` and `expandSpecPatterns` throw
394
+ for a pattern spec-core refuses - an unclosed `[` or `{`, an extended
395
+ glob, a `..`, `**` inside a name - naming the pattern, where they read it
396
+ as a literal or as whatever its `RegExp` meant. `globPatternError` and the
397
+ new `specPatternError` say why without throwing.
398
+ - `Annotation` has a required `identity`, which the GitLab fingerprint is
399
+ made of. `SpecSet` and `RunPlan` have required `warnings` and `masked`;
400
+ `RunReport` and `ProveReport` have optional `specWarnings` and
401
+ `maskedDirectives`, and `ParseResult` optional `warnings` and `masked`.
402
+ An `InactiveSpec`'s `directives` counts only those that resolve.
403
+ - New: `proveSpecGuard`, `PROVE_NAME`, `overlayIo` and `readOnce`;
404
+ `findCitations`, `findDocuments`, `deriveFamilies`, `scanCitations`,
405
+ `inComments`, `qualified`, `isStale`, `numberKey`, `documentNumber`,
406
+ `parseIdTemplate`, `parseFilesTemplate`, `citeIdError`, `citeFilesError`,
407
+ `CitesError` and `NUMBER`; `impactOf`, `impactDocument`, `dependentsOf`,
408
+ `buildReverseGraph`, `resolvePython`, `formatImpact`, `formatImpactJson`
409
+ and `ImpactError`; `formatProve`, `formatProveJson`, `formatProveSarif`,
410
+ `formatCites`, `formatCitesJson`, `formatCitesSarif`, `formatGitlab`,
411
+ `formatGithub`, `runAnnotations`, `proveAnnotations` and
412
+ `citesAnnotations`; `RUN_FORMAT_VERSION`, `QUERY_FORMAT_VERSION`,
413
+ `PROVE_FORMAT_VERSION`, `CITES_FORMAT_VERSION` and
414
+ `IMPACT_FORMAT_VERSION`; `globPatternError` and `specPatternError`; and
415
+ the types that go with them, `SpecWarning`, `MaskedDirective` and
416
+ `MaskedContext` among them.
417
+
8
418
  ## 0.11.0
9
419
 
10
420
  The literal this lexer never read. A JavaScript or TypeScript regular
@@ -12,7 +422,7 @@ expression can hold a quote — `/'/`, `/["']/`, `replace(/\/\//g, '')` — and
12
422
  without a rule for `/`, that quote opened a string that closed somewhere else
13
423
  entirely. Every text rule over a TypeScript project reads through this
14
424
  classifier, and this repository's own `src/parser.ts` was one of the files it
15
- lost its place in. [ADR-0006](docs/adr/0006-comment-classification.md).
425
+ lost its place in. [ADR-0006](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0006-comment-classification.md).
16
426
 
17
427
  ### Fixed
18
428
 
@@ -44,9 +454,11 @@ lost its place in. [ADR-0006](docs/adr/0006-comment-classification.md).
44
454
  code from there. It used to run to the end of the file. This is what JSX text
45
455
  costs as well: `<p>Don't click</p>` opens a literal nothing closes, and it
46
456
  now costs that line rather than everything after it.
47
- - **`.sh`, `.bash` and `.zsh` report their profile as `shell`,** not
48
- `shell-like`; `.yaml` and `.yml` report `yaml`. The name appears in
49
- `--json` as each result's comment syntax.
457
+ - **The `shell-like` profile split in two.** `.sh`, `.bash` and `.zsh` are
458
+ read as `shell`, `.yaml` and `.yml` as `yaml`, because their quoting
459
+ differs: a shell's quotes open mid-word and YAML's open only where a value
460
+ starts. The names themselves are internal — no output reports them — so what
461
+ changed for a caller is the reading, described above.
50
462
 
51
463
  Counts may move on a project that has any of these. A text rule over `.ts`,
52
464
  `.tsx`, `.yaml` or `.yml` can now see code it could not see before, and can
@@ -69,8 +481,8 @@ question asked per character has to be paid for per character.
69
481
  Fixes from running 0.10.2 over a Rust and .NET monorepo, where an import rule
70
482
  missed five `use` declarations in one crate and said nothing, and from probing
71
483
  every other language profile for the same mistake.
72
- [ADR-0006](docs/adr/0006-comment-classification.md),
73
- [ADR-0008](docs/adr/0008-polyglot-imports.md).
484
+ [ADR-0006](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0006-comment-classification.md),
485
+ [ADR-0008](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0008-polyglot-imports.md).
74
486
 
75
487
  ### Fixed
76
488
 
@@ -118,8 +530,8 @@ regular expression literal holding a quote, `/["']/` - is recorded in ADR-0006.
118
530
  Fixes from running 0.10.1 over a .NET solution, where a layer rule passed while
119
531
  the domain imported the application, and from reading C# as it is written in
120
532
  2026 while fixing that.
121
- [ADR-0011](docs/adr/0011-layers-and-cycles.md),
122
- [ADR-0008](docs/adr/0008-polyglot-imports.md).
533
+ [ADR-0011](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0011-layers-and-cycles.md),
534
+ [ADR-0008](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0008-polyglot-imports.md).
123
535
 
124
536
  ### Fixed
125
537
 
@@ -192,7 +604,7 @@ the domain imported the application, and from reading C# as it is written in
192
604
 
193
605
  Fixes from running 0.10.0 over a large monorepo, and from testing each exclude
194
606
  shape under both engines while making them.
195
- [ADR-0014](docs/adr/0014-configuration-and-watch.md).
607
+ [ADR-0014](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0014-configuration-and-watch.md).
196
608
 
197
609
  ### Fixed
198
610
 
@@ -268,8 +680,8 @@ shape under both engines while making them.
268
680
  Improvements from running 0.9.2 over a large polyglot monorepo: a
269
681
  configuration a Rust, Go or .NET root can hold, exclusions a whole project
270
682
  shares, and a cycle rule that can leave lazy loading out.
271
- [ADR-0014](docs/adr/0014-configuration-and-watch.md),
272
- [ADR-0011](docs/adr/0011-layers-and-cycles.md).
683
+ [ADR-0014](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0014-configuration-and-watch.md),
684
+ [ADR-0011](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0011-layers-and-cycles.md).
273
685
 
274
686
  ### Added
275
687
 
@@ -333,7 +745,7 @@ shares, and a cycle rule that can leave lazy loading out.
333
745
  unanalysable, as does `/*` inside JSX text.
334
746
  - Tracking JSX text in general was turned down: a TypeScript cast or a
335
747
  generic arrow read as markup would skip real code without a word.
336
- [ADR-0005](docs/adr/0005-import-assertions.md).
748
+ [ADR-0005](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0005-import-assertions.md).
337
749
 
338
750
  ## 0.9.1
339
751
 
@@ -349,13 +761,13 @@ shares, and a cycle rule that can leave lazy loading out.
349
761
  - `/>` anywhere else still starts a regular expression, so
350
762
  `replace(/>/g, '&gt;')` reads as before.
351
763
 
352
- [ADR-0005](docs/adr/0005-import-assertions.md).
764
+ [ADR-0005](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0005-import-assertions.md).
353
765
 
354
766
  ## 0.9.0
355
767
 
356
768
  A project's policy can live in its `package.json`, and `spec-guard --watch`
357
769
  reports again as the tree changes, re-executing only the rules a change
358
- affected. [ADR-0014](docs/adr/0014-configuration-and-watch.md).
770
+ affected. [ADR-0014](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0014-configuration-and-watch.md).
359
771
 
360
772
  ### Added
361
773
 
@@ -423,7 +835,7 @@ affected. [ADR-0014](docs/adr/0014-configuration-and-watch.md).
423
835
  reported every other shard's files with the previous sweep's verdicts. The
424
836
  merge refused that sweep.
425
837
  - No mutants were left out, the per-mutant timeout is unchanged, and the gate
426
- is unchanged. [ADR-0003](docs/adr/0003-mutation-testing.md).
838
+ is unchanged. [ADR-0003](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0003-mutation-testing.md).
427
839
 
428
840
  ### Fixed
429
841
 
@@ -445,7 +857,7 @@ affected. [ADR-0014](docs/adr/0014-configuration-and-watch.md).
445
857
 
446
858
  An architecture document can now state conventions about names and layout: what
447
859
  the files in a directory are called, what every package holds, and which files
448
- come in pairs. [ADR-0013](docs/adr/0013-structure-assertions.md).
860
+ come in pairs. [ADR-0013](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0013-structure-assertions.md).
449
861
 
450
862
  ### Added
451
863
 
@@ -496,7 +908,7 @@ come in pairs. [ADR-0013](docs/adr/0013-structure-assertions.md).
496
908
  had, less one in `glob.ts` that the new tests turned into a genuine hang. A
497
909
  local sweep had reported four survivors in the new listing cache as killed; CI
498
910
  found them, and they are tested. See
499
- [ADR-0003](docs/adr/0003-mutation-testing.md).
911
+ [ADR-0003](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0003-mutation-testing.md).
500
912
 
501
913
  ## 0.7.0
502
914
 
@@ -526,7 +938,7 @@ than every release, and it is fixed below.
526
938
  `types="ignore"` asks the runtime question. JavaScript and TypeScript only,
527
939
  because a cycle needs to know which file an import *is*; imports that should
528
940
  have become an edge and did not are reported, and fail under `--strict`.
529
- See [ADR-0011](docs/adr/0011-layers-and-cycles.md) for the resolution table,
941
+ See [ADR-0011](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0011-layers-and-cycles.md) for the resolution table,
530
942
  and for why the other four languages are not in the graph.
531
943
  - `buildGraph`, `resolveReference`, `stronglyConnected`, `cyclicComponents`,
532
944
  `witness`, `checkLayers` and `referenceForms` are exported, with their types.
@@ -538,7 +950,7 @@ than every release, and it is fixed below.
538
950
  scripts. Rules in documents that are not in force are counted and named, and
539
951
  listed with `--ignore-status`. The arithmetic behind it is tested against the
540
952
  files a real run searches, under both engines and on random trees; see
541
- [ADR-0012](docs/adr/0012-query-and-mcp.md) for the three things it cannot see.
953
+ [ADR-0012](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0012-query-and-mcp.md) for the three things it cannot see.
542
954
  - **`spec-guard mcp`** - a Model Context Protocol server on stdio, with no
543
955
  dependency on the MCP SDK. Tools `get_architectural_rules` (the query) and
544
956
  `check_architecture` (a run, narrowed to the rules governing given paths, each
@@ -588,7 +1000,7 @@ than every release, and it is fixed below.
588
1000
  mutated build against the real one on thousands of random inputs. The query
589
1001
  and the server (`query.ts`, `rules.ts`, `mcp.ts`, `specs.ts`) are all at 100%;
590
1002
  most of the survivors found on the way were code nothing could observe, and it
591
- was deleted. See [ADR-0003](docs/adr/0003-mutation-testing.md) for both
1003
+ was deleted. See [ADR-0003](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0003-mutation-testing.md) for both
592
1004
  accounts, and for why one sweep's count is good to about three.
593
1005
  - `version(manifest?)` takes the manifest to read, so its fallback for a broken
594
1006
  install - `0.0.0` rather than a crash - is tested.
@@ -633,7 +1045,7 @@ making sure a rule that stops running can never look like a rule that passed.
633
1045
  are still checked for typos, bad attributes and unresolvable values, so a
634
1046
  draft's mistake is found on the day it is written rather than on the day
635
1047
  everyone agrees the rule is right and stops looking at it.
636
- See [ADR-0010](docs/adr/0010-spec-status.md), which also records why there is
1048
+ See [ADR-0010](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0010-spec-status.md), which also records why there is
637
1049
  no per-directive `if-status` attribute.
638
1050
  - `--ignore-status` executes every directive whatever its document declares -
639
1051
  how to ask whether a draft would pass if you accepted it today.
@@ -657,7 +1069,7 @@ making sure a rule that stops running can never look like a rule that passed.
657
1069
  the whole suite per mutant, so on a loaded machine an unrelated test timing
658
1070
  out is scored as a kill. Those patterns are now verified by applying every
659
1071
  mutant by hand, `scripts/mutation-regex.mjs`, and the account is in
660
- [ADR-0003](docs/adr/0003-mutation-testing.md).
1072
+ [ADR-0003](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0003-mutation-testing.md).
661
1073
 
662
1074
  ## 0.5.1
663
1075
 
@@ -706,7 +1118,7 @@ new features, and nothing a user has to change.
706
1118
  individually and produce byte-identical output, so that figure is the ceiling
707
1119
  rather than a way-point — and 29 that were assumed equivalent turned out not
708
1120
  to be, and are now tested. See
709
- [ADR-0003](docs/adr/0003-mutation-testing.md), which also records a Stryker
1121
+ [ADR-0003](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0003-mutation-testing.md), which also records a Stryker
710
1122
  limitation found on the way: a mutant that stops a test file *loading* is
711
1123
  reported as survived even though the suite is killing it.
712
1124
  - **`src/imports.ts` went from 73.74% to 95.89%.** The tokenizer was tested as a
@@ -744,13 +1156,13 @@ scope holds no files at all.
744
1156
  Not four new tokenizers: the comment and string lexer from ADR-0006 masks the
745
1157
  source and the readers work on what is left. Tree-sitter measured 94 MB
746
1158
  unpacked against this package's 0.33 MB. See
747
- [ADR-0008](docs/adr/0008-polyglot-imports.md).
1159
+ [ADR-0008](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0008-polyglot-imports.md).
748
1160
  - **`baseline` and `ratchet`** on the absence assertions, for adopting a strict
749
1161
  rule on a codebase that already breaks it. `baseline="src/legacy/a.ts:2"`
750
1162
  names the debt by file and count. The ratchet is two-sided: new violations
751
1163
  fail, and so does an entry the code no longer supports, because a baseline
752
1164
  that only grows is an `exclude` with extra steps. `ratchet="one-way"` relaxes
753
- the second half. See [ADR-0009](docs/adr/0009-debt-baselines.md).
1165
+ the second half. See [ADR-0009](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0009-debt-baselines.md).
754
1166
  - **`--print-baseline`** prints the attribute that would exempt today's
755
1167
  violations, for a human to paste. It prints; it does not edit. That is the
756
1168
  whole answer to `--fix`, and ADR-0009 argues it.
@@ -780,7 +1192,7 @@ scope holds no files at all.
780
1192
  starts finding dependencies it previously reported as unanalysable.
781
1193
  - **Mutation testing in CI runs in two tiers**: incremental on branches,
782
1194
  a full authoritative sweep on `main` and nightly. The gate stays at 85 in
783
- both. See [ADR-0003](docs/adr/0003-mutation-testing.md).
1195
+ both. See [ADR-0003](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0003-mutation-testing.md).
784
1196
 
785
1197
  ### Fixed
786
1198
 
@@ -828,7 +1240,7 @@ found two and ripgrep found four, and neither said anything about the rest.
828
1240
  - **The engines are one implementation.** ripgrep now answers only *which files
829
1241
  contain this text*; the scanner does all counting, comment classification,
830
1242
  binary handling and reporting for both. `--engine` changes how long a run
831
- takes, not what it concludes. See [ADR-0007](docs/adr/0007-search-scope.md).
1243
+ takes, not what it concludes. See [ADR-0007](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0007-search-scope.md).
832
1244
 
833
1245
  ### Added
834
1246
 
@@ -867,7 +1279,7 @@ found two and ripgrep found four, and neither said anything about the rest.
867
1279
  directions. A zero-dependency tokenizer resolves `import`, `export … from`,
868
1280
  `import type`, dynamic `import()` with a literal, and `require()`. Module
869
1281
  references it cannot resolve statically are reported, never counted as clean.
870
- See [ADR-0005](docs/adr/0005-import-assertions.md).
1282
+ See [ADR-0005](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0005-import-assertions.md).
871
1283
  - **`comments` attribute.** See Changed, below.
872
1284
  - **`--allow-missing-targets`** to restore the previous handling of a `target`
873
1285
  path that does not exist.
@@ -883,7 +1295,7 @@ found two and ripgrep found four, and neither said anything about the rest.
883
1295
  uncertain the text counts as code, and every run reports how many matches it
884
1296
  excluded — a pass caused by comment exclusion is never silent.
885
1297
  `comments="include"` restores counting per assertion.
886
- See [ADR-0006](docs/adr/0006-comment-classification.md).
1298
+ See [ADR-0006](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0006-comment-classification.md).
887
1299
  - **A `target` path that does not exist now fails the run** instead of warning
888
1300
  and searching what remained. An assertion pointed at a renamed directory used
889
1301
  to search nothing, find nothing, and report success. `--allow-missing-targets`
@@ -919,9 +1331,9 @@ first published 0.3.0.
919
1331
  enough for the JavaScript scanner to beat a ripgrep process spawn. The probe
920
1332
  is the work — the enumeration it performs is reused by the scanner, so
921
1333
  choosing costs nothing when the scanner wins.
922
- See [ADR-0004](docs/adr/0004-adaptive-engine.md).
1334
+ See [ADR-0004](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0004-adaptive-engine.md).
923
1335
  - **Mutation testing** with Stryker, wired into CI on every push, plus the tests
924
- that closed the gaps it exposed. See [ADR-0003](docs/adr/0003-mutation-testing.md).
1336
+ that closed the gaps it exposed. See [ADR-0003](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0003-mutation-testing.md).
925
1337
 
926
1338
  ### Changed
927
1339