@descent-vtt/spec-guard 0.10.3 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/CHANGELOG.md +410 -0
  2. package/README.md +385 -27
  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 +51 -2
  12. package/dist/comments.d.ts.map +1 -1
  13. package/dist/comments.js +353 -57
  14. package/dist/comments.js.map +1 -1
  15. package/dist/config.d.ts +3 -0
  16. package/dist/config.d.ts.map +1 -1
  17. package/dist/config.js +46 -5
  18. package/dist/config.js.map +1 -1
  19. package/dist/engine.d.ts.map +1 -1
  20. package/dist/engine.js +38 -14
  21. package/dist/engine.js.map +1 -1
  22. package/dist/glob.d.ts +128 -24
  23. package/dist/glob.d.ts.map +1 -1
  24. package/dist/glob.js +304 -61
  25. package/dist/glob.js.map +1 -1
  26. package/dist/impact.d.ts +179 -0
  27. package/dist/impact.d.ts.map +1 -0
  28. package/dist/impact.js +360 -0
  29. package/dist/impact.js.map +1 -0
  30. package/dist/imports.d.ts.map +1 -1
  31. package/dist/imports.js +5 -58
  32. package/dist/imports.js.map +1 -1
  33. package/dist/index.d.ts +13 -5
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +7 -3
  36. package/dist/index.js.map +1 -1
  37. package/dist/mcp.d.ts +16 -75
  38. package/dist/mcp.d.ts.map +1 -1
  39. package/dist/mcp.js +198 -370
  40. package/dist/mcp.js.map +1 -1
  41. package/dist/overlay.d.ts +39 -0
  42. package/dist/overlay.d.ts.map +1 -0
  43. package/dist/overlay.js +133 -0
  44. package/dist/overlay.js.map +1 -0
  45. package/dist/parser.d.ts +41 -24
  46. package/dist/parser.d.ts.map +1 -1
  47. package/dist/parser.js +217 -160
  48. package/dist/parser.js.map +1 -1
  49. package/dist/prove.d.ts +46 -0
  50. package/dist/prove.d.ts.map +1 -0
  51. package/dist/prove.js +549 -0
  52. package/dist/prove.js.map +1 -0
  53. package/dist/query.d.ts +11 -0
  54. package/dist/query.d.ts.map +1 -1
  55. package/dist/query.js +9 -3
  56. package/dist/query.js.map +1 -1
  57. package/dist/reporter.d.ts +139 -1
  58. package/dist/reporter.d.ts.map +1 -1
  59. package/dist/reporter.js +593 -21
  60. package/dist/reporter.js.map +1 -1
  61. package/dist/rules.js +3 -3
  62. package/dist/rules.js.map +1 -1
  63. package/dist/runner.d.ts +24 -1
  64. package/dist/runner.d.ts.map +1 -1
  65. package/dist/runner.js +84 -21
  66. package/dist/runner.js.map +1 -1
  67. package/dist/specs.d.ts +5 -1
  68. package/dist/specs.d.ts.map +1 -1
  69. package/dist/specs.js +5 -1
  70. package/dist/specs.js.map +1 -1
  71. package/dist/structure.d.ts.map +1 -1
  72. package/dist/structure.js +7 -6
  73. package/dist/structure.js.map +1 -1
  74. package/dist/types.d.ts +193 -0
  75. package/dist/types.d.ts.map +1 -1
  76. package/dist/vendor/spec-core/jsonrpc/index.d.ts +3 -0
  77. package/dist/vendor/spec-core/jsonrpc/index.d.ts.map +1 -0
  78. package/dist/vendor/spec-core/jsonrpc/index.js +2 -0
  79. package/dist/vendor/spec-core/jsonrpc/index.js.map +1 -0
  80. package/dist/vendor/spec-core/jsonrpc/mcp.d.ts +158 -0
  81. package/dist/vendor/spec-core/jsonrpc/mcp.d.ts.map +1 -0
  82. package/dist/vendor/spec-core/jsonrpc/mcp.js +353 -0
  83. package/dist/vendor/spec-core/jsonrpc/mcp.js.map +1 -0
  84. package/dist/vendor/spec-core/markdown/frontmatter.d.ts +104 -0
  85. package/dist/vendor/spec-core/markdown/frontmatter.d.ts.map +1 -0
  86. package/dist/vendor/spec-core/markdown/frontmatter.js +459 -0
  87. package/dist/vendor/spec-core/markdown/frontmatter.js.map +1 -0
  88. package/dist/vendor/spec-core/markdown/headings.d.ts +46 -0
  89. package/dist/vendor/spec-core/markdown/headings.d.ts.map +1 -0
  90. package/dist/vendor/spec-core/markdown/headings.js +199 -0
  91. package/dist/vendor/spec-core/markdown/headings.js.map +1 -0
  92. package/dist/vendor/spec-core/markdown/index.d.ts +6 -0
  93. package/dist/vendor/spec-core/markdown/index.d.ts.map +1 -0
  94. package/dist/vendor/spec-core/markdown/index.js +4 -0
  95. package/dist/vendor/spec-core/markdown/index.js.map +1 -0
  96. package/dist/vendor/spec-core/markdown/layout.d.ts +40 -0
  97. package/dist/vendor/spec-core/markdown/layout.d.ts.map +1 -0
  98. package/dist/vendor/spec-core/markdown/layout.js +28 -0
  99. package/dist/vendor/spec-core/markdown/layout.js.map +1 -0
  100. package/dist/vendor/spec-core/markdown/links.d.ts +23 -0
  101. package/dist/vendor/spec-core/markdown/links.d.ts.map +1 -0
  102. package/dist/vendor/spec-core/markdown/links.js +364 -0
  103. package/dist/vendor/spec-core/markdown/links.js.map +1 -0
  104. package/dist/vendor/spec-core/markdown/lists.d.ts +23 -0
  105. package/dist/vendor/spec-core/markdown/lists.d.ts.map +1 -0
  106. package/dist/vendor/spec-core/markdown/lists.js +127 -0
  107. package/dist/vendor/spec-core/markdown/lists.js.map +1 -0
  108. package/dist/vendor/spec-core/markdown/scan.d.ts +37 -0
  109. package/dist/vendor/spec-core/markdown/scan.d.ts.map +1 -0
  110. package/dist/vendor/spec-core/markdown/scan.js +439 -0
  111. package/dist/vendor/spec-core/markdown/scan.js.map +1 -0
  112. package/dist/vendor/spec-core/markdown/syntax.d.ts +76 -0
  113. package/dist/vendor/spec-core/markdown/syntax.d.ts.map +1 -0
  114. package/dist/vendor/spec-core/markdown/syntax.js +181 -0
  115. package/dist/vendor/spec-core/markdown/syntax.js.map +1 -0
  116. package/dist/vendor/spec-core/markdown/tables.d.ts +19 -0
  117. package/dist/vendor/spec-core/markdown/tables.d.ts.map +1 -0
  118. package/dist/vendor/spec-core/markdown/tables.js +86 -0
  119. package/dist/vendor/spec-core/markdown/tables.js.map +1 -0
  120. package/dist/vendor/spec-core/markdown/types.d.ts +288 -0
  121. package/dist/vendor/spec-core/markdown/types.d.ts.map +1 -0
  122. package/dist/vendor/spec-core/markdown/types.js +9 -0
  123. package/dist/vendor/spec-core/markdown/types.js.map +1 -0
  124. package/dist/vendor/spec-core/path/index.d.ts +2 -0
  125. package/dist/vendor/spec-core/path/index.d.ts.map +1 -0
  126. package/dist/vendor/spec-core/path/index.js +2 -0
  127. package/dist/vendor/spec-core/path/index.js.map +1 -0
  128. package/dist/vendor/spec-core/path/posix.d.ts +84 -0
  129. package/dist/vendor/spec-core/path/posix.d.ts.map +1 -0
  130. package/dist/vendor/spec-core/path/posix.js +150 -0
  131. package/dist/vendor/spec-core/path/posix.js.map +1 -0
  132. package/dist/vendor/spec-core/pattern/automaton.d.ts +130 -0
  133. package/dist/vendor/spec-core/pattern/automaton.d.ts.map +1 -0
  134. package/dist/vendor/spec-core/pattern/automaton.js +340 -0
  135. package/dist/vendor/spec-core/pattern/automaton.js.map +1 -0
  136. package/dist/vendor/spec-core/pattern/charset.d.ts +59 -0
  137. package/dist/vendor/spec-core/pattern/charset.d.ts.map +1 -0
  138. package/dist/vendor/spec-core/pattern/charset.js +116 -0
  139. package/dist/vendor/spec-core/pattern/charset.js.map +1 -0
  140. package/dist/vendor/spec-core/pattern/glob.d.ts +122 -0
  141. package/dist/vendor/spec-core/pattern/glob.d.ts.map +1 -0
  142. package/dist/vendor/spec-core/pattern/glob.js +481 -0
  143. package/dist/vendor/spec-core/pattern/glob.js.map +1 -0
  144. package/dist/vendor/spec-core/pattern/index.d.ts +7 -0
  145. package/dist/vendor/spec-core/pattern/index.d.ts.map +1 -0
  146. package/dist/vendor/spec-core/pattern/index.js +4 -0
  147. package/dist/vendor/spec-core/pattern/index.js.map +1 -0
  148. package/dist/vendor/spec-core/pattern/regex.d.ts +68 -0
  149. package/dist/vendor/spec-core/pattern/regex.d.ts.map +1 -0
  150. package/dist/vendor/spec-core/pattern/regex.js +813 -0
  151. package/dist/vendor/spec-core/pattern/regex.js.map +1 -0
  152. package/dist/vendor/spec-core/text/index.d.ts +3 -0
  153. package/dist/vendor/spec-core/text/index.d.ts.map +1 -0
  154. package/dist/vendor/spec-core/text/index.js +2 -0
  155. package/dist/vendor/spec-core/text/index.js.map +1 -0
  156. package/dist/vendor/spec-core/text/lines.d.ts +73 -0
  157. package/dist/vendor/spec-core/text/lines.d.ts.map +1 -0
  158. package/dist/vendor/spec-core/text/lines.js +156 -0
  159. package/dist/vendor/spec-core/text/lines.js.map +1 -0
  160. package/package.json +8 -1
  161. package/src/vendor/spec-core/LICENSE +21 -0
  162. 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.0
