@descent-vtt/spec-guard 0.4.0 → 0.5.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
@@ -5,6 +5,71 @@ All notable changes to this project are documented here. Versions follow
5
5
  may change in a minor release — each such change is listed under **Changed**
6
6
  with the flag that restores the previous behaviour.
7
7
 
8
+ ## 0.5.0
9
+
10
+ Import assertions covered one language, so a dependency rule pointed at a
11
+ directory of Go files did not fail - it passed, having analysed nothing. That is
12
+ the same defect 0.4.0 was written to remove, and this release closes the two
13
+ remaining shapes of it: a rule that cannot read the language, and a rule whose
14
+ scope holds no files at all.
15
+
16
+ ### Added
17
+
18
+ - **Import assertions read Python, Go, Rust and C#**, alongside JavaScript and
19
+ TypeScript. `import a.b` / `from .rel import x`, `import ( ... )` groups,
20
+ `use a::{b, c}` with nested expansion, `using static` and `global using`, and
21
+ the dynamic forms (`importlib.import_module`) that can only be reported.
22
+ Not four new tokenizers: the comment and string lexer from ADR-0006 masks the
23
+ source and the readers work on what is left. Tree-sitter measured 94 MB
24
+ unpacked against this package's 0.33 MB. See
25
+ [ADR-0008](docs/adr/0008-polyglot-imports.md).
26
+ - **`baseline` and `ratchet`** on the absence assertions, for adopting a strict
27
+ rule on a codebase that already breaks it. `baseline="src/legacy/a.ts:2"`
28
+ names the debt by file and count. The ratchet is two-sided: new violations
29
+ fail, and so does an entry the code no longer supports, because a baseline
30
+ that only grows is an `exclude` with extra steps. `ratchet="one-way"` relaxes
31
+ the second half. See [ADR-0009](docs/adr/0009-debt-baselines.md).
32
+ - **`--print-baseline`** prints the attribute that would exempt today's
33
+ violations, for a human to paste. It prints; it does not edit. That is the
34
+ whole answer to `--fix`, and ADR-0009 argues it.
35
+ - **`--format sarif`** writes SARIF 2.1.0, which GitHub turns into an annotation
36
+ on the offending line. One alert per broken rule, anchored on the code, with
37
+ the directive as a related location and a fingerprint that survives the code
38
+ moving. `--json` is unchanged and is now also `--format json`.
39
+ - **`allow-empty` and `--allow-empty-scope`**, for the rules where covering
40
+ nothing is the honest state of the world.
41
+ - **`baselinedMatches`, `staleBaseline` and `fileMatches`** on each result in
42
+ `--json`.
43
+
44
+ ### Changed
45
+
46
+ - **An assertion that inspected no files now fails.** A rule whose scope holds
47
+ nothing passes every time and reads in the report exactly like a rule that
48
+ inspected a thousand files and found nothing. The usual causes are a `glob`
49
+ matching no extension in the tree, an `exclude` that swallowed the target, or
50
+ an emptied directory. **If a run starts failing this way, the rule was
51
+ covering nothing before it started failing;** `allow-empty="true"` on the
52
+ directive, or `--allow-empty-scope` for the run, restores the old behaviour.
53
+ - **An import assertion fails when nothing in scope is in a language it can
54
+ read**, rather than reporting "analysed 0 of 12 files" in a warning and
55
+ passing.
56
+ - **`.py`, `.pyi`, `.go`, `.rs`, `.cs` and `.csx` files are now analysed** by import
57
+ assertions rather than counted as skipped, so a rule over a polyglot tree
58
+ starts finding dependencies it previously reported as unanalysable.
59
+ - **Mutation testing in CI runs in two tiers**: incremental on branches,
60
+ a full authoritative sweep on `main` and nightly. The gate stays at 85 in
61
+ both. See [ADR-0003](docs/adr/0003-mutation-testing.md).
62
+
63
+ ### Fixed
64
+
65
+ - `excludeFiles` was applied after enumeration rather than during it, so an
66
+ excluded spec file counted towards a budgeted walk. Only reachable through the
67
+ adaptive engine's probe, where it could make a tree look larger than the file
68
+ set actually being searched.
69
+ - The README described the safe-batching apparatus - containment and
70
+ dovetailing checks before merging literals into a ripgrep alternation - as
71
+ current behaviour. It was deleted in 0.4.0 when ripgrep became a pre-filter.
72
+
8
73
  ## 0.4.0
9
74
 
10
75
  An audit found spec-guard reporting a clean pass on a repository that contained
