breakage 0.8.5 → 0.9.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 (42) hide show
  1. package/README.md +163 -47
  2. package/dist/{breakage-B7dJsgcu.js → breakage-DIY49fzP.js} +331 -217
  3. package/dist/breakage-DIY49fzP.js.map +1 -0
  4. package/dist/breakage.d.ts +111 -55
  5. package/dist/breakage.d.ts.map +1 -1
  6. package/dist/breakage.js +3 -1
  7. package/dist/breakage.x.js +78 -80
  8. package/dist/breakage.x.js.map +1 -1
  9. package/dist/changesets.d.ts +8 -0
  10. package/dist/changesets.d.ts.map +1 -0
  11. package/dist/changesets.js +27 -0
  12. package/dist/changesets.js.map +1 -0
  13. package/dist/release-BsdIDT4g.js +69 -0
  14. package/dist/release-BsdIDT4g.js.map +1 -0
  15. package/dist/release-D47BqRuB.d.ts +40 -0
  16. package/dist/release-D47BqRuB.d.ts.map +1 -0
  17. package/npm.md +55 -33
  18. package/package.json +45 -6
  19. package/src/breakage.ts +125 -73
  20. package/src/breakage.x.ts +148 -127
  21. package/src/changesets.ts +51 -0
  22. package/src/config.ts +128 -0
  23. package/src/npm/check.ts +51 -50
  24. package/src/npm/cli.ts +26 -22
  25. package/src/npm/config.ts +38 -6
  26. package/src/npm/environment.ts +10 -10
  27. package/src/npm/release.ts +9 -25
  28. package/src/npm/types.ts +5 -45
  29. package/src/release.ts +153 -0
  30. package/src/{npm/command.ts → task.ts} +2 -0
  31. package/dist/breakage-B7dJsgcu.js.map +0 -1
  32. package/dist/breakage.$configPath.schema.json +0 -33
  33. package/dist/breakage.help.schema.json +0 -6
  34. package/dist/breakage.main.schema.json +0 -33
  35. package/dist/breakage.npm.check.schema.json +0 -16
  36. package/dist/breakage.npm.help.schema.json +0 -6
  37. package/dist/breakage.npm.schema.json +0 -6
  38. package/dist/breakage.npm.test.$peer.$range.schema.json +0 -16
  39. package/dist/breakage.npm.test.schema.json +0 -16
  40. package/dist/breakage.prelude.$configPath.schema.json +0 -31
  41. package/dist/breakage.prelude.schema.json +0 -31
  42. package/dist/breakage.schema.schema.json +0 -10
package/README.md CHANGED
@@ -20,13 +20,121 @@ breakage distinguishes two kinds of tests:
20
20
  1. Public tests record behavior consumers can rely on across releases. breakage restores their released versions so a proposed change cannot weaken an assertion alongside the implementation it checks.
21
21
  2. Private tests cover implementation details, development diagnostics, and other behavior you want to verify without preserving it as a release commitment. They can detect real bugs too; they are not used as the historical compatibility contract.
22
22
 
