@descent-vtt/spec-graph 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +100 -45
- package/README.md +36 -36
- package/dist/cli.js +4 -4
- package/dist/cli.js.map +1 -1
- package/dist/extract.js +4 -1
- package/dist/extract.js.map +1 -1
- package/dist/rules.d.ts +8 -0
- package/dist/rules.js +10 -0
- package/dist/rules.js.map +1 -1
- package/dist/vendor/spec-core/markdown/links.d.ts +3 -0
- package/dist/vendor/spec-core/markdown/links.js +32 -2
- package/dist/vendor/spec-core/markdown/links.js.map +1 -1
- package/dist/vendor/spec-core/markdown/scan.js +31 -7
- package/dist/vendor/spec-core/markdown/scan.js.map +1 -1
- package/dist/vendor/spec-core/markdown/types.d.ts +4 -1
- package/dist/vendor/spec-core/markdown/types.js.map +1 -1
- package/package.json +3 -3
- package/src/vendor/spec-core/README.md +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -3,12 +3,67 @@
|
|
|
3
3
|
Notable changes, newest first. Versions follow [semver](https://semver.org):
|
|
4
4
|
a patch fixes behaviour without asking anything of a repository that upgrades.
|
|
5
5
|
|
|
6
|
+
## 0.9.2
|
|
7
|
+
|
|
8
|
+
Three small fixes found when 0.9.1 was read as a user reads it: `rules
|
|
9
|
+
--explain` points at ADRs a reader can open, a badge wrapped in a link reads
|
|
10
|
+
as its alt text in a finding, and the tarball carries one README.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **`rules --explain` names each ADR by its address on GitHub**, at the tag
|
|
15
|
+
of the running release, `https://github.com/DescentVTT/spec-graph/blob/v0.9.2/docs/adr/...`.
|
|
16
|
+
It printed the repository path, `docs/adr/0002-lifecycle-lattice.md`, and
|
|
17
|
+
no package ships `docs/`, so the path named nothing a user had.
|
|
18
|
+
- **A badge wrapped in a link reads as its alt text** in the obligation a
|
|
19
|
+
finding quotes. One pattern for images and links matched from the link's
|
|
20
|
+
`[` to the image's `)` and left `` in the text; images are
|
|
21
|
+
now read first, so `[](x.md)` reads `icon`.
|
|
22
|
+
- **The package ships one README, its own.** `files` named `README.md`,
|
|
23
|
+
which npm reads as a name at any depth, so the tarball carried spec-core's
|
|
24
|
+
vendored README beside the licence it ships; the entry is `/README.md`.
|
|
25
|
+
|
|
26
|
+
## 0.9.1
|
|
27
|
+
|
|
28
|
+
Nothing to do on upgrading. The same documents give the same findings, in
|
|
29
|
+
every format, and the same graph exports as they did under 0.9.0; the one
|
|
30
|
+
difference a program that imports spec-graph can see is a badge wrapped in a
|
|
31
|
+
link, which `ScannedDocument.links` now lists. The scanner is spec-core
|
|
32
|
+
8840d36, the README's and this changelog's links to the ADRs work on
|
|
33
|
+
npmjs.com and in `node_modules`, and `npm publish` refuses to run from a
|
|
34
|
+
checkout.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- **spec-core 8840d36.** An image inside a link's text, a badge wrapped in a
|
|
39
|
+
link such as `[](actions)`, is listed in
|
|
40
|
+
`ScannedDocument.links` after the link around it, where CommonMark renders
|
|
41
|
+
it; the scan used to skip it. spec-graph reads no image as a reference but a
|
|
42
|
+
wiki embed, so no finding and no edge moves: run over the same documents,
|
|
43
|
+
this repository's, its fixtures' and the rest of the family's, every `check`
|
|
44
|
+
format and every graph export is byte for byte what 0.9.0 wrote. A program
|
|
45
|
+
that reads `links` for images sees the badges it missed. `links`,
|
|
46
|
+
`listItems` and `masks.directives` are made the first time they are read,
|
|
47
|
+
and kept, rather than for every document; they hold what they held.
|
|
48
|
+
- `npm publish` in a checkout refuses to run outside GitHub Actions, so a
|
|
49
|
+
version cannot reach npm from a workstation by mistake, without provenance;
|
|
50
|
+
spec-harness 0.1.0 did. The release never runs it: it stages a tarball it
|
|
51
|
+
packed.
|
|
52
|
+
|
|
53
|
+
### Fixed
|
|
54
|
+
|
|
55
|
+
- The README's and this changelog's links to the ADRs and to `CLAUDE.md` were
|
|
56
|
+
relative, and the package ships neither, so on npmjs.com and inside
|
|
57
|
+
`node_modules` they led nowhere. They are GitHub URLs now. A test holds
|
|
58
|
+
every link in a shipped document to a file the package ships or to an
|
|
59
|
+
absolute URL, and every URL into this repository to a file it holds.
|
|
60
|
+
|
|
6
61
|
## 0.9.0
|
|
7
62
|
|
|
8
63
|
Versions are staged by CI from a `v*` tag, through npm's trusted
|
|
9
64
|
publishing, and released by a maintainer with a second factor, no longer
|
|
10
65
|
published from a workstation
|
|
11
|
-
([ADR-0021](docs/adr/0021-releases-are-published-by-ci.md)). There is no
|
|
66
|
+
([ADR-0021](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0021-releases-are-published-by-ci.md)). There is no
|
|
12
67
|
publish token anywhere, and every version from here on carries a provenance
|
|
13
68
|
attestation naming the repository, the commit and the run that built the
|
|
14
69
|
tarball. `npm audit signatures` checks it without taking anyone's word for
|
|
@@ -44,7 +99,7 @@ counts move when one of three boxes is ticked; a second finding identical in
|
|
|
44
99
|
all three is told apart by its order.
|
|
45
100
|
`error` is `critical`, or `major` when only `--strict` made it an error;
|
|
46
101
|
`warn` is `minor` and `info` is `info`. The README has the job to add. See
|
|
47
|
-
[ADR-0015](docs/adr/0015-feedback-goes-where-the-tools-already-look.md).
|
|
102
|
+
[ADR-0015](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0015-feedback-goes-where-the-tools-already-look.md).
|
|
48
103
|
|
|
49
104
|
### Breaking (library API)
|
|
50
105
|
|
|
@@ -87,7 +142,7 @@ before. `compilePattern` and `PatternError` are still exported and are the same
|
|
|
87
142
|
function and class; a caught error's `name` is now `RegexError`. The tests that
|
|
88
143
|
hold the matcher to `RegExp` moved with it, and the copy is left out of this
|
|
89
144
|
repository's mutation sweep and coverage, which spec-core's measure instead.
|
|
90
|
-
See [ADR-0017](docs/adr/0017-a-predicate-must-finish.md).
|
|
145
|
+
See [ADR-0017](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0017-a-predicate-must-finish.md).
|
|
91
146
|
|
|
92
147
|
**Globs are the dialect every spec-* tool reads.** The patterns to check,
|
|
93
148
|
`--ignore`, `--history` and `historyPatterns` are compiled by spec-core's glob
|
|
@@ -147,7 +202,7 @@ and `a//b` reads as `a/b`.
|
|
|
147
202
|
|
|
148
203
|
`globToRegExp` is deprecated, and stays as the old reading that the test of
|
|
149
204
|
every difference above runs against; it no longer folds case on Windows. See
|
|
150
|
-
[ADR-0022](docs/adr/0022-globs-are-the-family-path-dialect.md).
|
|
205
|
+
[ADR-0022](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0022-globs-are-the-family-path-dialect.md).
|
|
151
206
|
|
|
152
207
|
**Documents are read by spec-core's scanner.** It moved to spec-core, built on
|
|
153
208
|
this repository's scanner as its model, and is copied back into
|
|
@@ -237,7 +292,7 @@ names they had, and what that breaks is listed under
|
|
|
237
292
|
`isMasked(offset, mask?)` asks any of its three masks. The analysis leaves
|
|
238
293
|
images out but for wiki embeds. `parseDirectives` skips a comment whose
|
|
239
294
|
`closed` is `false`. `createLineIndex` is spec-core's, the same table. See
|
|
240
|
-
[ADR-0023](docs/adr/0023-the-scanner-is-the-familys.md).
|
|
295
|
+
[ADR-0023](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0023-the-scanner-is-the-familys.md).
|
|
241
296
|
|
|
242
297
|
**Front matter is read by spec-core's reader, and what it does not read says
|
|
243
298
|
why.** Keys are compared in lower case and one level of nesting is read as
|
|
@@ -273,7 +328,7 @@ why.** Keys are compared in lower case and one level of nesting is read as
|
|
|
273
328
|
|
|
274
329
|
`parseFrontMatter` keeps its signature and reads with spec-core's reader, so it
|
|
275
330
|
leaves out what the reader does not read. See
|
|
276
|
-
[ADR-0023](docs/adr/0023-the-scanner-is-the-familys.md).
|
|
331
|
+
[ADR-0023](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0023-the-scanner-is-the-familys.md).
|
|
277
332
|
|
|
278
333
|
**`archived` is a record, not a retired decision.** spec-brief closes a round
|
|
279
334
|
of work by writing `status: archived` and moving the brief to an archive
|
|
@@ -306,7 +361,7 @@ retired decision loses the `stale-premise` findings on documents that depend
|
|
|
306
361
|
on it, and the `orphaned-obligation` findings on its open items, and a baseline
|
|
307
362
|
that accepted one reports that entry as no longer occurring, which fails a run
|
|
308
363
|
under `--ratchet`. See
|
|
309
|
-
[ADR-0011](docs/adr/0011-a-record-is-not-a-specification.md).
|
|
364
|
+
[ADR-0011](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0011-a-record-is-not-a-specification.md).
|
|
310
365
|
|
|
311
366
|
### Fixed
|
|
312
367
|
|
|
@@ -517,7 +572,7 @@ the file - `--verbose` now says when it read nothing. Unreadable, as opposed to
|
|
|
517
572
|
absent, is an error wherever the path was written. A baseline that cannot be
|
|
518
573
|
*parsed* is still reported and ignored: it only ever suppresses, so a row that
|
|
519
574
|
is dropped is a finding reported. See
|
|
520
|
-
[ADR-0012](docs/adr/0012-a-baseline-is-a-ratchet.md).
|
|
575
|
+
[ADR-0012](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0012-a-baseline-is-a-ratchet.md).
|
|
521
576
|
|
|
522
577
|
### Fixed
|
|
523
578
|
|
|
@@ -527,7 +582,7 @@ selector under `query`, went to stdout above the report. With `--format json`
|
|
|
527
582
|
that made the output unparseable; with `--format sarif` it made a file the
|
|
528
583
|
code-scanning uploader rejects over a schema rather than over the command that
|
|
529
584
|
was run. Both now go to stderr, with everything else a run says about itself.
|
|
530
|
-
[ADR-0015](docs/adr/0015-feedback-goes-where-the-tools-already-look.md) already
|
|
585
|
+
[ADR-0015](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0015-feedback-goes-where-the-tools-already-look.md) already
|
|
531
586
|
had the rule - rows "cannot go on stdout beside a JSON document without
|
|
532
587
|
breaking the parse" - and these two lines were the places that did not follow
|
|
533
588
|
it.
|
|
@@ -589,7 +644,7 @@ matched against targets read out of documents, which nothing limits the length
|
|
|
589
644
|
of. Every glob - the patterns to check, `--ignore`, `--ignore-ref`, `--history` -
|
|
590
645
|
now runs on the same automaton as `~=`, verified against the `RegExp` it
|
|
591
646
|
replaces on every glob and path in a differential corpus. See
|
|
592
|
-
[ADR-0017](docs/adr/0017-a-predicate-must-finish.md).
|
|
647
|
+
[ADR-0017](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0017-a-predicate-must-finish.md).
|
|
593
648
|
|
|
594
649
|
- **An invalid glob names itself.** `docs/{a` used to report
|
|
595
650
|
`Invalid regular expression: /^docs\/(?:a$/i: Unterminated group`, about a
|
|
@@ -620,7 +675,7 @@ report a closed question as reopened when nobody touched it. An obligation is
|
|
|
620
675
|
paired only by a declared id, or by its document, section and title when nothing
|
|
621
676
|
else shares them; anything unpaired is reported as having appeared or
|
|
622
677
|
disappeared. Human, `--format json` or `--format markdown`, and exit 0 either
|
|
623
|
-
way. See [ADR-0020](docs/adr/0020-a-diff-names-what-it-can-tell-apart.md).
|
|
678
|
+
way. See [ADR-0020](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0020-a-diff-names-what-it-can-tell-apart.md).
|
|
624
679
|
|
|
625
680
|
- The JSON graph export names its `generator`, so a diff can warn when two
|
|
626
681
|
exports came from different versions, and each item says whether its id was
|
|
@@ -654,8 +709,8 @@ kills no assertion made: tests writing to fixed paths deleted each other's files
|
|
|
654
709
|
under Stryker's parallel workers. Race-free, the sweep read 73.42% before
|
|
655
710
|
`spec-graph diff` and 75.25% with this release: `diff.ts` at 98.35%, and
|
|
656
711
|
`directives.ts` from 66.88% to 78.01%. Lose every timeout and it reads 71.89%.
|
|
657
|
-
See [ADR-0007](docs/adr/0007-mutation-testing.md) and
|
|
658
|
-
[ADR-0019](docs/adr/0019-the-sweep-runs-in-shards.md).
|
|
712
|
+
See [ADR-0007](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0007-mutation-testing.md) and
|
|
713
|
+
[ADR-0019](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0019-the-sweep-runs-in-shards.md).
|
|
659
714
|
|
|
660
715
|
The sweep after the directive fix moved `extract.ts` down 0.18 points, among the
|
|
661
716
|
usual movement in files nobody touched, and this one was real. Four mutants
|
|
@@ -692,7 +747,7 @@ A region now carries its file's front matter minus two kinds of key. A
|
|
|
692
747
|
on behalf of the register, not of each decision in it. So does a key the region
|
|
693
748
|
**answers for itself** — its identifier, status, title and aliases, which are
|
|
694
749
|
precisely the things a register exists to vary row by row.
|
|
695
|
-
[ADR-0009](docs/adr/0009-a-specification-is-a-region.md) carried this as a
|
|
750
|
+
[ADR-0009](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0009-a-specification-is-a-region.md) carried this as a
|
|
696
751
|
narrowed open question and called it a gap. It was a defect, and it was found by
|
|
697
752
|
pointing the binary at a register rather than by reading the code.
|
|
698
753
|
|
|
@@ -705,7 +760,7 @@ that keeps registers and writes rules about front matter.
|
|
|
705
760
|
### Added
|
|
706
761
|
|
|
707
762
|
**`~=` is matched by an automaton that cannot backtrack.**
|
|
708
|
-
[ADR-0016](docs/adr/0016-a-query-needs-a-sentence.md) documented this hazard
|
|
763
|
+
[ADR-0016](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0016-a-query-needs-a-sentence.md) documented this hazard
|
|
709
764
|
with `(a+)+$`, which nobody writes, and priced the fix at "worth it only if a
|
|
710
765
|
real repository hangs". Both were wrong. `^([A-Za-z0-9_]+[ ]?)+$` is what
|
|
711
766
|
somebody writes to check that a title is words separated by single spaces:
|
|
@@ -736,8 +791,8 @@ over, on one clause of the language specification read backwards in two
|
|
|
736
791
|
different places - the word characters `\b` recognises, and whether a character
|
|
737
792
|
whose upper case is ASCII may match `[A-Z]`. The second was found by the
|
|
738
793
|
mutation score pointing at a corpus with a blind spot in it rather than at a
|
|
739
|
-
test. See [ADR-0017](docs/adr/0017-a-predicate-must-finish.md) and
|
|
740
|
-
[ADR-0007](docs/adr/0007-mutation-testing.md).
|
|
794
|
+
test. See [ADR-0017](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0017-a-predicate-must-finish.md) and
|
|
795
|
+
[ADR-0007](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0007-mutation-testing.md).
|
|
741
796
|
|
|
742
797
|
**Configuration is discovered upward, and the file holding it is the root.**
|
|
743
798
|
|
|
@@ -747,7 +802,7 @@ cd packages/auth && spec-graph check # the repository's rules, the
|
|
|
747
802
|
# repository's paths
|
|
748
803
|
```
|
|
749
804
|
|
|
750
|
-
[ADR-0010](docs/adr/0010-configuration-belongs-to-the-repository.md) worried
|
|
805
|
+
[ADR-0010](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0010-configuration-belongs-to-the-repository.md) worried
|
|
751
806
|
that discovery would make a run depend on where it started. Reading the working
|
|
752
807
|
directory already did, and silently: a check from a package directory found no
|
|
753
808
|
configuration, ran no project rules, used the default include patterns, and
|
|
@@ -758,7 +813,7 @@ to a run from the top. The walk stops at the repository — a directory holding
|
|
|
758
813
|
`.git` — so a stray file in a home directory cannot reach it. `--root` names the
|
|
759
814
|
root yourself and turns discovery off, and a path typed on the command line
|
|
760
815
|
stays relative to where you typed it. See
|
|
761
|
-
[ADR-0018](docs/adr/0018-the-configuration-file-is-the-root.md).
|
|
816
|
+
[ADR-0018](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0018-the-configuration-file-is-the-root.md).
|
|
762
817
|
|
|
763
818
|
**`spec-graph query project:<rule>` runs a registered rule by name.** The rule
|
|
764
819
|
is already compiled by the time the command runs, and the alternative was
|
|
@@ -863,7 +918,7 @@ re-litigated: a **relational diff** between two states of the graph, taken
|
|
|
863
918
|
between two `--graph-format json` exports rather than between two git revisions,
|
|
864
919
|
because revisions mean spawning a binary underneath a pipeline that is a pure
|
|
865
920
|
function of text. See
|
|
866
|
-
[ADR-0015](docs/adr/0015-feedback-goes-where-the-tools-already-look.md).
|
|
921
|
+
[ADR-0015](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0015-feedback-goes-where-the-tools-already-look.md).
|
|
867
922
|
|
|
868
923
|
### Verified
|
|
869
924
|
|
|
@@ -895,7 +950,7 @@ mostly the older formatters.
|
|
|
895
950
|
Between those two runs `rules.ts` moved from 79.42% to 76.02% and
|
|
896
951
|
`directives.ts` from 77.92% to 74.03%, with neither file touched: the tests added
|
|
897
952
|
in between changed which tests `perTest` believes cover which mutants.
|
|
898
|
-
[ADR-0007](docs/adr/0007-mutation-testing.md) has recorded that shape for two
|
|
953
|
+
[ADR-0007](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0007-mutation-testing.md) has recorded that shape for two
|
|
899
954
|
releases, and it is why a per-file drop is a question and not an answer.
|
|
900
955
|
|
|
901
956
|
The hosted number is the one that governs, because it is where the build fails:
|
|
@@ -905,12 +960,12 @@ points.
|
|
|
905
960
|
|
|
906
961
|
The incremental run on the same commit read 0.40 points low - the third
|
|
907
962
|
observation after 3.18 high at 0.3.0 and 1.29 low at 0.4.0, and the reason
|
|
908
|
-
[ADR-0007](docs/adr/0007-mutation-testing.md) calls it a signal rather than a
|
|
963
|
+
[ADR-0007](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0007-mutation-testing.md) calls it a signal rather than a
|
|
909
964
|
figure. The tag's run also logged a cache save, which this entry first read as
|
|
910
965
|
the cache-key fix from 0.4.0 doing its job. *Corrected 2026-09-13:* it saved a
|
|
911
966
|
copy of the stale report it had restored, because the full step never wrote one,
|
|
912
967
|
and an entry saved under a tag cannot be restored on `main` in any case. See
|
|
913
|
-
[ADR-0007](docs/adr/0007-mutation-testing.md).
|
|
968
|
+
[ADR-0007](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0007-mutation-testing.md).
|
|
914
969
|
|
|
915
970
|
**The rebuild took 168 of its 180 minutes.** 13% more mutants than 0.4.0, and 63%
|
|
916
971
|
longer on the hosted runner against 15% longer locally. At 0.4.0's old cap of
|
|
@@ -941,7 +996,7 @@ selector, a message, a hint and a severity, and runs it beside the built-ins:
|
|
|
941
996
|
}
|
|
942
997
|
```
|
|
943
998
|
|
|
944
|
-
[ADR-0005](docs/adr/0005-rules-are-queries.md) claimed for three releases that
|
|
999
|
+
[ADR-0005](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0005-rules-are-queries.md) claimed for three releases that
|
|
945
1000
|
the selector language was expressive enough for a team to write the equivalent
|
|
946
1001
|
of a built-in rule. It was only true at a prompt: `spec-graph query` could find
|
|
947
1002
|
the thing, and nothing could make finding it fail. Three ADRs recorded the same
|
|
@@ -960,7 +1015,7 @@ nothing answers to is reported when the file is read - a rule that cannot work
|
|
|
960
1015
|
has to say so, because silence from a linter is indistinguishable from health.
|
|
961
1016
|
`query` also takes a list, read as a union.
|
|
962
1017
|
|
|
963
|
-
See [ADR-0016](docs/adr/0016-a-query-needs-a-sentence.md).
|
|
1018
|
+
See [ADR-0016](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0016-a-query-needs-a-sentence.md).
|
|
964
1019
|
|
|
965
1020
|
**A broken reference suggests the document it was probably meant to name.**
|
|
966
1021
|
`ARD-0015` gets `did you mean ADR-0015?`; `0002-cacheing.md` gets its sibling
|
|
@@ -968,7 +1023,7 @@ one letter away. `ADR-0003` in a repository of two ADRs gets nothing, and that
|
|
|
968
1023
|
restraint is the design rather than a limitation of it: a family name is a word
|
|
969
1024
|
people misremember, a number *is* the identity, and every number sits one edit
|
|
970
1025
|
from its neighbours. Each gate ends the same way - exactly one candidate, or
|
|
971
|
-
silence. See [ADR-0004](docs/adr/0004-reference-resolution.md).
|
|
1026
|
+
silence. See [ADR-0004](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0004-reference-resolution.md).
|
|
972
1027
|
|
|
973
1028
|
**A stale baseline entry says `paid` or `gone`.** `paid` is the ratchet working.
|
|
974
1029
|
`gone` means the document was not in this corpus at all, so nothing is known
|
|
@@ -997,7 +1052,7 @@ UTF-16 read as UTF-8 rather than anybody's keystroke.
|
|
|
997
1052
|
### Fixed
|
|
998
1053
|
|
|
999
1054
|
Nothing that a corpus had hit. Two findings came out of probing rather than out
|
|
1000
|
-
of the suite, which is the practice [CLAUDE.md](CLAUDE.md) asks for:
|
|
1055
|
+
of the suite, which is the practice [CLAUDE.md](https://github.com/DescentVTT/spec-graph/blob/main/CLAUDE.md) asks for:
|
|
1001
1056
|
|
|
1002
1057
|
- A near-miss gate that suggested a file which had moved **never fired**.
|
|
1003
1058
|
Resolution already binds a path by its basename, so the suggestion had nothing
|
|
@@ -1035,7 +1090,7 @@ of the suite, which is the practice [CLAUDE.md](CLAUDE.md) asks for:
|
|
|
1035
1090
|
first time anybody checked. Across 73 keys that Jekyll, Hugo, Docusaurus, Astro,
|
|
1036
1091
|
MADR, KEP, the IETF datatracker and Obsidian actually write, one edit reads
|
|
1037
1092
|
*none* of them as a near-miss and two edits reads exactly one: `rfc`, which it
|
|
1038
|
-
would tell an author to spell `refs`. [ADR-0014](docs/adr/0014-a-relation-is-spelled-both-ways.md)
|
|
1093
|
+
would tell an author to spell `refs`. [ADR-0014](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0014-a-relation-is-spelled-both-ways.md)
|
|
1039
1094
|
said two edits would catch `dependancies`; one edit already did, along with
|
|
1040
1095
|
every other typo anybody produced to argue the case. Two buys nothing and costs
|
|
1041
1096
|
a false positive on a key that carries citations in exactly the corpora this
|
|
@@ -1068,7 +1123,7 @@ fails: **75.40%**, up from 75.14% at 0.3.0. Lose every timeout on a loaded runne
|
|
|
1068
1123
|
and it reads 71.76%, which is the most margin `break: 70` has ever had.
|
|
1069
1124
|
|
|
1070
1125
|
That run also caught the other half of a claim
|
|
1071
|
-
[ADR-0007](docs/adr/0007-mutation-testing.md) narrowed at 0.3.0. The `main` push
|
|
1126
|
+
[ADR-0007](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0007-mutation-testing.md) narrowed at 0.3.0. The `main` push
|
|
1072
1127
|
and the `v0.4.0` tag were the same commit on the same runner, and the incremental
|
|
1073
1128
|
read **1.29 points low** against the rebuild - having read 3.18 points *high* at
|
|
1074
1129
|
0.3.0. Two observations, opposite signs. The direction is not a property, and the
|
|
@@ -1077,7 +1132,7 @@ leaves it nothing to reuse.
|
|
|
1077
1132
|
|
|
1078
1133
|
### Withdrawn
|
|
1079
1134
|
|
|
1080
|
-
[ADR-0013](docs/adr/0013-the-scanner-hands-back-prose.md) said a lone `\r` was
|
|
1135
|
+
[ADR-0013](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0013-the-scanner-hands-back-prose.md) said a lone `\r` was
|
|
1081
1136
|
not treated as a line ending. It always was - `createLineIndex` has recognised
|
|
1082
1137
|
all three terminators since 0.1.0 and says so in its own doc comment. The claim
|
|
1083
1138
|
came from reading the scanner and not the table underneath it, and nobody had
|
|
@@ -1094,7 +1149,7 @@ built-ins needs a default arm. `BaselineOutcome.stale` carries `reason`, and
|
|
|
1094
1149
|
## 0.3.0
|
|
1095
1150
|
|
|
1096
1151
|
Found by writing a corpus designed to break the scanner and reading the edges it
|
|
1097
|
-
produced, which is the practice [CLAUDE.md](CLAUDE.md) asks for and the reason
|
|
1152
|
+
produced, which is the practice [CLAUDE.md](https://github.com/DescentVTT/spec-graph/blob/main/CLAUDE.md) asks for and the reason
|
|
1098
1153
|
it is there. The mutation score could not have found any of it: a mutant is a
|
|
1099
1154
|
change to code that exists, and a construct nobody parsed has no code to mutate.
|
|
1100
1155
|
|
|
@@ -1137,7 +1192,7 @@ GitHub's own copy-link all produce for a file with a space in its name. The
|
|
|
1137
1192
|
escapes are read as a fallback, so a filename that genuinely contains one keeps
|
|
1138
1193
|
resolving by the spelling it was written with, and a stray `100%` is left alone.
|
|
1139
1194
|
|
|
1140
|
-
See [ADR-0013](docs/adr/0013-the-scanner-hands-back-prose.md).
|
|
1195
|
+
See [ADR-0013](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0013-the-scanner-hands-back-prose.md).
|
|
1141
1196
|
|
|
1142
1197
|
**Front matter knew half of each relation.** `depends-on` was a key and
|
|
1143
1198
|
`depended-on-by` was not, so a repository whose filing convention records
|
|
@@ -1148,7 +1203,7 @@ now - twenty-five keys became fifty-six - and a test holds the invariant.
|
|
|
1148
1203
|
|
|
1149
1204
|
Keys also fold on separators and case, so `depends-on`, `depends_on` and
|
|
1150
1205
|
`dependsOn` are one key rather than the two that happened to be written out.
|
|
1151
|
-
See [ADR-0014](docs/adr/0014-a-relation-is-spelled-both-ways.md).
|
|
1206
|
+
See [ADR-0014](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0014-a-relation-is-spelled-both-ways.md).
|
|
1152
1207
|
|
|
1153
1208
|
**`--verbose --format json` printed human lines onto stdout beside the JSON.**
|
|
1154
1209
|
|
|
@@ -1159,11 +1214,11 @@ See [ADR-0014](docs/adr/0014-a-relation-is-spelled-both-ways.md).
|
|
|
1159
1214
|
ignored. Both halves of that gate matter: `deprecated: true` is one edit from
|
|
1160
1215
|
`deprecates` and a boolean is not a citation. It says what is true - *"the key
|
|
1161
1216
|
declares no relation"* - and offers the near spellings rather than asserting
|
|
1162
|
-
one. See [ADR-0014](docs/adr/0014-a-relation-is-spelled-both-ways.md).
|
|
1217
|
+
one. See [ADR-0014](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0014-a-relation-is-spelled-both-ways.md).
|
|
1163
1218
|
- **`--ratchet`**, and `"ratchet": true` in the configuration. The other side of
|
|
1164
1219
|
a baseline: with it, a declared finding that no longer occurs fails the build,
|
|
1165
1220
|
named, with `--record-baseline` as the fix. Off by default, because
|
|
1166
|
-
[ADR-0012](docs/adr/0012-a-baseline-is-a-ratchet.md) is right that failing a
|
|
1221
|
+
[ADR-0012](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0012-a-baseline-is-a-ratchet.md) is right that failing a
|
|
1167
1222
|
build because somebody fixed something is a strange way to encourage them -
|
|
1168
1223
|
and on for a team that has decided its debt only moves one way, because a note
|
|
1169
1224
|
on a green build is a line that scrolls past.
|
|
@@ -1173,7 +1228,7 @@ See [ADR-0014](docs/adr/0014-a-relation-is-spelled-both-ways.md).
|
|
|
1173
1228
|
same rule/specification/citation identity a baseline is keyed on, so an
|
|
1174
1229
|
annotation survives a reformat. Deterministic and timestamp-free like every
|
|
1175
1230
|
other output here. See
|
|
1176
|
-
[ADR-0015](docs/adr/0015-feedback-goes-where-the-tools-already-look.md).
|
|
1231
|
+
[ADR-0015](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0015-feedback-goes-where-the-tools-already-look.md).
|
|
1177
1232
|
|
|
1178
1233
|
### Not added
|
|
1179
1234
|
|
|
@@ -1183,14 +1238,14 @@ semantics that differ on all three platforms would be the one part of this
|
|
|
1183
1238
|
program that could not honour byte-determinism, inside a codebase whose whole
|
|
1184
1239
|
test strategy rests on everything between the edges being a pure function of
|
|
1185
1240
|
text. Reasoning in
|
|
1186
|
-
[ADR-0015](docs/adr/0015-feedback-goes-where-the-tools-already-look.md).
|
|
1241
|
+
[ADR-0015](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0015-feedback-goes-where-the-tools-already-look.md).
|
|
1187
1242
|
|
|
1188
1243
|
### Measured
|
|
1189
1244
|
|
|
1190
1245
|
684 tests, and a mutation score of **75.14% on the hosted runner** - the figure
|
|
1191
1246
|
that governs, up from 73.32% at v0.2.0 - against 79.69% over 8,126 mutants on a
|
|
1192
1247
|
developer machine. The guard stays at `break: 70`. See
|
|
1193
|
-
[ADR-0007](docs/adr/0007-mutation-testing.md), which 0.3.0 also amended: an
|
|
1248
|
+
[ADR-0007](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0007-mutation-testing.md), which 0.3.0 also amended: an
|
|
1194
1249
|
incremental run read 3.18 points *high* against a full rebuild of the same
|
|
1195
1250
|
commit, so the claim that incremental errs low was wrong.
|
|
1196
1251
|
|
|
@@ -1205,7 +1260,7 @@ spellings held one id per key. A second claimant therefore did not make the link
|
|
|
1205
1260
|
ambiguous; it overwrote the first, and the link went to whichever file was
|
|
1206
1261
|
indexed last.
|
|
1207
1262
|
|
|
1208
|
-
[ADR-0004](docs/adr/0004-reference-resolution.md) has always said two candidates
|
|
1263
|
+
[ADR-0004](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0004-reference-resolution.md) has always said two candidates
|
|
1209
1264
|
is worse than none, and a reference matching several documents is reported as
|
|
1210
1265
|
ambiguous rather than bound to whichever was indexed first. That now holds for
|
|
1211
1266
|
paths too.
|
|
@@ -1230,7 +1285,7 @@ keeps no registers is unaffected, and one that does needs no change beyond the
|
|
|
1230
1285
|
upgrade.
|
|
1231
1286
|
|
|
1232
1287
|
Since 0.1.3 a register has yielded one specification per row or per section
|
|
1233
|
-
([ADR-0009](docs/adr/0009-a-specification-is-a-region.md)), and every one of
|
|
1288
|
+
([ADR-0009](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0009-a-specification-is-a-region.md)), and every one of
|
|
1234
1289
|
those carried the file's path into the resolver's path index. That index keeps
|
|
1235
1290
|
whichever entry was written last, so the file's own entry was overwritten by its
|
|
1236
1291
|
final region, and:
|
|
@@ -1276,12 +1331,12 @@ requirements table headed `Status` was enough to trigger it.
|
|
|
1276
1331
|
`historyPatterns` in the configuration or by `<!-- @spec-history -->`, and
|
|
1277
1332
|
never guessed from a filename. Its links are still checked and work handed
|
|
1278
1333
|
*into* it is still a ghost handover; its own obligations and lifecycle are
|
|
1279
|
-
not. See [ADR-0011](docs/adr/0011-a-record-is-not-a-specification.md).
|
|
1334
|
+
not. See [ADR-0011](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0011-a-record-is-not-a-specification.md).
|
|
1280
1335
|
- **Baselines.** `--record-baseline` writes today's findings as accepted debt;
|
|
1281
1336
|
`--baseline` reports only what is new since. Keyed on the specification and
|
|
1282
1337
|
the citation rather than on a line number, so it survives edits, reordering
|
|
1283
1338
|
and renames. Paid debt is reported, not failed. See
|
|
1284
|
-
[ADR-0012](docs/adr/0012-a-baseline-is-a-ratchet.md).
|
|
1339
|
+
[ADR-0012](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0012-a-baseline-is-a-ratchet.md).
|
|
1285
1340
|
|
|
1286
1341
|
### Fixed
|
|
1287
1342
|
|
|
@@ -1296,10 +1351,10 @@ requirements table headed `Status` was enough to trigger it.
|
|
|
1296
1351
|
- **A specification is a region of a file, not a file.** A register kept as
|
|
1297
1352
|
headings or as a table yields one specification per decision, each with its
|
|
1298
1353
|
own lifecycle, obligations and relations. See
|
|
1299
|
-
[ADR-0009](docs/adr/0009-a-specification-is-a-region.md).
|
|
1354
|
+
[ADR-0009](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0009-a-specification-is-a-region.md).
|
|
1300
1355
|
- **Repository configuration.** `.spec-graph.json`, `spec-graph.config.json`, or
|
|
1301
1356
|
a `"spec-graph"` key in `package.json`, with family allow and deny rules. See
|
|
1302
|
-
[ADR-0010](docs/adr/0010-configuration-belongs-to-the-repository.md).
|
|
1357
|
+
[ADR-0010](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0010-configuration-belongs-to-the-repository.md).
|
|
1303
1358
|
|
|
1304
1359
|
## 0.1.2
|
|
1305
1360
|
|
|
@@ -1307,7 +1362,7 @@ requirements table headed `Status` was enough to trigger it.
|
|
|
1307
1362
|
|
|
1308
1363
|
- `--ignore-ref`, for repositories where `[[...]]` tags a concept rather than
|
|
1309
1364
|
naming a file. See
|
|
1310
|
-
[ADR-0008](docs/adr/0008-wiki-links-carry-no-path.md).
|
|
1365
|
+
[ADR-0008](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0008-wiki-links-carry-no-path.md).
|
|
1311
1366
|
|
|
1312
1367
|
## 0.1.1
|
|
1313
1368
|
|
package/README.md
CHANGED
|
@@ -258,7 +258,7 @@ Both forms need **two** signals, and that is deliberate: `## Q3 2026 Roadmap`
|
|
|
258
258
|
parses as family `Q`, number 3, and `## v1.2.0` in a changelog parses as family
|
|
259
259
|
`v`, number 1. Neither declares a status, so neither is a specification. A table
|
|
260
260
|
of identifiers and prose is a citation list, not a register. See
|
|
261
|
-
[ADR-0009](docs/adr/0009-a-specification-is-a-region.md).
|
|
261
|
+
[ADR-0009](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0009-a-specification-is-a-region.md).
|
|
262
262
|
|
|
263
263
|
### When `[[...]]` tags a concept
|
|
264
264
|
|
|
@@ -288,7 +288,7 @@ The tool names that flag in the hint, so it costs one run to find:
|
|
|
288
288
|
`--ignore-ref` suppresses *findings*, never edges. A reference that resolves is
|
|
289
289
|
still a relation in the graph no matter what you exclude — not even
|
|
290
290
|
`--ignore-ref "*"` can delete one. See
|
|
291
|
-
[ADR-0008](docs/adr/0008-wiki-links-carry-no-path.md).
|
|
291
|
+
[ADR-0008](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0008-wiki-links-carry-no-path.md).
|
|
292
292
|
|
|
293
293
|
#### A gap that is deliberate
|
|
294
294
|
|
|
@@ -417,7 +417,7 @@ which are not regular; both are refused when the selector is read, with the
|
|
|
417
417
|
character pointed at. Globs - the patterns to check, `--ignore`, `--ignore-ref`,
|
|
418
418
|
`--history` - are matched by an automaton too, because a glob with three stars
|
|
419
419
|
in it is enough to keep `RegExp` busy for two minutes over a long reference
|
|
420
|
-
target. See [ADR-0017](docs/adr/0017-a-predicate-must-finish.md). Both automata
|
|
420
|
+
target. See [ADR-0017](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0017-a-predicate-must-finish.md). Both automata
|
|
421
421
|
are spec-core's, the library the spec-* tools share, copied into this package
|
|
422
422
|
rather than installed.
|
|
423
423
|
|
|
@@ -425,7 +425,7 @@ On a register, a region answers its file's front matter for every descriptive
|
|
|
425
425
|
key — `fm.owner` on a decision inside a register is the register's owner — while
|
|
426
426
|
the keys a region owns itself, its identifier and status and title, and any key
|
|
427
427
|
that declares a relation, stay on the file. See
|
|
428
|
-
[ADR-0009](docs/adr/0009-a-specification-is-a-region.md).
|
|
428
|
+
[ADR-0009](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0009-a-specification-is-a-region.md).
|
|
429
429
|
|
|
430
430
|
## Your own rules
|
|
431
431
|
|
|
@@ -489,7 +489,7 @@ spec-graph: .spec-graph.json: rules.owner: "{1.phse}" asks for an attribute
|
|
|
489
489
|
nothing has
|
|
490
490
|
```
|
|
491
491
|
|
|
492
|
-
See [ADR-0016](docs/adr/0016-a-query-needs-a-sentence.md).
|
|
492
|
+
See [ADR-0016](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0016-a-query-needs-a-sentence.md).
|
|
493
493
|
|
|
494
494
|
## Directives
|
|
495
495
|
|
|
@@ -540,7 +540,7 @@ checked.
|
|
|
540
540
|
That last row is the point. Excluding the file with `--ignore` would have
|
|
541
541
|
silenced the whole lot, including the links, and a journal full of 404s is
|
|
542
542
|
exactly what this tool is for. See
|
|
543
|
-
[ADR-0011](docs/adr/0011-a-record-is-not-a-specification.md).
|
|
543
|
+
[ADR-0011](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0011-a-record-is-not-a-specification.md).
|
|
544
544
|
|
|
545
545
|
## Adopting this on a repository that predates it
|
|
546
546
|
|
|
@@ -596,7 +596,7 @@ nothing left to be stale about, so a typo in it looks exactly like a regression
|
|
|
596
596
|
nobody introduced: it exits `2` instead. A `"baseline"` in the configuration is
|
|
597
597
|
the other case and stays optional, because a repository declares the path before
|
|
598
598
|
the first run records the file — `--verbose` says when it read nothing. Either
|
|
599
|
-
may be absolute. See [ADR-0012](docs/adr/0012-a-baseline-is-a-ratchet.md).
|
|
599
|
+
may be absolute. See [ADR-0012](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0012-a-baseline-is-a-ratchet.md).
|
|
600
600
|
|
|
601
601
|
## In CI
|
|
602
602
|
|
|
@@ -687,7 +687,7 @@ the root**. So a run from `packages/auth` reports what a run from the top
|
|
|
687
687
|
reports, byte for byte, because every path here is relative to the root. The
|
|
688
688
|
nearest file wins, `--root` names the root yourself and turns discovery off, and
|
|
689
689
|
a path typed on the command line stays relative to where you typed it. See
|
|
690
|
-
[ADR-0018](docs/adr/0018-the-configuration-file-is-the-root.md).
|
|
690
|
+
[ADR-0018](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0018-the-configuration-file-is-the-root.md).
|
|
691
691
|
|
|
692
692
|
A flag always wins over the file, and list flags **add** to it rather than
|
|
693
693
|
replacing it — a `--ignore-ref` on the command line is one more exclusion, not a
|
|
@@ -737,7 +737,7 @@ any depth.
|
|
|
737
737
|
`--ignore-ref` is matched against reference targets rather than paths: a bare
|
|
738
738
|
pattern names one target exactly, case is ignored on every host, a `\` escapes
|
|
739
739
|
the next character, and `.` and `..` are the text of the link. See
|
|
740
|
-
[ADR-0022](docs/adr/0022-globs-are-the-family-path-dialect.md).
|
|
740
|
+
[ADR-0022](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0022-globs-are-the-family-path-dialect.md).
|
|
741
741
|
|
|
742
742
|
### Family rules
|
|
743
743
|
|
|
@@ -755,7 +755,7 @@ has — and turns every other noun-number construct back into prose.
|
|
|
755
755
|
Both are consulted only after resolution has already failed, so `RFC 0001` still
|
|
756
756
|
resolves to your local RFC-0001 with `RFC` on the ignore list. No configuration
|
|
757
757
|
can delete an edge. See
|
|
758
|
-
[ADR-0010](docs/adr/0010-configuration-belongs-to-the-repository.md).
|
|
758
|
+
[ADR-0010](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0010-configuration-belongs-to-the-repository.md).
|
|
759
759
|
|
|
760
760
|
`--verbose` lists everything they silenced, so one over-broad glob cannot quietly
|
|
761
761
|
turn the check off and look like a clean repository:
|
|
@@ -769,7 +769,7 @@ The JSON report carries the same rows under `suppressed`, without needing the
|
|
|
769
769
|
flag. What is deliberately *not* listed is a bare identifier spec-graph read as
|
|
770
770
|
prose on its own — `T-1000` was nobody's decision, and listing it would bury the
|
|
771
771
|
entries that were. See
|
|
772
|
-
[ADR-0008](docs/adr/0008-wiki-links-carry-no-path.md).
|
|
772
|
+
[ADR-0008](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0008-wiki-links-carry-no-path.md).
|
|
773
773
|
|
|
774
774
|
Prose like `Phase 1`, `R69`, `Q-120`, `Table 2` and `Step 4` has never needed
|
|
775
775
|
this: an identifier found in prose is only read as a citation when its family
|
|
@@ -880,7 +880,7 @@ document, section and title when nothing else shares them. Anything unpaired
|
|
|
880
880
|
is reported as having appeared or disappeared, never as resolved or reopened.
|
|
881
881
|
Findings are left to `--baseline`, which already reports what is new. The exit
|
|
882
882
|
code is `0` whether anything changed or not: a diff describes, and `check`
|
|
883
|
-
gates. See [ADR-0020](docs/adr/0020-a-diff-names-what-it-can-tell-apart.md).
|
|
883
|
+
gates. See [ADR-0020](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0020-a-diff-names-what-it-can-tell-apart.md).
|
|
884
884
|
|
|
885
885
|
## Visualising
|
|
886
886
|
|
|
@@ -919,29 +919,29 @@ exported. Reporters, editor extensions and custom rules are all first-class.
|
|
|
919
919
|
|
|
920
920
|
Twenty-three ADRs, which `spec-graph` validates on every CI run:
|
|
921
921
|
|
|
922
|
-
- [ADR-0001 — A hand-written Markdown scanner](docs/adr/0001-hand-written-markdown-scanner.md)
|
|
923
|
-
- [ADR-0002 — A four-phase lifecycle lattice](docs/adr/0002-lifecycle-lattice.md)
|
|
924
|
-
- [ADR-0003 — Item state is resolved from competing signals](docs/adr/0003-item-state-signals.md)
|
|
925
|
-
- [ADR-0004 — Reference resolution is deliberately asymmetric](docs/adr/0004-reference-resolution.md)
|
|
926
|
-
- [ADR-0005 — The rules and the query language share one engine](docs/adr/0005-rules-are-queries.md)
|
|
927
|
-
- [ADR-0006 — False positives cost more than misses](docs/adr/0006-false-positives-cost-more.md)
|
|
928
|
-
- [ADR-0007 — Mutation testing, and what the score actually means](docs/adr/0007-mutation-testing.md)
|
|
929
|
-
- [ADR-0008 — A wiki link carries a name, not a path](docs/adr/0008-wiki-links-carry-no-path.md)
|
|
930
|
-
- [ADR-0009 — A specification is a region of a file, not a file](docs/adr/0009-a-specification-is-a-region.md)
|
|
931
|
-
- [ADR-0010 — Configuration belongs to the repository](docs/adr/0010-configuration-belongs-to-the-repository.md)
|
|
932
|
-
- [ADR-0011 — A historical record is not a specification](docs/adr/0011-a-record-is-not-a-specification.md)
|
|
933
|
-
- [ADR-0012 — A baseline is a ratchet, keyed on identity](docs/adr/0012-a-baseline-is-a-ratchet.md)
|
|
934
|
-
- [ADR-0013 — When the scanner is unsure, it hands back prose](docs/adr/0013-the-scanner-hands-back-prose.md)
|
|
935
|
-
- [ADR-0014 — A relation is spelled in both directions](docs/adr/0014-a-relation-is-spelled-both-ways.md)
|
|
936
|
-
- [ADR-0015 — Feedback goes where the tools already look](docs/adr/0015-feedback-goes-where-the-tools-already-look.md)
|
|
937
|
-
- [ADR-0016 — A query needs a sentence before it is a rule](docs/adr/0016-a-query-needs-a-sentence.md)
|
|
938
|
-
- [ADR-0017 — A predicate must finish](docs/adr/0017-a-predicate-must-finish.md)
|
|
939
|
-
- [ADR-0018 — The configuration file is the root](docs/adr/0018-the-configuration-file-is-the-root.md)
|
|
940
|
-
- [ADR-0019 — The sweep runs in shards](docs/adr/0019-the-sweep-runs-in-shards.md)
|
|
941
|
-
- [ADR-0020 — A diff names only what it can tell apart](docs/adr/0020-a-diff-names-what-it-can-tell-apart.md)
|
|
942
|
-
- [ADR-0021 — Releases are published by CI, with provenance](docs/adr/0021-releases-are-published-by-ci.md)
|
|
943
|
-
- [ADR-0022 — Globs are the family's path dialect](docs/adr/0022-globs-are-the-family-path-dialect.md)
|
|
944
|
-
- [ADR-0023 — The scanner is the family's](docs/adr/0023-the-scanner-is-the-familys.md)
|
|
922
|
+
- [ADR-0001 — A hand-written Markdown scanner](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0001-hand-written-markdown-scanner.md)
|
|
923
|
+
- [ADR-0002 — A four-phase lifecycle lattice](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0002-lifecycle-lattice.md)
|
|
924
|
+
- [ADR-0003 — Item state is resolved from competing signals](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0003-item-state-signals.md)
|
|
925
|
+
- [ADR-0004 — Reference resolution is deliberately asymmetric](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0004-reference-resolution.md)
|
|
926
|
+
- [ADR-0005 — The rules and the query language share one engine](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0005-rules-are-queries.md)
|
|
927
|
+
- [ADR-0006 — False positives cost more than misses](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0006-false-positives-cost-more.md)
|
|
928
|
+
- [ADR-0007 — Mutation testing, and what the score actually means](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0007-mutation-testing.md)
|
|
929
|
+
- [ADR-0008 — A wiki link carries a name, not a path](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0008-wiki-links-carry-no-path.md)
|
|
930
|
+
- [ADR-0009 — A specification is a region of a file, not a file](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0009-a-specification-is-a-region.md)
|
|
931
|
+
- [ADR-0010 — Configuration belongs to the repository](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0010-configuration-belongs-to-the-repository.md)
|
|
932
|
+
- [ADR-0011 — A historical record is not a specification](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0011-a-record-is-not-a-specification.md)
|
|
933
|
+
- [ADR-0012 — A baseline is a ratchet, keyed on identity](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0012-a-baseline-is-a-ratchet.md)
|
|
934
|
+
- [ADR-0013 — When the scanner is unsure, it hands back prose](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0013-the-scanner-hands-back-prose.md)
|
|
935
|
+
- [ADR-0014 — A relation is spelled in both directions](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0014-a-relation-is-spelled-both-ways.md)
|
|
936
|
+
- [ADR-0015 — Feedback goes where the tools already look](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0015-feedback-goes-where-the-tools-already-look.md)
|
|
937
|
+
- [ADR-0016 — A query needs a sentence before it is a rule](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0016-a-query-needs-a-sentence.md)
|
|
938
|
+
- [ADR-0017 — A predicate must finish](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0017-a-predicate-must-finish.md)
|
|
939
|
+
- [ADR-0018 — The configuration file is the root](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0018-the-configuration-file-is-the-root.md)
|
|
940
|
+
- [ADR-0019 — The sweep runs in shards](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0019-the-sweep-runs-in-shards.md)
|
|
941
|
+
- [ADR-0020 — A diff names only what it can tell apart](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0020-a-diff-names-what-it-can-tell-apart.md)
|
|
942
|
+
- [ADR-0021 — Releases are published by CI, with provenance](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0021-releases-are-published-by-ci.md)
|
|
943
|
+
- [ADR-0022 — Globs are the family's path dialect](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0022-globs-are-the-family-path-dialect.md)
|
|
944
|
+
- [ADR-0023 — The scanner is the family's](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0023-the-scanner-is-the-familys.md)
|
|
945
945
|
|
|
946
946
|
**Zero runtime dependencies.** Node 22+, native ESM, TypeScript strict with
|
|
947
947
|
`noUncheckedIndexedAccess` and `exactOptionalPropertyTypes`. The Markdown
|
|
@@ -957,7 +957,7 @@ coverage. Coverage says a line ran, so the suite is also held to a mutation
|
|
|
957
957
|
score - 76.73% over 8,400 mutants at 0.9.0, measured incrementally in CI -
|
|
958
958
|
because a vocabulary entry or a boundary
|
|
959
959
|
condition can be weakened by an ordinary-looking refactor without a single test
|
|
960
|
-
going red. [ADR-0007](docs/adr/0007-mutation-testing.md) is straight about what
|
|
960
|
+
going red. [ADR-0007](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0007-mutation-testing.md) is straight about what
|
|
961
961
|
that number is and is not.
|
|
962
962
|
|
|
963
963
|
**Fast enough to run on save.** A synthetic corpus of 2,000 documents — 8.9 MB,
|
package/dist/cli.js
CHANGED
|
@@ -15,7 +15,7 @@ import { DiffInputError, diffExports, formatDiffJson, formatDiffMarkdown, format
|
|
|
15
15
|
import { isGlob, underRoot } from './glob.js';
|
|
16
16
|
import { analyse, DEFAULT_PATTERNS, withDiagnostics } from './runner.js';
|
|
17
17
|
import { formatGitlab, formatGraph, formatJson, formatMarkdown, formatReport, formatSarif, shouldUseAscii, shouldUseColor, } from './report.js';
|
|
18
|
-
import { DEFAULT_SEVERITIES, resolveStrict, RULE_DECISIONS, RULE_DESCRIPTIONS, RULE_IDS, RULE_QUERIES, } from './rules.js';
|
|
18
|
+
import { DEFAULT_SEVERITIES, resolveStrict, RULE_DECISIONS, decisionUrl, RULE_DESCRIPTIONS, RULE_IDS, RULE_QUERIES, } from './rules.js';
|
|
19
19
|
import { isAbsolutePath, toPosix } from './paths.js';
|
|
20
20
|
import { formatRef } from './source.js';
|
|
21
21
|
import { execute, parseQuery, QueryError, renderMatch } from './select.js';
|
|
@@ -549,7 +549,7 @@ export async function main(io = {}) {
|
|
|
549
549
|
err(`spec-graph: unknown rule "${wanted}"\n known rules: ${RULE_IDS.join(', ')}\n ${knownProjectRules(projectRules)}\n`);
|
|
550
550
|
return EXIT_ERROR;
|
|
551
551
|
}
|
|
552
|
-
out(renderRules(options.verbose, projectRules, wanted ?? null, loaded.source));
|
|
552
|
+
out(renderRules(options.verbose, await readVersion(), projectRules, wanted ?? null, loaded.source));
|
|
553
553
|
return EXIT_OK;
|
|
554
554
|
}
|
|
555
555
|
const patterns = options.patterns.length > 0 ? options.patterns.map((pattern) => anchor(here, pattern)) : (file.patterns ?? DEFAULT_PATTERNS);
|
|
@@ -790,7 +790,7 @@ function union(graph, queries) {
|
|
|
790
790
|
}
|
|
791
791
|
return matches;
|
|
792
792
|
}
|
|
793
|
-
function renderRules(explain, projectRules = [], only = null, configSource = null) {
|
|
793
|
+
function renderRules(explain, version, projectRules = [], only = null, configSource = null) {
|
|
794
794
|
// A project rule is described by the selector it is, because that is what it
|
|
795
795
|
// is - its message is a template, and a template is not a description.
|
|
796
796
|
const rows = [
|
|
@@ -799,7 +799,7 @@ function renderRules(explain, projectRules = [], only = null, configSource = nul
|
|
|
799
799
|
severity: DEFAULT_SEVERITIES[id],
|
|
800
800
|
description: RULE_DESCRIPTIONS[id],
|
|
801
801
|
query: RULE_QUERIES[id],
|
|
802
|
-
decided: RULE_DECISIONS[id],
|
|
802
|
+
decided: decisionUrl(RULE_DECISIONS[id], version),
|
|
803
803
|
})),
|
|
804
804
|
...projectRules.map((rule) => ({
|
|
805
805
|
id: rule.id,
|