package/README.md CHANGED
@@ -281,10 +281,33 @@ matches a mention in a comment, and it misses `export * from '../db'`.
281
281
  <!-- @assert-import-count target="src" module="axios" max="1" -->
282
282
  ```
283
283
 
284
- These read the dependency rather than the text. A JavaScript/TypeScript
285
- tokenizer - comments, strings, template substitutions and regular expressions,
286
- no parser and no new dependency - extracts module references and counts the
287
- **files** that depend on the module. It understands:
284
+ These read the dependency rather than the text, in **five languages**:
285
+
286
+ | Language | Extensions | Reads |
287
+ | --- | --- | --- |
288
+ | JavaScript / TypeScript | `.js .mjs .cjs .jsx .ts .mts .cts .tsx` | `import`, `export ... from`, `require()`, `import()` |
289
+ | Python | `.py .pyi` | `import a.b`, `from .rel import x`, `importlib.import_module("x")` |
290
+ | Go | `.go` | `import "x"` and grouped `import ( ... )`, including aliases and `_` |
291
+ | Rust | `.rs` | `use a::{b, c}` with nested groups, `pub use`, `extern crate` |
292
+ | C# | `.cs .csx` | `using`, `using static`, `global using`, `using X = A.B` |
293
+
294
+ No parser and no new dependency. JavaScript gets a full tokenizer because a
295
+ module reference can appear anywhere in an expression; the other four are read
296
+ by masking comments and string literals with the classifier from
297
+ [ADR-0006](docs/adr/0006-comment-classification.md) and then reading statements
298
+ off what is left. Tree-sitter would have cost 94 MB unpacked against this
299
+ package's 0.33 MB - [ADR-0008](docs/adr/0008-polyglot-imports.md) has the
300
+ measurements, and the list of things a real parser would genuinely see that
301
+ this does not.
302
+
303
+ Module patterns are matched against the resolved `/`-separated path *and*
304
+ against the specifier as written, so `module="System.Text.Json"` and
305
+ `module="System/Text/Json"` both work. Matching is case-sensitive, so a rule
306
+ spanning C# and Go needs both conventions:
307
+ `module="app/db/** App/Db/**"`.
308
+
309
+ The unit counted is **files** that depend on the module. For JavaScript and
310
+ TypeScript it understands:
288
311
 
289
312
  | Form | Counted |
290
313
  | --- | --- |
@@ -296,7 +319,8 @@ no parser and no new dependency - extracts module references and counts the
296
319
  | `import(name)`, `require(expr)` | **no - reported, see below** |
297
320
 
298
321
  Relative specifiers are resolved by path arithmetic, so `module="src/db"`
299
- matches `../db/client.js` seen from `src/ui/`. There is deliberately no
322
+ matches `../db/client.js` seen from `src/ui/`, and Python's `from ..core import
323
+ x` resolves against the importing file the same way. There is deliberately no
300
324
  filesystem resolution: tsconfig `paths` aliases and package `exports` maps are
301
325
  not followed, so an alias is written out as itself (`module="@app/db"`).
302
326
 
@@ -313,8 +337,9 @@ are reported rather than counted as clean:
313
337
  The count is still true of everything that could be seen; the warning is what
314
338
  stops it being mistaken for a complete answer. `--strict` turns those warnings
315
339
  into failures. Files in scope that are not JavaScript or TypeScript are counted
316
- and reported too, so a rule pointed at the wrong tree says "analysed 1 of 3
317
- files" rather than quietly passing.
340
+ and reported too, so a rule pointed at the wrong tree says "analysed 2 of 3
341
+ files" rather than quietly passing - and if *none* of them can be read, the
342
+ assertion fails rather than passing on an empty analysis.
318
343
 
319
344
  [ADR-0005](docs/adr/0005-import-assertions.md) has the measurements and the
320
345
  reasoning behind each boundary.
@@ -345,11 +370,77 @@ Passes when every listed path exists relative to `--root`. Directories count.
345
370
  | `comments` | absence, count | `ignore` (default) or `include` for matches inside comments |
346
371
  | `module` | import assertions | Which dependency, matched like `exclude` |
347
372
  | `types` | import assertions | `include` (default) or `ignore` for `import type` |
373
+ | `allow-empty` | absence, count, import assertions | Tolerate a scope that holds no files. Off by default - see below |
374
+ | `baseline` | absence, import-absence | Known violations that do not count: `path` or `path:count` |
375
+ | `ratchet` | absence, import-absence | `two-sided` (default) or `one-way` - see below |
348
376
  | `reason` | all | Human-readable justification, printed on failure |
349
377
 
350
378
  Unknown attributes are an error, not a shrug: `expct="1"` fails the run instead
351
379
  of silently asserting nothing.
352
380
 
381
+ ### `allow-empty` - an assertion that covers nothing is a failure
382
+
383
+ ```md
384
+ <!-- @assert-absence target="services" symbol="Legacy" glob="*.ts" -->
385
+ ```
386
+
387
+ If `services/` holds no `.ts` file, that rule passes every time, forever,
388
+ without inspecting anything - and in the report it is indistinguishable from a
389
+ rule that inspected a thousand files and found nothing. So it fails instead:
390
+
391
+ ```text
392
+ ✖ docs/adr.md:3 @assert-absence
393
+ "Legacy" must not appear in services
394
+ no files were inspected, so this assertion verified nothing
395
+ (add allow-empty="true" if that is expected)
396
+ ```
397
+
398
+ The usual causes are a `glob` matching no extension in the tree, an `exclude`
399
+ that swallowed the target, or a directory somebody emptied. Where covering
400
+ nothing yet is the honest state of the world - a rule written before the code
401
+ it guards - `allow-empty="true"` says so, and `--allow-empty-scope` says it for
402
+ a whole run.
403
+
404
+ ### `baseline` - adopting a rule the codebase already breaks
405
+
406
+ A strict rule introduced into a mature codebase lands on violations that already
407
+ exist. `exclude` turns those into a permanent blind spot, and `expected="5"`
408
+ cannot tell "one fixed, one added" from "nothing happened". A baseline names
409
+ them:
410
+
411
+ ```md
412
+ <!-- @assert-absence target="src" symbol="LegacyGateway"
413
+ baseline="src/legacy/gateway.ts:2
414
+ src/legacy/adapter.ts" -->
415
+ ```
416
+
417
+ The rule now holds when no file outside the list matches and no file inside it
418
+ matches more than it declares. A bare path means one match.
419
+
420
+ **It ratchets in both directions.** New debt fails, obviously. So does debt that
421
+ has been *paid* and left on the list, because an entry the code no longer
422
+ supports is a spec asserting something untrue:
423
+
424
+ ```text
425
+ expected no matches, found 0; the baseline is out of date and must be
426
+ pruned: src/legacy/adapter.ts (no longer matches)
427
+ ```
428
+
429
+ The fix is deleting the line the message names. `ratchet="one-way"` relaxes that
430
+ half to a report-only note. Every run says how many matches the baseline
431
+ excluded, because a pass bought by an exemption is never silent.
432
+
433
+ To adopt a rule on a codebase that already violates it:
434
+
435
+ ```bash
436
+ spec-guard docs/adr.md --print-baseline
437
+ ```
438
+
439
+ prints the attribute that would exempt exactly today's violations, for you to
440
+ paste in. It prints; it does not edit. [ADR-0009](docs/adr/0009-debt-baselines.md)
441
+ explains why that distinction is the whole design, and why there is no `--fix`
442
+ for architecture rules.
443
+
353
444
  ## CLI
354
445
 
355
446
  ```bash