23
- Identify the public tests with a glob pattern and provide a command that runs those tests once against source. The tested package's build and current-test preflight checks belong in independent jobs. Upstream dependency builds, such as Turbo's `^build`, can run before compatibility checks. Individual public contracts may build a disposable package or fixture when published entries, declaration files, or compiled runtimes are themselves the behavior being verified. A passing comparison means the selected released tests passed; its strength depends on the promises those tests actually protect.
23
+ Identify the public tests with a glob pattern and provide a command that runs those tests once against source. The tested package's build and current-test preflight checks belong in independent jobs. Upstream dependency builds, such as Turbo's `^build`, can run before compatibility checks. Individual public contracts may build a disposable package or fixture when published entries, declaration files, or compiled runtimes are themselves the behavior being verified. A comparison without detected breakage means the selected released tests passed; its strength depends on the promises those tests actually protect.
24
+
25
+ ## interpreting results
26
+
27
+ **Run normal tests alongside breakage, and require both checks to pass.** Normal tests check the candidate against its current requirements, including new public contracts and private implementation tests. breakage checks the candidate against the public contracts preserved in a previous release. Run them in independent jobs or checkouts so restoring historical tests does not interfere with the current suite. A passing breakage check never overrides failing normal tests, builds, or type checks.
28
+
29
+ Read the two branches below together; they describe independent checks, not an execution order. The breakage branch describes the public historical check after successful cleanup.
30
+
31
+ ```mermaid
32
+ flowchart TD
33
+ candidate["Candidate release"] --> current{"Normal tests pass?"}
34
+ current -->|No| invalid["Current requirements failed; overall checks fail"]
35
+ current -->|Yes| valid["Current requirements passed; also require breakage"]
36
+ candidate --> baseline{"Historical baseline and setup ready?"}
37
+ baseline -->|No| inconclusive["No compatibility conclusion; breakage exits 2"]
38
+ baseline -->|Yes| historical{"Historical test command result?"}
39
+ historical -->|Exit 0| preserved["Tested historical contracts passed; breakage exits 0"]
40
+ historical -->|Exit 1| changed["Historical contract failure reported"]
41
+ historical -->|Other exit or process error| inconclusive
42
+ changed --> policy{"Release policy permits breakage?"}
43
+ policy -->|No| blocked["Breakage is unapproved; breakage exits 1"]
44
+ policy -->|Yes| allowed["Breakage is permitted for this release; breakage exits 0"]
45
+ ```
46
+
47
+ When normal tests pass but historical tests fail, the candidate satisfies the tested current requirements while failing an expectation encoded by the released suite. For example, removing an exported function and updating current callers can leave normal tests passing while a historical consumer test can no longer import that function. The import failure is evidence of a broken consumer contract even though no assertions execute. The point at which a test stops does not by itself distinguish incompatibility from a setup problem.
48
+
49
+ General misconfigurations that also affect the current suite surface in normal tests, even if release policy permits the historical failure. Failure to acquire a baseline, invalid configuration, and other setup errors leave the comparison inconclusive and fail the check. The test command's exit codes are a reporting convention, not an automatic diagnosis of the cause: exit 1 reports incompatibility; other nonzero exits or process errors report setup failure. See the [test command contract](#typescript-configuration-and-release-intent) when configuring your runner.
50
+
51
+ A successful check has two possible meanings: the selected historical tests passed, or they reported incompatibility that release policy permits. Permission to release a breaking change does not establish that compatibility was preserved. Passing historical tests establish only the promises those tests cover; normal tests remain independently required in either case.
24
52
 
25
53
  ## help
26
54
 