9
+
10
+ The family's core, and three commands. Patterns and Markdown are read through
11
+ spec-core, copied in and checked by hash: a pattern that cannot be read is
12
+ refused rather than matched as a literal, a glob no longer backtracks, and
13
+ directives are found as CommonMark finds code, comments and front matter.
14
+ `prove` shows every rule in force catching a violation, `cites` holds code
15
+ comments to the specs they cite, and `impact` names who depends on a path.
16
+ `archived` withholds a document.
17
+
18
+ ### Upgrading
19
+
20
+ Run this release beside the one it replaces, each with `--json`, and compare
21
+ `summary.total` before trusting a green run - and, if the totals differ, each
22
+ result's `spec` file and line, which say which rule moved. A rule that stops
23
+ running reads exactly like one that passes. On a consumer of 114 specs and 301
24
+ assertions, both releases ran the same 301 at the same places with the same
25
+ counts. What can differ, and where this changelog says why:
26
+
27
+ - **A run that exits 2**, or a directive that is invalid, where one ran: a
28
+ pattern that cannot be read - `**` inside a name (`docs/**.md`), an
29
+ extended glob, an unclosed `[` or `{` - in `specs`, `exclude`, `--spec`,
30
+ `--exclude` or a directive's attributes.
31
+ - **`summary.total` moving** because a directive moved between prose and code:
32
+ the shapes under Fixed. Every directive-shaped comment left in code, raw
33
+ HTML or front matter is on one line of the report and in `maskedDirectives`.
34
+ - **`summary.inactive` moving**: `archived` withholds a document; a
35
+ front-matter `status` the reader cannot read - text after a closing quote,
36
+ `: ` in a plain value - keeps its document in force with a warning, where
37
+ 0.11.0 took its first word, and nothing below the front matter is read in
38
+ its place; and an invalid directive in a draft is counted once, as invalid.
39
+ - **Warnings that fail nothing**, in `specWarnings`: a front-matter status
40
+ that cannot be read, a block never closed.
41
+ - **`impact` exits 2** when no spec matched, as `query` does.
42
+
43
+ ### Added
44
+
45
+ - **`spec-guard impact <paths...>`: who depends on this?** For each path, the
46
+ files that import it, transitively, each with its distance and the import
47
+ that takes it one step closer, and the rules in force that govern the path or
48
+ any of them, by `query`'s arithmetic. The import graph is ADR-0011's, read
49
+ backwards over the whole tree: relative JavaScript and TypeScript imports by
50
+ its resolution table, and relative Python imports, which name a module
51
+ beside the importing file. Go, Rust, C# and absolute Python references name
52
+ modules and are counted rather than followed; a path in one of those
53
+ languages says its dependents are not computed. An import naming no file, an
54
+ alias or a dynamic `import(name)` is listed, never guessed. `--depth <n>`,
55
+ `--ignore-status`, and `--json` with `formatVersion`. A path that does not
56
+ exist is exit 2, and so is a run in which no spec matched, as for `query`:
57
+ the dependents are written, and `--allow-empty` exits 0 with them. `impactOf`
58
+ is the API. [ADR-0018](docs/adr/0018-impact.md).
59
+ - **`get_dependents` on the MCP server**, a third tool beside
60
+ `get_architectural_rules` and `check_architecture`: `impact` for a list of
61
+ paths, with `depth` and `include_inactive`. Its structured content is the
62
+ document `impact --json` writes and its text the command's report, and it is
63
+ annotated read-only, as the other two are. Its description, and the server's
64
+ instructions, tell an agent to call it before changing a file others import.
65
+ A path that does not exist is a tool error the model can read. A client
66
+ that pinned the tool list sees three. `impactDocument` is the API for the
67
+ document both answer with. ADR-0018's amendment.
68
+ - **`spec-guard cites`: do the comments cite decisions in force?** A comment
69
+ such as `// ADR-0011: the domain imports no infrastructure` is a claim, and it
70
+ goes stale when the document is never written or is superseded. `cites` reads
71
+ the comment text of every source file - by the classifier's reading, so never
72
+ a string - for the ids of the families a project names in a new `cites`
73
+ option, `[{ "id": "ADR-{n}", "files": "docs/adr/{n}-*.md" }]`, where `{n}` is
74
+ a number (`ADR-7`, `ADR-007` and `0007-x.md` are one document). Without it, a
75
+ directory of numbered specs whose titles all begin with one id and their own
76
+ number is read as a family; anything less is a note and exit 0, not a guess.
77
+ A **ghost** citation, of no document, is an error with the nearest ids as its
78
+ hint. A **stale** one, of a document superseded, deprecated, rejected or
79
+ archived, is a warning (an error under `--strict`) that names the successor
80
+ the document's status line gives, followed to the one in force. Ids another
81
+ project qualifies - `spec-core's ADR-0005` - are counted and not checked,
82
+ and so are ids in a link: after a `/`, `#`, `=` or `?`, or past a `://`,
83
+ as in `https://github.com/org/repo/blob/main/docs/adr/ADR-0042.md`.
84
+ Markdown, spec files, excluded paths and files in no known language are not
85
+ read, and are counted. Human, `--json` (with `formatVersion`), SARIF, GitHub
86
+ and GitLab output; exit 1 on a ghost, 2 for a family whose files match no
87
+ document. `findCitations` is the API.
88
+ [ADR-0017](docs/adr/0017-citations-in-comments.md).
89
+ - **`spec-guard prove`: can each rule actually fail?** A passing rule cannot
90
+ say whether the code holds or the rule cannot see the code. `prove` shows
91
+ each rule in force a violation of itself - a file holding the forbidden text,
92
+ a file importing the forbidden module, a cycle, an import across layers, a
93
+ misnamed file, a required entry or a partner taken away, a required file
94
+ removed - and runs the rule over it, in memory, through the door every read
95
+ goes through: nothing is written to disk and nothing touches git. Each rule
96
+ is `killed`, `survived` (the finding, with the violation shown) or
97
+ `unprovable` (with the reason). Exit 1 when a rule survived, and under
98
+ `--strict` when one is unprovable; `--json` is versioned by `formatVersion`,
99
+ and `--format sarif` puts a survivor on its directive.
100
+ `proveSpecGuard` is the same for a caller of the API. The first proof of
101
+ this repository's own rules found one that could not fail as written, and
102
+ CI now proves every rule the repository states on each change.
103
+ [ADR-0016](docs/adr/0016-rules-seen-to-fail.md).
104
+ - **`RunOptions.io`: a run over a tree that is not on disk.** `runSpecGuard`
105
+ reads through the `Io` it is given - finding and reading the specs, checking
106
+ targets exist, walking them, scanning files, reading imports and listing
107
+ directories - and through `nodeIo` when it is given none, as before. The run
108
+ had the door all along and handed the filesystem to every reader itself.
109
+ ripgrep reads the disk in a process of its own, where no door reaches, so
110
+ `engine: 'ripgrep'` with an `io` is refused with a message that says so, and
111
+ `auto` with one searches with the built-in scanner. A search that fails
112
+ outright through the door fails the run, rather than falling back to the
113
+ scanner that reads the disk. [ADR-0014](docs/adr/0014-configuration-and-watch.md).
114
+ - **`--format gitlab` and `--format github`**, for the run, `prove` and
115
+ `cites`. `gitlab` writes a GitLab Code Quality report - `description`,
116
+ `check_name`, a `fingerprint`, a `severity` and a `location` - which a
117
+ merge request shows beside its changes. The fingerprint is the SHA-256 of
118
+ what a finding is about - a rule's document, kind, subject and targets, as
119
+ SARIF's is; a citation's file, family and id; a document's path - and never
120
+ of its message, which holds the count of matches and the directive's line:
121
+ one more match, or a line added above a directive, would otherwise have
122
+ closed every open issue and opened a new one. Findings that share an
123
+ identity, one ghost cited on two lines, are numbered in order. `github` writes one workflow command per finding, which a job's log
124
+ turns into an annotation on the pull request with no upload step. Both place
125
+ the findings SARIF places: a failing assertion or a surviving rule is
126
+ `critical`, a directive that could not be read `major`, a rule `prove` could
127
+ make no violation for `minor`, and a document not in force is named, as
128
+ `info`, on its first line. `formatGitlab`, `formatGithub`, `runAnnotations`,
129
+ `proveAnnotations` and `citesAnnotations` are the API.
130
+ - **What was not run is counted.** A comment shaped like a directive in code,
131
+ raw HTML or front matter is not run, and a report now says how many and
132
+ where: one dim line in the human report - "7 directive-shaped comments in
133
+ code, raw HTML or front matter were not run: README.md:120, ..." - naming
134
+ the first five, or every one under `--verbose`, and `maskedDirectives` in
135
+ the `--json` of a run and of `prove`, each with what hid it. Most are
136
+ examples; the ones that are not are rules 0.11.0 ran that this release does
137
+ not, such as a directive in an indented line or under a leading `---` read
138
+ as front matter. Warnings about how a document was read - a front-matter
139
+ status that cannot be read, a block never closed - are `specWarnings`.
140
+ Both fields are added, so no `formatVersion` moves.
141
+ [ADR-0002](docs/adr/0002-directive-format.md)'s amendment.
142
+ - **`formatVersion: 1` on the run's `--json` and on `query --json`**, as
143
+ `prove --json` has had from the start: a field removed or renamed moves it,
144
+ a field added does not. Both documents are otherwise unchanged; this is one
145
+ field more, first in each.
146
+ - **`io` on `loadRuleSet` and `queryRules`, too.** A query found and read the
147
+ specs, and asked whether each path exists, through the filesystem whatever
148
+ the caller had; it now goes through the door it is given, as a run does, so
149
+ a query over a tree in memory sees that tree.
150
+
151
+ ### Fixed
152
+
153
+ - **An invalid directive in a document not in force was counted twice**, as
154
+ invalid and again as a rule not in force, so one bad directive in a draft
155
+ read "1 invalid · 1 not in force". It is counted once, as invalid, in the
156
+ summary, in the document's own line, and in the `inactive` of every JSON
157
+ report: it is no rule the document would run in force.
158
+ - **A glob could take a minute to fail one file name.** Every pattern was
159
+ compiled to a `RegExp`, and `*-*-*-*-*-*x` against a name of 121 dashes made
160
+ V8 try every way of dividing the name between six `[^/]*` groups: 55 seconds,
161
+ measured. Patterns are now read by the glob engine the spec-* tools share,
162
+ spec-core's, whose automaton keeps a set of live states and cannot backtrack:
163
+ the same match takes under a millisecond. spec-core is copied into
164
+ `src/vendor/spec-core` and verified by hash, so `dependencies` stays empty,
165
+ and its MIT notice ships in the package at `src/vendor/spec-core/LICENSE`.
166
+ [ADR-0015](docs/adr/0015-globs-from-spec-core.md).
167
+ - **ripgrep and the scanner still read some patterns differently**, so the same
168
+ rule could count differently on either side of the tree size at which `auto`
169
+ changes engine. ripgrep anchored every alternative of `{src/*.ts,*.md}`
170
+ because one held a `/`, dropped the empty one in `{,src/}a.ts`, matched
171
+ nothing for `src/./a.ts`, and failed on a `}` that closes nothing. It is now
172
+ handed each alternative as a glob of its own, spelled as the scanner reads
173
+ it. And ripgrep applies no glob to a path named on its command line, so a
174
+ rule whose target is a file its `glob` does not match, or a directory its
175
+ `exclude` does, counted under ripgrep what the scanner left out; its list of
176
+ files is now held to the same filters. ADR-0015.
177
+ - **What is code in a spec is what CommonMark says it is.** Directives inside
178
+ code never execute, and what counted as code was decided by `maskCode`'s own
179
+ rules. It is now decided by spec-core's Markdown scanner, which every spec-*
180
+ tool reads documents with, copied into `src/vendor/spec-core` from `cbe2223`
181
+ and verified by hash; `maskCode` is still exported, and is the scanner's
182
+ mask. [ADR-0002](docs/adr/0002-directive-format.md)'s amendment.
183
+
184
+ Directives that were hidden, and now execute:
185
+ - **A backtick fence's info string may not hold a backtick.** A line that
186
+ reads ```` ```js`x ```` is prose that opens with a code span. Read as a
187
+ fence, it hid every line under it, a real directive included, until
188
+ something closed it - and nothing said a rule had gone quiet.
189
+ - **A code span ends with its paragraph.** A backtick that closes nothing
190
+ there, as in ``press ` to open the console``, paired with the next
191
+ backtick anywhere in the document and hid every directive between the
192
+ two. An escaped backtick opens nothing.
193
+ - **A fence shown inside a comment opens nothing.** A template that showed
194
+ one hid every directive after it.
195
+ - **A fence line shown as code in a list item opens nothing.** Four columns
196
+ past an item's text is indented code, so a fence line written there - six
197
+ spaces under a `-` item, seven under `1.`, deeper in a nested item - is a
198
+ line of code. The first copy of the scanner read no indented code inside a
199
+ list, took the line for an opener that nothing closed, and hid every
200
+ directive after it.
201
+
202
+ And a code fence or a `<pre>`, `<script>`, `<style>` or `<textarea>`
203
+ block that is never closed, and runs to the end of the document, is a
204
+ warning on its opening line (`specWarnings`), since no directive after it
205
+ runs. It fails nothing: it is read as every renderer shows it, as code.
206
+
207
+ Directives that executed, and are now code:
208
+ - **A closing fence has no info string.** Inside a block opened by three
209
+ backticks, a line of three backticks followed by `js` is a line of the
210
+ block. It used to close it, and a directive shown after it executed.
211
+ - **A fence may be indented with the list item it sits in.** CommonMark
212
+ counts its three spaces from the item, so a fence in a `1.` item nested in
213
+ a `-` item is five or more spaces from the margin. It was not recognised,
214
+ and a directive shown inside it executed: always behind `~~~`, and behind
215
+ backticks whenever the prose above mentioned one, since the two fence lines
216
+ otherwise paired as a code span by accident. An opener may sit at any
217
+ indentation outside indented code, and a closer at most three columns
218
+ deeper than its opener.
219
+ - **Indented code, raw-text HTML and front matter are not read.** Four
220
+ columns past the margin, or past the text of the list item a line sits
221
+ in, after a blank line, are code in every renderer;
222
+ `<script>`, `<pre>`, `<style>` and `<textarea>` hold no Markdown; front
223
+ matter is metadata. `<details>` and `<div>` hold Markdown, and are read.
224
+
225
+ And a backtick inside a comment is a character, so a directive holding one
226
+ keeps it: ``symbol="`eval`"`` is searched for as written, where the two
227
+ backticks paired and the rule searched for six spaces.
228
+
229
+ This repository's own specs execute the same assertions, at the same lines,
230
+ and every directive in the 143 Markdown files of the five spec-* repositories
231
+ is read as before. A test holds the scanner to 0.11.0's masking on every
232
+ Markdown file here, and on random documents built from lines both read alike,
233
+ and shows it parting from 0.11.0 on each shape above.
234
+
235
+ ### Changed
236
+
237
+ - **Mutation testing in CI runs in eight shards**, not four. Three of the four
238
+ took 23 to 28 minutes on the sweep that merged this release's code, and the
239
+ fourth was cancelled at the 30-minute job limit. ADR-0003 has the per-file
240
+ minutes and the split; the longest of the eight took 16m16s.
241
+ - **A pattern that cannot be read is refused.** An unclosed `[` or `{`, an
242
+ extended glob such as `+(a|b)` (a group holding a `|`: `C++(notes).md` and
243
+ `*(2017).md` are names with parentheses), a `..`, or a range that runs
244
+ backwards, in `glob`, `exclude`, `module`, `order`, `pattern`, `dirs` or
245
+ `required`, makes the directive invalid; in `exclude` in the configuration or `--exclude`, or in
246
+ a spec pattern, it is exit 2. Each used to be read as a literal or as whatever
247
+ its `RegExp` happened to mean, which is a filter that matches nothing and
248
+ passes. So is an alternative that names no path, as in `{dist/**,}`, whose
249
+ empty alternative matched the empty string. A brace group with a comma in it
250
+ in a directive is one more case: a list attribute splits on commas, so
251
+ `glob="*.{ts,tsx}"` was always the two patterns `*.{ts` and `tsx}`, and the
252
+ message now says so. A configuration's `exclude` is a JSON array, where braces
253
+ keep their commas. Nothing restores the old readings.
254
+ [ADR-0015](docs/adr/0015-globs-from-spec-core.md).
255
+ - **`**` inside a name is refused**: `docs/**.md`, `src/**.ts`, `**.ts`,
256
+ `a**b`. The message says what `**` means and gives the two ways to write
257
+ what was meant, `docs/**/*.md` for any depth and `*.md` for one level. It
258
+ used to cross directories under the scanner and not under ripgrep, so a rule
259
+ counted differently by engine; read as `*`, as `.gitignore` reads it,
260
+ `"specs": ["docs/**.md"]` would quietly have stopped finding every nested
261
+ document. In a directive the directive is invalid, in a draft as in a
262
+ document in force. A spec pattern is refused before any command starts: in
263
+ the configuration's `specs`, naming the file, and on the command line or in
264
+ `--spec`, for `query`, `impact`, `cites`, `prove` and `mcp` as for a run -
265
+ exit 2, as `exclude` in the configuration and `--exclude` are.
266
+ `specPatternError` is the check, for a caller of the API.
267
+ - **A `.` or empty segment is no segment** (`src/./a.ts` is `src/a.ts`), and
268
+ **a leading `/` anchors a `glob`** at the root, as it anchors an `exclude`.
269
+ Both matched nothing in the scanner before; ripgrep already anchored.
270
+ - **`archived` withholds a document**, a sixth word beside `draft`,
271
+ `proposed`, `rejected`, `deprecated` and `superseded`. `spec-brief` closes a
272
+ task brief's round by setting `status: archived` and moving the brief to an
273
+ archive directory. Its directives stated the round's premises and goals,
274
+ which are history once the round is closed, and executed anyway they failed.
275
+ An archived document is still parsed, validated and named in every output
276
+ format. `--ignore-status` executes it as before. The list stays closed:
277
+ `done`, `closed`, `archive` and every other near word stay in force, and
278
+ [ADR-0010](docs/adr/0010-spec-status.md)'s amendment says why this word and
279
+ no other.
280
+ - **A status is found by the scanner's headings and front-matter reader.**
281
+ The three spellings, their order and the words are unchanged, and every
282
+ status in the five spec-* repositories reads as it did. Where the scanner
283
+ reads a document differently, the status follows it:
284
+ - A `## Status` kept in a comment - a template not yet filled in - no longer
285
+ withholds the document, and a `##` in a comment or in code no longer ends
286
+ the preamble where the `**Status:**` label is read.
287
+ - An underlined heading is a heading. An underlined `Status` is the section,
288
+ and an underlined section ends the preamble, so a `status: draft` line with
289
+ `---` directly under it is a heading, as every renderer shows it, and not a
290
+ label.
291
+ - Front matter is read by spec-core's reader, the family's: a quoted value
292
+ to its closing quote and a plain one to a comment, as before. A value it
293
+ refuses declares nothing - a `: ` in a plain value, text after a closing
294
+ quote, a value continued on the next line, `*Draft*`, which YAML reads as
295
+ an alias - and a `status` nested under another key is that key's. Front
296
+ matter behind a byte-order mark, or closed by `...`, is read as front
297
+ matter.
298
+ - A front-matter `status` decides even when it cannot be read. A value the
299
+ reader refuses, an empty one, one with no word or a list leaves the
300
+ status unrecognised and the document in force, and the `## Status`
301
+ section and the `Status:` label are not read in its place: under 0.11.0
302
+ `status: "accepted" (2024-05-01)` was accepted, and falling through to a
303
+ section still saying `Proposed` would have withheld its rules. Every
304
+ format says so, on the key's line: a warning in the human report, a
305
+ `specWarnings` entry in `--json` (run and `prove`), a SARIF notification
306
+ and a `spec-warning` annotation for GitHub and GitLab.
307
+
308
+ [ADR-0010](docs/adr/0010-spec-status.md)'s second amendment.
309
+ - **A title is the first level-one heading as the scanner reads it.** An
310
+ underlined one (`===`) is a title and one kept in a comment is not; a code
311
+ span in it is kept as written, where it was blanked, and a comment in it is
312
+ dropped. A first level-one heading with no text leaves the document
313
+ untitled rather than promoting the next. In this repository one title
314
+ changed: ADR-0009's keeps its `` `--fix` ``.
315
+ - **`maskCode` keeps a carriage return inside code**, where it blanked one:
316
+ every line terminator stays where it was, a lone `\r` included.
317
+ - **Reading a spec costs about four times what it did**: 17 ms against 4 ms
318
+ over this repository's 18 ADRs, and 23 ms against 9 ms for a warm query of
319
+ one path, past the 20 ms ADR-0012 set out to meet. The scanner finds list
320
+ items, links and tables that nothing here reads.
321
+ [ADR-0002](docs/adr/0002-directive-format.md)'s amendment says where the
322
+ remedy lies.
323
+ - **The MCP server speaks the protocol through spec-core.** Classifying each
324
+ request, the two eras, dispatch, result shapes and the stdio framing are
325
+ spec-core's `jsonrpc` module, copied from `f085f29` and verified by hash;
326
+ `src/mcp.ts` keeps the instructions, the tools and the resources. What a
327
+ client sees is unchanged, and every test of the server passes as it was
328
+ written. One edge moved: a last line cut inside a UTF-8 character when stdin
329
+ closes is read as far as it goes rather than dropped. The protocol's names
330
+ are still exported from the package. [ADR-0015](docs/adr/0015-globs-from-spec-core.md).
331
+ - **API:**
332
+ - `createGlobMatcher`, `createExcludeMatcher` and `expandSpecPatterns` throw
333
+ for a pattern spec-core refuses - an unclosed `[` or `{`, an extended
334
+ glob, a `..`, `**` inside a name - naming the pattern, where they read it
335
+ as a literal or as whatever its `RegExp` meant. `globPatternError` and the
336
+ new `specPatternError` say why without throwing.
337
+ - `Annotation` has a required `identity`, which the GitLab fingerprint is
338
+ made of. `SpecSet` and `RunPlan` have required `warnings` and `masked`;
339
+ `RunReport` and `ProveReport` have optional `specWarnings` and
340
+ `maskedDirectives`, and `ParseResult` optional `warnings` and `masked`.
341
+ An `InactiveSpec`'s `directives` counts only those that resolve.
342
+ - New: `proveSpecGuard`, `PROVE_NAME`, `overlayIo` and `readOnce`;
343
+ `findCitations`, `findDocuments`, `deriveFamilies`, `scanCitations`,
344
+ `inComments`, `qualified`, `isStale`, `numberKey`, `documentNumber`,
345
+ `parseIdTemplate`, `parseFilesTemplate`, `citeIdError`, `citeFilesError`,
346
+ `CitesError` and `NUMBER`; `impactOf`, `impactDocument`, `dependentsOf`,
347
+ `buildReverseGraph`, `resolvePython`, `formatImpact`, `formatImpactJson`
348
+ and `ImpactError`; `formatProve`, `formatProveJson`, `formatProveSarif`,
349
+ `formatCites`, `formatCitesJson`, `formatCitesSarif`, `formatGitlab`,
350
+ `formatGithub`, `runAnnotations`, `proveAnnotations` and
351
+ `citesAnnotations`; `RUN_FORMAT_VERSION`, `QUERY_FORMAT_VERSION`,
352
+ `PROVE_FORMAT_VERSION`, `CITES_FORMAT_VERSION` and
353
+ `IMPACT_FORMAT_VERSION`; `globPatternError` and `specPatternError`; and
354
+ the types that go with them, `SpecWarning`, `MaskedDirective` and
355
+ `MaskedContext` among them.
356
+
357
+ ## 0.11.0
358
+
359
+ The literal this lexer never read. A JavaScript or TypeScript regular
360
+ expression can hold a quote — `/'/`, `/["']/`, `replace(/\/\//g, '')` — and
361
+ without a rule for `/`, that quote opened a string that closed somewhere else
362
+ entirely. Every text rule over a TypeScript project reads through this
363
+ classifier, and this repository's own `src/parser.ts` was one of the files it
364
+ lost its place in. [ADR-0006](docs/adr/0006-comment-classification.md).
365
+
366
+ ### Fixed
367
+
368
+ - **A regular expression holding a quote desynchronised the scan.** The quote
369
+ opened a string that ran to the next quote in the file, so the comments it
370
+ covered were counted as code — and, once it closed, the scan was half a
371
+ literal out of step, where a `//` inside a real string opened a comment that
372
+ **hid real code**. `/` now opens a literal in `.js`, `.mjs`, `.cjs`, `.jsx`,
373
+ `.ts`, `.mts`, `.cts` and `.tsx`, decided by the token before it with the
374
+ same two tables the import tokenizer has used since 0.2.0 — which now live in
375
+ one place and are read by both, so the two scanners cannot disagree. A
376
+ character class holds an unescaped `/`, `\/` does not close, and a `/` that
377
+ closes nothing before the end of its line was a division.
378
+ - **`/*` in JSX text opened a comment**, so `<div>/*</div>` hid every line up
379
+ to the next `*/` in the file. After a tag's `>` it is text.
380
+ - **An apostrophe in a YAML value opened a string.** Most YAML scalars are
381
+ unquoted and may hold one — `- name: Build the decoder's artefact` — and read
382
+ with the shell's quoting that apostrophe closed on the next one, lines away,
383
+ with every `#` between them no longer a comment. `.yaml` and `.yml` have a
384
+ profile of their own, `yaml`, which keeps the shell's `#` rule and takes a
385
+ quote as opening a scalar only where a word could start. `'it''s'` is one
386
+ scalar, YAML's own escape. A shell's quotes still open mid-word, because
387
+ `dir='C:\'` needs them to.
388
+
389
+ ### Changed
390
+
391
+ - **A JavaScript string ends at its line.** Only a template may hold a line
392
+ break, so a quoted string that reaches one was never a string and is read as
393
+ code from there. It used to run to the end of the file. This is what JSX text
394
+ costs as well: `<p>Don't click</p>` opens a literal nothing closes, and it
395
+ now costs that line rather than everything after it.
396
+ - **The `shell-like` profile split in two.** `.sh`, `.bash` and `.zsh` are
397
+ read as `shell`, `.yaml` and `.yml` as `yaml`, because their quoting
398
+ differs: a shell's quotes open mid-word and YAML's open only where a value
399
+ starts. The names themselves are internal — no output reports them — so what
400
+ changed for a caller is the reading, described above.
401
+
402
+ Counts may move on a project that has any of these. A text rule over `.ts`,
403
+ `.tsx`, `.yaml` or `.yml` can now see code it could not see before, and can
404
+ stop counting comment text it was counting: on 7,953 files here, 119 phantom
405
+ comments in 63 files disappeared and 1,948 real comments in 53 files came back.
406
+ 44 JavaScript files reported a lost scan before; 4 do now, every one of them a
407
+ template nested inside a `${…}` substitution, which ADR-0006 records as the
408
+ open item this release does not close.
409
+
410
+ The scan is also **faster than it has ever been** — 5.1× for TypeScript, 5.2×
411
+ for C#, 3.4× for C, 2.8× for shell scripts and 1.3× for Rust, whose raw strings
412
+ open with the letter `r` and so reach the slow path often. A character that
413
+ opens nothing in a language now costs one array lookup rather than four
414
+ searches that were always going to fail. That was not a bonus: adding the
415
+ regular-expression branch made C, which never enters it, 16% slower, and a
416
+ question asked per character has to be paid for per character.
417
+
8
418
  ## 0.10.3
9
419
 
10
420
  Fixes from running 0.10.2 over a Rust and .NET monorepo, where an import rule