@@ -361,15 +452,18 @@ spec-guard [patterns...] [options]
361
452
  | `-r, --root <path>` | Codebase root that assertions resolve against (default: cwd) |
362
453
  | `-v, --verbose` | Print passing assertions too |
363
454
  | `--fail-fast` | Stop at the first failing assertion |
364
- | `--json` | Machine-readable report on stdout |
455
+ | `--json` | Machine-readable report on stdout (same as `--format json`) |
456
+ | `--format <human\|json\|sarif>` | Output format. `sarif` uploads to GitHub code scanning |
365
457
  | `--engine <auto\|rg\|js>` | Search engine (default `auto`: scanner for small trees, ripgrep for big ones) |
366
458
  | `--strict` | Treat analysis that could not be completed as a failure |
367
459
  | `--allow-missing-targets` | Warn instead of failing when a `target` path does not exist |
460
+ | `--allow-empty-scope` | Warn instead of failing when an assertion inspects no files |
461
+ | `--print-baseline` | Print the `baseline="..."` that would exempt today's violations, and exit |
368
462
  | `--no-default-skips` | Search `.git`, `.hg`, `.svn` and `node_modules` too |
369
463
  | `--include-specs` | Also count matches inside the spec files themselves |
370
464
  | `--max-snippets <n>` | Failure snippets per assertion (default 5) |
371
465
  | `--concurrency <n>` | Search passes in flight at once (default 8) |
372
- | `--allow-empty` | Exit 0 when no spec file matched the patterns |
466
+ | `--allow-empty` | Exit 0 when no spec file matched the patterns (about the run, not an assertion) |
373
467
  | `--color` / `--no-color` | Force colour on or off (`NO_COLOR` honoured) |
