@descent-vtt/spec-graph 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -11,6 +11,115 @@ the family's
11
11
  [versioning policy](https://github.com/DescentVTT/spec-core/blob/main/docs/adr/0009-versions-before-1-0.md)
12
12
  records.
13
13
 
14
+ ## 0.14.0
15
+
16
+ An input that is set and names nothing - a family name no family has, a list
17
+ of families no document belongs to, a `--root` that is no directory, a blank
18
+ value, an option of another command - was read as if it had not been given.
19
+ It is refused now: exit 2 and one line that names it
20
+ ([ADR-0010](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0010-configuration-belongs-to-the-repository.md)).
21
+
22
+ ### Changed
23
+
24
+ - **A family name no family has is refused.** `--family ""`, which an unset
25
+ variable leaves, and `--family ADR,RFC` allowed no family at all, so every
26
+ citation was prose and a check that failed on a dangling one exited 0. Each
27
+ is exit 2 now, `--family expects one family's name, such as ADR, got ""`,
28
+ and so are `--ignore-family`, `families` and `ignoreFamilies` in the
29
+ configuration, and `createFamilyFilter()` from the package.
30
+ Upgrading: give one name each time, the prefix the identifiers are written
31
+ with, as in `--family ADR --family RFC`, and leave the option out where
32
+ there is none to give.
33
+ - **A `--root` that is no directory is refused by name.** `rules --root` given
34
+ a path that is not there, a file or an empty value listed the built-in rules
35
+ and exited 0, and `check`, `query` and `graph` exited 2 saying only that no
36
+ specification matched. Each ends with exit 2 and the option named: `--root
37
+ "<path>" is not there`, `is not a directory`, or `--root expects a
38
+ directory, got ""`.
39
+ Upgrading: name a directory that exists, or leave `--root` out and the
40
+ directory holding the configuration is the root.
41
+ - **A configuration file that is there and cannot be read stops the run.** A
42
+ directory called `.spec-graph.json`, or a file the run may not open, was
43
+ passed over as absent, and the run checked on defaults or on the
44
+ configuration of a directory above. It is a problem in the configuration
45
+ now, exit 2.
46
+ Upgrading: remove or repair the file, or run with `--no-config`.
47
+ - **A blank `--ignore` or `--record-baseline` is refused, as an empty one
48
+ was.** `--ignore " "` was read as a directory's name since 0.9.0 and
49
+ ignored nothing, `"ignore": [" "]` in the configuration likewise, and
50
+ `--record-baseline " "` wrote a file named with a space.
51
+ Upgrading: pass the option only where its variable is set, as in
52
+ `${IGNORE:+--ignore "$IGNORE"}`.
53
+ - **`rules` takes one rule id.** `spec-graph rules a b` listed `a` and said
54
+ nothing of `b`; it is exit 2 now, with both named.
55
+ Upgrading: one run for each rule, or no id for all of them.
56
+ - **`--format json` is refused by `graph` and `rules`.** `graph --format
57
+ json` wrote DOT and `rules --format json` its list, each with exit 0, as
58
+ `--format sarif` was already refused there.
59
+ Upgrading: `graph --graph-format json` writes the graph as JSON; `rules`
60
+ has one format.
61
+ - **A list of families that names none the documents belong to stops the
62
+ check.** `--family RFC` in a repository of ADRs, or `ADRS` for `ADR`,
63
+ allowed nothing, as `--family ""` did: `check` printed `the specification
64
+ graph is consistent` over a dangling citation, under `--strict` too. It is
65
+ exit 2 now, for `--family` and `families` alike, with the list and the
66
+ families the documents do belong to; a list that names such a family beside
67
+ one they belong to gets a line on stderr and fails nothing.
68
+ Upgrading: name a family whose identifiers the documents carry, `ADR` for
69
+ `ADR-0007`, or leave the list out until the first such document is written.
70
+ - **An option a command does not read is refused by that command.** `query
71
+ --strict`, `graph --record-baseline f`, `rules --max 1`, `diff --root x` and
72
+ some sixty more pairings ran as if the option were absent, and `spec-graph
73
+ --graph-format mermaid > graph.mmd` wrote the check's report into the file.
74
+ Each is exit 2 with the option, the command and the commands that read it:
75
+ `--graph-format belongs to graph, not to check`. `--no-config`,
76
+ `--no-color` and `--ascii` are taken everywhere
77
+ ([the table](README.md#cli)).
78
+ Upgrading: drop the option from the command that does not read it, or move
79
+ what every run repeats into `.spec-graph.json`, which every command reads.
80
+ - **A value that begins with `--` is not taken as a value.** `--ignore
81
+ --strict` ignored a directory called `--strict` and left the check
82
+ unstrict, and `--record-baseline --verbose` wrote a file of that name; each
83
+ option that takes a value now says `--ignore needs a value, and --strict
84
+ reads as an option`, and so does one followed by `-h` or `-v`.
85
+ Upgrading: give the value, or for a name that does begin with two dashes
86
+ write `./--name` for a file, `[-]-name` in a pattern and `\--name` for a
87
+ reference target.
88
+ - **`CI` no longer turns colour off.** With `CI` set and stdout a terminal the
89
+ report was plain; it is coloured there, as spec-brief's and spec-guard's
90
+ are, and where stdout is no terminal it is plain as before.
91
+ Upgrading: set `NO_COLOR=1` or pass `--no-color` where a pipeline's
92
+ terminal should stay plain.
93
+
94
+ ### Fixed
95
+
96
+ - `SPEC_GRAPH_ASCII=0`, `false` and a blank value no longer turn ASCII glyphs
97
+ on: the variable was read for being set. `--help` and the README name it
98
+ beside `--ascii`, and the README says how colour is decided; neither was
99
+ written anywhere.
100
+ - An empty `--baseline` or `--record-baseline` is refused by the option's
101
+ name, `--baseline expects a file, got ""`, where the line was `cannot read
102
+ the baseline : EISDIR: illegal operation on a directory, read`. Exit 2, as
103
+ before.
104
+ - An empty name given to `diff` is refused as `diff expects a graph export on
105
+ each side, got "" for the first`, where the line was `cannot read : EISDIR`.
106
+ Exit 2, as before.
107
+
108
+ ## 0.13.1
109
+
110
+ A reader that closed the output, as `| head` does, was answered with
111
+ `unexpected error:` and a stack; it is one line now, and still exit 2.
112
+
113
+ ### Fixed
114
+
115
+ - **A reader that closes the output is answered in one line, not a stack.**
116
+ `spec-graph graph | head` ended with `spec-graph: unexpected error: Error:
117
+ EPIPE: broken pipe, write` and a stack once `head` had left before the
118
+ graph was written; it now prints `spec-graph: stdout was closed before all
119
+ of the output was written` on stderr, from every command. The exit code is
120
+ 2, as it was
121
+ ([ADR-0010](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0010-configuration-belongs-to-the-repository.md)).
122
+
14
123
  ## 0.13.0
15
124
 
16
125
  An error spec-graph did not expect ended the run with exit 1, which reads as
package/README.md CHANGED
@@ -869,7 +869,8 @@ SARIF — and is 8 unless set; `0` lists none.
869
869
 
870
870
  A broken config stops the run with exit `2`, each problem named on stderr:
871
871
  invalid JSON, a value of the wrong type, an unknown key, a rule that does not
872
- compile. Why a mistake stops the run:
872
+ compile, a family name no family has, a file that is there and cannot be read.
873
+ Why a mistake stops the run:
873
874
 
874
875
  - A configuration that did not load checks a different repository than the one
875
876
  configured, and would report *that* one as consistent: a `{1.phse}` for
@@ -986,6 +987,37 @@ Every specification cites RFC 2119. In a repository that also keeps its own
986
987
  `families` is the stronger statement — *these* are the families this repository
987
988
  has — and turns every other noun-number construct back into prose.
988
989
 
990
+ Each entry, and each `--family` or `--ignore-family`, is one family's name:
991
+ the prefix its identifiers are written with, `ADR` for `ADR-0007`, in any case.
992
+ A name no family has — `""`, or `"ADR,RFC"` where two entries go — stops the
993
+ run with exit `2`: alone in `families` it allowed nothing, so every citation
994
+ was prose and a dangling one passed.
995
+
996
+ A name that is a family's, and that no document here belongs to, allows
997
+ nothing either. `--family RFC` in a repository of ADRs, or `ADRS` for `ADR`,
998
+ leaves every citation as prose, and the check would report a consistent graph
999
+ over a citation of an ADR nobody wrote. So a list has to name a family a
1000
+ document belongs to, or the check stops with exit `2`, before it reports or
1001
+ records anything:
1002
+
1003
+ ```text
1004
+ spec-graph: no document here belongs to a family named by --family (RFC); the documents here belong to ADR
1005
+ name a family the documents belong to, or leave the list out
1006
+ ```
1007
+
1008
+ A document belongs to a family when its identifier is written with it, as
1009
+ `ADR-0007` is with `ADR`. A list that names such a family beside one the
1010
+ documents do belong to is a family the repository means to start, or a typo in
1011
+ half the list: it is said on stderr and fails nothing.
1012
+
1013
+ ```text
1014
+ spec-graph: no document here belongs to RFC, named by --family; the documents here belong to ADR
1015
+ ```
1016
+
1017
+ A list whose every name has a document sees neither line. `ignoreFamilies`
1018
+ needs no such check: a name on it passes over citations of that family alone,
1019
+ so a mistyped one passes over nothing and the run reports more, not less.
1020
+
989
1021
  Both are consulted only after resolution has already failed, so `RFC 0001` still
990
1022
  resolves to your local RFC-0001 with `RFC` on the ignore list. No configuration
991
1023
  can delete an edge. See
@@ -1025,15 +1057,18 @@ spec-graph diff <before> <after> Compare two JSON graph exports.
1025
1057
  its directory is the root
1026
1058
  --ignore <glob> Skip paths (repeatable)
1027
1059
  --ignore-ref <glob> Do not report these reference targets (repeatable)
1028
- --family <name> Families a bare identifier may name (repeatable)
1029
- --ignore-family <name> Families that are never citations (repeatable)
1060
+ --family <name> Families a bare identifier may name (repeatable, one
1061
+ name each time)
1062
+ --ignore-family <name> Families that are never citations (repeatable, one
1063
+ name each time)
1030
1064
  --history <glob> Files that log decisions rather than making them
1031
1065
  --baseline <file> Accept these findings; report only what is new
1032
1066
  --record-baseline <f> Write today's findings as accepted debt, exit 0
1033
1067
  --ratchet Also fail when a baseline entry no longer occurs
1034
1068
  --no-config Ignore .spec-graph.json and the package.json key
1035
- --format <fmt> human | json | sarif (check) | github (check)
1036
- | gitlab (check) | markdown (check, diff)
1069
+ --format <fmt> human | json (check, query, diff) | sarif (check)
1070
+ | github (check) | gitlab (check)
1071
+ | markdown (check, diff)
1037
1072
  --graph-format <fmt> dot | mermaid | json
1038
1073
  --documents-only Hide items; their relations lift onto their documents
1039
1074
  --rule <id>=<severity> error | warn | info | off (repeatable)
@@ -1041,7 +1076,7 @@ spec-graph diff <before> <after> Compare two JSON graph exports.
1041
1076
  --max <n> Show at most n findings
1042
1077
  --max-warnings <n> Fail when warnings exceed n
1043
1078
  --color / --no-color Force colour
1044
- --ascii ASCII glyphs only
1079
+ --ascii ASCII glyphs only, as SPEC_GRAPH_ASCII=1 asks too
1045
1080
  --verbose Include parse problems
1046
1081
  ```
1047
1082
 
@@ -1049,7 +1084,60 @@ Exit codes: `0` clean, `1` findings, `2` the tool could not run. A mistyped flag
1049
1084
  never masquerades as a passing build, and neither does a pattern that matches
1050
1085
  nothing, a configuration file that did not load, or a `--baseline` that is not
1051
1086
  there. An error spec-graph did not expect is `2` as well, never findings: it is
1052
- reported on stderr as `spec-graph: unexpected error:` with its stack.
1087
+ reported on stderr as `spec-graph: unexpected error:` with its stack. So is a
1088
+ stdout its reader closed before all of the output was written, as a pipeline
1089
+ into `head` does: the answer did not arrive, and one line on stderr says so.
1090
+
1091
+ **An input that is set and names nothing is refused**: exit `2` and one line
1092
+ that names it, never a run that reads it as if it had not been given. An unset
1093
+ variable in `--family "$FAMILIES"` is the usual way to write one:
1094
+
1095
+ - a `--family` or `--ignore-family` holding a name no family has - empty,
1096
+ blank, or a list such as `ADR,RFC` where one name goes. Alone on the
1097
+ allowlist it would leave every citation as prose, and a dangling one
1098
+ unreported;
1099
+ - a `--root` that is empty, is not there, or is a file, for every command
1100
+ that reads one, `rules` included;
1101
+ - an empty or blank `--baseline`, `--record-baseline`, `--ignore`,
1102
+ `--ignore-ref`, `--history` or pattern;
1103
+ - a list of families, `--family` or `families`, none of which a document
1104
+ here belongs to ([Family rules](#family-rules));
1105
+ - a second word after `rules`, which takes one rule id, and an empty name
1106
+ given to `diff`;
1107
+ - `--format json` asked of `graph`, whose JSON is `--graph-format json`, or
1108
+ of `rules`, which has one format;
1109
+ - an option given to a command that does not read it, in the table below;
1110
+ - a value that begins with `--`, which reads as the option after one whose
1111
+ value was forgotten: `--ignore --strict` would ignore a directory called
1112
+ `--strict`, and `--record-baseline --verbose` write a file of that name.
1113
+ A name that does begin so is written `./--name` for a file, `[-]-name` in
1114
+ a pattern and `\--name` for a reference target;
1115
+ - a configuration file that is there and cannot be read, a directory called
1116
+ `.spec-graph.json` among them.
1117
+
1118
+ An option belongs to the commands that read it. Any other command refuses it
1119
+ by name, `--strict belongs to check, not to query`, rather than run as if the
1120
+ option were absent: `query --strict` would be no stricter, `graph
1121
+ --record-baseline` would write no file, and with the command forgotten,
1122
+ `spec-graph --graph-format mermaid > graph.mmd` would write the check's report
1123
+ into the file.
1124
+
1125
+ | Option | Read by |
1126
+ | --- | --- |
1127
+ | `--root`, `--verbose` | `check`, `query`, `graph`, `rules` |
1128
+ | `--ignore`, `--history` | `check`, `query`, `graph` |
1129
+ | `--format` | `check`, `query`, `diff` |
1130
+ | `--graph-format`, `--documents-only` | `graph` |
1131
+ | `--explain` | `rules` |
1132
+ | `--ignore-ref`, `--family`, `--ignore-family`, `--baseline`, `--record-baseline`, `--ratchet`, `--rule`, `--strict`, `--max`, `--max-warnings`, `--color` | `check` |
1133
+ | `--no-config`, `--no-color`, `--ascii`, `--help`, `--version` | every command |
1134
+
1135
+ `--no-config`, `--no-color` and `--ascii` are taken by every command because
1136
+ each is true already of a command with nothing to read it for: only a check
1137
+ colours its report or draws a glyph, and `diff` reads no configuration. A
1138
+ script can pass them to whatever it runs. The configuration file is not an
1139
+ option: `ignoreReferences` in `.spec-graph.json` stops no `graph`, which
1140
+ draws the same graph with it as without.
1053
1141
 
1054
1142
  **There is no `--watch`.** A full run on this repository takes 60 ms, so the
1055
1143
  loop that would justify a resident process is one line of shell, and it belongs
@@ -1059,6 +1147,18 @@ to whatever the developer already uses rather than to this tool:
1059
1147
  npx --no-install @descent-vtt/spec-graph --format json | jq -r '.summary'
1060
1148
  ```
1061
1149
 
1150
+ ### Colour and glyphs
1151
+
1152
+ `--color` and `--no-color` decide, then the environment, in this order:
1153
+ `NO_COLOR` set and not empty turns colour off; `FORCE_COLOR` set and not empty
1154
+ turns it on, and `0` turns it off; a `TERM` of `dumb` turns it off; otherwise
1155
+ the report is coloured when stdout is a terminal. `CI` is not read.
1156
+
1157
+ `--ascii` draws the report in ASCII glyphs, and so does `SPEC_GRAPH_ASCII`
1158
+ set to anything but `0`, `false` or nothing. Without either, a Windows console
1159
+ that sets neither `WT_SESSION` nor `TERM_PROGRAM` gets ASCII and every other
1160
+ terminal the box-drawing characters.
1161
+
1062
1162
  ## In CI
1063
1163
 
1064
1164
  The exit code is the contract: `0` clean, `1` findings, `2` the tool could not
@@ -1318,9 +1418,9 @@ tools share, whose code is copied into `src/vendor/` and checked by hash rather
1318
1418
  than installed. So the whole package is auditable in an afternoon — and nothing
1319
1419
  in the pipeline can take longer than its input.
1320
1420
 
1321
- **Verified, not just covered.** 2,074 tests; 99.4% statement and 99.8% line
1421
+ **Verified, not just covered.** 2,141 tests; 99.4% statement and 99.8% line
1322
1422
  coverage. Coverage says a line ran, so the suite is also held to a mutation
1323
- score - 96.38% over 8,544 mutants, measured by CI's full sweep, and a build
1423
+ score - 96.50% over 8,832 mutants, measured by CI's full sweep, and a build
1324
1424
  fails under 93 - because a vocabulary entry or a boundary
1325
1425
  condition can be weakened by an ordinary-looking refactor without a single test
1326
1426
  going red. [ADR-0007](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0007-mutation-testing.md) is straight about what
package/bin/spec-graph.js CHANGED
@@ -8,8 +8,15 @@ import { pathToFileURL } from 'node:url';
8
8
  // main answers the errors it awaits with exit 2. What nothing awaits - a
9
9
  // stream's error, a timer's, a promise nobody holds - Node ends with exit 1,
10
10
  // which a script reads as findings: the same answer for those.
11
+ //
12
+ // A reader that closed stdout, as `| head` does, is no defect, and it arrives
13
+ // here, as the stream's error: one line, with no stack to send a person
14
+ // looking for a bug. The code alone names it, since stdout and stderr are the
15
+ // only pipes spec-graph writes to, and a line about a closed stderr reaches
16
+ // nobody.
11
17
  process.on('uncaughtException', (error) => {
12
- process.stderr.write(`spec-graph: unexpected error: ${error?.stack ?? error}\n`);
18
+ const said = error?.code === 'EPIPE' ? 'stdout was closed before all of the output was written' : `unexpected error: ${error?.stack ?? error}`;
19
+ process.stderr.write(`spec-graph: ${said}\n`);
13
20
  process.exit(2);
14
21
  });
15
22
 
package/dist/cli.d.ts CHANGED
@@ -78,7 +78,7 @@ export interface CliIO {
78
78
  readonly env?: Readonly<Record<string, string | undefined>> | undefined;
79
79
  readonly isTTY?: boolean | undefined;
80
80
  }
81
- export declare const HELP = "spec-graph - turn Markdown specifications into a verifiable graph\n\nUSAGE\n spec-graph [check] [patterns...] [options]\n spec-graph query <selector|project:rule> [patterns...] [options]\n spec-graph graph [patterns...] [--graph-format dot|mermaid|json]\n spec-graph rules [rule-id] [--explain]\n spec-graph diff <before.json> <after.json> [--format human|json|markdown]\n\nCOMMANDS\n check Validate the specification graph. The default.\n query Run a selector - or a registered project rule, by its id - and\n print the matching paths.\n graph Export the graph for Graphviz, Mermaid, or another tool.\n rules List the diagnostics that will run, built in and project. Name one\n to see only that one; --explain adds its selector and the ADR that\n decided it.\n diff Compare two graph exports: documents added, removed, moved or\n accepted; relations added or removed; and obligations resolved or\n reopened where they can be told apart. Make both exports with the\n same spec-graph. Exits 0 whether anything changed or not.\n\nOPTIONS\n --root <dir> Directory the patterns resolve against. Without it,\n .spec-graph.json is looked for in the working\n directory and then upward as far as the repository,\n and the directory holding it becomes the root - so a\n run from a subdirectory reports what a run from the\n top reports. Paths typed on the command line stay\n relative to where they were typed.\n --ignore <glob> Skip paths. Repeatable.\n --ignore-ref <glob> Do not report these reference targets when they fail\n to resolve, for repositories where [[...]] tags a\n concept rather than naming a file. Repeatable.\n Suppresses findings only, never edges.\n --family <name> Families a bare identifier in prose may name. When\n given, everything else stays prose. Repeatable.\n --ignore-family <name> Families that are never citations - RFC when the repo\n cites RFC 2119 and keeps its own RFCs. Repeatable.\n --history <glob> Files that log what was decided rather than deciding\n it - journals, changelogs, minutes. Their links are\n still checked; their obligations are not. Repeatable.\n --baseline <file> Accept the findings recorded in this file and report\n only what is new since. The file has to be there: a\n path that reads nothing accepts nothing and says so\n nowhere. A \"baseline\" in the configuration may name a\n file not recorded yet; a path typed here may not.\n --record-baseline <f> Write today's findings to this file as accepted debt,\n and exit 0 without judging them.\n --ratchet Also fail when a baseline entry no longer occurs, so\n a paid-off exemption cannot outlive the defect.\n --no-config Ignore .spec-graph.json and the package.json key.\n --format <fmt> human, json, sarif, github, gitlab or markdown. sarif\n is the interchange format GitHub code scanning and\n editors already read, github the workflow commands a\n GitHub Actions log turns into annotations, and gitlab\n the Code Quality report a GitLab merge request reads,\n all three for check; markdown is a table for a\n pull-request comment or $GITHUB_STEP_SUMMARY, for\n check or diff (default: human)\n --graph-format <fmt> dot, mermaid or json (default: dot)\n --documents-only Leave items out of the exported graph\n --rule <id>=<severity> Override one rule: error, warn, info or off. A project\n rule is named in full: project:no-draft-dependency.\n Repeatable.\n --max <n> Show at most n findings (0 = no limit)\n --max-warnings <n> Fail when warnings exceed n (default: no limit)\n --strict Raise every warning to an error. An explicit --rule\n still wins, so --strict --rule x=warn exempts x.\n --color / --no-color Force colour on or off\n --ascii Use ASCII glyphs only\n --verbose Include parse problems, per-file detail, and every\n reference --ignore-ref or --ignore-family silenced\n -h, --help Show this help\n -v, --version Show the version\n\nSELECTORS\n A selector is a path through the graph.\n\n item[openness=open] -delegates-to-> document[phase=retired]\n document[phase=active] -assumes-> document[phase=retired]\n document =supersedes=> document\n *[path^=docs/adr] -contains-> item[state=accepted-debt]\n\n Node types: document, item, * (any)\n Attributes: id, kind, title, path, file, line, phase, status, receptivity,\n alias, document, state (or disposition), openness, section,\n text, body, evidence, conflicted, fm.<front-matter-key>\n Operators: = != ^= $= *= ~= and [attr] for \"is present\"\n ~= is a regular expression, matched by an automaton that cannot\n backtrack: linear in the subject, whatever the pattern. It reads\n the usual syntax minus backreferences and lookaround, which are\n not regular - both are refused when the selector is read, with\n the character pointed at\n Relations: -kind-> <-kind- =kind=> <=kind= (= forms are transitive)\n\nEXIT CODES\n 0 clean\n 1 findings at error severity (or over --max-warnings)\n 2 the tool could not run\n\nCONFIGURATION\n Anything repeated on every run belongs in the repository rather than in the\n command. spec-graph reads the first of these that exists:\n\n .spec-graph.json, spec-graph.config.json, or a \"spec-graph\" key in\n package.json\n\n { \"patterns\": [\"docs/**/*.md\"],\n \"ignoreReferences\": [\"trap *\"],\n \"ignoreFamilies\": [\"RFC\"],\n \"historyPatterns\": [\"**/JOURNAL_*.md\", \"archive/**\"],\n \"baseline\": \".spec-graph-baseline.json\",\n \"severities\": { \"self-reference\": \"off\" },\n \"strict\": true,\n \"maxRelated\": 3 }\n\n A flag always wins over the file, and list flags add to it rather than\n replacing it. maxRelated has no flag: it caps how many related locations one\n finding lists, 8 unless set, and 0 lists none.\n\n A problem in that file stops the run with exit 2, named on stderr: invalid\n JSON, an unknown key, a rule that does not compile. A configuration that did\n not load checks a different repository than the one configured, and would\n report that one as consistent. --no-config runs on defaults instead.\n\nPROJECT RULES\n A convention spec-graph never anticipated is a selector plus a sentence, and\n belongs in the same file. Every rule declared here runs beside the built-ins\n and is reported, baselined, escalated by --strict and silenced by --rule in\n exactly the same way.\n\n { \"rules\": {\n \"no-draft-dependency\": {\n \"query\": \"document[phase=active] -depends-on-> document[phase=draft]\",\n \"message\": \"{0} depends on {1}, which is still a draft\",\n \"hint\": \"wait for {1} to be accepted, or drop it from {0.path}\",\n \"severity\": \"error\" } } }\n\n query One selector, or a list of them read as a union.\n message The headline. {0} is the first node on the path, {1} the next;\n {1.phase} and {0.fm.owner} read any selector attribute.\n hint The next action. Optional, and templated the same way.\n severity error, warn, info or off. Defaults to warn - a rule a team has\n just written has not yet earned the right to fail their build.\n\n The id is the name with project: in front, which is why it can never collide\n with a built-in. A selector that does not parse, or a {2} the query can never\n reach, is reported when the file is read rather than found missing later.\n\n To see what one matches without copying its selector out of the file:\n\n spec-graph query project:no-draft-dependency --verbose\n\nADOPTING THIS ON AN OLD REPOSITORY\n Record what is already wrong, then report only what happens next:\n\n spec-graph check --record-baseline .spec-graph-baseline.json\n git add .spec-graph-baseline.json\n\n The file is keyed by specification and citation, not by line number, so it\n survives edits, moves and renames. Findings that stop occurring are reported\n so the file can be tightened; nothing new gets in.\n\nEXAMPLES\n spec-graph \"docs/**/*.md\"\n spec-graph check --rule self-reference=off --format json\n spec-graph query 'item[openness=open] -delegates-to-> document[phase=retired]'\n spec-graph check --ignore-ref \"trap *\" # [[trap 55]] tags a concept, not a file\n spec-graph graph --documents-only --graph-format mermaid > graph.mmd\n spec-graph check --history \"**/JOURNAL_*.md\" # a log is not a specification\n spec-graph check --baseline .spec-graph-baseline.json\n spec-graph rules --explain # including this repository's own\n spec-graph rules ghost-handover --explain # and why it exists\n spec-graph check --rule project:no-draft-dependency=off\n spec-graph query project:no-draft-dependency # what does that rule match?\n spec-graph check --format markdown >> \"$GITHUB_STEP_SUMMARY\"\n spec-graph check --format github # annotations from an Actions log\n spec-graph check --format gitlab > gl-code-quality-report.json\n spec-graph diff base.json head.json --format markdown >> \"$GITHUB_STEP_SUMMARY\"\n";
81
+ export declare const HELP = "spec-graph - turn Markdown specifications into a verifiable graph\n\nUSAGE\n spec-graph [check] [patterns...] [options]\n spec-graph query <selector|project:rule> [patterns...] [options]\n spec-graph graph [patterns...] [--graph-format dot|mermaid|json]\n spec-graph rules [rule-id] [--explain]\n spec-graph diff <before.json> <after.json> [--format human|json|markdown]\n\nCOMMANDS\n check Validate the specification graph. The default.\n query Run a selector - or a registered project rule, by its id - and\n print the matching paths.\n graph Export the graph for Graphviz, Mermaid, or another tool.\n rules List the diagnostics that will run, built in and project. Name one\n to see only that one; --explain adds its selector and the ADR that\n decided it.\n diff Compare two graph exports: documents added, removed, moved or\n accepted; relations added or removed; and obligations resolved or\n reopened where they can be told apart. Make both exports with the\n same spec-graph. Exits 0 whether anything changed or not.\n\nOPTIONS\n --root <dir> Directory the patterns resolve against. Without it,\n .spec-graph.json is looked for in the working\n directory and then upward as far as the repository,\n and the directory holding it becomes the root - so a\n run from a subdirectory reports what a run from the\n top reports. Paths typed on the command line stay\n relative to where they were typed. It has to be a\n directory that is there.\n --ignore <glob> Skip paths. Repeatable.\n --ignore-ref <glob> Do not report these reference targets when they fail\n to resolve, for repositories where [[...]] tags a\n concept rather than naming a file. Repeatable.\n Suppresses findings only, never edges.\n --family <name> Families a bare identifier in prose may name. When\n given, everything else stays prose. Repeatable: one\n name each time, the prefix its identifiers are\n written with, ADR for ADR-0007. A document here has\n to belong to one of them, or the check stops: a list\n that names none allows nothing.\n --ignore-family <name> Families that are never citations - RFC when the repo\n cites RFC 2119 and keeps its own RFCs. Repeatable,\n one name each time.\n --history <glob> Files that log what was decided rather than deciding\n it - journals, changelogs, minutes. Their links are\n still checked; their obligations are not. Repeatable.\n --baseline <file> Accept the findings recorded in this file and report\n only what is new since. The file has to be there: a\n path that reads nothing accepts nothing and says so\n nowhere. A \"baseline\" in the configuration may name a\n file not recorded yet; a path typed here may not.\n --record-baseline <f> Write today's findings to this file as accepted debt,\n and exit 0 without judging them.\n --ratchet Also fail when a baseline entry no longer occurs, so\n a paid-off exemption cannot outlive the defect.\n --no-config Ignore .spec-graph.json and the package.json key.\n --format <fmt> human, json, sarif, github, gitlab or markdown. json\n is for check, query or diff. sarif is the interchange\n format GitHub code scanning and editors already read,\n github the workflow commands a GitHub Actions log\n turns into annotations, and gitlab the Code Quality\n report a GitLab merge request reads, all three for\n check; markdown is a table for a pull-request comment\n or $GITHUB_STEP_SUMMARY, for check or diff (default:\n human)\n --graph-format <fmt> dot, mermaid or json (default: dot)\n --documents-only Leave items out of the exported graph\n --rule <id>=<severity> Override one rule: error, warn, info or off. A project\n rule is named in full: project:no-draft-dependency.\n Repeatable.\n --max <n> Show at most n findings (0 = no limit)\n --max-warnings <n> Fail when warnings exceed n (default: no limit)\n --strict Raise every warning to an error. An explicit --rule\n still wins, so --strict --rule x=warn exempts x.\n --color / --no-color Force colour on or off\n --ascii Use ASCII glyphs only. SPEC_GRAPH_ASCII asks for the\n same, unless it is empty, 0 or false\n --verbose Include parse problems, per-file detail, and every\n reference --ignore-ref or --ignore-family silenced\n --explain With rules: add each rule's selector and the ADR\n that decided it\n -h, --help Show this help\n -v, --version Show the version\n\n An option belongs to the commands that read it, and any other command\n refuses it by name:\n\n --root, --verbose check, query, graph, rules\n --ignore, --history check, query, graph\n --format check, query, diff\n --graph-format, --documents-only graph\n --explain rules\n every other option check\n\n --no-config, --no-color and --ascii are taken by every command: each is\n true already of a command with nothing to read it for.\n\n A value that begins with -- reads as an option, so the option before it is\n refused as missing its value. A name that does begin so is written ./--name\n for a file, [-]-name in a pattern and \\--name for a reference target.\n\nSELECTORS\n A selector is a path through the graph.\n\n item[openness=open] -delegates-to-> document[phase=retired]\n document[phase=active] -assumes-> document[phase=retired]\n document =supersedes=> document\n *[path^=docs/adr] -contains-> item[state=accepted-debt]\n\n Node types: document, item, * (any)\n Attributes: id, kind, title, path, file, line, phase, status, receptivity,\n alias, document, state (or disposition), openness, section,\n text, body, evidence, conflicted, fm.<front-matter-key>\n Operators: = != ^= $= *= ~= and [attr] for \"is present\"\n ~= is a regular expression, matched by an automaton that cannot\n backtrack: linear in the subject, whatever the pattern. It reads\n the usual syntax minus backreferences and lookaround, which are\n not regular - both are refused when the selector is read, with\n the character pointed at\n Relations: -kind-> <-kind- =kind=> <=kind= (= forms are transitive)\n\nEXIT CODES\n 0 clean\n 1 findings at error severity (or over --max-warnings)\n 2 the tool could not run\n\n An input that is set and names nothing is a 2, named on stderr, and never\n read as if it had not been given: an empty value, a family name no family\n has, a list of families no document here belongs to, a --root that is no\n directory, a second word after rules, an option of another command.\n\nCONFIGURATION\n Anything repeated on every run belongs in the repository rather than in the\n command. spec-graph reads the first of these that exists:\n\n .spec-graph.json, spec-graph.config.json, or a \"spec-graph\" key in\n package.json\n\n { \"patterns\": [\"docs/**/*.md\"],\n \"ignoreReferences\": [\"trap *\"],\n \"ignoreFamilies\": [\"RFC\"],\n \"historyPatterns\": [\"**/JOURNAL_*.md\", \"archive/**\"],\n \"baseline\": \".spec-graph-baseline.json\",\n \"severities\": { \"self-reference\": \"off\" },\n \"strict\": true,\n \"maxRelated\": 3 }\n\n A flag always wins over the file, and list flags add to it rather than\n replacing it. maxRelated has no flag: it caps how many related locations one\n finding lists, 8 unless set, and 0 lists none.\n\n A problem in that file stops the run with exit 2, named on stderr: invalid\n JSON, an unknown key, a rule that does not compile, a file that is there and\n cannot be read. A configuration that did not load checks a different\n repository than the one configured, and would report that one as consistent.\n --no-config runs on defaults instead.\n\nPROJECT RULES\n A convention spec-graph never anticipated is a selector plus a sentence, and\n belongs in the same file. Every rule declared here runs beside the built-ins\n and is reported, baselined, escalated by --strict and silenced by --rule in\n exactly the same way.\n\n { \"rules\": {\n \"no-draft-dependency\": {\n \"query\": \"document[phase=active] -depends-on-> document[phase=draft]\",\n \"message\": \"{0} depends on {1}, which is still a draft\",\n \"hint\": \"wait for {1} to be accepted, or drop it from {0.path}\",\n \"severity\": \"error\" } } }\n\n query One selector, or a list of them read as a union.\n message The headline. {0} is the first node on the path, {1} the next;\n {1.phase} and {0.fm.owner} read any selector attribute.\n hint The next action. Optional, and templated the same way.\n severity error, warn, info or off. Defaults to warn - a rule a team has\n just written has not yet earned the right to fail their build.\n\n The id is the name with project: in front, which is why it can never collide\n with a built-in. A selector that does not parse, or a {2} the query can never\n reach, is reported when the file is read rather than found missing later.\n\n To see what one matches without copying its selector out of the file:\n\n spec-graph query project:no-draft-dependency --verbose\n\nADOPTING THIS ON AN OLD REPOSITORY\n Record what is already wrong, then report only what happens next:\n\n spec-graph check --record-baseline .spec-graph-baseline.json\n git add .spec-graph-baseline.json\n\n The file is keyed by specification and citation, not by line number, so it\n survives edits, moves and renames. Findings that stop occurring are reported\n so the file can be tightened; nothing new gets in.\n\nEXAMPLES\n spec-graph \"docs/**/*.md\"\n spec-graph check --rule self-reference=off --format json\n spec-graph query 'item[openness=open] -delegates-to-> document[phase=retired]'\n spec-graph check --ignore-ref \"trap *\" # [[trap 55]] tags a concept, not a file\n spec-graph graph --documents-only --graph-format mermaid > graph.mmd\n spec-graph check --history \"**/JOURNAL_*.md\" # a log is not a specification\n spec-graph check --baseline .spec-graph-baseline.json\n spec-graph rules --explain # including this repository's own\n spec-graph rules ghost-handover --explain # and why it exists\n spec-graph check --rule project:no-draft-dependency=off\n spec-graph query project:no-draft-dependency # what does that rule match?\n spec-graph check --format markdown >> \"$GITHUB_STEP_SUMMARY\"\n spec-graph check --format github # annotations from an Actions log\n spec-graph check --format gitlab > gl-code-quality-report.json\n spec-graph diff base.json head.json --format markdown >> \"$GITHUB_STEP_SUMMARY\"\n";
82
82
  /** Parses argv into options. Throws {@link UsageError} on anything malformed. */
83
83
  export declare function parseArgs(argv: readonly string[], cwd: string): CliOptions;
84
84
  /**
@@ -90,5 +90,12 @@ export declare function parseArgs(argv: readonly string[], cwd: string): CliOpti
90
90
  * error, exit 1, which CI reads as findings. The stack is what makes a report
91
91
  * of it something to act on, and it goes to stderr alone: a script that reads
92
92
  * a document from stdout is handed no part of one.
93
+ *
94
+ * A write its reader had closed the pipe for is not such an error: the answer
95
+ * was not delivered, which is still 2, and nothing in spec-graph is at fault,
96
+ * so it is said in a line and no stack sends a person looking for a defect.
97
+ * The process's own streams report it as an event, which the launcher answers
98
+ * in the same words; here it is a caller's stream that throws it. It is read
99
+ * by its code: stdout and stderr are the only pipes spec-graph writes to.
93
100
  */
94
101
  export declare function main(io?: CliIO): Promise<number>;