27
- Run `breakage help` to show usage for the CLI, including the check options, `prelude`, and `schema` commands. Like `schema`, the `help` command skips configuration discovery; it works without required check options and even when `breakage.config.json` is malformed. It does not run checks or write schema files.
55
+ Run `breakage help` to show usage for the CLI, including the check options, `prelude`, and npm commands. The `help` command skips configuration discovery; it works without required check options and even when `breakage.config.ts` is malformed. It does not run checks.
56
+
57
+ Use `breakage help` instead of the former `--help` or `-h` options. Configuration no longer controls help. The empty route and an explicit `.ts` configuration path run checks.
58
+
59
+ ## TypeScript configuration and release intent
60
+
61
+ Create `breakage.config.ts` with a default export. Public checks, `prelude`, and npm checks load this file by default; explicit `.ts` configuration paths still work. JSON configuration is no longer supported. Running TypeScript configuration on Node requires native erasable TypeScript support (Node 22.18 or newer). Information-producing hooks are functions, while tests and setup commands remain shell strings.
62
+
63
+ `defineConfig` and `satisfies BreakageConfig` check config keys and hook signatures while authoring. The same strict schemas validate loaded configuration, including nested release and npm options: misspelled keys fail setup instead of falling back to defaults. Comline awaits module initialization, validates the file before applying CLI overrides, and validates the merged result. Config modules and their imports may use top-level await to construct the default-exported config object. Initialization failures fail setup; Promise-valued default exports and config factories are not supported. Release inspection can remain inside `release.resolveDelta`, where the selected baseline and package context are available.
64
+
65
+ ```ts
66
+ import { defineConfig, semverPolicy } from "breakage"
67
+ import { changesetsReleaseDelta } from "breakage/changesets"
68
+
69
+ export default defineConfig({
70
+ tagPattern: "refs/tags/my-library@",
71
+ testPattern: "tests/public/**/*",
72
+ test: "npm run test:public",
73
+ release: {
74
+ resolveDelta: changesetsReleaseDelta(),
75
+ policy: semverPolicy({ beforeV1: "minor" }),
76
+ },
77
+ })
78
+ ```
79
+
80
+ `release.resolveDelta` is a synchronous/asynchronous function returning a `VersionTransition`: `{ previousVersion, version, delta }`, or `undefined` for no release evidence. The resolver receives `packageName`, `packageDirectory`, and `currentVersion`. Public checks also supply `baseline: { version, ref, commit }` from the exact commit selected for replay; npm support checks supply the historical manifest's baseline version. Omit `resolveDelta` to compare that baseline with the current manifest. For candidate identity checks without a historical baseline, the default comparison starts at the current manifest version.
81
+
82
+ For example, a pending Changesets minor release from `0.53.3` returns `{ previousVersion: "0.53.3", version: "0.54.0", delta: "minor" }`. After `changeset version` removes the changeset, the manifest's `0.54.0` supplies the same transition against the pinned `0.53.3` baseline. The resolver reports the transition; `release.policy` decides whether it permits breaking changes. A patch release to `0.53.4` does not permit a break with `beforeV1: "minor"`.
83
+
84
+ Breakage validates both versions, requires the target to be at least the current manifest version, checks `previousVersion` against any supplied baseline, and verifies that `delta` matches the actual semver difference. Invalid evidence and resolver errors fail setup. Release resolution happens before historical tests replace current tests. A resolver cannot override policy by returning a `breaking` field.
85
+
86
+ `semverPolicy()` permits major increments by default. `beforeV1: "minor"` additionally permits minor increments while the baseline major is zero; `beforeV1: "patch"` also permits patch increments there. A prerelease identifier change alone does not permit a break. At and after 1.0, the policy always requires a major increment. Equal and older versions never permit breaking changes.
87
+
88
+ Use `versionTransition(previousVersion, version)` to construct evidence without applying policy. Its `delta` is a semver release type or `"none"` for equal versions. `resolveReleaseDelta(release, context)` invokes and validates the resolver without applying policy. `versionDelta(previousVersion, version, policy?)` independently obtains `{ previousVersion, version, delta, breaking }`; a custom policy receives the transition and returns a boolean, and is called only for advancing versions.
89
+
90
+ A custom resolver can compose the plan reader with the transition helper:
91
+
92
+ ```ts
93
+ import { defineConfig, semverPolicy, versionTransition } from "breakage"
94
+ import { readChangesetsReleasePlan } from "breakage/changesets"
95
+
96
+ export default defineConfig({
97
+ release: {
98
+ resolveDelta: async ({
99
+ packageName,
100
+ packageDirectory,
101
+ currentVersion,
102
+ baseline,
103
+ }) => {
104
+ const plan = await readChangesetsReleasePlan(packageDirectory)
105
+ return versionTransition(
106
+ baseline?.version ?? currentVersion,
107
+ plan.get(packageName) ?? currentVersion,
108
+ )
109
+ },
110
+ policy: semverPolicy({ beforeV1: "minor" }),
111
+ },
112
+ })
113
+ ```
114
+
115
+ `test` is a nonempty shell-command string. Breakage runs it in the configured directory and captures stdout and stderr as diagnostics. Exit 0 succeeds, exit 1 reports incompatibility, and other nonzero exits or process failures report setup errors. A breaking release can permit incompatibility, but cannot permit setup errors. Runners using exit 1 for infrastructure failures need a wrapper that distinguishes those failures. Test functions are rejected; there is no implicit-success return convention. `testCommand` remains a string alias; configure only one.
116
+
117
+ `release.resolveDelta` and `release.policy` are functions whose return values carry transitions and policy decisions; stdout is never parsed as release information. `semverPolicy({ beforeV1: "minor" })` creates a policy function. If a resolver invokes an external program, it owns parsing and validating that program's output.
118
+
119
+ Comline treats `release` and `npm` as `configOnly` options. Their functions survive loading and validation without being called; they have no CLI flags or completion suggestions. Attempts to set them through flags, including dotted names such as `--release.policy`, fail setup. Ordinary scalar options retain their flags, aliases, and overrides.
28
120
 
