diff --git a/docs/src/antora-template.yml b/docs/src/antora-template.yml
index ef5d4ce77d..0577fb49ea 100644
--- a/docs/src/antora-template.yml
+++ b/docs/src/antora-template.yml
@@ -12,5 +12,8 @@ asciidoc:
maven-version: ${maven.min.version}
quarkus-version: ${version.io.quarkus}
spring-boot-version: ${version.org.springframework.boot}
+ logback-version: ${version.ch.qos.logback}
+ exec-maven-plugin-version: ${version.exec.plugin}
+ rewrite-maven-plugin-version: ${version.rewrite.plugin}
nav:
- modules/ROOT/nav.adoc
diff --git a/docs/src/modules/ROOT/nav.adoc b/docs/src/modules/ROOT/nav.adoc
index 40b322b432..20ac527e88 100644
--- a/docs/src/modules/ROOT/nav.adoc
+++ b/docs/src/modules/ROOT/nav.adoc
@@ -1,9 +1,10 @@
* xref:introduction/introduction.adoc[leveloffset=+1]
* Quickstart
-** xref:quickstart/overview/overview-quickstarts.adoc[leveloffset=+1]
+** xref:quickstart/overview.adoc[leveloffset=+1]
** xref:quickstart/hello-world/hello-world-quickstart.adoc[leveloffset=+1]
** xref:quickstart/quarkus/quarkus-quickstart.adoc[leveloffset=+1]
** xref:quickstart/spring-boot/spring-boot-quickstart.adoc[leveloffset=+1]
+** xref:quickstart/quarkus-vehicle-routing/quarkus-vehicle-routing-quickstart.adoc[leveloffset=+1]
* xref:use-cases-and-examples/use-cases-and-examples.adoc[leveloffset=+1]
* Using Timefold Solver
** xref:using-timefold-solver/overview.adoc[leveloffset=+1]
@@ -21,4 +22,8 @@
* xref:responding-to-change/responding-to-change.adoc[leveloffset=+1]
* xref:integration/integration.adoc[leveloffset=+1]
* xref:design-patterns/design-patterns.adoc[leveloffset=+1]
+* Upgrade and Migration
+** xref:upgrade-and-migration/overview.adoc[leveloffset=+1]
+** xref:upgrade-and-migration/upgrade-to-latest-version.adoc[leveloffset=+1]
+** xref:upgrade-and-migration/migrate-from-optaplanner.adoc[leveloffset=+1]
* xref:enterprise-edition/enterprise-edition.adoc[leveloffset=+1]
\ No newline at end of file
diff --git a/docs/src/modules/ROOT/pages/.index.adoc b/docs/src/modules/ROOT/pages/.index.adoc
index fd71964668..0476580241 100644
--- a/docs/src/modules/ROOT/pages/.index.adoc
+++ b/docs/src/modules/ROOT/pages/.index.adoc
@@ -27,4 +27,5 @@ include::optimization-algorithms/optimization-algorithms.adoc[leveloffset=+1]
include::responding-to-change/responding-to-change.adoc[leveloffset=+1]
include::integration/integration.adoc[leveloffset=+1]
include::design-patterns/design-patterns.adoc[leveloffset=+1]
+include::upgrade-and-migration/.upgrade-and-migration.adoc[leveloffset=+1]
include::enterprise-edition/enterprise-edition.adoc[leveloffset=+1]
diff --git a/docs/src/modules/ROOT/pages/constraints-and-score/score-calculation.adoc b/docs/src/modules/ROOT/pages/constraints-and-score/score-calculation.adoc
index ec9bb7fd7f..68cd2a7636 100644
--- a/docs/src/modules/ROOT/pages/constraints-and-score/score-calculation.adoc
+++ b/docs/src/modules/ROOT/pages/constraints-and-score/score-calculation.adoc
@@ -27,7 +27,7 @@ When the planning variable of a single `Shift` changes, to recalculate the score
the normal Streams API has to execute the entire stream from scratch.
[#constraintStreams]
-== Introducing constraint streams
+== Introducing Constraint Streams
Constraint streams are a Functional Programming form of incremental score calculation in plain Java
that is easy to read, write and debug.
diff --git a/docs/src/modules/ROOT/pages/introduction/introduction.adoc b/docs/src/modules/ROOT/pages/introduction/introduction.adoc
index 00728415d6..45d4ca4191 100644
--- a/docs/src/modules/ROOT/pages/introduction/introduction.adoc
+++ b/docs/src/modules/ROOT/pages/introduction/introduction.adoc
@@ -48,7 +48,7 @@ The ability to achieve these goals relies on the number of resources available,
Specific constraints related to these resources must also be taken into account, such as the number of hours a person works, their ability to use certain machines, or compatibility between pieces of equipment.
-Timefold Solver helps Java^TM^ programmers solve constraint satisfaction problems efficiently.
+Timefold Solver helps Java^TM^, Kotlin and Python programmers solve constraint satisfaction problems efficiently.
Under the hood, it combines optimization heuristics and metaheuristics with very efficient score calculation.
@@ -95,7 +95,7 @@ Some problems have three or more levels of constraints, for example hard, medium
These constraints define the _score calculation_ (AKA __fitness function__) of a planning problem.
Each solution of a planning problem can be graded with a score.
-**With Timefold Solver, score constraints are written in an Object Oriented language, such as Java^TM^ code**.
+**With Timefold Solver, problems are modeled in an object-oriented paradigm in languages such as Java^TM^, Kotlin or Python.**.
Such code is easy, flexible and scalable.
@@ -115,7 +115,8 @@ As you can see in the examples, most instances have a lot more possible solution
Timefold Solver supports several optimization algorithms to efficiently wade through that incredibly large number of possible solutions.
Depending on the use case, some optimization algorithms perform better than others, but it's impossible to tell in advance.
-**With Timefold Solver, it is easy to switch the optimization algorithm**, by changing the solver configuration in a few lines of XML or code.
+**With Timefold Solver, it is easy to switch the optimization algorithm**,
+by changing the solver configuration in a few lines of code.
[#timefoldSolverStatus]
== Status of Timefold Solver
@@ -123,6 +124,7 @@ Depending on the use case, some optimization algorithms perform better than othe
Timefold Solver is 100% pure Java^TM^ and runs on Java {java-version} or higher.
It xref:integration/integration.adoc#integration[integrates very easily] with other Java^TM^ technologies.
Timefold Solver works on any Java Virtual Machine and is compatible with the major JVM languages and all major platforms.
+It also supports Kotlin and Python.
image::introduction/compatibility.png[align="center"]
@@ -156,15 +158,24 @@ To find out more, see xref:enterprise-edition/enterprise-edition.adoc[Enterprise
[#backwardsCompatibility]
== Backwards compatibility
-Timefold Solver separates its API and implementation:
-
-* **Public API**: All classes in the package namespace *ai.timefold.solver.core.api*, *ai.timefold.solver.benchmark.api*, *ai.timefold.solver.test.api* and *ai.timefold.solver...api* are 100% *backwards compatible* in future releases (especially minor and hotfix releases).
-In rare circumstances, if the major version number changes, a few specific classes might have a few backwards incompatible changes, but those will be clearly documented in https://timefold.ai/docs/[the upgrade recipe].
-* **XML configuration**: The XML solver configuration is backwards compatible for all elements, except for elements that require the use of non-public API classes.
-The XML solver configuration is defined by the classes in the package namespace *ai.timefold.solver.core.config* and *ai.timefold.solver.benchmark.config*.
+Timefold Solver separates its API from its implementation:
+
+* **Public API**: All classes in the following package namespaces are 100% *backwards compatible* in future releases,
+especially minor and hotfix releases:
+** `ai.timefold.solver.core.api`
+** `ai.timefold.solver.benchmark.api`
+** `ai.timefold.solver.test.api`
+** `ai.timefold.solver...api*`
+* **Configuration**: The solver configuration is backwards compatible for all elements,
+except for elements that require the use of non-public API classes.
+The solver configuration is defined by the classes in the following package namespaces:
+** `ai.timefold.solver.core.config`
+** `ai.timefold.solver.benchmark.config`
* **Implementation classes**: All other classes are _not_ backwards compatible.
-They will change in future major or minor releases (but probably not in hotfix releases).
-https://timefold.ai/docs/[The upgrade recipe] describes every such relevant change and on how to quickly deal with it when upgrading to a newer version.
+They will change in future major or minor releases,
+but probably not in hotfix releases.
+
+Backwards incompatible changes will be clearly documented in xref:upgrade-and-migration/upgrade-to-latest-version.adoc#manualUpgrade[the upgrade recipe].
[NOTE]
@@ -244,84 +255,14 @@ dependencies {
----
--
====
+
See also how to xref:enterprise-edition/enterprise-edition.adoc#switchToEnterpriseEdition[switch To Enterprise Edition].
+To find out how to get started with Timefold Solver,
+see xref:quickstart/overview.adoc[Quickstarts].
+
[#useWithOtherBuildTools]
=== Upgrade to the latest version
-Quickly upgrade your source code to the latest and greatest version of Timefold Solver,
-run this command:
-
-[tabs]
-====
-Maven::
-+
---
-[source,shell,subs=attributes+]
-----
-$ mvn org.openrewrite.maven:rewrite-maven-plugin:LATEST:run \
- -Drewrite.recipeArtifactCoordinates=ai.timefold.solver:timefold-solver-migration:{timefold-solver-version} \
- -Drewrite.activeRecipes=ai.timefold.solver.migration.ToLatest
-----
---
-Gradle::
-+
---
-[source,shell,subs=attributes+]
-----
-$ curl https://timefold.ai/product/upgrade/upgrade-timefold.gradle > upgrade-timefold.gradle
-$ gradle -Dorg.gradle.jvmargs=-Xmx2G --init-script upgrade-timefold.gradle rewriteRun -DtimefoldSolverVersion={timefold-solver-version}
-$ rm upgrade-timefold.gradle
-----
---
-====
-
-It automatically replaces deprecated methods with better alternatives.
-Run your tests and commit the changes.
-
-[#buildFromSource]
-=== Build Timefold Solver from source
-
-*Prerequisites*
-
-* Set up https://git-scm.com/[Git].
-* Authenticate on GitHub using either HTTPS or SSH.
-** See https://help.github.com/articles/set-up-git/[GitHub] for more information about setting up and authenticating Git.
-* Set up http://maven.apache.org/[Maven].
-
-Build and run the examples from source.
-
-. Clone `timefold` from GitHub (or alternatively, download https://github.com/TimefoldAI/timefold-solver/zipball/main[the zipball]):
-+
-[source,sh,options="nowrap"]
-----
-$ git clone https://github.com/TimefoldAI/timefold-solver.git
-...
-----
-
-. Build it with Maven:
-+
-[source,sh,options="nowrap"]
-----
-$ cd timefold
-$ mvn clean install -DskipTests
-...
-----
-+
-[NOTE]
-====
-The first time, Maven might take a long time, because it needs to download jars.
-====
-
-. Run the examples:
-+
-[source,sh,options="nowrap"]
-----
-$ cd examples
-$ mvn exec:java
-...
-----
-
-. Edit the sources in your favorite IDE.
-
+See the dedicated section on xref:upgrade-and-migration/upgrade-to-latest-version.adoc[Upgrade and Migration].
diff --git a/docs/src/modules/ROOT/pages/quickstart/.quickstart.adoc b/docs/src/modules/ROOT/pages/quickstart/.quickstart.adoc
index 82cfcfdad9..4a6fc8078a 100644
--- a/docs/src/modules/ROOT/pages/quickstart/.quickstart.adoc
+++ b/docs/src/modules/ROOT/pages/quickstart/.quickstart.adoc
@@ -4,7 +4,7 @@
:sectnums:
:icons: font
-include::overview/overview-quickstarts.adoc[leveloffset=+1]
+include::overview.adoc[leveloffset=+1]
include::hello-world/hello-world-quickstart.adoc[leveloffset=+1]
diff --git a/docs/src/modules/ROOT/pages/quickstart/hello-world/hello-world-quickstart.adoc b/docs/src/modules/ROOT/pages/quickstart/hello-world/hello-world-quickstart.adoc
index 4f78552285..db75a2af3a 100644
--- a/docs/src/modules/ROOT/pages/quickstart/hello-world/hello-world-quickstart.adoc
+++ b/docs/src/modules/ROOT/pages/quickstart/hello-world/hello-world-quickstart.adoc
@@ -122,7 +122,7 @@ Your `pom.xml` file has the following content:
ch.qos.logback
logback-classic
- 1.4.14
+ {logback-version}
@@ -151,7 +151,7 @@ Your `pom.xml` file has the following content:
org.codehaus.mojo
exec-maven-plugin
- 3.0.0
+ {exec-maven-plugin-version}
org.acme.schooltimetabling.TimetableApp
@@ -175,7 +175,7 @@ plugins {
}
def timefoldSolverVersion = "{timefold-solver-version}"
-def logbackVersion = "1.4.14"
+def logbackVersion = "{logback-version}"
group = "org.acme"
version = "1.0-SNAPSHOT"
diff --git a/docs/src/modules/ROOT/pages/quickstart/overview/overview-quickstarts.adoc b/docs/src/modules/ROOT/pages/quickstart/overview.adoc
similarity index 100%
rename from docs/src/modules/ROOT/pages/quickstart/overview/overview-quickstarts.adoc
rename to docs/src/modules/ROOT/pages/quickstart/overview.adoc
diff --git a/docs/src/modules/ROOT/pages/upgrade-and-migration/.upgrade-and-migration.adoc b/docs/src/modules/ROOT/pages/upgrade-and-migration/.upgrade-and-migration.adoc
new file mode 100644
index 0000000000..dffb3e095f
--- /dev/null
+++ b/docs/src/modules/ROOT/pages/upgrade-and-migration/.upgrade-and-migration.adoc
@@ -0,0 +1,9 @@
+[#upgradeAndMigration]
+= Upgrade and Migration
+:doctype: book
+:sectnums:
+:icons: font
+
+include::overview.adoc[leveloffset=+1]
+include::upgrade-to-latest-version.adoc[leveloffset=+1]
+include::migrate-from-optaplanner.adoc[leveloffset=+1]
\ No newline at end of file
diff --git a/docs/src/modules/ROOT/pages/upgrade-and-migration/migrate-from-optaplanner.adoc b/docs/src/modules/ROOT/pages/upgrade-and-migration/migrate-from-optaplanner.adoc
new file mode 100644
index 0000000000..d2e9690b60
--- /dev/null
+++ b/docs/src/modules/ROOT/pages/upgrade-and-migration/migrate-from-optaplanner.adoc
@@ -0,0 +1,61 @@
+[#migrateFromOptaPlanner]
+= Migrate from OptaPlanner
+:doctype: book
+:sectnums:
+:icons: font
+
+Timefold Solver is a faster, feature-rich and actively developed fork of OptaPlanner.
+Upgrading from OptaPlanner to Timefold Solver Community Edition only takes two minutes.
+Run the command below to upgrade your code automatically.
+
+[tabs]
+====
+Maven::
++
+--
+[source,shell,subs=attributes+]
+----
+mvn org.openrewrite.maven:rewrite-maven-plugin:{rewrite-maven-plugin-version}:run -Drewrite.recipeArtifactCoordinates=ai.timefold.solver:timefold-solver-migration:{timefold-solver-version} -Drewrite.activeRecipes=ai.timefold.solver.migration.ToLatest
+----
+--
+
+Gradle::
++
+--
+[source,shell,subs=attributes+]
+----
+curl https://timefold.ai/product/upgrade/upgrade-timefold.gradle > upgrade-timefold.gradle ; gradle -Dorg.gradle.jvmargs=-Xmx2G --init-script upgrade-timefold.gradle rewriteRun -DtimefoldSolverVersion={timefold-solver-version} ; rm upgrade-timefold.gradle
+----
+--
+====
+
+Having done that, do a test run of the solver and commit the changes.
+If it doesn't work, just revert it instead and
+https://github.com/timefoldai/timefold-solver/issues[submit an issue].
+We'll fix it with the highest priority.
+
+Timefold Solver 1.x is backward compatible with OptaPlanner 8.x,
+except for the following changes:
+
+* Minimum Java 17 (LTS). Java 21 (LTS) and the latest Java version are also supported.
+* The Maven/Gradle GAVs changed:
+** The groupId changed from `org.optaplanner` to `ai.timefold.solver`.
+** The artifactIds changed from `optaplanner-\*` to `timefold-solver-*`.
+** ArtifactIds containing `peristence-` changed from `optaplanner-persistence-\*` to `timefold-solver-*`.
+*** For example, `optaplanner-persistence-jackson` changed to `timefold-solver-jackson`.
+* The import statements changed accordingly:
+** `import org.optaplanner...;` changed to `import ai.timefold.solver...;`.
+** `import org.optaplanner.persistence...;` changed to `import ai.timefold.solver...;` too.
+* The JEE dependencies changed from `javax` to `jakarta` to accommodate Spring 3 and Quarkus 3.
+** This is the same difference as between OptaPlanner 8.x and OptaPlanner 9.x.
+* The `OptaPlannerJacksonModule` class is now called `TimefoldJacksonModule`.
+* The deprecated `scoreDRL` support is removed, because Drools with its transitive dependencies have been removed entirely.
+* The unsecure module `persistence-xstream` is removed, because of old, unresolved CVEs in XStream.
+* The deprecated, undocumented `ScoreHibernateType` has been removed because of Jakarta.
+Use JPA's `ScoreConverter` instead.
+
+Timefold Solver 1.x does not support `scoreDRL`, nor is it upgraded automatically.
+If you're still using `scoreDRL` from OptaPlanner 7.x,
+please link:https://timefold.ai/blog/2023/migrating-score-drl-to-constraint-streams/[upgrade to Constraint Streams first].
+
+All other deprecated code remains to make upgrading easy.
diff --git a/docs/src/modules/ROOT/pages/upgrade-and-migration/overview.adoc b/docs/src/modules/ROOT/pages/upgrade-and-migration/overview.adoc
new file mode 100644
index 0000000000..a39d247c80
--- /dev/null
+++ b/docs/src/modules/ROOT/pages/upgrade-and-migration/overview.adoc
@@ -0,0 +1,18 @@
+[#upgradeAndMigrationOverview]
+= Upgrade and Migration: Overview
+:doctype: book
+:sectnums:
+:icons: font
+
+In this section, you will:
+
+- Learn how to upgrade from OptaPlanner to Timefold Solver.
+- Learn how to upgrade to the latest version of Timefold Solver automatically.
+- Find a detailed upgrade recipe for every major change we make to the Timefold Solver APIs.
+
+To find out what's new in the latest version of Timefold Solver,
+see the https://github.com/TimefoldAI/timefold-solver/releases[Timefold Solver release notes]
+in our Github repository,
+read https://timefold.ai/blog[our blog]
+or subscribe to our newsletter.
+
diff --git a/docs/src/modules/ROOT/pages/upgrade-and-migration/upgrade-to-latest-version.adoc b/docs/src/modules/ROOT/pages/upgrade-and-migration/upgrade-to-latest-version.adoc
new file mode 100644
index 0000000000..abfb5ae1c1
--- /dev/null
+++ b/docs/src/modules/ROOT/pages/upgrade-and-migration/upgrade-to-latest-version.adoc
@@ -0,0 +1,211 @@
+[#upgradeToLatestVersion]
+= Upgrade to the latest version
+:doctype: book
+:sectnums:
+:icons: font
+
+Timefold Solver public APIs are backwards compatible,
+but users often also use internal Solver classes which are not guaranteed to stay compatible.
+This upgrade recipe minimizes the pain to upgrade your code
+and to take advantage of the newest features in Timefold Solver.
+
+[#automaticUpgradeToLatestVersion]
+== Automatic upgrade to the latest version
+
+For many of the upgrade steps mentioned later,
+we actually provide a migration tool that can automatically apply those changes to Java files.
+This tool is based on OpenRewrite and can be run as a Maven or Gradle plugin.
+To run the tool, execute the following command in your project directory:
+
+[tabs]
+====
+Maven::
++
+--
+[source,shell,subs=attributes+]
+----
+mvn org.openrewrite.maven:rewrite-maven-plugin:{rewrite-maven-plugin-version}:run -Drewrite.recipeArtifactCoordinates=ai.timefold.solver:timefold-solver-migration:{timefold-solver-version} -Drewrite.activeRecipes=ai.timefold.solver.migration.ToLatest
+----
+--
+
+Gradle::
++
+--
+[source,shell,subs=attributes+]
+----
+curl https://timefold.ai/product/upgrade/upgrade-timefold.gradle > upgrade-timefold.gradle ; gradle -Dorg.gradle.jvmargs=-Xmx2G --init-script upgrade-timefold.gradle rewriteRun -DtimefoldSolverVersion={timefold-solver-version} ; rm upgrade-timefold.gradle
+----
+--
+====
+
+Having done that, you can check the local changes and commit them.
+Note that none of the upgrade steps could be automatically applied,
+and it may still be worth your while to read the rest the upgrade recipe below.
+
+For the time being,
+Kotlin users need to follow the upgrade recipe and apply the steps manually.
+
+[#manualUpgrade]
+== Manual upgrade recipe
+
+Every upgrade note indicates how likely your code will be affected by that change:
+
+- icon:magic[] *Automated*: Can be applied automatically using our <>.
+- icon:exclamation-triangle[role=red] *Major*: Likely to affect your code.
+- icon:info-circle[role=yellow] *Minor*: Unlikely to affect your code, unless you use internal classes.
+- icon:eye[] *Recommended*: Not a backwards-incompatible change, but you probably want to do this.
+
+The upgrade recipe often lists the changes as they apply to Java code.
+Kotlin users should translate the changes accordingly.
+
+=== Upgrade from 1.7.0 to 1.8.0
+
+.icon:exclamation-triangle[role=red] Constraint Verifier: Check your tests if you use the planning list variable
+[%collapsible%open]
+====
+In some cases,
+especially if you've reused our https://github.com/TimefoldAI/timefold-quickstarts/tree/stable/use-cases/food-packaging[Food Packaging quickstart],
+you may see your tests failing after the upgrade.
+This is due to a bug fix in xref:constraints-and-score/score-calculation.adoc#constraintStreams[Constraint Streams],
+which now currently handles values not present in any list variable.
+
+If your code has a shadow entity
+whose xref:using-timefold-solver/modeling-planning-problems.adoc#listVariableShadowVariablesInverseRelation[inverse relation shadow variable] is a planning list variable
+and your test leaves that reference `null`,
+the constraints will no longer take that shadow entity into account.
+This will result in `ConstraintVerifier` failing the test,
+as the expected number of penalties/rewards will no longer match the actual number.
+
+You can solve this problem by manually assigning a value to the inverse relation shadow variable.
+
+Before in `*ConstraintProviderTest.java`:
+
+[source,java]
+----
+Job job = new Job("job1", ...);
+
+constraintVerifier.verifyThat(FoodPackagingConstraintProvider::dueDateTime)
+ .given(job)
+ .penalizesBy(...);
+----
+
+After in `*ConstraintProviderTest.java`:
+
+[source,java]
+----
+Job job = new Job("job1", ...);
+Line line = new Line("line1", ...);
+job.setLine(line);
+
+constraintVerifier.verifyThat(FoodPackagingConstraintProvider::dueDateTime)
+ .given(job)
+ .penalizesBy(...);
+----
+
+The aforementioned quickstart unfortunately did not follow our own guidance on the use of shadow variables,
+which is why it exposed this bug.
+====
+
+'''
+
+.icon:magic[] Constraint Streams: Rename `forEachIncludingNullVars` to `forEachIncludingUnassigned`
+[%collapsible%open]
+====
+To better align with the newly introduced support for
+xref:using-timefold-solver/modeling-planning-problems.adoc#planningListVariableAllowingUnassigned[unassigned values in list variables],
+several methods in xref:constraints-and-score/score-calculation.adoc#constraintStreams[Constraint Streams]
+which dealt with `null` variable values have been renamed.
+
+Before in `*ConstraintProvider.java`:
+
+[source,java]
+----
+Constraint myConstraint(ConstraintFactory constraintFactory) {
+ return constraintFactory.forEachIncludingNullVars(Shift.class)
+ ...;
+}
+----
+
+After in `*ConstraintProvider.java`:
+
+[source,java]
+----
+Constraint myConstraint(ConstraintFactory constraintFactory) {
+ return constraintFactory.forEachIncludingUnassigned(Shift.class)
+ ...;
+}
+----
+
+Similarly, the following methods on `UniConstraintStream` have been renamed:
+
+* `ifExistsIncludingNullVars` to `ifExistsIncludingUnassigned`,
+* `ifExistsOtherIncludingNullVars` to `ifExistsOtherIncludingUnassigned`,
+* `ifNotExistsIncludingNullVars` to `ifNotExistsIncludingUnassigned`,
+* `ifNotExistsOtherIncludingNullVars` to `ifNotExistsOtherIncludingUnassigned`.
+
+On `BiConstraintStream` and its `Tri` and `Quad` counterparts, the following methods have been renamed as well:
+
+* `ifExistsIncludingNullVars` to `ifExistsIncludingUnassigned`,
+* `ifNotExistsIncludingNullVars` to `ifNotExistsIncludingUnassigned`.
+====
+
+'''
+
+.icon:magic[] Rename `nullable` attribute of `@PlanningVariable` to `allowsUnassigned`
+[%collapsible%open]
+====
+To better align with the newly introduced support for
+xref:using-timefold-solver/modeling-planning-problems.adoc#planningListVariableAllowingUnassigned[unassigned values in list variables],
+the `nullable` attribute of `@PlanningVariable` has been renamed to `allowsUnassigned`.
+
+Before in `*.java`:
+
+[source,java]
+----
+@PlanningVariable(nullable = true)
+private Bed bed;
+----
+
+After in `*.java`:
+
+[source,java]
+----
+@PlanningVariable(allowsUnassigned = true)
+private Bed bed;
+----
+====
+
+'''
+
+.icon:magic[] Constraint Verifier: assertion methods `message` argument comes first now
+[%collapsible%open]
+====
+To better align with the newly introduced support for testing justifications and indictments,
+the assertion methods which accepted a `message` argument now have it as the first argument.
+
+Before in `*ConstraintProviderTest.java`:
+
+[source,java]
+----
+constraintVerifier.verifyThat(MyConstraintProvider::myConstraint)
+ .given()
+ .penalizesBy(0, "There should no penalties");
+----
+
+After in `*ConstraintProvider.java`:
+
+[source,java]
+----
+constraintVerifier.verifyThat(MyConstraintProvider::myConstraint)
+ .given()
+ .penalizesBy("There should no penalties", 0);
+----
+
+Similarly to the `penalizesBy` method, the following methods were also affected:
+
+* `penalizes`,
+* `rewards`,
+* `rewardsWith`.
+====
+
+