@avi2dg/checks 0.26.0 → 0.27.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 (46) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +6 -6
  3. package/dist/data-shape/index.js +428 -0
  4. package/dist/templates/reference.md +1 -1
  5. package/docs/configs/commit-messages.md +1 -1
  6. package/docs/configs/dependency-rules.md +7 -1
  7. package/docs/configs/effect-rules.md +10 -0
  8. package/docs/configs/native-settings.md +8 -30
  9. package/docs/configs/typescript-rules.md +43 -1
  10. package/docs/design.md +210 -104
  11. package/docs/gates/checks-changelog.md +8 -5
  12. package/docs/gates/checks-ci-wiring.md +2 -2
  13. package/docs/gates/checks-comment-gate.md +3 -4
  14. package/docs/gates/checks-commit-identity.md +3 -3
  15. package/docs/gates/checks-docs.md +56 -36
  16. package/docs/gates/checks-exports.md +4 -4
  17. package/docs/gates/checks-flake.md +3 -3
  18. package/docs/gates/checks-lint-coverage.md +17 -9
  19. package/docs/gates/checks-lint.md +8 -4
  20. package/docs/gates/checks-mutation-compare.md +3 -3
  21. package/docs/gates/checks-quarantine-clock.md +3 -5
  22. package/docs/gates/checks-release-notes.md +4 -4
  23. package/docs/gates/checks-release-report.md +3 -3
  24. package/docs/gates/checks-repetition.md +2 -1
  25. package/docs/gates/checks-subsumed-tests.md +3 -3
  26. package/docs/gates/checks-suppressions-ratchet.md +3 -4
  27. package/docs/gates/checks-test-layout.md +4 -4
  28. package/docs/gates/checks-test.md +3 -2
  29. package/docs/gates/checks-unused.md +4 -4
  30. package/docs/gates/checks-vendor.md +3 -3
  31. package/oxlintrc.json +22 -2
  32. package/package.json +10 -5
  33. package/src/complexity/exports.ts +8 -16
  34. package/src/complexity/knip.ts +1 -1
  35. package/src/delivery/ci-wiring.ts +1 -1
  36. package/src/dependencies/vendor.ts +4 -5
  37. package/src/docs/doc-names.ts +128 -0
  38. package/src/docs/doc-templates.ts +1 -1
  39. package/src/docs/docs.ts +17 -14
  40. package/src/docs/prose-matchers.ts +11 -1
  41. package/src/quality/lint-coverage.sh +35 -2
  42. package/src/quality/presets/effect.language-service.json +3 -1
  43. package/src/testing/mutation-compare.ts +51 -45
  44. package/src/testing/test-layout.ts +1 -1
  45. package/ts-reset.d.ts +2 -0
  46. package/tsconfig.effect.json +3 -0
package/docs/design.md CHANGED
@@ -3,111 +3,217 @@ kind: explanation
3
3
  ---
4
4
  # Why it is shaped this way
5
5
 
