Skip to content

Compatibility policy ​

What a version number promises, so a minor upgrade is a decision you can make without reading a diff.

1. Scope ​

The public API is every type marked @api. Types marked @internal are implementation and may change in any release, including a patch — the Rasuvaeff\PropertyTesting\Internal namespace above all.

2. Seed → values is not under SemVer ​

A minor release may shift what a given seed generates. When it does:

  • FilesystemCorpus::SEQUENCE_EPOCH is bumped in the same release, so seed entries recorded under an older epoch are dropped rather than replayed as a different input;
  • the change is named in the changelog.

Values entries — the minimised input stored as data — survive every release, which is what makes the corpus the durable half of a regression.

CounterExample::$path is a debugging aid, not a durable identifier: it indexes into each node's shrink candidates, so editing a generator orphans it.

This is the clause that has already been exercised. Core 0.6.0 changed the distribution of Gen::string() and bumped the epoch to 2; 0.5.0 changed the order of list shrink candidates. Both were minors, and both were correct — an engine that could never improve a generator's distribution would be frozen at its first mistake.

3. Corpus format ​

FilesystemCorpus::FORMAT_VERSION does not change within 1.x. The document grows only by optional fields, and a document written by any 0.x release — or by the frozen rasuvaeff/property-testing 2.8 — stays readable. RedisCorpus writes the byte-identical document, so moving a corpus between the two backends is a copy.

4. Message texts may be reworded; the machine-readable form may not ​

Human-readable exception and warning texts are prose for a developer reading a red run, not a parsing surface. They may be reworded in a minor, with the change named in the changelog. The adapters' golden-message suites pin them as characterizations, which is what makes such a rewording visible rather than silent.

Frozen instead:

5. Events ​

ChangeRelease
A new event typeminor
A new field at the end of an existing eventminor
A new PropertyResult implementationminor — consumers must carry a default branch
Removing an event or a fieldmajor
Reordering the sequence emitted for an existing outcomemajor

6. Constructors are append-only ​

Constructors of @api final readonly classes take new parameters with defaults, at the end. Positional construction of CounterExample, PropertyConfig, RunStatistics and the events keeps working across a minor.

7. How data is reached is frozen as it stands ​

The accessor style is not uniform, and it stays that way. Results, events and most exceptions expose public readonly properties; PropertyViolationException, ExampleViolationException, PathViolationException and RegressionViolationException expose getters.

Unifying them would break every consumer for a cosmetic gain. The split is at least stable: a type either has the field or the getter, and one never becomes the other.

8. The family moves together ​

The adapters (rasuvaeff/property-testing-testo, rasuvaeff/property-testing-phpunit) and rasuvaeff/property-testing-names require the engine with a caret range on its current major. A major here is a major there, and the release order is fixed: engine first, then the adapters, then -names.

9. PHP ​

8.3 - 8.5. Support for a newer PHP minor ships as a patch that widens the constraint. Dropping a PHP version is a major.