374
468
 
375
469
  Patterns are expanded by spec-guard itself, so quoted globs behave identically
@@ -418,6 +512,35 @@ As a pre-commit hook (assuming a local install, so the bare command resolves):
418
512
  npx spec-guard "docs/**/*.md" --fail-fast
419
513
  ```
420
514
 
515
+ ### Annotations on the pull request
516
+
517
+ `--format sarif` writes a SARIF 2.1.0 document, which GitHub turns into a
518
+ comment on the offending line:
519
+
520
+ ```yaml
521
+ - run: npx @descent-vtt/spec-guard "docs/**/*.md" --format sarif > spec-guard.sarif || true
522
+ - uses: github/codeql-action/upload-sarif@v3
523
+ with:
524
+ sarif_file: spec-guard.sarif
525
+ category: spec-guard
526
+ ```
527
+
528
+ That job needs `permissions: security-events: write`. One alert per broken rule
529
+ rather than per match - the thing that broke is the rule - anchored on the first
530
+ offending line, with the directive as a related location because that is often
531
+ where the fix goes. Alerts carry a fingerprint derived from what the assertion
532
+ is about rather than where its matches landed, so inserting a line above a
533
+ violation does not close the alert and open a new one.
534
+
535
+ **There is no language server, and that is a decision rather than a gap.**
536
+ spec-guard's claims are about a whole repository - "this symbol appears nowhere
537
+ in `src`" - and a language server is handed one buffer at a time. Answering a
538
+ repository-wide question on every keystroke means rescanning the tree on every
539
+ keystroke; the alternative is answering a smaller question and calling it the
540
+ same one. The CLI does a full run of this repository in about 130 ms, so a
541
+ pre-commit hook or a watch loop already closes the feedback gap without a
542
+ daemon, an extension per editor, or a protocol version matrix.
543
+
421
544
  ## How it works
422
545
 
423
546
  ```text
@@ -473,6 +596,21 @@ was 379-1039 ms - wide enough that the two are not really distinguishable at
473
596
  that size. The order-of-magnitude differences at the ends of the table are the
474
597
  part worth trusting.
475
598
 
599
+ 0.5.0 added four language analysers, a scope probe on every assertion and a
600
+ baseline pass, so the obvious question is what that cost. Both versions were run
601
+ against the same synthetic 1,000-file tree, alternating rounds inside one
602
+ process so that this machine's load - which varies by a factor of two over a
603
+ session - falls on both equally:
604
+
605
+ | | median | min |
606
+ | --- | --- | --- |
607
+ | 0.4.0 | 908.8 ms | 493.7 ms |
608
+ | 0.5.0 | 913.4 ms | 496.6 ms |
609
+
610
+ 0.5% apart, against a run-to-run spread far wider than that. The two versions
611
+ also returned identical counts for all five assertions, which is the more
612
+ useful half of the result.
613
+
476
614
  Two honest caveats:
477
615
 
478
616
  - **The reported engine is the one that ran, not the one available.** On a small
@@ -547,12 +685,14 @@ process spawn (~27 ms on Windows) on the critical path of every run, including
547
685
  runs where ripgrep is missing. Discovering its absence from the first real
548
686
  search is free.
549
687
 
550
- **Assertions are batched, but only when provably safe.** Merging literals into
551
- one ripgrep alternation can lose matches two ways: containment (`Primary` /
552
- `PrimaryButton`) and dovetailing (`abc` / `cd` in `abcd`). spec-guard checks for
553
- both and falls back to separate passes when either is possible, and never
554
- batches regexes or case-insensitive searches. Speed is never traded for a wrong
555
- count.
688
+ **Assertions that share a target list share one pass over the tree.** This used
689
+ to need a proof: merging literals into a ripgrep alternation can lose matches
690
+ through containment (`Primary` / `PrimaryButton`) and dovetailing (`abc` / `cd`
691
+ in `abcd`), so spec-guard checked for both and fell back to separate passes.
692
+ Since 0.4.0 it does not need the proof, because ripgrep no longer counts
693
+ anything - it answers only *which files contain this text*, and the scanner
694
+ counts each pattern separately over the shared file contents. The batching
695
+ checks were deleted along with the risk they guarded.
556
696
 
557
697
  **Exit code 2 exists.** "Your specs failed" and "spec-guard could not run" are
558
698
  different facts, and CI should be able to tell them apart.