6
- Each entry below is a choice in the kit's shape and the constraint that forced it.
7
-
8
- - Every config in an oxlint `extends` chain brings its own `plugins`, and one that sets none brings oxlint's default plugins, whose category rules the base's `categories` then turn on across the tree.
9
- `rules`, `categories` and `jsPlugins` inherit as expected.
10
- That is why the consumer config and each override restate `plugins`.
11
- - `node_modules/` is excluded through the consumer's `.gitignore`, not `ignorePatterns`: oxlint still walks the installed package when only `ignorePatterns` names it.
12
- - `files` in package.json is the published surface: `tests/`, `AGENTS.md` and the `.ts` plugin source never reach an install.
13
- npm adds `package.json`, `README` and `LICENSE` to the tarball whatever `files` says.
14
- `bun pm pack` builds the same tarball the registry serves, which is what the packed-tarball consumer e2e test installs.
15
- - Each plugin ships compiled under `dist/`, built with `bun build src/<vector>/<name>/index.ts --outdir dist/<name> --target node --format esm`.
16
- Node refuses to type-strip a `.ts` plugin under `node_modules`, so the `.ts` source would fail to load from an installed package.
17
- - The source sits under `src/<vector>/`, one directory for each thing the kit judges a repository on: complexity, quality, testing, docs, delivery and dependencies.
18
- `src/core/` holds what every vector runs on, and `scripts/` holds only the kit's own build, which nothing ships.
19
- Sorting by what loads a file put the two oxlint plugins at the root and every bin in one flat `scripts/`, so the files for one purpose sat in several places and nothing said which gate a helper served.
20
- A mutation runner's default scope covers `src/`, so the kit's own Stryker run mutates its source without a `mutate` list.
21
- The testing vector and its directory are named testing rather than tests, since a `src/tests/` beside the root `tests/` would read as a second suite.
22
- An `exports` key a consumer resolves as a specifier keeps pointing at the file's new home, so that specifier resolves as before while a path read without resolution does not.
23
- - The size rules are plain oxlint rules at `error` in `.oxlintrc.json`, and `bun run lint` enforces them on the whole tree.
24
- A repository records its existing violations with `oxlint --suppress-all`, and `checks-suppressions-ratchet` refuses any count that rises.
25
- The kit runs no size script of its own, since restating how oxlint reads its config and compares sites left corners the native rules never had.
26
- The suppression count lets a file or function already over its limit grow without a new site, which was accepted in exchange for dropping that script, and each repository is to work its counts down to zero.
27
- - `checks-repetition` writes both ends of the range to temporary directories and runs jscpd in each with the head's `.jscpd.json`, so its `path` and `ignore` globs decide the files at both ends.
28
- It compares each file's count of repeated lines rather than using jscpd's `--baseline-from-ref`.
29
- That flag reports a repeated block as new once its text changes, so a change that shortens a grandfathered block would fail.
30
- - `dist/` is committed.
31
- No `prepack` or `prepublishOnly` builds it, so a publish ships whatever bundle the publishing worktree holds.
32
- Rebuild it after pulling with `bun run build`.
33
- `bun run build` also emits `dist/templates/` and `CHANGELOG.md`, which are committed the same way.
34
- CI runs `git diff --exit-code` over the whole tree after the build, because a test that compares a generated file with its source passes on the copy the build just rewrote.
35
- - `CHANGELOG.md` is generated, so a release commit carries it and the tarball ships it.
36
- The commit that bumps package.json `version` closes its release, and the `v*` tag goes on that commit, so commits merged after it wait for the next release.
37
- Releases come from the commits, over all of HEAD's ancestry, so a checkout without tags, a fork and a branch that merged `main` in write the same file.
38
- A bump not newer than the release before it is a revert: it cancels every release above the version it returns to, and a tag only keeps a reverted release that was already published.
39
- The committed changelog is the record of what was released: a version older than the newest it lists and absent from it was never published, and its commits go into the next release.
40
- A section keeps the date it was written with, since the squash merge that lands the release commit may fall on another day.
41
- Entries come from commit subjects, the squash-merged pull request titles commitlint holds to the conventional format.
42
- The bodies are the branch's own messages, which nothing lints.
43
- The changelog still arrives in the release commit's pull request, not from a workflow that pushes.
44
- The release path writes to the repository only through the `github-release` job's `contents: write`, which creates or updates the GitHub release from the tag's `CHANGELOG.md` section.
45
- - Workflow YAML owns CI execution, and the kit owns the required commands, but a repository is held only to the commands its own `package.json` can run.
46
- `checks-ci-wiring` requires `bun run` with each of `lint`, `build`, `typecheck` and `test` that `package.json` defines as a script, `git diff --exit-code` when a `build` script exists, and commitlint always.
47
- The target branch comes from git's own record in `refs/remotes/origin/HEAD`, or in CI from the pull request base or the default branch GitHub's event names, and never from a guess at the workflow files, and `checks-lint` starts its local range from the same branch.
48
- Oxlint and TypeScript read their own overrides directly, so the consumer can change a rule where its tool reads it.
49
- The Effect scopes occur in two native files, and the installed consumer test checks both independently.
50
- - The base parses with swc because typescript 7, which is tsgo, has no compiler API for dependency-cruiser to use.
51
- Without `@swc/core` installed the cruise silently skips every `.ts` file, so this repo's test asserts its own TypeScript is cruised.
52
- - `bunfig.toml` has no `extends` and no include: bun ignores an unknown top-level key in silence, so a preset cannot be inherited and the consumer's copy is compared key by key against the installed one instead.
53
- `[test] pathIgnorePatterns` is a real bunfig key, and an empty `--path-ignore-patterns` flag overrides the file's own list.
54
- - The Stryker preset is a JavaScript module, not JSON: Stryker 10 does not resolve `extends` in a JSON config, but a `.mjs` config that spreads an imported object consumes it.
55
- Keys the consumer sets after the spread win.
56
- - The `.ts` bins are written in Effect, so `effect` is a peer dependency and `@effect/platform-bun`, which only the bins use, is a dependency.
57
- `@effect/platform-node-shared` is a direct dependency at the same exact version only to pin it: `@effect/platform-bun` asks for it with a `^` range, and a newer rc peers on a newer `effect` than consumers install, so all three move together.
58
- - Each runnable script ships a `checks-` bin entry, so consumer `package.json` scripts call the short name, which the package manager puts on `PATH` only there.
59
- A shell runs it through `bun run`, which never falls back to the registry the way `bunx` does.
60
- The `.ts` checks keep a `bun` shebang, which needs no build step and no `dist/` entry, unlike the oxlint plugins that node loads.
61
- - `checks-lint` runs each gate as its own bin in a child process rather than importing it, so a gate behaves the same called alone or through the entry point, and `lint-coverage.sh` stays a shell script.
62
- The gates run one at a time with their output passed straight through, so each report reads whole and in the table's order.
63
- - `checks-lint` determines applicable gates from tracked files instead of accepting a repository selection.
64
- A TypeScript gate starts running as soon as TypeScript source is tracked.
65
- - `checks-test` runs bun itself rather than reading a report some other run left: a skip taken only on CI is visible only in CI's own run, and an earlier run's report may be stale or narrowed.
66
- It reads the JUnit report bun writes to a temporary directory, since bun has no other per-test output meant for a program.
67
- - `checks-ci-wiring` runs inside `lint`, not in a workflow of its own: deleting the step that runs a check is the violation it catches, so the local `lint` is where it has to fail.
68
- - Workflows are parsed with `Bun.YAML`, which the `bun` shebang already provides, so the check adds no dependency.
69
- It reads `on` as a string key, not as the YAML 1.1 boolean.
70
- - `bun` counts as a built-in module.
71
- Nothing installed resolves it except `@types/bun`, which would otherwise make every runtime `bun` import look like a dev-only dependency.
72
- - The pull request merge commit GitHub builds is authored by `GitHub <noreply@github.com>`, which commit-identity refuses as an author.
73
- `checks-lint` ends a pull request's range at the event's head sha, so the merge commit is never in it.
74
- A `lint` that calls `checks-commit-identity HEAD` itself checks out `github.event.pull_request.head.sha` instead of the default merge ref.
75
- - `no-deep-imports` judges the import specifier, never the resolved file.
76
- The base honours `exports` maps, so a subpath the map publishes resolves and passes, one it omits fails to resolve and is reported, and a package without an `exports` map publishes every file.
77
- A bare import always passes whatever file its entry lives in.
78
- Setting your own `options.enhancedResolveOptions` replaces the base's, so restate `exportsFields` and `conditionNames` if you do.
79
- - dependency-cruiser `extends` merges same-name `forbidden` rules with the child's fields winning.
80
- That is the entry-point and layer recipe under Boundaries in [The dependency rules](configs/dependency-rules.md).
81
- - The templates in `dist/templates/` are rendered from `src/docs/doc-templates.ts`, the spec `checks-docs` reads.
82
- A template written by hand beside the check agrees with it only until someone edits one of them.
83
- - A page's Diátaxis mode comes from `kind` front matter on the page, whatever directory holds it.
84
- The repository makes the judgment beside the page, and the check holds it to that template.
85
- - `checks-docs` holds a doc file to its template when a change touches it, the way `checks-comment-gate` judges the comments a change adds.
86
- A repository adopts the templates as its files change, and an untouched file is listed as advisory rather than failing a change that never read it.
87
- - A task heading is verb first, and review holds it there rather than the check.
88
- No word list tells `Test layout` from `Test the layout`, and a check that passes the noun is worse than none.
89
- - The prose rules judge only the lines a change adds or edits, the way `checks-comment-gate` judges comments.
90
- Text nobody touched never turns a change red, a record keeps the words it was written in, and a repository needs no cleanup pass before the gate runs.
91
- - A living doc takes one sentence per line, so a changed line is a changed sentence.
92
- Under a hard wrap a one-word edit reflows a paragraph, and the gate would then demand fixes to sentences the edit never touched.
93
- - An agent file such as `AGENTS.md` takes the separator rules and no other prose rule.
94
- One sentence per line serves the people who review a doc's diffs, and an agent file keeps each entry to one line however many sentences it holds.
95
- - `src/docs/prose-matchers.ts` imports nothing, so the gate and a write-time hook run one matcher and refuse in the same words.
96
- A hook bundle ships without `node_modules`, so a matcher that needed Vale or a package could not refuse at write time.
97
- - Readability grades and words such as easy stay out of the prose rules.
98
- A score cannot fail a change without failing correct prose, and a suggestion nobody runs an editor for is never seen.
99
- - A path, link or command on a line the range leaves alone fails when the range broke it, as by deleting the file it names.
100
- A reference goes stale when the code it names moves far more often than when its own line is edited, so a gate on edited lines alone would miss the usual break.
101
- - A path under a top directory the repository lacks names a file in another repository, such as a consumer's, and no program tells that from a typo.
102
- A directory the range deletes still counts as this repository's, so a path under it reads as stale rather than foreign.
103
- - The command check passes over a page whose front matter sets `audience: consumers`.
104
- Such a page speaks to a consuming repository, whose scripts are not this one's.
105
- - `checks-vendor` strips an owner write bit that came back on a cached tree and keeps the tree, rather than refusing it or cloning it again.
106
- The GitHub Actions runner clears the read only mode of each item before it deletes `$RUNNER_TEMP`, and on a directory link that chmod lands on the shared tree's top directory.
107
- Refusing the tree would fail every later job on the runner until a person cleared it, and cloning it again would need the network after every such job.
108
- A write bit is not a write, so the run strips it and then holds the tree to the recorded commit as it holds any tree, and a write it finds there still fails the run.
109
- A group or other write bit still fails the run, since another user could have edited `.git/config` through it before `git status` reads it.
6
+ Most of the kit's shape follows from a limit in a tool it runs on, such as oxlint, bun, npm, Stryker or GitHub Actions.
7
+ Each section names the limit and what the obvious alternative would break.
8
+
9
+ ## Each tool reads its own config
10
+
11
+ A repository keeps each setting in the file its tool reads, such as `.oxlintrc.json`, `tsconfig.json` or `.jscpd.json`.
12
+ A developer then changes a rule where the tool reads it, and the tool's own docs describe that file.
13
+ The Effect paths sit in two of those files, `.oxlintrc.json` and `tsconfig.json`, and the installed consumer test checks each one on its own.
14
+
15
+ oxlint does not pass `plugins` down an `extends` chain.
16
+ A config in the chain that sets no `plugins` gets oxlint's default plugins, and the base's `categories` then turn on their rules across the tree.
17
+ So a consumer's `.oxlintrc.json` and each of its overrides list `plugins` again.
18
+ `rules`, `categories` and `jsPlugins` pass down the chain as expected.
19
+ `.gitignore` keeps oxlint out of `node_modules/`, because oxlint still walks the installed package when only `ignorePatterns` names it.
20
+
21
+ The base turns `data-shape/readonly-collection-param` on for every TypeScript file and `data-shape/schema-twin` on for production files only.
22
+ `typescript/prefer-readonly-parameter-types` would flag every object parameter for deep `readonly`, so the kit rule stays with the collections a function never mutates.
23
+ A test that declares its own schema as an oracle for a production type is reasonable, so the twin rule leaves `tests/` out.
24
+
25
+ bun has no bunfig `extends`, and it ignores an unknown top-level key without a warning.
26
+ So a repository copies the kit's `bunfig.toml`, and `checks-test-layout` compares the copy with the installed one key by key.
27
+ An empty `--path-ignore-patterns` flag overrides the copied `[test] pathIgnorePatterns`, which is how a quarantined test still runs on demand.
28
+
29
+ Stryker 10 does not resolve `extends` in a JSON config.
30
+ So the Stryker preset is a JavaScript module that a repository's `stryker.conf.mjs` spreads, and a key set after the spread wins.
31
+
32
+ The dependency-cruiser base parses with swc, because TypeScript 7, which is tsgo, has no compiler API that dependency-cruiser can use.
33
+ Without `@swc/core` installed, the cruise skips every `.ts` file without a warning, so the kit's own suite asserts that its TypeScript is cruised.
34
+ The base counts `bun` as a built-in module.
35
+ Only `@types/bun` resolves it, and that package is a dev dependency, so every runtime `bun` import would otherwise read as dev only.
36
+
37
+ ## The shared configs close gaps in the types
38
+
39
+ The shared configs refuse four places where a value's type says less than the value does.
40
+ Each sits in a file a consumer already extends or copies.
41
+
42
+ The base `oxlintrc.json` turns on the five `typescript/no-unsafe-*` rules in an override for `.ts` and `.tsx` files outside `tests/`, so every consumer gets them through `extends`.
43
+ `typescript/no-explicit-any` and `strict` already refuse an `any` someone writes, so the `any` left is one nobody wrote.
44
+ `Array.isArray` narrows an `unknown` to `any[]`, `JSON.parse` returns `any`, `Object.entries` lists `any` values from an `object`, and a defaulted parameter in a generator passed to `Effect.fnUntraced` is typed `any`.
45
+ Tests stay out because bun:test types its asymmetric matchers, such as `expect.arrayContaining`, as returning `any`.
46
+ Those matchers made 22 of the 34 findings in the tests of the kit and its consumers.
47
+
48
+ `tsconfig.effect.json` sets `exactOptionalPropertyTypes`, so every repository that extends it gets the option in the same release.
49
+ `Schema.optionalKey` means the key is missing, never `undefined`.
50
+ Without the option, a type derived from the schema accepts an `undefined` the schema rejects when it decodes.
51
+ tsc keeps no baseline, and the only escape for one site is a `@ts-expect-error`, which `typescript/ban-ts-comment` refuses.
52
+ So each error the option raises is fixed where it lands.
53
+
54
+ The language service preset sets `processEnv` and `processEnvInEffect` at error, so code in a repository's Effect paths reads the environment through `Config`.
55
+ `Config` decodes a variable and fails in the error channel when it is missing, where `process.env` hands back a `string | undefined` that each caller checks by hand.
56
+ The language service keeps no baseline either, so each read the two diagnostics find moves to `Config` when a repository takes the preset.
57
+
58
+ `tsconfig.effect.json` loads the `is-array` and `json-parse` rules of `@total-typescript/ts-reset`, so tsc refuses code that relied on the `any` that `Array.isArray` and `JSON.parse` hand out.
59
+ The `no-unsafe-*` rules flag each place that `any` is used, and these two rules close it where it starts, in `tests/` as well.
60
+ The other eight ts-reset rules stay out, because `set-has` breaks correct code in the kit and a consumer, and the rest find nothing.
61
+ A global declaration reaches a program only when a root file, a `types` entry or an import names it, and a repository's own `files`, `include` or `types` replaces the fragment's list of the same name.
62
+ So the fragment lists the rules in `files`, and in `include` beside every file under the repository's `tsconfig.json`, and one of the two lists survives unless a repository sets both.
63
+ `types` cannot carry the rules, since every consumer sets its own `types` to reach `bun`.
64
+ `checks-lint-coverage` fails the repository that sets both lists, because nothing in tsc would say the rules were gone.
65
+
66
+ ## The source is sorted by what it judges
67
+
68
+ The source sits under `src/<vector>/`, one directory for each thing the kit judges a repository on: complexity, quality, testing, docs, delivery and dependencies.
69
+ `src/core/` holds what every vector runs on.
70
+ `scripts/` holds only the kit's own build, and nothing in it ships.
71
+ Sorting files by what loads them would put both oxlint plugins at the root and every bin in one flat directory.
72
+ Nothing would then say which gate a helper serves.
73
+ A mutation runner's default scope covers `src/`, so the kit's own Stryker run mutates its source with no `mutate` list.
74
+ The testing directory is named `testing` rather than `tests`, because a `src/tests/` beside the root `tests/` would read as a second suite.
75
+
76
+ Four `exports` keys name a path the file does not sit at, because consumers resolve them by that name.
77
+ `@avi2dg/checks/scripts/test-skips.ts`, `@avi2dg/checks/scripts/comment-matchers.ts` and `@avi2dg/checks/scripts/prose-matchers.ts` point at their files under `src/`.
78
+ `@avi2dg/checks/templates/*` points at `dist/templates/`.
79
+ A path read without the resolver, such as `node_modules/@avi2dg/checks/scripts/test-skips.ts`, does not exist.
80
+
81
+ ## The package ships built plugins and runnable bins
82
+
83
+ `files` in `package.json` lists what an install gets.
84
+ `tests/`, `AGENTS.md` and the TypeScript source of the oxlint plugins never reach an install.
85
+ npm adds `package.json`, `README.md` and `LICENSE` whatever `files` says.
86
+ `bun pm pack` builds the same tarball the registry serves, and the consumer e2e test installs that tarball.
87
+
88
+ Each oxlint plugin ships compiled under `dist/`, because Node refuses to strip types from a `.ts` file under `node_modules`.
89
+ `dist/` is committed, with the doc templates in `dist/templates/`, and so is `CHANGELOG.md`, which the same build writes.
90
+ No `prepack` or `prepublishOnly` script rebuilds them, so a publish ships the committed files.
91
+ CI runs `git diff --exit-code` over the whole tree after `bun run build`.
92
+ A test that compares a generated file with its source cannot do this job, because it would read the copy the build just rewrote.
93
+
94
+ Each runnable script ships as a `checks-` bin, so a consumer's `package.json` script calls it by the short name.
95
+ The package manager puts a bin on `PATH` only inside a package script.
96
+ A shell reaches it through `bun run`, which never falls back to the registry the way `bunx` does.
97
+ The `.ts` bins keep a `bun` shebang and need no build step, unlike the oxlint plugins that Node loads.
98
+
99
+ The bins are written in Effect.
100
+ So `effect` is a peer dependency, and `@effect/platform-bun`, which only the bins use, is a dependency.
101
+ `@effect/platform-node-shared` is a direct dependency only to pin its version.
102
+ `@effect/platform-bun` asks for it with a `^` range, and a newer release candidate of it peers on a newer `effect` than consumers install.
103
+ So the three packages move together at one exact version.
104
+
105
+ ## checks-lint runs each gate as its own bin
106
+
107
+ `checks-lint` runs each gate in a child process rather than importing it.
108
+ A gate then behaves the same alone or through `checks-lint`, and `lint-coverage.sh` can stay a shell script.
109
+ The gates run one at a time and pass their output straight through, so each report reads whole and in the order of the gate table.
110
+ `checks-lint` picks the gates that apply from the tracked files, and a repository cannot select gates.
111
+ A TypeScript gate runs as soon as the repository tracks TypeScript source.
112
+
113
+ GitHub authors the pull request merge commit it builds as `GitHub <noreply@github.com>`, and `checks-commit-identity` refuses that author.
114
+ So `checks-lint` ends a pull request's range at the event's head commit, and the merge commit is never in it.
115
+ A `lint` that calls `checks-commit-identity HEAD` directly checks out `github.event.pull_request.head.sha` rather than the default merge ref.
116
+
117
+ ## Workflows say how CI runs
118
+
119
+ A repository's workflow YAML says how CI runs, and the kit says which commands must run.
120
+ `checks-ci-wiring` holds a repository to title lint on every pull request and to the `bun run` commands its own `package.json` defines, so a repository with no `build` script is not asked to run one.
121
+ The target branch comes from git's `refs/remotes/origin/HEAD`, or in CI from the pull request base or the default branch in GitHub's event.
122
+ It never comes from the workflow files, and `checks-lint` starts its local range from the same branch.
123
+ `checks-ci-wiring` runs inside `lint`, not in a workflow of its own.
124
+ The violation it catches is a deleted workflow step, so it has to fail in the local `lint`.
125
+ It parses workflows with `Bun.YAML`, which the `bun` shebang already provides, so it adds no dependency.
126
+ It reads `on` as a string key, not as the YAML 1.1 boolean.
127
+
128
+ ## Size and repetition only fail on growth
129
+
130
+ The size limits are oxlint's own rules at `error`, not a script of the kit's.
131
+ A script of the kit's own would restate how oxlint reads its config and compares sites, and each restatement adds cases oxlint itself does not have.
132
+ A repository records its existing violations with `oxlint --suppress-all`, and `checks-suppressions-ratchet` refuses any count that rises.
133
+ The cost is that a file or function already over its limit can grow without adding a site the count sees.
134
+ Each repository works its counts down to zero.
135
+
136
+ `checks-repetition` runs jscpd at both ends of the range with the head's `.jscpd.json`, so the same `path` and `ignore` globs pick the files at both ends.
137
+ It compares each file's count of repeated lines, and does not use jscpd's `--baseline-from-ref`.
138
+ That flag reports a repeated block as new once its text changes, so a change that shortens an existing repeated block would fail.
139
+
140
+ ## checks-test runs the suite itself
141
+
142
+ `checks-test` runs bun itself rather than reading a report that another run left.
143
+ A skip taken only on CI shows only in CI's own run, and an earlier run's report may be stale or narrowed.
144
+ It reads the JUnit report bun writes to a temporary directory, because bun has no other per-test output meant for a program.
145
+
146
+ ## Quarantine has one limit
147
+
148
+ `checks-quarantine-clock` holds every quarantined test to one limit of 30 days.
149
+ GitLab quarantines a flaky test for 3 days on its fast path, and for at most 3 months on its long path.
150
+ It then opens a merge request that deletes the test.
151
+ The kit's one limit falls between GitLab's two, so a flaky test gets a month to be fixed, and no path keeps it out of the run for 3 months.
152
+
153
+ ## The changelog comes from the commits
154
+
155
+ `CHANGELOG.md` is generated from the commits, so the release commit carries it and the tarball ships it.
156
+ The commit that bumps `version` in `package.json` closes a release, and the `v*` tag goes on that commit.
157
+ Commits merged after the bump wait for the next release.
158
+ Releases come from the version bumps across all of `HEAD`'s ancestry, not from tags.
159
+ So a checkout without tags, a fork, and a branch that merged `main` in all write the same file when no release was reverted.
160
+ A section keeps the date it was written, because the squash merge that lands the release commit may fall on another day.
161
+ Entries come from commit subjects, which are the squash-merged pull request titles that commitlint holds to the conventional format.
162
+ A commit body holds the branch's own messages, and nothing lints it, so no entry comes from a body.
163
+ The changelog arrives in the release pull request, and no workflow pushes to the repository.
164
+ The only write access the release path holds is the `github-release` job's `contents: write`, which creates or updates the GitHub release from the tag's `CHANGELOG.md` section.
165
+
166
+ ## The docs gate judges what a change touches
167
+
168
+ The templates in `dist/templates/` are rendered from `src/docs/doc-templates.ts`, which is the spec `checks-docs` reads.
169
+ A template written by hand beside the check would agree with it only until someone edits one of them.
170
+ A page's Diátaxis mode comes from `kind` front matter on the page, whatever directory holds it.
171
+ The repository judges the mode beside the page, and the check holds the page to that mode's template.
172
+
173
+ `checks-docs` holds a doc file to its template only when a change touches it, the way `checks-comment-gate` judges only the comments a change adds.
174
+ A repository adopts the templates as its files change, and an untouched file is listed as advisory.
175
+ The prose rules judge only the lines a change adds or edits.
176
+ A report about the past is refused the way a promise about the future is, because history on a living page reads as current fact.
177
+ Text nobody touched never breaks the templates or the prose rules, and a record keeps the words it was written in.
178
+ A repository needs no cleanup pass before the gate runs.
179
+ Review, not the check, keeps a task heading verb first.
180
+ No word list tells `Test layout` from `Test the layout`, and a check that passes the noun would be worse than none.
181
+
182
+ A living doc takes one sentence per line, so a changed line is a changed sentence.
183
+ Under a hard wrap, a one-word edit reflows a paragraph, and the gate would demand fixes to sentences the edit never touched.
184
+ An agent file such as `AGENTS.md` takes the separator rules and the rule against a report about the past, and no other prose rule.
185
+ An agent reads stale history as literally as a person, so the past rule judges agent files too.
186
+ One sentence per line serves the people who review a doc's diffs.
187
+ An agent file keeps each entry on one line, however many sentences it holds.
188
+ The reference check reads agent files too, because a path they name goes stale the same way.
189
+ Readability grades and words such as easy stay out of the prose rules.
190
+ A score cannot fail a change without failing correct prose, and a suggestion that only an editor shows is never seen.
191
+
192
+ `src/docs/prose-matchers.ts` imports nothing, so the gate and a write-time hook run one matcher and refuse in the same words.
193
+ A hook bundle ships without `node_modules`, so a matcher that needed Vale or another package could not refuse at write time.
194
+
195
+ A path, link or command on a line the range leaves alone still fails when the range broke it, for example by deleting the file it names.
196
+ A reference goes stale far more often because the code it names moves than because its own line is edited.
197
+ So a gate on edited lines alone would miss the usual break.
198
+ A name that is not a path goes stale the same way, and the reference check never reads it.
199
+ So the range that removes such a name from every file outside the docs fails on each line that still carries it.
200
+ A path under a top directory the repository lacks names a file in another repository, such as a consumer's.
201
+ No program can tell such a path from a typo.
202
+ A directory the range deletes still counts as this repository's, so a path under it reads as stale rather than foreign.
203
+ The command check passes over a page whose front matter sets `audience: consumers`, because such a page names a consuming repository's scripts.
204
+
205
+ ## checks-vendor keeps a tree whose write bit came back
206
+
207
+ The GitHub Actions runner clears the read only mode of each item before it deletes `$RUNNER_TEMP`.
208
+ On a directory link, that change lands on the shared tree's top directory.
209
+ Refusing the tree would fail every later job on the runner until a person cleared it.
210
+ Cloning the tree again would need the network after every such job.
211
+ A write bit is not a write.
212
+ So `checks-vendor` strips an owner write bit and checks the tree against its recorded commit like any other tree.
213
+ A write it finds there still fails the run.
214
+ A group or other write bit fails the run, because another user could have edited `.git/config` through it before `git status` reads it.
110
215
 
