@avi2dg/checks 0.50.1 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.bun-version +1 -0
  2. package/CHANGELOG.md +27 -0
  3. package/README.md +24 -249
  4. package/commitlint.config.ts +0 -1
  5. package/dist/mutation/unit-test-order.js +1 -1
  6. package/dist/presets/knip.d.ts +1 -1
  7. package/dist/presets/knip.js +1 -1
  8. package/dist/templates/how-to.md +1 -1
  9. package/dist/templates/readme.md +1 -1
  10. package/dist/templates/tutorial.md +1 -1
  11. package/docs/bins.md +67 -0
  12. package/docs/gates/checks-advisories.md +2 -2
  13. package/docs/gates/checks-browser.md +5 -1
  14. package/docs/gates/checks-ci-wiring.md +18 -9
  15. package/docs/gates/checks-commit-identity.md +6 -3
  16. package/docs/gates/checks-copy.md +4 -0
  17. package/docs/gates/checks-docs.md +24 -3
  18. package/docs/gates/checks-exports.md +1 -1
  19. package/docs/gates/checks-frontend-syntax.md +5 -1
  20. package/docs/gates/checks-lint.md +2 -2
  21. package/docs/gates/checks-mutation-baseline.md +36 -7
  22. package/docs/gates/checks-mutation-compare.md +3 -3
  23. package/docs/gates/checks-mutation-scope.md +1 -1
  24. package/docs/gates/checks-mutation.md +26 -9
  25. package/docs/gates/checks-night-run.md +1 -1
  26. package/docs/gates/checks-secrets.md +2 -2
  27. package/docs/gates/checks-unused.md +1 -1
  28. package/docs/how-to/set-up-a-repository.md +142 -0
  29. package/docs/how-to/turn-on-the-css-families.md +35 -0
  30. package/docs/how-to/upgrade-the-kit.md +42 -0
  31. package/docs/shipped-files.md +36 -0
  32. package/package.json +13 -1
  33. package/src/delivery/bun-pin.ts +19 -5
  34. package/src/delivery/ci-wiring.ts +11 -77
  35. package/src/delivery/commit-identity.ts +14 -7
  36. package/src/delivery/runner-policy.ts +385 -0
  37. package/src/delivery/workflow-references.ts +1 -1
  38. package/src/docs/doc-templates.ts +1 -1
  39. package/src/docs/doc-versions.ts +137 -0
  40. package/src/docs/docs.ts +7 -0
  41. package/src/testing/mutation-baseline-full.ts +68 -0
  42. package/src/testing/mutation-baseline-saved.ts +52 -0
  43. package/src/testing/mutation-baseline.ts +27 -23
  44. package/src/testing/mutation-scope-graph.ts +89 -0
  45. package/src/testing/mutation-scope.ts +48 -93
@@ -12,6 +12,7 @@ It also fails when a doc names a file, a heading or a script that does not exist
12
12
  It holds each doc file a change touches to the template for its kind.
13
13
  It lists every other doc file that does not match its template yet, and does not fail on it.