@@ -577,6 +717,28 @@ the documentation this tool exists to keep honest ([ADR-0006](docs/adr/0006-comm
577
717
  The reverse risk — a rule that quietly stops checking anything because every
578
718
  match now sits in a comment — is why every run says how many matches it dropped.
579
719
 
720
+ **An assertion that inspected no files fails.** It is the same defect as a
721
+ missing `target` seen from a different angle: a rule whose scope is empty passes
722
+ forever and reads exactly like a rule that found nothing. Turning this on found
723
+ a vacuous assertion inside this repository's own test suite on the first run.
724
+ `allow-empty="true"` covers the honest case of a rule written before the code it
725
+ guards.
726
+
727
+ **Four more languages, and no parser.** Import assertions read Python, Go, Rust
728
+ and C# as well as JavaScript. Not by adding four tokenizers - by reusing the
729
+ comment and string lexer that already existed, masking the source with it, and
730
+ reading statements off what is left. Tree-sitter would have been the modern
731
+ answer and costs 94 MB unpacked against this package's 0.33 MB; the four things
732
+ a real parser would genuinely see that this cannot are listed in
733
+ [ADR-0008](docs/adr/0008-polyglot-imports.md) rather than glossed over.
734
+
735
+ **There is no `--fix`.** Every edit a machine can make to a failing boundary
736
+ assertion is an edit that records the rule no longer holding: widen the bound,
737
+ add an exclusion, append to the baseline, insert an ignore comment. A one-flag
738
+ path from red to green is a bad button for a person and a much worse one for an
739
+ agent whose loop terminates on a green build. `--print-baseline` prints what you
740
+ could paste; it does not paste it ([ADR-0009](docs/adr/0009-debt-baselines.md)).
741
+
580
742
  ## spec-guard checks itself
581
743
 
582
744
  The invariants in [`docs/adr/0001-invariants.md`](docs/adr/0001-invariants.md)
@@ -597,7 +759,7 @@ npm install
597
759
  npm run build # tsc -> dist/
598
760
  npm test # vitest
599
761
  npm run test:coverage
600
- npm run test:mutation # stryker (~16 min in CI; much slower locally)
762
+ npm run test:mutation # stryker (~16 min in CI; over two hours locally)
601
763
  npm run lint # tsc --noEmit
602
764
  npm run selfcheck # run spec-guard on its own docs
603
765
  ```
@@ -611,8 +773,8 @@ prebuilt binary and is never a runtime dependency.
611
773
  ### Mutation testing
612
774
 
613
775
  Coverage says a line ran. It does not say an assertion would notice if the line
614
- behaved differently. This repository measures the difference: **84.63%** of
615
- 2,949 mutants are killed in CI, against high line coverage.
776
+ behaved differently. This repository measures the difference: **87.21%** of
777
+ 4,512 mutants are killed in CI, against high line coverage.
616
778
 
617
779
  That gap is the point. The first run scored 77.23%, and the weakest file was the
618
780
  reporter at 65.48% - not because it lacked tests, but because its tests were
@@ -622,17 +784,33 @@ test still passed. Exact whole-output comparison took it to 87.30%. The engine
622
784
  and the walker were then rewritten for testability rather than papered over with
623
785
  more tests, which is what moved them from 75%/78% to 83%/93%.
624
786
 
625
- `npm run test:mutation` runs it locally and takes about an hour; CI runs it on
626
- **every push**, gated at 84%. The CI figure is the one
627
- quoted above, and it is the lower of the two: the same commit scores 88.76% on
628
- Windows, where three times as many mutants hang and Stryker counts a hang as a
629
- kill.
630
-
631
- Four cautionary tales are in [ADR-0003](docs/adr/0003-mutation-testing.md): a
787
+ CI runs it in **two tiers**, both gated at 85%. Branches and pull requests run
788
+ Stryker incrementally, reusing the verdict for any mutant whose source and
789
+ covering tests are both unchanged. Pushes to `main`, the weekly schedule and
790
+ manual runs do the full sweep, which is the authoritative number and the one
791
+ quoted above; a full sweep is also what publishes the cache the branches start
792
+ from, so an incremental verdict can never be built on another incremental
793
+ verdict.
794
+
795
+ The full sweep was 6m54s at 2,118 mutants, 15m51s at 3,493, and is 22m11s at
796
+ 4,512. That growth is why the tiers exist: a check that gets quietly more
797
+ expensive every release is a check somebody eventually proposes lowering.
798
+
799
+ Run it locally with `npm run test:mutation` if you like, but do not calibrate
800
+ anything on the result: on the Windows machine this was developed on the same
801
+ suite takes over two hours against 22 minutes hosted, and it scores *higher*,
802
+ because far more mutants hang there and Stryker counts a hang as a kill. Linux
803
+ CI is the measurement.
804
+
805
+ Six cautionary tales are in [ADR-0003](docs/adr/0003-mutation-testing.md): a
632
806
  run whose score was pure fiction because the mutants were never activated, a
633
807
  tuning knob that lifted the score six points without adding a test, the platform
634
- gap above, and the baseline being re-anchored when a tokenizer arrived - along
635
- with the real bug that chasing the gate uncovered.
808
+ gap above, the baseline being re-anchored when a tokenizer arrived, and a score
809
+ that rose partly because code carrying hard-to-kill mutants was deleted rather
810
+ than because tests improved, and the 0.5.0 sweep that failed the build at 83.77%
811
+ because a suite can be thorough about the thing it was written to test and
812
+ silent about the machinery underneath it - along with the real bug that chasing
813
+ the gate uncovered.
636
814
 
637
815
  ## Requirements
638
816
 
package/dist/cli.d.ts CHANGED
@@ -17,16 +17,22 @@ export interface CliIO {
17
17
  cwd: string;
18
18
  isTTY: boolean;
19
19
  }
20
+ /** How a finished run is written out. */
21
+ export type OutputFormat = 'human' | 'json' | 'sarif';
20
22
  export interface CliOptions {
21
23
  patterns: string[];
22
24
  root: string;
23
25
  verbose: boolean;
24
26
  failFast: boolean;
27
+ format: OutputFormat;
28
+ /** Kept as its own field so `--json` remains exactly what it always was. */
25
29
  json: boolean;
26
30
  engine: EnginePreference;
27
31
  allowMissingTargets: boolean;
28
32
  defaultSkips: boolean;
29
33
  strictTargets: boolean;
34
+ allowEmptyScope: boolean;
35
+ printBaseline: boolean;
30
36
  includeSpecs: boolean;
31
37
  allowEmpty: boolean;
32
38
  maxSnippets: number;
@@ -37,7 +43,8 @@ export interface CliOptions {
37
43
  }
38
44
  export declare class UsageError extends Error {
39
45
  }
40
- export declare const HELP = "spec-guard - Executable architecture assertions for Markdown specs & ADRs\n\nUsage\n spec-guard [patterns...] [options]\n\nPatterns\n Globs or paths to the Markdown specs to execute. A directory expands to the\n Markdown files inside it. Defaults to \"docs/**/*.md\" when omitted.\n\nOptions\n -r, --root <path> Codebase root that assertions are resolved against (default: cwd)\n -v, --verbose Print passing assertions too\n --fail-fast Stop at the first failing assertion\n --json Emit a machine-readable JSON report\n --engine <name> auto | rg | js (default: auto - scanner for small trees, ripgrep for big ones)\n --strict Treat analysis that could not be completed as a failure\n --allow-missing-targets\n Tolerate target paths that do not exist (they fail by default)\n --no-default-skips Search .git, .hg, .svn and node_modules too\n --include-specs Also count matches inside the spec files themselves\n --max-snippets <n> Failure snippets per assertion (default: 5)\n --concurrency <n> Assertions executed in parallel (default: 8)\n --allow-empty Exit 0 when no spec files matched\n --color/--no-color Force colour on or off (NO_COLOR is honoured)\n -h, --help Show this help\n --version Print the version\n\nDirectives\n <!-- @assert-absence target=\"src/\" symbol=\"LegacyGateway\" exclude=\"src/legacy/**\" -->\n <!-- @assert-count target=\"src/\" symbol=\"SessionManager\" expected=\"1\" -->\n <!-- @assert-present file=\"SECURITY.md\" -->\n\n Matches inside comments do not count; add comments=\"include\" to count them.\n\nExit codes\n 0 all assertions passed 1 an assertion failed 2 spec-guard could not run";
46
+ export declare function version(): string;
47
+ export declare const HELP = "spec-guard - Executable architecture assertions for Markdown specs & ADRs\n\nUsage\n spec-guard [patterns...] [options]\n\nPatterns\n Globs or paths to the Markdown specs to execute. A directory expands to the\n Markdown files inside it. Defaults to \"docs/**/*.md\" when omitted.\n\nOptions\n -r, --root <path> Codebase root that assertions are resolved against (default: cwd)\n -v, --verbose Print passing assertions too\n --fail-fast Stop at the first failing assertion\n --json Emit a machine-readable JSON report (same as --format json)\n --format <name> human | json | sarif (sarif uploads to GitHub code scanning)\n --engine <name> auto | rg | js (default: auto - scanner for small trees, ripgrep for big ones)\n --strict Treat analysis that could not be completed as a failure\n --allow-missing-targets\n Tolerate target paths that do not exist (they fail by default)\n --allow-empty-scope Tolerate assertions whose scope holds no files (they fail by default)\n --print-baseline Print the baseline=\"...\" that would exempt today's violations, and exit\n --no-default-skips Search .git, .hg, .svn and node_modules too\n --include-specs Also count matches inside the spec files themselves\n --max-snippets <n> Failure snippets per assertion (default: 5)\n --concurrency <n> Assertions executed in parallel (default: 8)\n --allow-empty Exit 0 when no spec files matched (about the run, not an assertion)\n --color/--no-color Force colour on or off (NO_COLOR is honoured)\n -h, --help Show this help\n --version Print the version\n\nDirectives\n <!-- @assert-absence target=\"src/\" symbol=\"LegacyGateway\" exclude=\"src/legacy/**\" -->\n <!-- @assert-count target=\"src/\" symbol=\"SessionManager\" expected=\"1\" -->\n <!-- @assert-present file=\"SECURITY.md\" -->\n\n Matches inside comments do not count; add comments=\"include\" to count them.\n An assertion whose scope holds no files fails; add allow-empty=\"true\" to allow it.\n\nExit codes\n 0 all assertions passed 1 an assertion failed 2 spec-guard could not run";
41
48
  /** Minimal, dependency-free argv parser. Supports `--flag value` and `--flag=value`. */
42
49
  export declare function parseArgs(argv: readonly string[], cwd: string): CliOptions;
43
50
  /** Runs the CLI and resolves to the process exit code. */
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,UAAU,IAAI,CAAC;AAE5B,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,gBAAgB,CAAC;IACzB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,UAAW,SAAQ,KAAK;CAAG;AAaxC,eAAO,MAAM,IAAI,2xDAmC8D,CAAC;AAgChF,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CA2G1E;AAYD,0DAA0D;AAC1D,wBAAsB,IAAI,CAAC,IAAI,GAAE,SAAS,MAAM,EAA0B,EAAE,EAAE,GAAE,KAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiEpH"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,UAAU,IAAI,CAAC;AAE5B,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,yCAAyC;AACzC,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;AAEtD,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,YAAY,CAAC;IACrB,4EAA4E;IAC5E,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,gBAAgB,CAAC;IACzB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,UAAW,SAAQ,KAAK;CAAG;AAExC,wBAAgB,OAAO,IAAI,MAAM,CAShC;AAED,eAAO,MAAM,IAAI,4sEAuC8D,CAAC;AAgChF,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CA8H1E;AAYD,0DAA0D;AAC1D,wBAAsB,IAAI,CAAC,IAAI,GAAE,SAAS,MAAM,EAA0B,EAAE,EAAE,GAAE,KAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CA2EpH"}
package/dist/cli.js CHANGED
@@ -8,14 +8,14 @@
8
8
  */
9
9
  import { createRequire } from 'node:module';
10
10
  import path from 'node:path';
11
- import { formatJson, formatReport, shouldUseAscii, shouldUseColor } from './reporter.js';
11
+ import { formatBaselines, formatJson, formatReport, formatSarif, shouldUseAscii, shouldUseColor } from './reporter.js';
12
12
  import { DEFAULT_CONCURRENCY, DEFAULT_MAX_SNIPPETS, runSpecGuard } from './runner.js';
13
13
  export const EXIT_OK = 0;
14
14
  export const EXIT_FAILED = 1;
15
15
  export const EXIT_ERROR = 2;
16
16
  export class UsageError extends Error {
17
17
  }
18
- function version() {
18
+ export function version() {
19
19
  try {
20
20
  const require = createRequire(import.meta.url);
21
21
  const pkg = require('../package.json');
@@ -39,16 +39,19 @@ Options
39
39
  -r, --root <path> Codebase root that assertions are resolved against (default: cwd)
40
40
  -v, --verbose Print passing assertions too
41
41
  --fail-fast Stop at the first failing assertion
42
- --json Emit a machine-readable JSON report
42
+ --json Emit a machine-readable JSON report (same as --format json)
43
+ --format <name> human | json | sarif (sarif uploads to GitHub code scanning)
43
44
  --engine <name> auto | rg | js (default: auto - scanner for small trees, ripgrep for big ones)
44
45
  --strict Treat analysis that could not be completed as a failure
45
46
  --allow-missing-targets
46
47
  Tolerate target paths that do not exist (they fail by default)
48
+ --allow-empty-scope Tolerate assertions whose scope holds no files (they fail by default)
49
+ --print-baseline Print the baseline="..." that would exempt today's violations, and exit
47
50
  --no-default-skips Search .git, .hg, .svn and node_modules too
48
51
  --include-specs Also count matches inside the spec files themselves
49
52
  --max-snippets <n> Failure snippets per assertion (default: ${DEFAULT_MAX_SNIPPETS})
50
53
  --concurrency <n> Assertions executed in parallel (default: ${DEFAULT_CONCURRENCY})
51
- --allow-empty Exit 0 when no spec files matched
54
+ --allow-empty Exit 0 when no spec files matched (about the run, not an assertion)
52
55
  --color/--no-color Force colour on or off (NO_COLOR is honoured)
53
56
  -h, --help Show this help
54
57
  --version Print the version
@@ -59,6 +62,7 @@ Directives
59
62
  <!-- @assert-present file="SECURITY.md" -->
60
63
 
61
64
  Matches inside comments do not count; add comments="include" to count them.
65
+ An assertion whose scope holds no files fails; add allow-empty="true" to allow it.
62
66
 
63
67
  Exit codes
64
68
  0 all assertions passed 1 an assertion failed 2 spec-guard could not run`;
@@ -97,11 +101,14 @@ export function parseArgs(argv, cwd) {
97
101
  root: cwd,
98
102
  verbose: false,
99
103
  failFast: false,
104
+ format: 'human',
100
105
  json: false,
101
106
  engine: 'auto',
102
107
  allowMissingTargets: false,
103
108
  defaultSkips: true,
104
109
  strictTargets: false,
110
+ allowEmptyScope: false,
111
+ printBaseline: false,
105
112
  includeSpecs: false,
106
113
  allowEmpty: false,
107
114
  maxSnippets: DEFAULT_MAX_SNIPPETS,
@@ -146,13 +153,29 @@ export function parseArgs(argv, cwd) {
146
153
  break;
147
154
  case '--json':
148
155
  options.json = true;
156
+ options.format = 'json';
149
157
  break;
158
+ case '--format': {
159
+ const value = nextValue().toLowerCase();
160
+ if (value !== 'human' && value !== 'json' && value !== 'sarif') {
161
+ throw new UsageError(`Unknown format "${value}". Expected human, json or sarif.`);
162
+ }
163
+ options.format = value;
164
+ options.json = value === 'json';
165
+ break;
166
+ }
150
167
  case '--strict':
151
168
  options.strictTargets = true;
152
169
  break;
153
170
  case '--allow-missing-targets':
154
171
  options.allowMissingTargets = true;
155
172
  break;
173
+ case '--allow-empty-scope':
174
+ options.allowEmptyScope = true;
175
+ break;
176
+ case '--print-baseline':
177
+ options.printBaseline = true;
178
+ break;
156
179
  case '--no-default-skips':
157
180
  options.defaultSkips = false;
158
181
  break;
@@ -232,6 +255,7 @@ export async function main(argv = process.argv.slice(2), io = defaultIO()) {
232
255
  engine: options.engine,
233
256
  failFast: options.failFast,
234
257
  allowMissingTargets: options.allowMissingTargets,
258
+ allowEmptyScope: options.allowEmptyScope,
235
259
  defaultSkips: options.defaultSkips,
236
260
  strictTargets: options.strictTargets,
237
261
  includeSpecs: options.includeSpecs,
@@ -244,15 +268,24 @@ export async function main(argv = process.argv.slice(2), io = defaultIO()) {
244
268
  return EXIT_ERROR;
245
269
  }
246
270
  if (report.summary.specs === 0) {
247
- if (options.json) {
248
- io.stdout(formatJson(report));
271
+ if (options.format !== 'human') {
272
+ io.stdout(options.format === 'sarif' ? formatSarif(report, { version: version() }) : formatJson(report));
249
273
  }
250
274
  else {
251
275
  io.stderr(`spec-guard: no spec files matched ${options.patterns.map((p) => `"${p}"`).join(', ')}`);
252
276
  }
253
277
  return options.allowEmpty ? EXIT_OK : EXIT_ERROR;
254
278
  }
255
- if (options.json) {
279
+ if (options.printBaseline) {
280
+ // Printed, never written. See formatBaselines for why that distinction is
281
+ // the whole design and not a missing feature.
282
+ io.stdout(formatBaselines(report));
283
+ return report.ok ? EXIT_OK : EXIT_FAILED;
284
+ }
285
+ if (options.format === 'sarif') {
286
+ io.stdout(formatSarif(report, { version: version() }));
287
+ }
288
+ else if (options.format === 'json') {
256
289
  io.stdout(formatJson(report));
257
290
  }
258
291
  else {
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}