111
216
  ## Related topics
112
217
 
113
218
  - [checks](../README.md)
219
+ - [The dependency rules](configs/dependency-rules.md)
@@ -22,9 +22,12 @@ A version with no conventional commit worth listing writes no section.
22
22
 
23
23
  ## What it reads
24
24
 
25
- It reads `package.json`, `CHANGELOG.md` and the git history from the repository root.
26
- It finds releases in the version bumps of `package.json` across all of `HEAD` ancestry, so a checkout without tags writes the same file.
25
+ It reads `package.json`, `CHANGELOG.md`, the `v*` tags and the git history from the repository root.
26
+ It finds releases in the version bumps of `package.json` across all of `HEAD` ancestry, so a checkout without tags writes the same file when no release was reverted.
27
27
  It reads a commit whose `package.json` is missing or has no version as unversioned, so the commit that adds the version is the first release and a repository that adopts a version late still writes its changelog.
28
+ A bump to a version no newer than the release before it is a revert.
29
+ A revert cancels every release above the version it returns to, except a release a `v` tag marks as published.
30
+ A version older than the newest in the committed `CHANGELOG.md` and absent from it was never published, so its commits go into the next release.
28
31
  It refuses a shallow checkout, since the releases reach back past its history.
29
32
  It refuses a `package.json` with no repository address, since each entry links its pull request under it.