14
14
  It holds each line a change adds or edits in a living doc or an agent file to the prose rules, as [The prose rules](#the-prose-rules) says.
15
+ It fails when such a line copies a version `package.json` or `.bun-version` pins, as [Pinned versions](#pinned-versions) says.
15
16
  It fails when a living doc or an agent file names a path, link or command that does not resolve, and the range added or broke it.
16
17
  It fails when an agent file holds more than 3,000 characters or a `## Maintaining this file` section, whatever the range touches, as [Agent files](#agent-files) says.
17
18
  It fails when an entry in an agent file names no tracked path, link or `bun run` command, whatever the range touches.
@@ -119,6 +120,24 @@ The cap is 20 words in an ordered list item and 25 words elsewhere.
119
120
  The package exports it as `@avi2dg/checks/scripts/prose-matchers.ts`.
120
121
  A host such as a write-time hook can copy that one file into a directory with no `node_modules`, and refuse the same lines.
121
122
 
123
+ ## Pinned versions
124
+
125
+ A version a manifest pins has one home, and a doc that copies it by hand goes stale when the pin moves.
126
+ Prose and code are judged alike.
127
+ A line a change adds or edits in a living doc or an agent file fails when it carries one of these:
128
+
129
+ - A package `package.json` lists in `dependencies`, `devDependencies`, `peerDependencies` or `optionalDependencies` at one version, written as `name@version` or beside the name in prose or a table.
130
+ - The `version`, `packageManager` or an `engines` version from `package.json`, or the version in `.bun-version`, written on its own.
131
+
132
+ The doc names the file that holds the version instead, as in `Bun, at the version .bun-version pins`.
133
+ A command that needs the exact version sits in a generated block, between a `<!-- generated <name>: ... -->` line and its `<!-- end generated <name> -->` line, which a build writes from the manifest.
134
+ No line in a complete same-named generated block is judged.
135
+ A version the manifests do not pin passes, such as a sample's `lodash@4.17.20`.
136
+ So does a longer version that opens with a pin.
137
+ A manifest range with no single version pins nothing, as in `>=1.2.3 <2` in `package.json`.
138
+ A doc line that carries a pinned version still fails when that version sits inside a range.
139
+ It reads the root `package.json` and `.bun-version`, so a nested package's pins are not checked.
140
+
122
141
  ## Paths, links and commands
123
142
 
124
143
  Each reference a living doc or an agent file names has to resolve at the head commit:
@@ -177,6 +196,7 @@ It reads the lines the range adds or edits from the diff, with renames detected,
177
196
  It reads the files tracked at both ends of the range, and the `scripts` of each `package.json` a living doc or an agent file sits under.
178
197
  It compares each code span a living doc or an agent file names with the text git tracks outside the docs at both ends of the range.
179
198
  It reads the repository's own name and its direct dependencies from the root `package.json` at the head commit.
199
+ It reads the versions the root `package.json` and `.bun-version` pin at the head commit.
180
200
  It reads each agent file at the head commit for the ceiling, its sections and its entries, whatever the range touches.
181
201
  A name that an installed direct dependency still holds counts as present.
182
202
  From the working tree it reads the ignore files git reads, `node_modules/.bin`, and the directory of each direct dependency under `node_modules`.
@@ -195,16 +215,17 @@ With one it is that commit against its parent, or against the empty tree for a r
195
215
 
196
216
  | Code | When |
197
217
  | --- | --- |
198
- | 0 | every doc file the range touches holds to its template, every line it adds to a living doc or an agent file holds to the prose rules, it adds or breaks no reference that does not resolve, every agent file holds to the ceiling and holds no `## Maintaining this file`, every entry in an agent file names a tracked path, a link or a command, and no code span a living doc or an agent file names vanished from every file outside the docs |
199
- | 1 | a doc file the range touches does not hold to its template, a line the range adds to a living doc or an agent file breaks a prose rule, the range adds or breaks a reference that does not resolve, an agent file is over the ceiling or holds `## Maintaining this file`, an entry in an agent file names no tracked path, link or command, or the range removes a name a living doc or an agent file still carries |
218
+ | 0 | every doc file the range touches holds to its template, every line it adds to a living doc or an agent file holds to the prose rules and copies no pinned version, it adds or breaks no reference that does not resolve, every agent file holds to the ceiling and holds no `## Maintaining this file`, every entry in an agent file names a tracked path, a link or a command, and no code span a living doc or an agent file names vanished from every file outside the docs |
219
+ | 1 | a doc file the range touches does not hold to its template, a line the range adds to a living doc or an agent file breaks a prose rule or copies a pinned version, the range adds or breaks a reference that does not resolve, an agent file is over the ceiling or holds `## Maintaining this file`, an entry in an agent file names no tracked path, link or command, or the range removes a name a living doc or an agent file still carries |
200
220
  | 2 | a `package.json` does not decode, a ref does not resolve, or `grep` cannot read an installed direct dependency |
201
221
 
202
222
  ## Sample output
203
223
 
204
224
  ```
205
- docs: 6 violation(s):
225
+ docs: 7 violation(s):
206
226
  README.md:1: lacks `## Before you begin`
207
227
  README.md:12: carries `;`, a semicolon. Use two sentences
228
+ README.md:13: carries `1.3.5`, a version .bun-version pins. Name .bun-version instead, or let a build write the line between generated markers
208
229
  README.md:14: carries `former`, a report about the past. Say what is true now, and leave what changed to the changelog, a commit message or a decision record
209
230
  README.md:20: names `scripts/bild.ts`, which is not in the repository
210
231
  docs/parts.md: is a page under docs/ with no mode; add kind: tutorial, how-to, reference, explanation in YAML front matter
@@ -34,7 +34,7 @@ import { defineConfig } from "@avi2dg/checks/knip";
34
34
  export default defineConfig({ entry: ["src/index.ts"] });
35
35
  ```
36
36
 
37
- The builder sets `include` to unreferenced files alone, and adds `tests/**/*.test.ts` and `dependency-cruiser.config.ts` to the entries.
37
+ The builder sets `include` to unreferenced files alone, and adds `tests/**/*.test.ts`, `dependency-cruiser.config.ts` and `oxlint.config.ts` to the entries.
38
38
  The gate resolves the Knip binary from the installed kit, so a consumer installs nothing beyond the kit.
39
39
 
40
40
  ## Arguments
@@ -133,10 +133,14 @@ A config not built with `defineConfig` names its own `customSyntax` for markup,
133
133
 
134
134
  The repository installs the optional peers the gate loads, at the versions the kit pins:
135
135
 
136
+ <!-- generated install: bun run build writes it from package.json and scripts/doc-blocks.ts -->
137
+
136
138
  ```sh
137
- bun add -d stylelint@17.16.0 postcss-html@2.0.0 stylelint-config-standard@40.0.0 stylelint-plugin-use-baseline@1.4.7 postcss-value-parser@4.2.0
139
+ bun add -d postcss-html@2.0.0 postcss-value-parser@4.2.0 stylelint@17.16.0 stylelint-config-standard@40.0.0 stylelint-plugin-use-baseline@1.4.7
138
140
  ```
139
141
 
142
+ <!-- end generated install -->
143
+
140
144
  ## Arguments
141
145
 
142
146
  ```sh
@@ -8,7 +8,7 @@ audience: consumers
8
8
 
9
9
  ## What it checks
10
10
 
11
- It runs the gates under [What runs](../../README.md#what-runs), each in its own process.
11
+ It runs the gates under [What runs](../bins.md#what-runs), each in its own process.
12
12
  Gates requiring tracked TypeScript files begin running when the repository tracks TypeScript.
13
13
  `checks-lint-coverage` and `checks-unused` also begin running when the repository tracks an `.astro` file.
14
14
  `checks-advisories` begins running when the repository tracks `bun.lock`.
@@ -62,5 +62,5 @@ It never runs `checks-browser`, which a product runs after it builds its site.
62
62
 
63
63
  ## Related topics
64
64
 
65
- - [What runs](../../README.md#what-runs)
65
+ - [What runs](../bins.md#what-runs)
66
66
  - [checks-ci-wiring](checks-ci-wiring.md)
@@ -22,6 +22,16 @@ With `--full` it lists up to 50 `schedule` runs and up to 50 `workflow_dispatch`
22
22
  It tries those runs newest first, and restores the first `mutation-baseline-full` artifact that has not expired, downloads and holds the report.
23
23
  It tries no run after that one.
24
24
  It takes the report from `mutation.json` at the artifact's root, or else from `mutation/mutation.json`, and copies it to the destination.
25
+ With an incremental destination it also copies `stryker-incremental.json` when the artifact holds one, so the next full run retests the mutants its changes reach, while a result made stale by a file a test reads at runtime lasts until the next 28-day refresh.
26
+ The restore then drops each test that reads a changed file and each mutant result for a changed source from the copied state.
27
+ It drops non-killed results when any test goes stale.
28
+ It drops the whole state when a Stryker config, stryker.preset.js, the lockfile, the Bun version, a file the active config reaches through a relative import at either revision, or the bunfig preload set or a file it reaches through a relative import changed.
29
+ It walks the config and preload dependencies at both revisions, so a file only the older revision resolved still drops the state when it changes.
30
+ A file a config or preload reaches only through a package main escapes invalidation until the next 28-day refresh.
31
+ A state that does not parse fails the restore instead of running on unknown results.
32
+ It drops the whole state when the restored run started before the current 28-day refresh window, and the windows count from the Unix epoch.
33
+ Every state a window keeps was built inside it, so each reused result is at most 28 days old, and a weekly full baseline retests every mutant every fourth week.
34
+ Without the state file the report still restores.
25
35
 
26
36
  It creates the directories each destination needs.
27
37
 
@@ -41,9 +51,15 @@ It deletes through `gh`, which needs a token that deletes the repository's Actio
41
51
 
42
52
  It reads the runs and their artifacts through `gh`, which needs a token that reads the repository's Actions, such as `GH_TOKEN: ${{ github.token }}` with `actions: read`.
43
53
  The `mutation-baseline` artifact holds `stryker-incremental.json` and `mutation/mutation.json`, which an upload of `reports/stryker-incremental.json` and `reports/mutation/mutation.json` writes.
44
- The `mutation-baseline-full` artifact holds `mutation.json` at its root, which an upload of `reports/mutation/mutation.json` alone writes.
54
+ The `mutation-baseline-full` artifact holds `mutation.json` and `stryker-incremental.json` at its root, which an upload of `${{ runner.temp }}/full/` writes.
45
55
  It reads `RUNNER_ENVIRONMENT` and `HOME` to place the runner cache, and `GITHUB_REPOSITORY_ID` to find the saved baselines.
46
56
  `--save` and `--prune` read `GITHUB_RUN_ID`.
57
+ With `--full` and an incremental destination it reads the run's head through `gh run view <run> --json headSha`.
58
+ It diffs that head against `HEAD`, which needs the checkout's full history.
59
+ It shows `bunfig.toml` and the files the active Stryker config imports at both revisions.
60
+ It reads every tracked `.ts`, `.tsx`, `.mts`, `.cts`, `.js`, `.jsx`, `.mjs`, and `.cjs` file from the working tree for their relative imports.
61
+ Inputs a test reads at runtime without a static import, such as markdown files, escape invalidation until the next refresh window.
62
+ It reads the restored run's `createdAt` from the run list to place that run in a refresh window.
47
63
 
48
64
  ## The runner cache
49
65
 
@@ -91,13 +107,13 @@ It also refuses when `HOME`, `GITHUB_REPOSITORY_ID` or `GITHUB_RUN_ID` is unset.
91
107
 
92
108
  ```sh
93
109
  checks-mutation-baseline <incremental-dest> [mutation-json-dest]
94
- checks-mutation-baseline --full <mutation-json-dest>
110
+ checks-mutation-baseline --full <mutation-json-dest> [incremental-dest]
95
111
  checks-mutation-baseline --save <incremental-src> [mutation-json-src]
96
112
  checks-mutation-baseline --slim <mutation-json-src> <dest>
97
113
  checks-mutation-baseline --prune [--dry-run]
98
114
  ```
99
115
 
100
- `--full` restores only the report of a run that started with no state.
116
+ `--full` restores the report of a full run, and its incremental state when given an incremental destination.
101
117
  `--save` keeps this run's baseline on the runner's disk in place of an upload.
102
118
  `--slim` writes a report without `statusReason` to `<dest>`, for a report that still goes up as an artifact.
103
119
  `--prune` deletes superseded baselines after a save or an upload.
@@ -108,7 +124,7 @@ checks-mutation-baseline --prune [--dry-run]
108
124
  | Code | When |
109
125
  | --- | --- |
110
126
  | 0 | it restored a baseline, found none to restore, saved the baseline or wrote the slim report |
111
- | 2 | the arguments do not parse, `gh` cannot list the runs, the cache cannot be written, another job removed the entry before it was copied, a file to save or slim does not parse, or `--save` runs where it refuses |
127
+ | 2 | the arguments do not parse, `gh` cannot list the runs, the cache cannot be written, another job removed the entry before it was copied, a file to save or slim does not parse, `--save` runs where it refuses, or `--full` with an incremental destination cannot read the run's head, diff it, or parse the restored state |
112
128
 
113
129
  A failed artifact lookup or download prints the `gh` error and moves on, as a missing artifact does.
114
130
  With `--prune`, `0` means it deleted the superseded artifacts, found none, or could not delete some.
@@ -166,15 +182,28 @@ The saved baseline uses none of the account's Actions storage, which every priva
166
182
  That storage accrues by the hour over the month, so an artifact deleted early still counts until the month ends.
167
183
  A baseline job on a GitHub-hosted runner uploads the same two files as a `mutation-baseline` artifact with `retention-days: 7` instead, since the restore reads only the newest `main` run.
168
184
 
169
- A full run uploads its report as the `mutation-baseline-full` artifact, which a branch comparison's scope step reads on any runner.
170
- It slims the report first, so the artifact holds about 0.5 MB in place of about 100 MB:
185
+ A full baseline job restores the previous full state before its incremental Stryker run, so it retests the mutants its changes reach, while a result made stale by a file a test reads at runtime lasts until the next 28-day refresh.
186
+ The checkout behind that step fetches the full history, so the restore can diff the restored run against `HEAD`:
187
+
188
+ ```yaml
189
+ - name: Restore previous full baseline state
190
+ env:
191
+ GH_TOKEN: ${{ github.token }}
192
+ run: bunx checks-mutation-baseline --full reports/mutation/mutation.json reports/stryker-incremental.json
193
+ ```
194
+
195
+ The restore reads the repository's Actions through `gh`, so the job that holds it grants `contents: read` and `actions: read`.
196
+
197
+ A full run uploads its slimmed report and its slimmed state as the `mutation-baseline-full` artifact, which a branch comparison's scope step reads on any runner.
198
+ It slims the report and the state first, so the one directory holds about 10 MB in place of about 500 MB:
171
199
 
172
200
  ```yaml
173
201
  - run: bunx checks-mutation-baseline --slim reports/mutation/mutation.json "$RUNNER_TEMP/full/mutation.json"
202
+ - run: bunx checks-mutation-baseline --slim reports/stryker-incremental.json "$RUNNER_TEMP/full/stryker-incremental.json"
174
203
  - uses: actions/upload-artifact@v4
175
204
  with:
176
205
  name: mutation-baseline-full
177
- path: ${{ runner.temp }}/full/mutation.json
206
+ path: ${{ runner.temp }}/full/
178
207
  retention-days: 30
179
208
  ```
180
209
 
@@ -136,9 +136,9 @@ A changed unit test that `main` edited after that baseline widens the scope to e
136
136
  Start a full baseline with `gh workflow run mutation`, as [checks-mutation](checks-mutation.md#running-it-in-ci) shows.
137
137
  A run with `--mutate` is scoped, so the preset lets it through without `CI=true`.
138
138
  The base worktree's path carries the run's id and attempt, and the last step removes it even when a run fails, so a runner kept between jobs starts each job clean.
139
- A public repository keeps `runs-on: ubuntu-latest`, as [checks-ci-wiring](checks-ci-wiring.md#runners) allows a public repository on every job.
140
- A private repository sets `runs-on: [self-hosted, Linux, X64, winbox]` instead, which is the fleet's self-hosted Linux runner where it sends its mutation runs.
141
- The job never reads `CI_RUNS_ON`, so an override that moves a repository's other jobs during a runner outage leaves the comparison waiting for `winbox`.
139
+ A public repository keeps `runs-on: ubuntu-latest`, as [checks-ci-wiring](checks-ci-wiring.md#runners) requires of a job a fork can reach.
140
+ A private repository sets `runs-on` to the labels it declares in `package.json` under `checks.mutationRunner` instead, which is where it sends its mutation runs.
141
+ The job never reads `CI_RUNS_ON`, so an override that moves a repository's other jobs during a runner outage leaves the comparison waiting for those labels.
142
142
  [checks-ci-wiring](checks-ci-wiring.md#runners) refuses a mutation job that names it in `runs-on`.
143
143
 
144
144
  ## Related topics
@@ -53,7 +53,7 @@ So a helper, fixture or preload changed on `main` since that baseline can hide a
53
53
  - The full baseline report that `checks-mutation-baseline --full` restores, for each mutant's `coveredBy` and `killedBy` and the `testFiles` that own those tests.
54
54
 
55
55
  Stryker leaves `coveredBy` empty on a static mutant, so only `killedBy` names its tests.
56
- The report must come from a run that restored no state, so each mutant's tests are the ones it ran against.
56
+ A reused result needs its affected tests, sources, and configuration invalidated before reuse, so each mutant's tests are the ones the run recorded.
57
57
  With bail, Stryker's default, `killedBy` names the one test that killed the mutant first.
58
58
  One killer is enough, since a branch loses a kill through its tests only when it changes every test that made the kill.
59
59
  A test left alone, whose sources the branch also leaves alone, still kills the mutant at `HEAD`.
@@ -30,7 +30,7 @@ So when a `stryker run` without `--ignoreStatic` loads the preset, the preset fi
30
30
  `unitTestFiles` then lists each file with a leading `./`, fastest first, and Bun runs such paths in the order given.
31
31
  It then lists each path again without the `./`, the spelling the runner matches a covering test against.
32
32
  Bun loads a file once however often the list names it.
33
- When that timing run fails, the preset prints why on stderr and `unitTestFiles` keeps the path order.
33
+ When that timing run fails, the preset prints why on stderr and `unitTestFiles` keeps Bun's default directory order.
34
34
  The config then reads:
35
35
 
36
36
  ```js
@@ -82,7 +82,7 @@ An `--incremental` run with no report stops before instrumenting with the missin
82
82
  A repository runs full baselines from the mutation workflow on `workflow_dispatch`.
83
83
  A repository that picks a branch scope from a baseline starts that workflow weekly with `gh workflow run mutation`, because a GitHub schedule can start hours late.
84
84
  Run scoped checks locally during development.
85
- Each baseline costs a full Stryker run.
85
+ A first baseline with no state costs a full Stryker run, and each later one retests the mutants its changes reach, while a result made stale by a file a test reads at runtime lasts until the next 28-day refresh.
86
86
 
87
87
  ## Running it in CI
88
88
 
@@ -94,32 +94,49 @@ on:
94
94
  workflow_dispatch:
95
95
  jobs:
96
96
  mutation:
97
- runs-on: [self-hosted, Linux, X64, winbox]
97
+ permissions:
98
+ contents: read
99
+ actions: read
100
+ runs-on: [self-hosted, gpu, x64]
98
101
  steps:
99
102
  - uses: actions/checkout@v5
103
+ with:
104
+ fetch-depth: 0
100
105
  - uses: oven-sh/setup-bun@v2
101
106
  with:
102
107
  bun-version-file: .bun-version
103
108
  - run: bun install --frozen-lockfile
104
- - run: bunx stryker run
109
+ - env:
110
+ GH_TOKEN: ${{ github.token }}
111
+ run: bunx checks-mutation-baseline --full reports/mutation/mutation.json reports/stryker-incremental.json
112
+ - run: bunx stryker run --incremental
105
113
  - run: bunx checks-mutation-baseline --slim reports/mutation/mutation.json "$RUNNER_TEMP/full/mutation.json"
114
+ - run: bunx checks-mutation-baseline --slim reports/stryker-incremental.json "$RUNNER_TEMP/full/stryker-incremental.json"
106
115
  - uses: actions/upload-artifact@v4
107
116
  with:
108
117
  name: mutation-baseline-full
109
- path: ${{ runner.temp }}/full/mutation.json
118
+ path: ${{ runner.temp }}/full/
110
119
  retention-days: 30
111
120
  ```
112
121
 
113
- The run restores no state, so its report names every test that covers each covered mutant and the first test that killed each killed mutant.
122
+ A first baseline with no state tests every mutant.
123
+ Each later restore drops the tests that read a changed file and the mutant results for a changed source from the restored state, and drops non-killed results when any test goes stale.
124
+ It drops the whole state when a Stryker config, stryker.preset.js, the lockfile, the Bun version, a file the active config reaches through a relative import at either revision, or the bunfig preload set or a file it reaches through a relative import changed.
125
+ It fails the restore when the state does not parse.
126
+ The run retests every mutant those changes reach and reuses the rest.
127
+ A result made stale by a file a test reads at runtime lasts until the next 28-day refresh.
128
+ A file a config or preload reaches only through a package main escapes invalidation until the next 28-day refresh.
129
+ The checkout fetches the full history, so the restore can tell which files changed since the previous full run.
130
+ Its report names every test that covers each covered mutant and the first test that killed each killed mutant.
114
131
  [checks-mutation-scope](checks-mutation-scope.md#what-it-reads) says why that one killer is enough.
115
132
  The upload is the `mutation-baseline-full` artifact that a branch comparison's scope step restores with `checks-mutation-baseline --full`.
116
133
  [checks-mutation-scope](checks-mutation-scope.md) reads it to bring in the sources a changed unit test covers or kills.
117
134
  The restore reads only a successful run of `mutation.yml` on `main`, so a sweep that fails publishes nothing a scope step reads.
118
135
  The artifact keeps 30 days, so the newest full baseline outlives the week between full runs.
119
- The upload holds the report without each mutant's `statusReason`, as [checks-mutation-baseline](checks-mutation-baseline.md#the-saved-baselines) says, since the full report of a large repository passes 100 MB as an artifact.
136
+ The upload holds the slimmed report and the slimmed state without each mutant's `statusReason`, as [checks-mutation-baseline](checks-mutation-baseline.md#the-saved-baselines) says, so the one directory holds about 10 MB in place of about 500 MB.
120
137
 
121
- The job runs on the fleet's self-hosted Linux runner labelled `winbox`, which is where a repository sends its full sweeps.
122
- It never reads `CI_RUNS_ON`, so an override that moves a repository's other jobs during a runner outage leaves the sweep waiting for `winbox`.
138
+ The job runs on the self-hosted labels the repository declares in `package.json` under `checks.mutationRunner`, which is where a repository sends its full sweeps.
139
+ It never reads `CI_RUNS_ON`, so an override that moves a repository's other jobs during a runner outage leaves the sweep waiting for those labels.
123
140
  [checks-ci-wiring](checks-ci-wiring.md#runners) refuses a mutation job that names it in `runs-on`.
124
141
  The `name: mutation` line is what `gh workflow run mutation` looks up.
125
142
  GitHub sets `CI=true` on every runner, so the preset lets the full run through there.
@@ -177,7 +177,7 @@ The concurrency group queues a run started by hand behind the scheduled one, so
177
177
  A dispatch on a branch runs both jobs and lists its findings in the report job's log, and `if-no-files-found: ignore` lets that run upload no record.
178
178
  `issues: write` on the report job is the one permission beyond reading the repository and its runs.
179
179
  A public repository keeps `runs-on: ubuntu-latest` on the mutation job.
180
- A private repository sets `runs-on: [self-hosted, Linux, X64, winbox]` on the mutation job, and [checks-ci-wiring](checks-ci-wiring.md#runners) refuses a mutation job that names `CI_RUNS_ON`.
180
+ A private repository sets `runs-on` on the mutation job to the labels declared under `checks.mutationRunner` in `package.json`, and [checks-ci-wiring](checks-ci-wiring.md#runners) refuses a mutation job that names `CI_RUNS_ON`.
181
181
  The report job reads `CI_RUNS_ON`, because it runs no mutant.
182
182
  A successful run uploads `night-run`, so the next night compares with it, and a failed run uploads nothing a later night reads.
183
183
  The artifact keeps 90 days, so a gap of nights still leaves a previous night to compare with.
@@ -46,8 +46,8 @@ It reads the commits of the range from git, not the working tree.
46
46
  It needs git 2.36 or newer, which diffs a merge against the merge git would make on its own, and it exits 2 on an older git.
47
47
  It reads its rules from `src/delivery/gitleaks.toml` in the installed package.
48
48
 
49
- It runs gitleaks 8.30.1, pinned by the SHA-256 of each platform's archive and of the binary inside it.
50
- On first use it downloads the archive from the scanner's GitHub release and unpacks the binary into `~/.cache/avi2dg-checks/gitleaks/8.30.1/`.
49
+ It runs the gitleaks release `src/delivery/gitleaks.ts` pins, by the SHA-256 of each platform's archive and of the binary inside it.
50
+ On first use it downloads the archive from the scanner's GitHub release and unpacks the binary into `~/.cache/avi2dg-checks/gitleaks/<version>/`.
51
51
  It checks the binary's SHA-256 again on every run and exits 2 on a copy that differs.
52
52
  Builds are pinned for macOS and Linux, each on x64 and arm64.
53
53
  On any other platform it exits 2, and no setting runs a scanner other than the pinned build.
@@ -26,7 +26,7 @@ import { defineConfig } from "@avi2dg/checks/knip";
26
26
  export default defineConfig({ entry: ["src/index.ts"] });
27
27
  ```
28
28
 
29
- The builder sets `include` to unreferenced files alone, and adds `tests/**/*.test.ts` and `dependency-cruiser.config.ts` to the entries.
29
+ The builder sets `include` to unreferenced files alone, and adds `tests/**/*.test.ts`, `dependency-cruiser.config.ts` and `oxlint.config.ts` to the entries.
30
30
  In a repository that depends on `astro`, Knip reads `.astro` imports, so a file only an `.astro` entry imports counts as used and an `.astro` file no entry reaches is named.
31
31
  The gate resolves the Knip binary from the installed kit, so a consumer installs nothing beyond the kit.
32
32
 
@@ -0,0 +1,142 @@
1
+ ---
2
+ kind: how-to
3
+ audience: consumers
4
+ ---
5
+ # Set up a repository
6
+
7
+ A repository owner follows these steps once, after [Install](../../README.md#install), so each gate `checks-lint` runs finds the file it reads.
8
+
9
+ ## Before you begin
10
+
11
+ - The kit in `devDependencies` and the kit's `.bun-version` copied, as [Install](../../README.md#install) says.
12
+
13
+ ## Write the tool configs
14
+
15
+ To give each tool the config it reads:
16
+
17
+ 1. Write `oxlint.config.ts` with the kit's builder, saying whether the sources are Effect programs:
18
+
19
+ ```ts
20
+ import { defineConfig } from "@avi2dg/checks/oxlint";
21
+
22
+ export default defineConfig({ effect: true });
23
+ ```
24
+
25
+ 1. Keep oxlint out of `node_modules/` through `.gitignore`:
26
+
27
+ ```
28
+ node_modules/
29
+ ```
30
+
31
+ 1. Add the Bun types, which the kit's config builders need to typecheck:
32
+
33
+ ```sh
34
+ bun add -d @types/bun
35
+ ```
36
+
37
+ 1. Extend the tsconfig fragment in `tsconfig.json`, naming the Bun types:
38
+
39
+ ```json
40
+ {
41
+ "extends": "@avi2dg/checks/tsconfig.effect.json",
42
+ "compilerOptions": { "types": ["bun"] }
43
+ }
44
+ ```
45
+
46
+ 1. Copy the bunfig preset:
47
+
48
+ ```sh
49
+ cp node_modules/@avi2dg/checks/bunfig.toml bunfig.toml
50
+ ```
51
+
52
+ 1. Name the files nothing imports in `knip.config.ts`:
53
+
54
+ ```ts
55
+ import { defineConfig } from "@avi2dg/checks/knip";
56
+
57
+ export default defineConfig({ entry: ["src/index.ts"] });
58
+ ```
59
+
60
+ 1. Write `dependency-cruiser.config.ts` only when the kit's import rules need an entry point or a boundary of the repository's own:
61
+
62
+ ```ts
63
+ import { defineConfig } from "@avi2dg/checks/dependency-cruiser";
64
+
65
+ export default defineConfig({ orphans: ["^src/bin[.]ts$"] });
66
+ ```
67
+
68
+ ## Run the checks on every pull request
69
+
70
+ To run the gates in CI:
71
+
72
+ 1. Add scripts to `package.json`, replacing the build entry with the repository's own build command:
73
+
74
+ ```json
75
+ "build": "bun build src/index.ts --outdir dist --target node && checks-effect-scope",
76
+ "lint": "oxlint && checks-lint",
77
+ "typecheck": "tsc --noEmit && effect-tsgo diagnostics --project tsconfig.json --format text --strict",
78
+ "test": "checks-test"
79
+ ```
80
+
81
+ 1. Run the scripts on every pull request in `.github/workflows/ci.yml`, fetching the whole history the range needs:
82
+
83
+ ```yaml
84
+ on:
85
+ pull_request:
86
+ jobs:
87
+ checks:
88
+ runs-on: ubuntu-latest
89
+ steps:
90
+ - uses: actions/checkout@v5
91
+ with:
92
+ fetch-depth: 0
93
+ - uses: oven-sh/setup-bun@v2
94
+ with:
95
+ bun-version-file: .bun-version
96
+ - run: bun install --frozen-lockfile
97
+ - run: bun run build
98
+ - run: git diff --exit-code
99
+ - run: bun run lint
100
+ - run: bun run typecheck
101
+ - run: bun run test
102
+ ```
103
+
104
+ Lint the pull request title in the same job with one step:
105
+
106
+ ```yaml
107
+ - id: title
108
+ name: lint the title
109
+ env:
110
+ PR_TITLE: ${{ github.event.pull_request.title }}
111
+ GH_TOKEN: ${{ github.token }}
112
+ run: bunx checks-commit-title
113
+ ```
114
+
115
+ A repository whose branch protection requires a `commitlint` status reports the title step under that name using the following step.
116
+ The same job reports the title step's outcome under that name on every run, which needs `statuses: write`:
117
+
118
+ ```yaml
119
+ - name: report the title as commitlint
120
+ if: always()
121
+ env:
122
+ GH_TOKEN: ${{ github.token }}
123
+ HEAD_SHA: ${{ github.event.pull_request.head.sha }}
124
+ OUTCOME: ${{ steps.title.outcome }}
125
+ run: |
126
+ state=failure
127
+ if [ "$OUTCOME" = success ]; then state=success; fi
128
+ gh api "repos/$GITHUB_REPOSITORY/statuses/${HEAD_SHA:-$GITHUB_SHA}" -f state="$state" -f context=commitlint -f target_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" --silent
129
+ ```
130
+
131
+ A private repository sets `runs-on: ${{ vars.CI_RUNS_ON || 'ubuntu-latest' }}` on each job, as [checks-ci-wiring](../gates/checks-ci-wiring.md#runners) requires.
132
+ `checks-effect-scope` writes the Effect paths of `oxlint.config.ts` and the kit's severities into `tsconfig.json`, as [The Effect rules](../configs/effect-rules.md#language-service) says.
133
+ `bun run lint` then ends with `checks-lint: <count> gate(s) pass`.
134
+
135
+ ## Related topics
136
+
137
+ - [The bins](../bins.md)
138
+ - [Native settings](../configs/native-settings.md)
139
+ - [The Effect rules](../configs/effect-rules.md)
140
+ - [The TypeScript rules](../configs/typescript-rules.md)
141
+ - [The dependency rules](../configs/dependency-rules.md)
142
+ - [The commit message lint](../configs/commit-messages.md)
@@ -0,0 +1,35 @@
1
+ ---
2
+ kind: how-to
3
+ audience: consumers
4
+ ---
5
+ # Turn on the CSS families
6
+
7
+ A repository owner follows these steps to judge the repository's CSS with the stylelint families it needs.
8
+
9
+ ## Turn them on
10
+
11
+ To turn on the CSS families:
12
+
13
+ 1. Install the peers [checks-frontend-syntax](../gates/checks-frontend-syntax.md#what-it-reads) loads, with the `bun add` line on its page.
14
+ 1. Declare `frontend-syntax.json`, which turns the gate on, as its page says.
15
+ 1. Write `stylelint.config.ts` with the families the repository needs:
16
+
17
+ ```ts
18
+ import { defineConfig } from "@avi2dg/checks/stylelint";
19
+
20
+ export default defineConfig({
21
+ baseline: "widely",
22
+ tokens: ["src/styles/tokens.css"],
23
+ bem: true,
24
+ flatSelectors: true,
25
+ noPageHas: true,
26
+ });
27
+ ```
28
+
29
+ [checks-frontend-syntax](../gates/checks-frontend-syntax.md#what-it-reads) says what each key takes and which family it turns on.
30
+ `noPageHas` sees a `:has` nested under a page selector only together with `flatSelectors`, which refuses the nesting.
31
+
32
+ ## Related topics
33
+
34
+ - [checks-frontend-syntax](../gates/checks-frontend-syntax.md)
35
+ - [checks-css-size](../gates/checks-css-size.md)
@@ -0,0 +1,42 @@
1
+ ---
2
+ kind: how-to
3
+ audience: consumers
4
+ ---
5
+ # Upgrade the kit
6
+
7
+ A repository owner follows these steps to move a repository the kit holds to a newer release.
8
+
9
+ ## Before you begin
10
+
11
+ - A repository set up as [Set up a repository](set-up-a-repository.md) says.
12
+
13
+ ## Upgrade
14
+
15
+ To move a repository to a newer release of the kit:
16
+
17
+ 1. Rerun the [install line](../../README.md#install), which moves the kit to its newest release.
18
+ A repository that lists a required peer explicitly also reruns the upgrade line below.
19
+ The upgrade line keeps each declaration the repository imports.
20
+ The upgrade line writes the lockfile, so the checks below run on the new pins.
21
+
22
+ <!-- generated install: bun run build writes it from package.json and scripts/doc-blocks.ts -->
23
+
24
+ ```sh
25
+ bun add -d @effect/tsgo@0.45.0 @swc/core@1.16.2 dependency-cruiser@18.4.0 effect@4.0.0 jscpd@5.3.2 oxlint@1.83.0 oxlint-tsgolint@7.0.2002 typescript@7.0.2
26
+ ```
27
+
28
+ <!-- end generated install -->
29
+
30
+ A repository that opted in to `checks-browser`, `checks-frontend-syntax` or `checks-copy` also reruns the `bun add` line on its page.
31
+ 1. Run `bun run build`, whose `checks-effect-scope` rewrites the Effect override and the kit's severities in `tsconfig.json` when a release changes them.
32
+ A severity key the release drops stays in `tsconfig.json` until the repository removes it.
33
+ 1. Copy `node_modules/@avi2dg/checks/bunfig.toml` over `bunfig.toml` again, since `checks-test-layout` compares the copy with the installed preset.
34
+ 1. Run `bun run lint`, `bun run typecheck` and `bun run test`.
35
+
36
+ The repository's lockfile pins the kit, so a repository moves only when it runs these steps.
37
+ [CHANGELOG.md](../../CHANGELOG.md), shipped in the package, lists what each release changed.
38
+
39
+ ## Related topics
40
+
41
+ - [The shipped files](../shipped-files.md)
42
+ - [CHANGELOG.md](../../CHANGELOG.md)
@@ -0,0 +1,36 @@
1
+ ---
2
+ kind: reference
3
+ audience: consumers
4
+ ---
5
+ # The shipped files
6
+
7
+ The package ships these paths, and a repository reads or copies them from its install.
8
+
9
+ ## Where things are
10
+
11
+ Every path is relative to the installed package, `node_modules/@avi2dg/checks/`.
12
+
13
+ <!-- generated shipped: bun run build writes it from package.json and scripts/doc-blocks.ts -->
14
+
15
+ | Path | What it holds |
16
+ | --- | --- |
17
+ | `CHANGELOG.md` | every release, and what it changed |
18
+ | `docs/` | the how-to guides, a reference page per bin and per shared config, and why the kit is shaped this way |
19
+ | `bunfig.toml` | the bunfig preset a repository copies |
20
+ | `.bun-version` | the fleet bun version the bun pin check reads |
21
+ | `commitlint.config.ts` | the shared commitlint config |
22
+ | `dependency-cruiser.config.js` | the kit's dependency-cruiser rules as a base a `.dependency-cruiser.cjs` extends by path, which the build writes |
23
+ | `knip-base.json` | the Knip `include` setting, for a configuration that spreads it |
24
+ | `src/` | every bin, which a package script calls by its `checks-` name, the modules the bins import, and the Effect language service severities under `src/quality/presets/` |
25
+ | `dist/` | the compiled oxlint plugins, the config builders `@avi2dg/checks/oxlint`, `@avi2dg/checks/knip`, `@avi2dg/checks/dependency-cruiser` and `@avi2dg/checks/stylelint` resolve to, and the doc templates, one template per kind of doc file |
26
+ | `oxlintrc.json` | the oxlint `base` as a config a `.oxlintrc.json` extends by path, which the build writes |
27
+ | `stryker.preset.js` | the Stryker mutation-testing preset, which refuses a full run outside CI |
28
+ | `tsconfig.effect.json` | the tsconfig fragment with the shared compiler options and the Effect language-service block |
29
+ | `ts-reset.d.ts` | the two ts-reset rules `tsconfig.effect.json` lists in `files` |
30
+
31
+ <!-- end generated shipped -->
32
+
33
+ ## Related topics
34
+
35
+ - [Set up a repository](how-to/set-up-a-repository.md)
36
+ - [Upgrade the kit](how-to/upgrade-the-kit.md)