@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 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 `![icon](x.md)` in the text; images are
21
+ now read first, so `[![icon](icon.png)](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 `[![build](badge.svg)](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,