30
33
  It refuses a repository address that is no `https` address once `git+`, a trailing slash and `.git` are dropped, since a pull request link needs one.
@@ -51,10 +54,10 @@ Run it through the build, as the release workflow in [checks-release-notes](chec
51
54
  checks-changelog: wrote 2 release(s) to CHANGELOG.md
52
55
  ```
53
56
 
54
- ## Opting out
57
+ ## When it runs
55
58
 
56
- Nothing runs it but the build of a repository that keeps a changelog.
57
- A repository with no versioned releases leaves it out.
59
+ Only the build of a repository that keeps a changelog runs it.
60
+ A repository with no versioned releases does not need it.
58
61
 
59
62
  ## Related topics
60
63
 
@@ -49,9 +49,9 @@ ci-wiring: 1 of 6 gate(s) do not run on pull requests to main:
49
49
  no run step invokes it
50
50
  ```
51
51
 
52
- ## Opting out
52
+ ## When it runs
53
53
 
54
- Every repository runs this gate through `checks-lint`.
54
+ `checks-lint` runs it in every repository.
55
55
 
56
56
  ## Related topics
57
57
 
@@ -4,7 +4,7 @@ audience: consumers
4
4
  ---
5
5
  # checks-comment-gate
6
6
 
7
- `checks-comment-gate` is the gate that refuses a banned comment on a line a change adds, and a reader looks it up to learn which comments it refuses.
7
+ `checks-comment-gate` is the gate that refuses a banned comment on a line a change adds.
8
8
 
9
9
  ## What it checks
10
10
 
@@ -53,10 +53,9 @@ comment-gate: 2 violation(s):
53
53
  src/a.ts:2 carries the machine-read directive `eslint-disable-next-line`. Fix what the tool is reporting, or stop running the tool on this file
54
54
  ```
55
55
 
56
- ## Opting out
56
+ ## When it runs
57
57
 
58
- It applies to every repository, so no selection leaves it out.
59
- `checks-lint` runs it over each pull request's range, as [checks-lint](checks-lint.md) says.
58
+ `checks-lint` runs it over each pull request's range in every repository, as [checks-lint](checks-lint.md) says.
60
59
 
61
60
  ## Related topics
62
61
 
@@ -48,10 +48,10 @@ commit-identity: 1 of 1 commit(s) in HEAD carry a foreign identity:
48
48
  allowed: avi2d <avi2dg@gmail.com>
49
49
  ```
50
50
 
51
- ## Opting out
51
+ ## When it runs
52
52
 
53
- This gate runs on every repository through `checks-lint`.
54
- The repository adds owners through `package.json` rather than omitting the gate.
53
+ `checks-lint` runs it over each pull request's range in every repository.
54
+ A repository allows another author by adding them to `author` or `contributors` in `package.json`.
55
55
 
56
56
  ## Related topics
57
57