{"id":50686907,"url":"https://github.com/clerk/break-check","last_synced_at":"2026-06-09T00:00:35.272Z","repository":{"id":363417602,"uuid":"1140987013","full_name":"clerk/break-check","owner":"clerk","description":"CLI tool to detect API breaking changes in TypeScript packages","archived":false,"fork":false,"pushed_at":"2026-06-08T19:53:26.000Z","size":389,"stargazers_count":1,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-08T21:23:59.314Z","etag":null,"topics":["api","api-extractor","breaking-changes","cli","developer-tools","github-action","semver","typescript"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/clerk.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"docs/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"docs/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"docs/SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-01-24T03:25:58.000Z","updated_at":"2026-06-08T19:15:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/clerk/break-check","commit_stats":null,"previous_names":["clerk/break-check"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/clerk/break-check","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clerk%2Fbreak-check","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clerk%2Fbreak-check/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clerk%2Fbreak-check/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clerk%2Fbreak-check/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/clerk","download_url":"https://codeload.github.com/clerk/break-check/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clerk%2Fbreak-check/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34085321,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-08T02:00:07.615Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","api-extractor","breaking-changes","cli","developer-tools","github-action","semver","typescript"],"created_at":"2026-06-09T00:00:19.400Z","updated_at":"2026-06-09T00:00:35.266Z","avatar_url":"https://github.com/clerk.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @clerk/break-check\n\nCLI tool for detecting TypeScript API changes in packages that publish\ndeclaration files.\n\nBreak Check uses Microsoft API Extractor to snapshot public `.d.ts` surfaces, then\ncompares a current snapshot against a baseline snapshot. It is designed for PR\nchecks where a package should fail CI when a breaking API change is not matched\nby the expected version bump.\n\n## Requirements\n\n- Node.js 22.13 or newer\n- Packages must be built before snapshotting so their declaration files exist\n- Each configured package must expose a declaration entrypoint through\n  `types`, `typings`, root `exports[\".\"].types`, `main` plus matching `.d.ts`,\n  `dist/index.d.ts`, or root `index.d.ts`\n\n## Installation\n\n```bash\nnpm install -D @clerk/break-check\npnpm add -D @clerk/break-check\nyarn add -D @clerk/break-check\n```\n\n## Quick Start\n\nCreate a config:\n\n```bash\nnpx break-check init\n```\n\nEdit `break-check.config.json`:\n\n```json\n{\n  \"packages\": [\"packages/my-lib\", \"packages/my-other-lib\"],\n  \"snapshotDir\": \".api-snapshots\",\n  \"mainBranch\": \"main\",\n  \"checkVersionBump\": true,\n  \"outputFormat\": \"markdown\"\n}\n```\n\nGenerate a baseline from your main branch:\n\n```bash\ngit switch main\npnpm build\nnpx break-check snapshot --output .api-snapshots-baseline\n```\n\nCompare the current branch against that baseline:\n\n```bash\ngit switch -\npnpm build\nnpx break-check detect --baseline .api-snapshots-baseline --fail-on-breaking\n```\n\nRelative package, snapshot, and baseline paths are resolved from the directory\nthat contains `break-check.config.json`.\n\n## CLI Commands\n\n### `break-check init`\n\nCreate a default `break-check.config.json` configuration file.\n\n```bash\nbreak-check init [options]\n\nOptions:\n  -o, --output \u003cpath\u003e  Output path (default: \"break-check.config.json\")\n  -f, --force          Overwrite existing config file\n```\n\n### `break-check snapshot`\n\nGenerate API snapshots for all configured packages.\n\n```bash\nbreak-check snapshot [options]\n\nOptions:\n  -c, --config \u003cpath\u003e  Config file path (default: \"break-check.config.json\")\n  -o, --output \u003cpath\u003e  Output directory (overrides config)\n  -v, --verbose        Show verbose output\n```\n\n`snapshot` exits non-zero when a configured package cannot be analyzed.\n\n### `break-check detect`\n\nDetect API changes between baseline and current snapshots.\n\n```bash\nbreak-check detect [options]\n\nOptions:\n  -c, --config \u003cpath\u003e     Config file path (default: \"break-check.config.json\")\n  -b, --baseline \u003cpath\u003e   Baseline snapshots directory (required)\n  -o, --output \u003cpath\u003e     Output report path\n  --format \u003cformat\u003e       Output format: markdown|json\n  --fail-on-breaking      Exit with code 1 if breaking changes found\n  --fail-on-skipped       Exit with code 1 if any subpath could not be snapshotted\n  --no-ai                 Disable the AI reviewer even if BREAK_CHECK_ANTHROPIC_API_KEY is set\n  --ai-model \u003cmodel\u003e      Override the AI model (e.g. claude-opus-4-7)\n  --ai-apply-downgrades   Apply the AI's breaking-\u003enon-breaking downgrades (default: record as suggestions)\n  --ai-scan               Run the missed-breaks audit (both surfaces; reviews additions-only diffs)\n  -v, --verbose           Show verbose output\n```\n\nBy default a subpath that API Extractor can't process (ambient-global\naugmentations, a `.d.ts` outside `dist/`, etc.) is skipped with a warning and\nthe run continues; the report lists what was omitted. Pass `--fail-on-skipped`\n(available on both `snapshot` and `detect`) to turn those skips into a non-zero\nexit, which is the safer default when producing a committed baseline.\n\nWhen `--format json` writes to stdout, progress and summary logs are written to\nstderr so stdout remains parseable JSON.\n\n## Configuration\n\n| Option                 | Type     | Default          | Description                                                            |\n| ---------------------- | -------- | ---------------- | ---------------------------------------------------------------------- |\n| `packages`             | string[] | required         | Package paths to analyze                                               |\n| `snapshotDir`          | string   | `.api-snapshots` | Snapshot output directory                                              |\n| `mainBranch`           | string   | `main`           | Base branch name for repo-specific workflows                           |\n| `checkVersionBump`     | boolean  | `true`           | Mark insufficient version bumps in reports                             |\n| `outputFormat`         | string   | `markdown`       | Default report format                                                  |\n| `ignoreSubpaths`       | string[] | `[]`             | Subpath exports to skip (exact, or glob with `*`/`**`)                 |\n| `ignoreHashedChunks`   | boolean  | `true`           | Drop content-hashed bundler chunks matched by `./*`                    |\n| `acknowledgedChanges`  | string[] | `[]`             | Breaking changes you've verified safe (downgraded + tagged)            |\n| `resolvableSpecifiers` | string[] | `[]`             | Module-specifier globs to exempt from the unresolvable-reference guard |\n| `ai`                   | object   | unset            | AI reviewer options (see below)                                        |\n\nA `\"./*\"` export that points into a bundler output dir will glob in the shared\nchunks emitted by rolldown/tsdown/esbuild/rollup (`index-Dq-_K2VH.mjs`,\n`url-CcPzUbGM.mjs`, ...). Those chunks are not public API, and their content\nhash changes every build, so left alone they show up as a removed subpath plus\nan added subpath on every meaningful change. `ignoreHashedChunks` (on by\ndefault) drops wildcard matches whose basename ends in a `-\u003c8-char hash\u003e`\nsuffix. For anything the heuristic misses, `ignoreSubpaths` accepts globs\n(`./internal-*`, `./chunk-*`). Set `ignoreHashedChunks: false` to treat every\nwildcard match as a real subpath.\n\n`acknowledgedChanges` is the escape hatch for a change the differ flags as\nbreaking that you have verified is safe. Each entry is the change's qualified\nname (`OAuthConsentInfo`, `User.email`), optionally prefixed with the package\n(`@clerk/shared#OAuthConsentInfo`), and the name may use `*` globs\n(`Clerk.__internal_*`). A matched breaking change is downgraded to non-breaking,\ntagged `acknowledged` in the report, and dropped from the recommended version\nbump. Unlike an AI downgrade this is unconditional: it always applies and does\nnot need `--ai-apply-downgrades`. Use it sparingly, and for one symbol at a time.\n\nWhen a public signature starts referencing a dependency subpath consumers\ncannot resolve, the change is breaking no matter how the underlying type looks.\nThis happens when a bundler moves a re-exported type into an internal chunk that\nthe dependency blocks in its `exports` (e.g. `@clerk/shared` declares\n`\"./_chunks/*\": null`), so a `.d.ts` ends up emitting\n`import(\"@clerk/shared/_chunks/index-DcO1-lAR\").Jwt`. The specifier does not\nresolve downstream: under `nodenext` it errors (`TS2307`), and with the common\n`skipLibCheck: true` it silently degrades to `any`. break-check detects this by\nextracting the inline `import(\"...\")` specifiers a new signature introduces and\nresolving each against the dependency's `package.json` `exports` (falling back\nto a `/_chunks/` and content-hash heuristic when the dependency can't be located\non disk). A flagged change is kept breaking and the AI **cannot** relax it, even\nunder `--ai-apply-downgrades`. If a referenced subpath is in fact a legitimate\npublic entry point the heuristic mis-flags, exempt it with `resolvableSpecifiers`\n(specifier globs, e.g. `@scope/pkg/internal/*`); an explicit `acknowledgedChanges`\nentry also clears it. The guard applies to changed and removed exports, including\nescalating an otherwise non-breaking modification (say a new optional parameter)\nwhen its type is provably export-blocked. A brand-new export is still reported as\nan addition, not a breaking change, even when its type is unresolvable.\n\n### AI reviewer config\n\n| Field               | Type    | Default             | Description                                                                               |\n| ------------------- | ------- | ------------------- | ----------------------------------------------------------------------------------------- |\n| `enabled`           | boolean | unset               | Force-enable or force-disable. Unset: runs iff `BREAK_CHECK_ANTHROPIC_API_KEY` set        |\n| `model`             | string  | `claude-sonnet-4-6` | Anthropic model identifier                                                                |\n| `maxChangesPerCall` | number  | `80`                | Maximum rule-based changes batched into a single AI call                                  |\n| `applyDowngrades`   | boolean | `false`             | Apply the AI's breaking-\u003enon-breaking downgrades instead of recording them as suggestions |\n| `scanForMissed`     | boolean | `false`             | Run the missed-breaks audit (both surfaces; also reviews additions-only diffs)            |\n\n## AI Review\n\nBreak Check can optionally route the rule-based diff through Claude for a second\nopinion. By default the reviewer is conservative: it confirms the rule-based\nverdicts, may **escalate** a change the rule pass under-classified (non-breaking\nto breaking), and adds a one-sentence migration hint per breaking change. What\nit will **not** do by default is **relax** a flagged break. Relaxing a breaking\nverdict to non-breaking, walking back the rule pass's deliberately pessimistic\n\"any type change is breaking\" stance, is the AI's main value but also the only\noperation that can clear a real break, so by default it is recorded as a\nsuggestion in the report (the change stays breaking) and applied only when you\npass `--ai-apply-downgrades`. The default path therefore cannot turn a flagged\nbreak into a non-break.\n\nThe downgrade decision is the flow worth getting right, so the verdict call\nsends a **focused context**: for each change, only the definitions of the types\nits signature references (resolved transitively through API Extractor's\ncanonical references), with a referenced type's baseline definition included\nwhere it changed so equivalence can be judged old-vs-new. On a large package\nthat is a handful of types instead of the whole surface. The previous signature\nof each change itself rides along inline in its diff snippet. The context also\nlists each changed type's **usage sites**, the signatures that reference it,\ngathered across the package's subpath surfaces (the changed type and the\nfunction that returns it often live in different subpath rollups), so the model\ncan tell a consumer-constructed input type from a read-only output type: adding\na required field to a type consumers only read (the resolved value of a\n`Promise\u003cT\u003e` return, a hook result field) is non-breaking, while adding it to a\ntype they construct is not. If a referenced type can't be resolved, or a type's\nusage is invisible (used only by another package, or unresolved), the model is\ntold to keep \"breaking\", so a thin context costs a missed downgrade (noise),\nnever a shipped break.\n\n`--ai-scan` (or `BREAK_CHECK_AI_SCAN=1`, `ai.scanForMissed: true`) adds the\nopposite, paranoid pass: it ships both the baseline and current surfaces (the\nmodel has to diff old against new to find a break the rule pass missed entirely)\nand reviews additions-only diffs too. It is independent of\n`--ai-apply-downgrades`; combine them for the most thorough run.\n\nEnable it by exporting an API key:\n\n```bash\nexport BREAK_CHECK_ANTHROPIC_API_KEY=sk-ant-...\nnpx break-check detect --baseline .api-snapshots-baseline --fail-on-breaking\n```\n\nThe reviewer is fail-soft: if the API is unreachable, the key is missing while\n`ai.enabled` is unset, or the model returns a malformed response, Break Check falls\nback to the rule-based result and exits the same way it would without AI. A call\nthat fails on a large surface is retried in smaller batches first; anything still\nunreviewed is reported as partial coverage (the \"reviewed by\" stamp is marked\n`(partial)` and a callout lists the affected subpaths) rather than being silently\ntrusted, so those changes keep their pessimistic rule-based verdict.\n\n### Picking a model\n\n- **`claude-sonnet-4-6`** (default): the right balance for CI. Reliable\n  tool-use output, cheap enough to run on every PR.\n- **`claude-opus-4-7`**: better at the open-ended \"what did the rule-based\n  pass miss?\" scan on large or variance-heavy API surfaces. Worth opting into\n  for high-stakes releases.\n\nOverride per-invocation with `--ai-model claude-opus-4-7`, set\n`BREAK_CHECK_AI_MODEL` in the environment (handy for CI, where you might want\nOpus on release workflows and Sonnet on PRs without editing config), or\nset it permanently in `break-check.config.json`:\n\n```json\n{\n  \"ai\": {\n    \"model\": \"claude-opus-4-7\",\n    \"applyDowngrades\": false,\n    \"scanForMissed\": false\n  }\n}\n```\n\nPriority is `--ai-model` \u003e `BREAK_CHECK_AI_MODEL` \u003e `ai.model` in config \u003e\n`claude-sonnet-4-6`.\n\n### Environment variables\n\n| Variable                          | Effect                                                                                                |\n| --------------------------------- | ----------------------------------------------------------------------------------------------------- |\n| `BREAK_CHECK_ANTHROPIC_API_KEY`   | Anthropic API key. Required to enable the reviewer (unless `ai.enabled` is `false`).                  |\n| `BREAK_CHECK_AI_MODEL`            | Override the model. Equivalent to `--ai-model`; loses to the flag, wins over config.                  |\n| `BREAK_CHECK_AI_APPLY_DOWNGRADES` | Set to `1` (or any truthy value) to apply the AI's downgrades. Equivalent to `--ai-apply-downgrades`. |\n| `BREAK_CHECK_AI_SCAN`             | Set to `1` (or any truthy value) to run the missed-breaks audit. Equivalent to `--ai-scan`.           |\n\n## GitHub Actions Integration\n\nUse the bundled composite Action. It snapshots the base ref and the PR head in\nseparate git worktrees, diffs them with `break-check detect`, and posts (or\nupdates) a single PR comment. Pinning both sides to commit SHAs (rather than the\n`refs/pull/N/merge` ref `actions/checkout` resolves by default) keeps the report\nscoped to the PR's own changes even after the base branch advances.\n\n```yaml\nname: API Check\n\non:\n  pull_request:\n\npermissions:\n  contents: read\n  pull-requests: write\n\njobs:\n  api-check:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - uses: pnpm/action-setup@v4\n\n      - uses: actions/setup-node@v4\n        with:\n          node-version: \"24\"\n          cache: \"pnpm\"\n\n      - uses: clerk/break-check@v1\n        with:\n          fail-on-breaking: true\n```\n\n### Action inputs\n\n| Input                    | Default                                        | Description                                                                                                         |\n| ------------------------ | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |\n| `config-path`            | `break-check.config.json`                      | Path to the config file, relative to the repo root.                                                                 |\n| `base-ref`               | PR base SHA                                    | Git ref or SHA to snapshot as the baseline.                                                                         |\n| `head-ref`               | PR head SHA                                    | Git ref or SHA to build as the \"current\" side. Pins the diff to the PR head, not the merge ref.                     |\n| `setup-command`          | `pnpm install --frozen-lockfile \u0026\u0026 pnpm build` | Shell command run inside both the base checkout and the current checkout to produce `.d.ts` files.                  |\n| `break-check-version`    | `latest`                                       | npm version of `@clerk/break-check` to fetch with `npx`.                                                            |\n| `baseline-artifact-name` | unset                                          | Name of a snapshot artifact uploaded from a push-to-`base-ref` workflow. See [Larger monorepos](#larger-monorepos). |\n| `baseline-max-age`       | unset                                          | Maximum age (hours) for a downloaded baseline artifact before falling back to a worktree rebuild.                   |\n| `comment`                | `true`                                         | Post or update a PR comment with the report.                                                                        |\n| `fail-on-breaking`       | `false`                                        | Fail the workflow when breaking changes are detected.                                                               |\n| `policy-mode`            | `false`                                        | Enforce the config from the base ref so a PR cannot suppress its own break by editing its config.                   |\n| `github-token`           | `${{ github.token }}`                          | Token used to read/write PR comments and (when `baseline-artifact-name` is set) fetch the artifact.                 |\n\n### Action outputs\n\n| Output                 | Description                                                    |\n| ---------------------- | -------------------------------------------------------------- |\n| `has-breaking-changes` | `\"true\"` if Break Check detected at least one breaking change. |\n| `report-path`          | Filesystem path to the generated markdown report.              |\n\n### When the base ref doesn't yet have a config\n\nOn the first PR that introduces Break Check, the base ref won't contain a\n`break-check.config.json` and the snapshot would otherwise fail. The Action copies\nthe PR's config into the base checkout in that case so the first run still\nproduces a usable baseline. Subsequent runs always use the base ref's own\nconfig.\n\n### Required-gate hardening\n\nThe `break-check.config.json` lives in the repo, so a pull request can edit its\nown config the same way it edits any other file: drop the changed package from\n`packages`, add an `acknowledgedChanges` entry, or widen `ignoreSubpaths` /\n`resolvableSpecifiers`. Any of those greens the PR's own breaking change. That is\nacceptable when the config is itself reviewed (for example under CODEOWNERS), but\nif you rely on this Action as a required merge gate, set `policy-mode: true`:\n\n```yaml\n- uses: clerk/break-check@v1\n  with:\n    fail-on-breaking: true\n    policy-mode: true\n```\n\nIn policy mode the Action reads `break-check.config.json` from the base ref\nbefore running the diff, so a config change takes effect only once it has landed\non the base branch and passed that branch's review. A base ref that has no config\nyet (the first PR introducing Break Check) falls back to the PR's config.\n\n### Larger monorepos\n\nRebuilding `main` on every PR is too slow for large monorepos. Instead, snapshot\n`main` once per push and have PR checks download the artifact.\n\n**Producer** (`.github/workflows/break-check-baseline.yml`):\n\n```yaml\nname: Break Check baseline\n\non:\n  push:\n    branches: [main]\n\npermissions:\n  contents: read\n\njobs:\n  snapshot:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: pnpm/action-setup@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: \"24\"\n          cache: \"pnpm\"\n      - run: pnpm install --frozen-lockfile \u0026\u0026 pnpm build\n      - run: npx @clerk/break-check snapshot --output .api-baseline-main\n      - uses: actions/upload-artifact@v4\n        with:\n          name: break-check-baseline-main\n          path: .api-baseline-main\n          retention-days: 30\n          if-no-files-found: error\n          # .api-baseline-main is dot-prefixed; upload-artifact@v4 treats it as\n          # a hidden directory and skips it without this flag.\n          include-hidden-files: true\n```\n\n**Consumer** (PR check): point the Action at the artifact name.\n\n```yaml\n- uses: clerk/break-check@v1\n  with:\n    baseline-artifact-name: break-check-baseline-main\n    fail-on-breaking: true\n```\n\nThe Action looks up the most recent non-expired artifact with that name on\n`base-ref`, downloads it, and uses it directly. If no artifact is found, it's\nexpired, or it's older than `baseline-max-age` (when set), the Action falls\nback to the worktree rebuild. The fallback also covers the first PR after\nadding break-check, before the producer has run.\n\nWorkflow runs that download the artifact need `actions:read` on\n`github-token`; the default `github.token` has it. Pin the break-check version\nidentically in producer and consumer if you want to guarantee snapshot\ncompatibility.\n\n## Change Detection\n\nBreak Check classifies each diff as one of three types.\n\n| Type         | Severity | What it covers                                                                                                                                                                               |\n| ------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Breaking     | Major    | Removed exports or members; required parameter added; optional parameter or property made required; a parameter's rest-ness changed; parameter or property type changed; return type changed |\n| Non-breaking | Minor    | Optional parameter added; rest parameter added; required parameter or property made optional                                                                                                 |\n| Addition     | Minor    | New exports, new interface/class members                                                                                                                                                     |\n\nThe analyzer compares parameters, return types, property types, and enum values\nstructurally. The following are deliberately **not** flagged:\n\n- whitespace or formatting differences in declarations\n- parameter renames where the type and optionality are unchanged\n- container-level diffs that are already explained by their member-level diffs\n  (e.g., adding a property to an interface produces one addition, not an\n  addition plus an interface modification)\n- equivalent import-reference notation: a namespace-import alias (`_ns.Foo`)\n  and an inline import type (`import(\"pkg\").Foo`) resolve to the same type, so\n  the difference in spelling (which depends on how a package builds its\n  `.d.ts`) is normalized away before diffing\n\nWhat Break Check does **not** yet do:\n\n- type variance: any parameter, property, or return-type change is treated as\n  breaking, even when the new type is strictly wider. Widening (e.g.,\n  `string` → `string | number` on a return type) is technically non-breaking\n  but is reported as breaking today.\n- generic-parameter changes are detected as text differences only; adding,\n  removing, or constraining a type parameter is not classified.\n- TSDoc-only changes are ignored, which is the intended behavior.\n\n## Roadmap\n\nNear-term, in rough priority order:\n\n- **Type variance awareness.** Stop classifying strictly-widening type\n  changes as breaking. Return type `string` → `string | number`, parameter\n  type `string` → `unknown`, and similar should be non-breaking; only\n  narrowing should be.\n- **Generic-parameter analysis.** Today generics are detected as text\n  diffs only. Classify adding, removing, reordering, or constraining\n  type parameters with the same rigor as regular parameters.\n- **Structural-equivalence pass for unions and discriminated unions.**\n  The rule-based diff currently flags reorderings and equivalent\n  rewrites as breaking; the AI reviewer can catch these but we want\n  the rule pass to handle the obvious cases on its own.\n- **Richer report output.** Group changes by package and by entrypoint\n  in the markdown report, and include a stable JSON schema version so\n  downstream tooling can depend on the output shape.\n\nLonger-term ideas (less committed):\n\n- A `break-check explain \u003csymbol\u003e` command that prints the before/after rollup\n  for a single export, for use during code review.\n- Per-package severity overrides in `break-check.config.json` (e.g. treat\n  internal packages as non-breaking by default).\n- Pluggable analyzers so consumers can add project-specific rules\n  (deprecation policies, naming conventions) without forking.\n\nIf you want to pick one up, open an issue first so we can align on\nscope before you start.\n\n## Troubleshooting\n\n### No TypeScript declarations found\n\nBuild the package first and confirm `package.json` points to a real `.d.ts`\nentrypoint.\n\n### Baseline directory not found\n\nGenerate the baseline first, or pass an absolute path to `--baseline`.\nRelative baseline paths are resolved from the config directory.\n\n### API Extractor failed\n\nRun with `--verbose` to see API Extractor diagnostics:\n\n```bash\nbreak-check snapshot --verbose\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclerk%2Fbreak-check","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclerk%2Fbreak-check","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclerk%2Fbreak-check/lists"}