29
- Use `breakage help` instead of the former `--help` or `-h` options. Configuration no longer controls help. The empty route and a configuration path still run checks; if your configuration file is named `help` or `prelude`, pass an explicit path such as `./help` to select it explicitly.
121
+ Relative `baseDirname` values resolve from the configuration file's directory, which is also the default base directory when loading a config. CLI `--base-dir` values resolve from the caller's directory. CLI options override config fields, including `--test-command` overriding `test`. Test patterns, `baselineFile`, and prelude `out` are relative to the resulting base directory. Imports and additional filesystem work in callbacks should use `import.meta.url` or the supplied directory rather than assuming a process working directory.
122
+
123
+ Omitting `release` leaves detected breaks unapproved. Configure `release: { policy: semverPolicy({ beforeV1: "minor" }) }` to use a versioned manifest without a custom resolver. See [migration to the release model](#migration-to-the-release-model) for removed certification hooks and JSON configuration.
124
+
125
+ ### Optional Changesets helpers
126
+
127
+ The `breakage/changesets` subpath provides two independent helpers: `readChangesetsReleasePlan(directory?)` returns a map of package names to planned versions, and `changesetsReleaseDelta({ directory? })` returns a release-delta resolver. The resolver discovers the workspace from each package directory unless given an explicit directory, reads a fresh plan, and compares its intended version (or the current manifest after versioning) with the supplied baseline. Without a baseline it compares against the current manifest, which supplies candidate identity without claiming a historical compatibility decision. The plan uses Changesets' own assembly logic, configuration, dependency propagation, and prerelease state. Errors reading the plan fail setup instead of silently falling back to the manifest.
128
+
129
+ These helpers use optional peer dependencies. Install them alongside Breakage when using the subpath:
130
+
131
+ ```sh
132
+ npm install --save-dev @changesets/assemble-release-plan@^7 @changesets/config@^4 @changesets/pre@^3 @changesets/read@^1 @manypkg/get-packages@^3
133
+ ```
134
+
135
+ The main `breakage` export and commands do not import these dependencies. You can compose the plan reader with your own delta resolver or use the core release model with any other release process.
136
+
137
+ `defineConfig` shares a top-level `release` with `npm` checks unless `npm.release` is explicitly configured. Both workflows apply the same version comparison and policy to their own historical manifests. npm preparation, installation, and suite commands are shell strings; see the [npm guide](npm.md#shared-release-configuration).
30
138
 
31
139
  ## examples
32
140
 
@@ -36,11 +144,10 @@ Use `breakage help` instead of the former `--help` or `-h` options. Configuratio
36
144
  npx breakage \
37
145
  --tagPattern="refs/tags/my-library@" \
38
146
  --testPattern="tests/public/**/*" \
39
- --testCommand="npm run test:public" \
40
- --certifyCommand="false"
147
+ --testCommand="npm run test:public"
41
148
  ```
42
149
 
43
- Run this from the project root. It restores files matching `tests/public/**/*` from the newest matching `my-library@<version>` release tag and runs your project's `npm run test:public` command. `certifyCommand="false"` leaves any detected break uncertified; replace it with a project command that validates your release plan when you add intentional-break certification.
150
+ Run this from the project root. It restores files matching `tests/public/**/*` from the newest matching `my-library@<version>` release tag and runs your project's `npm run test:public` command. Without `release` configuration, any detected break fails the check.
44
151
 
45
152
  ### multi-project monorepo
46
153
 
@@ -48,13 +155,12 @@ Run this from the project root. It restores files matching `tests/public/**/*` f
48
155
  npx breakage \
49
156
  --tagPattern="refs/tags/my-library@" \
50
157
  --testPattern="packages/my-library/tests/public/**/*" \
51
- --testCommand="cd packages/my-library && npm run test:public" \
52
- --certifyCommand="false"
158
+ --testCommand="cd packages/my-library && npm run test:public"
53
159
  ```
54
160
 
55
161
  Run this from the repository root. It restores the package's released public tests and runs its current public-test command. Patterns and commands are relative to the check's working directory; use `--baseDirname` if you want to run the check from a package directory instead.
56
162
 
57
- In both examples, a failed test command remains uncertified and makes the CLI exit nonzero. Inspect the failure to distinguish a consumer regression from a build, runner, or setup problem.
163
+ In both examples, a failed test command requires a breaking release and makes the CLI exit nonzero. Inspect the failure to distinguish a consumer regression from a build, runner, or setup problem.
58
164
 
59
165
  Release tags may use `1.2.3`, `v1.2.3`, `package@1.2.3`, or `@scope/package@1.2.3`, including valid prerelease and build identifiers. The newest matching version is selected by semantic-version precedence; tags without a supported version are ignored.
60
166
 
@@ -69,15 +175,15 @@ breakage prelude --out .breakage/baseline.json
69
175
  breakage --baseline-file .breakage/baseline.json
70
176
  ```
71
177
 
72
- Both commands discover `breakage.config.json` and accept the same `--tag-pattern` and `--base-dir` options. An explicit configuration path works as `breakage prelude ./custom.config.json --out .breakage/baseline.json` and `breakage ./custom.config.json --baseline-file .breakage/baseline.json`. The prelude does not require test or certification options, and never runs those commands or builds the package. Existing full check configurations can be reused. Output and baseline-file paths are relative to `baseDirname`, which defaults to the current working directory. The default prelude output is `.breakage/baseline.json`.
178
+ Both commands discover `breakage.config.ts` and accept the same `--tag-pattern` and `--base-dir` options. An explicit configuration path works as `breakage prelude ./custom.config.ts --out .breakage/baseline.json` and `breakage ./custom.config.ts --baseline-file .breakage/baseline.json`. The prelude does not require test or release options, and never runs tasks, resolves release versions, or builds the package. Existing full check configurations can be reused. Output and baseline-file paths are relative to `baseDirname`, which defaults to the configuration file's directory, or the current working directory without a config. The default prelude output is `.breakage/baseline.json`.
73
179
 
74
180
  The versioned JSON snapshot contains the selected tag ref, its resolved commit SHA, the tag pattern, and the package directory relative to the repository root. Annotated tags are peeled to commits. Contents are deterministic and written atomically, with no timestamp or nonce. Fetching uses the SHA from discovery rather than a mutable tag name. An unchanged baseline produces identical snapshot bytes; a newer release or a moved tag targeting another commit changes them. A failed discovery, fetch, or write fails the prelude and removes an older snapshot at the validated output path.
75
181
 
76
- The pinned check reads that locally available commit without contacting `origin` or discovering tags. A release published between prelude and check does not change this invocation's baseline. Missing, malformed, incompatible, or unavailable snapshots fail the check; rerun the prelude to refresh them. Each package or configuration needs its own snapshot path. Test execution, certification, Git locking, and restoration follow the same lifecycle as the ordinary command.
182
+ The pinned check reads that locally available commit without contacting `origin` or discovering tags. A release published between prelude and check does not change this invocation's baseline. Missing, malformed, incompatible, or unavailable snapshots fail the check; rerun the prelude to refresh them. Each package or configuration needs its own snapshot path. Release resolution, test execution, Git locking, and restoration follow the same lifecycle as the ordinary command.
77
183
 
78
184
  ### Turbo
79
185
 
80
- Add these scripts to the tested package, preserving your existing runner and certification configuration:
186
+ Add these scripts to the tested package, preserving your existing runner and release configuration:
81
187
 
82
188
  ```json
83
189
  {
@@ -108,7 +214,7 @@ Use [Turbo's deferred hashing](https://turborepo.dev/docs/reference/configuratio
108
214
  }
109
215
  ```
110
216
 
111
- Run `turbo run test:breakage` as usual. The prelude runs even when the compatibility check hits its cache; a failed prelude prevents the downstream task from replaying a cached success. Keep all additional runner, helper, configuration, environment, and certification inputs in the compatibility task's hash, including release-plan files outside the package directory. Keep upstream `^build` prerequisites; the tested package does not need a self-build preflight. For runners without deferred hashing, finish the prelude before starting the task runner and include its snapshot in the check's cache key.
217
+ Run `turbo run test:breakage` as usual. The prelude runs even when the compatibility check hits its cache; a failed prelude prevents the downstream task from replaying a cached success. Keep all additional runner, helper, configuration, environment, and release inputs in the compatibility task's hash, including release-plan files outside the package directory. Keep upstream `^build` prerequisites; the tested package does not need a self-build preflight. For runners without deferred hashing, finish the prelude before starting the task runner and include its snapshot in the check's cache key.
112
218
 
113
219
  The JavaScript API exports `breakagePrelude({ out, tagPattern, baseDirname })` and accepts `baselineFile` in `breakage` options. Without `baselineFile`, the existing single-command discovery behavior is preserved.
114
220
 
@@ -150,16 +256,15 @@ Released assertions need trustworthy readers. If today's writer and today's obse
150
256
 
151
257
  Mondrian publishes its independent inspection tools through its testing entrypoint and restores their source alongside the public tests:
152
258
 
153
- ```json
154
- {
155
- "tagPattern": "refs/tags/mondrian\\.pdf@",
156
- "testPattern": "{tests/public,src/testing/inspection}/**/*",
157
- "testCommand": "pnpm test:public",
158
- "certifyCommand": "false"
259
+ ```ts
260
+ export default {
261
+ tagPattern: "refs/tags/mondrian\\.pdf@",
262
+ testPattern: "{tests/public,src/testing/inspection}/**/*",
263
+ testCommand: "pnpm test:public",
159
264
  }
160
265
  ```
161
266
 
162
- This illustrates the restore boundary; `test:public` is a project-provided command that only runs its public suite against source. Consumer type checks and builds have separate commands. `false` keeps detected breaks uncertified until the project supplies its release-plan check.
267
+ This illustrates the restore boundary; `test:public` is a project-provided command that only runs its public suite against source. Consumer type checks and builds have separate commands. Omitting `release` keeps detected breaks unapproved.
163
268
 
164
269
  Match the assertions and observation code, and leave the implementation they are meant to evaluate outside that boundary. When an observer also ships as a library feature, its restored implementation is trusted test infrastructure for this comparison; verify its current implementation separately too. Execute observation source directly after restoration so historical readers run without a build.
165
270
 
@@ -188,7 +293,7 @@ Run the current suite in a parallel test job so newly added contracts are checke
188
293
 
189
294
  Run the check from a clean checkout with access to `origin` and release tags. An initial release without matched public tests provides no baseline: preserve the CLI's inconclusive result until a release includes them. A missing baseline is not evidence of compatibility.
190
295
 
191
- When the job fails, identify the phase before deciding on a breaking release. A failed current test, incompatible runner types, a build problem, a missing baseline, and a released assertion detecting a consumer regression require different responses. Certification should validate an intentional breaking-release plan after that review; using a successful certification command to bypass an unexplained infrastructure failure does not establish compatibility.
296
+ When the job fails, identify the phase before deciding on a breaking release. A failed current test, incompatible runner types, a build problem, a missing baseline, and a released assertion detecting a consumer regression require different responses. A breaking version delta permits a released contract failure (exit 1); setup errors (other nonzero exits or thrown errors) remain failures. A breaking release acknowledges changed consumer behavior rather than establishing compatibility.
192
297
 
193
298
  ### case study: mondrian.pdf
194
299
 
@@ -200,58 +305,69 @@ The contracts continued to pass through [PDFium changes](https://github.com/jere
200
305
 
201
306
  ## git environment
202
307
 
203
- breakage uses simple-git v4, which filters inherited `GIT_*` environment variables and certain other Git-related variables, including `SSH_ASKPASS`, from its Git subprocesses. Environment-based settings such as `GIT_SSH_COMMAND`, `GIT_ASKPASS`, and `GIT_CONFIG_*` no longer configure tag discovery, fetching, or file restoration. Configure authentication and Git behavior through Git configuration files, SSH configuration, or an SSH agent instead. This filtering applies to breakage's Git operations; test and certification commands still inherit the normal process environment.
308
+ breakage uses simple-git v4, which filters inherited `GIT_*` environment variables and certain other Git-related variables, including `SSH_ASKPASS`, from its Git subprocesses. Environment-based settings such as `GIT_SSH_COMMAND`, `GIT_ASKPASS`, and `GIT_CONFIG_*` no longer configure tag discovery, fetching, or file restoration. Configure authentication and Git behavior through Git configuration files, SSH configuration, or an SSH agent instead. This filtering applies to breakage's Git operations; test tasks still inherit the normal process environment.
204
309
 
205
310
  ## parallel checks and file restoration
206
311
 
207
- Checks with disjoint public test paths can run their test and certification commands in parallel, in their original working directories. breakage coordinates Git setup and cleanup with a short-lived lock and records each running check's test paths. The clean-repository check ignores only the paths owned by active checks; unrelated uncommitted changes still prevent a new check from starting. Checks with overlapping test paths are rejected before replacing files. Remote tag discovery runs outside this lock, and fetches use a separate lock in the common Git directory. Cleanup waits for file-restoration access instead of giving up when a setup timeout expires.
312
+ Checks with disjoint public test paths can run their test tasks in parallel, in their original working directories. breakage coordinates Git setup and cleanup with a short-lived lock and records each running check's test paths. The clean-repository check ignores only the paths owned by active checks; unrelated uncommitted changes still prevent a new check from starting. Checks with overlapping test paths are rejected before replacing files. Remote tag discovery runs outside this lock, and fetches use a separate lock in the common Git directory. Cleanup waits for file-restoration access instead of giving up when a setup timeout expires.
208
313
 
209
- Released tests are loaded with `git restore --worktree`. After testing and certification, including failures, breakage restores only those paths to their starting commit and removes tests that existed only in the release. The Git index and existing stashes are preserved. Unrelated files, command outputs, and changes made by other packages are left in place. There is no repository or dependency copy. Commands that themselves modify shared files or Git state still need their own coordination.
314
+ Released tests are loaded with `git restore --worktree`. After testing, including failures, breakage restores only those paths to their starting commit and removes tests that existed only in the release. The Git index and existing stashes are preserved. Unrelated files, command outputs, and changes made by other packages are left in place. There is no repository or dependency copy. Commands that themselves modify shared files or Git state still need their own coordination.
210
315
 
211
316
  If a process is forcibly interrupted or restoration fails, breakage retains recovery information under `breakage/` in the worktree's Git directory (`git rev-parse --absolute-git-dir`). After confirming the process has stopped, restore each recorded path from the recorded `head` commit, remove any recorded paths absent from that commit, and remove the record. An interrupted setup or cleanup can also leave the `breakage/lock` directory; remove it only after confirming no setup or cleanup is still running. An interrupted fetch can leave `breakage-fetch.lock` in the common Git directory; remove it only after confirming no fetch is still running.
212
317
 
213
318
  ## options
214
319
 
215
- The optional [`breakage npm` command family](./npm.md) checks peer compatibility and end-of-support changes for npm packages. Its representatives, packed candidates, and release-intent hooks apply only to that namespace. The ordinary public check retains the adoption conditions and behavior described above. Both commands can use `breakage.config.ts`; default discovery prefers it over `breakage.config.json`, and an explicit configuration path wins. TypeScript configuration uses the runtime's native support for erasable TypeScript.
320
+ The optional [`breakage npm` command family](./npm.md) checks peer compatibility and end-of-support changes for npm packages. Its representatives and packed candidates apply only to that namespace; the release model is shared with public checks. The ordinary public check retains the adoption conditions and behavior described above. Both commands can use `breakage.config.ts`; default discovery uses it, and an explicit `.ts` configuration path wins. TypeScript configuration uses the runtime's native support for erasable TypeScript.
216
321
 
217
- | Check option | Shorthand | Required | Purpose |
218
- | ------------------ | --------- | -------- | ------------------------------------------------- |
219
- | `--tagPattern` | `-g` | | RegExp selecting release tags. |
220
- | `--testPattern` | `-p` | Yes | Glob selecting released public tests and helpers. |
221
- | `--testCommand` | `-t` | Yes | Command running the restored public contracts. |
222
- | `--certifyCommand` | `-c` | Yes | Command certifying intentional breaking changes. |
223
- | `--baseDirname` | `-d` | | Working directory for Git and commands. |
224
- | `--baselineFile` | | | Prelude snapshot pinning the release commit. |
225
- | `--verbose` | `-v` | | Print timing information for the check. |
322
+ | Check option | Shorthand | Required | Purpose |
323
+ | ---------------- | --------- | ------------------ | ------------------------------------------------- |
324
+ | `--tagPattern` | `-g` | | RegExp selecting release tags. |
325
+ | `--testPattern` | `-p` | Yes | Glob selecting released public tests and helpers. |
326
+ | `--testCommand` | `-t` | Either task option | Shell alias for `test`. |
327
+ | `--test` | | Either task option | Command running the restored public contracts. |
328
+ | `--baseDirname` | `-d` | | Working directory for Git and commands. |
329
+ | `--baselineFile` | | | Prelude snapshot pinning the release commit. |
330
+ | `--verbose` | `-v` | | Print timing information for the check. |
226
331
 
227
- `prelude --out` sets its gitignored snapshot path. `schema --outdir` sets the schema output directory. Run `breakage help` for the full option descriptions and examples.
332
+ `prelude --out` sets its gitignored snapshot path. Run `breakage help` for the full option descriptions and examples.
228
333
 
229
334
  ## cli completion
230
335
 
231
336
  With `breakage` installed on PATH, run `breakage completion install bash` to install Bash completion. Replace `bash` with `zsh`, `fish`, `nushell`, or `carapace` for the other supported integrations. Installation uses the shell's existing completion setup and does not edit shell profiles; open a new shell afterward. `breakage completion bash` prints the integration for manual installation. See [Comline's shell setup requirements](https://github.com/jeremybanka/wayforge/tree/main/packages/comline#shell-integrations).
232
337
 
233
- Completion suggests commands, option names, config file paths, snapshot paths for `--baseline-file` and `prelude --out`, and directories for `--base-dir` and `schema --out-dir`. It works without a valid config file or required option values and does not run checks. Singleton options disappear from suggestions after use; parsing behavior is unchanged. The completion transport reserves its management and protocol command names; use an explicit path such as `./completion` for a config file whose name collides with a reserved command.
338
+ Completion suggests commands, option names, config file paths, snapshot paths for `--baseline-file` and `prelude --out`, and directories for `--base-dir`. It works without a valid config file or required option values and does not run checks. Singleton options disappear from suggestions after use; parsing behavior is unchanged.
234
339
 
235
340
  ## cli option aliases and warnings
236
341
 
237
- These aliases work alongside the original option names. Configuration file keys remain unchanged.
342
+ These aliases work alongside the original option names. Removed certification options fail with migration guidance.
238
343
 
239
- | Original option | Aliases |
240
- | --------------------------- | ------------------------------ |
241
- | `--tagPattern` | `--tag-pattern` |
242
- | `--testPattern` | `--test-pattern`, `--pattern` |
243
- | `--testCommand` | `--test-command` |
244
- | `--certifyCommand` | `--certify-command` |
245
- | `--baseDirname` | `--base-dir`, `--base-dirname` |
246
- | `--baselineFile` | `--baseline-file` |
247
- | `--outdir` (schema command) | `--out-dir` |
344
+ | Original option | Aliases |
345
+ | ---------------- | ------------------------------ |
346
+ | `--tagPattern` | `--tag-pattern` |
347
+ | `--testPattern` | `--test-pattern`, `--pattern` |
348
+ | `--testCommand` | `--test-command` |
349
+ | `--baseDirname` | `--base-dir`, `--base-dirname` |
350
+ | `--baselineFile` | `--baseline-file` |
248
351
 
249
352
  After successful parsing, breakage warns on stderr about unknown options and options that do not apply to the selected command. Warnings appear before check output is captured and do not change the command's exit status.
250
353
 
251
354
  ## migrating from break-check
252
355
 
253
- Breakage continues [break-check](https://github.com/jeremybanka/wayforge/tree/break-check%400.8.1/packages/break-check) in its own repository. Install `breakage`, replace the `break-check` command with `breakage`, and rename `break-check.config.json` to `breakage.config.json`. Configuration keys and check outcomes are unchanged. Regenerate schemas with `breakage schema`; generated files now use the `breakage` prefix. Reinstall shell completions with `breakage completion install <shell>`.
356
+ Breakage continues [break-check](https://github.com/jeremybanka/wayforge/tree/break-check%400.8.1/packages/break-check) in its own repository. Install `breakage`, replace the `break-check` command with `breakage`, and convert `break-check.config.json` to a default export in `breakage.config.ts`. The original rename preserved configuration keys and outcomes; the release-model migration below applies when upgrading to this breaking minor release. Reinstall shell completions with `breakage completion install <shell>`.
254
357
 
255
358
  For programmatic use, import `breakage`, `BreakageOptions`, and `BreakageOutcome` from `breakage` in place of `breakCheck`, `BreakCheckOptions`, and `BreakCheckOutcome` from `break-check`.
256
359
 
257
360
  Finish active break-check runs and recover any interrupted runs before switching. Breakage stores new recovery records under `breakage/` in the worktree's Git directory and its fetch lock at `breakage-fetch.lock` in the common Git directory.
361
+
362
+ ## Migration to the release model
363
+
364
+ This is a breaking minor release before 1.0. Configuration is TypeScript-only; shell-command strings and the `testCommand` alias remain supported. There is one task executor and one release decision model.
365
+
366
+ - Remove `certifyCommand` and its CLI flags (`--certifyCommand`, `--certify-command`, `-c`). Configure `release.resolveDelta` and `release.policy` instead. Removed hooks fail explicitly.
367
+ - Replace npm `releaseIntent` with `release.resolveDelta`. Return `{ previousVersion, version, delta }` or `undefined`, rather than `{ status: "release", version }` or `{ status: "none" }`. Throw when inspection fails.
368
+ - Replace npm `certifyBreaking` with `release.policy`. Use `semverPolicy({ beforeV1: "minor" })` for this pre-1.0 convention, or a function receiving `{ previousVersion, version, delta }`. Support findings remain in the npm report; the policy receives version evidence only.
369
+ - Replace the removed `ReleaseIntent`, `ReleaseContext`, and `ReleaseEvidence` types with `ReleaseDeltaResolver`, `ReleaseDeltaContext`, `VersionTransition`, and `VersionDelta` as appropriate.
370
+ - Public outcomes expose `release?: VersionDelta`; use `outcome.release?.breaking` to check release approval. `breakingChangesCertified`, `certificationStdout`, and `certificationStderr` are removed. npm support status `certified` becomes `breaking-release`, with the full version delta in `support.release`.
371
+ - Tests, npm setup, and npm suite commands must be shell strings. Exit 0 succeeds, 1 detects incompatibility for tests, and other nonzero statuses or process errors fail setup. Release intent never approves setup errors. `test` and `testCommand` cannot both be configured.
372
+ - Convert JSON configuration to `breakage.config.ts` with a default export. JSON config, JSON schema generation, and the `--release` JSON flag are removed. Express `release.resolveDelta` as a function returning a transition, using `versionTransition(previousVersion, version)` if helpful, and `release.policy` as a function or `semverPolicy(...)`.
373
+ - Relative config directories now resolve from the config file. Check filesystem paths when migrating configurations previously loaded from another working directory.