Docs & Rules

TimefoldAI/timefold-solverGitHubLast refreshed Oct 3, 2026

This is a public, read-only report. Striff reads this repository's docs, turns each sentence that makes a claim about the code into a rule, and checks the rule against the code on the default branch. How this works

2 names in these docs no longer match the code.

main965e875listed 7h ago

Is this yours? Install to manage it

Once installed, Striff checks every pull request.

60
58
58
58
58
58
2
2
2
2
43
6
4
26
7
4
1 stale
4
4
7
1 stale
2
1
1
5
2
38 docs, listed Oct 3
TimefoldAI/timefold-solverOpen repository

Names these docs write that the code no longer has2

Each sentence below names a type the default branch doesn't declare, or declares somewhere else. Edit the doc so it matches the code, or bring the type back.

  • Goneai.timefold.solver.jackson.api.score.analysis.AbstractScoreAnalysisJacksonDeserializer
    import ai.timefold.solver.jackson.api.score.analysis.AbstractScoreAnalysisJacksonDeserializer;

    The repository once held persistence/jackson/src/main/java/ai/timefold/solver/jackson/api/score/analysis/AbstractScoreAnalysisJacksonDeserializer.java. It doesn't now.

    View on GitHub

  • Goneai.timefold.solver.spring.boot.autoconfigure.TimefoldAutoConfiguration
    import ai.timefold.solver.spring.boot.autoconfigure.TimefoldAutoConfiguration;

    ai.timefold.solver.spring.boot.autoconfigure declares nothing by this name. Closest names there: TimefoldBenchmarkAutoConfiguration, TimefoldSolverAutoConfiguration, TimefoldJacksonConfiguration.

    View on GitHub

TimefoldAI/timefold-solver — documented rules

60 of 60 rules, printed October 3, 2026.

The sentence in your docs
Read Oct 3
✅ interface Solver → class DefaultSolver;
DefaultSolver implements Solver
Holds
Read Oct 3
✅ abstract class AbstractPhase;
AbstractPhase is declared abstract
Holds
Read Oct 3
On UniConstraintStream, the ifExistsOther() building block is also available which is useful in situations where the forEach() constraint match type is the same as the ifExists() type.
UniConstraintStream has an ifExistsOther method
HoldsPR #2431 · Sep 30
Read Oct 3
Conversely, if the ifNotExists() building block is used (as well as the ifNotExistsOther() building block on UniConstraintStream) you can achieve the opposite effect:
UniConstraintStream has an ifNotExistsOther method
HoldsPR #2431 · Sep 30
Read Oct 3
The utility method SolutionManager::updateShadowVariables reads the entity and updates its shadow variables.
SolutionManager has an updateShadowVariables method
Holds
Read Oct 3
If you don't need to control the score type, extend AbstractSimpleModel instead. It implements SolverModel<HardMediumSoftScore> and includes the @PlanningScore field, so you only need to define your problem facts and planning entities:
AbstractSimpleModel implements SolverModel
Holds
Read Oct 3
Per step, the QueuedEntityPlacer selects one uninitialized entity from the EntitySelector and applies the winning Move (out of all the moves for that entity generated by the `MoveSelector`).
QueuedEntityPlacer depends on EntitySelector
Holds
Read Oct 3
Per step, the QueuedEntityPlacer selects one uninitialized entity from the EntitySelector and applies the winning Move (out of all the moves for that entity generated by the `MoveSelector`).
QueuedEntityPlacer depends on MoveSelector
Holds
Read Oct 3
Per step, the QueuedEntityPlacer selects one uninitialized entity from the EntitySelector and applies the winning Move (out of all the moves for that entity generated by the `MoveSelector`).
QueuedEntityPlacer depends on Move
Holds
Read Oct 3
Per step, the PooledEntityPlacer applies the winning Move (out of all the moves for that entity generated by the `MoveSelector`).
PooledEntityPlacer depends on Move
Holds
Read Oct 3
Per step, the PooledEntityPlacer applies the winning Move (out of all the moves for that entity generated by the `MoveSelector`).
MoveSelector depends on Move
Holds
Read Oct 3
Per step, the PooledEntityPlacer applies the winning Move (out of all the moves for that entity generated by the `MoveSelector`).
PooledEntityPlacer depends on MoveSelector
Holds
Read Oct 3
Therefore, implement the NearbyDistanceMeter interface:
NearbyDistanceMeter is a contract
Holds
Read Oct 3
For information on the Move interface, check out the xref:optimization-algorithms/neighborhoods.adoc#neighborhoodsMove[Move Anatomy] section of the Neighborhoods chapter.
Move is a contract
HoldsPR #2431 · Sep 30
Read Oct 3
The easiest way to generate custom moves is by implementing the interface `MoveListFactory`:
MoveListFactory is a contract
HoldsPR #2431 · Sep 30
Read Oct 3
Use this advanced form to generate custom moves Just In Time by implementing the MoveIteratorFactory interface:
MoveIteratorFactory is a contract
HoldsPR #2431 · Sep 30
Read Oct 3
The Move interface specifies the following methods that must be implemented: void execute(MutableSolutionView solutionView):: This method makes changes to the given solution.
Move depends on MutableSolutionView
Holds
Read Oct 3
In technical terms, a move is an implementation of the ai.timefold.solver.core.preview.api.move.Move interface.
Move is a contract
Holds
Read Oct 3
In technical terms, a move is an implementation of the ai.timefold.solver.core.preview.api.move.Move interface.
Move is in ai.timefold.solver.core.preview.api.move
Holds
Read Oct 3
void execute(MutableSolutionView solutionView):: This method makes changes to the given solution.
Move has an execute method
Holds
Read Oct 3
Collection<Object> getPlanningEntities(),
Move has a getPlanningEntities method
Holds
Read Oct 3
and Collection<Object> getPlanningValues().
Move has a getPlanningValues method
Holds
Read Oct 3
Move rebase(Lookup lookup):: This method creates a copy of the move that is applicable to a different working solution.
Move has a rebase method
HoldsPR #2431 · Sep 30
Read Oct 3
solutionView.changeVariable(...) is a method provided by MutableSolutionView that safely changes the value of a planning variable on a planning entity.
MutableSolutionView has a changeVariable method
HoldsPR #2431 · Sep 30
Read Oct 3
other examples include specialized methods such as swapValuesBetweenLists(...).
MutableSolutionView has a swapValuesBetweenLists method
HoldsPR #2431 · Sep 30
Read Oct 3
The entry point for enumeration is the MoveStreamFactory.forEach method, which operates much like the xref:constraints-and-score/score-calculation.adoc#constraintStreamsForEach[forEach() method of Constraint Streams].
MoveStreamFactory has a forEach method
HoldsPR #2431 · Sep 30
Read Oct 3
In this case, we have used the SolutionView.isValueInRange method to check whether the MONDAY_MORNING timeslot is a valid value for the timeslotVariable of the given lesson.
SolutionView has an isValueInRange method
HoldsPR #2431 · Sep 30
Read Oct 3
In the previous example, we created a built-in change move using the Moves.change factory method, which takes the planning variable meta-model, the entity to change, and the new value to assign to the variable.
Moves has a change method
HoldsPR #2431 · Sep 30
Read Oct 3
To use the Neighborhoods API, you need to implement the ai.timefold.solver.core.preview.api.neighborhood.NeighborhoodProvider interface and reference the implementation class in the solver configuration like so:
NeighborhoodProvider is a contract
HoldsPR #2431 · Sep 30
Read Oct 3
Use the add method to include every single move provider you want to use in the neighborhood.
NeighborhoodBuilder has an add method
HoldsPR #2431 · Sep 30
Read Oct 3
Finally, call the build method to create the Neighborhood instance.
NeighborhoodBuilder has a build method
HoldsPR #2431 · Sep 30
Read Oct 3
MoveStreamFactory.pick(), handing the rows to the <<neighborhoodsPicking,picking layer>> below, or asCachedDataset(), handing you the dataset handle to resolve and pick yourself.
MoveStreamFactory has a pick method
HoldsPR #2431 · Sep 30
Read Oct 3
For those cases, terminate your enumerating streams with asCachedDataset() instead of pick(), then supply your own Iterator<Move> that combines the resulting dataset handles however you need.
UniEnumeratingStream has an asCachedDataset method
Holds
Read Oct 3
For those cases, terminate your enumerating streams with asCachedDataset() instead of pick(), then supply your own Iterator<Move> that combines the resulting dataset handles however you need.
BiEnumeratingStream has an asCachedDataset method
Holds
Read Oct 3
UniEnumeratingStream#asCachedDataset() and BiEnumeratingStream#asCachedDataset() turn a stream into a UniDataset/BiDataset handle, mirroring pick() but without committing to the declarative picking approach. Call UniDataset#join(...) to correlate a cached dataset with another stream, using the same joiners as pick().
UniDataset has a join method
Holds
Read Oct 3
Inside buildMoveStream(...), the MoveIteratorSession resolves each dataset handle to a UniDatasetInstance or BiDatasetInstance, which exposes size(), iterator(RandomGenerator) and exhaustiveIterator(RandomGenerator) - the same random-walk picking used throughout Move Streams.
UniDatasetInstance has a size method
Holds
Read Oct 3
Inside buildMoveStream(...), the MoveIteratorSession resolves each dataset handle to a UniDatasetInstance or BiDatasetInstance, which exposes size(), iterator(RandomGenerator) and exhaustiveIterator(RandomGenerator) - the same random-walk picking used throughout Move Streams.
UniDatasetInstance has an iterator method
Holds
Read Oct 3
Inside buildMoveStream(...), the MoveIteratorSession resolves each dataset handle to a UniDatasetInstance or BiDatasetInstance, which exposes size(), iterator(RandomGenerator) and exhaustiveIterator(RandomGenerator) - the same random-walk picking used throughout Move Streams.
UniDatasetInstance has an exhaustiveIterator method
Holds
Read Oct 3
Inside buildMoveStream(...), the MoveIteratorSession resolves each dataset handle to a UniDatasetInstance or BiDatasetInstance, which exposes size(), iterator(RandomGenerator) and exhaustiveIterator(RandomGenerator) - the same random-walk picking used throughout Move Streams.
BiDatasetInstance has a size method
Holds
Read Oct 3
Inside buildMoveStream(...), the MoveIteratorSession resolves each dataset handle to a UniDatasetInstance or BiDatasetInstance, which exposes size(), iterator(RandomGenerator) and exhaustiveIterator(RandomGenerator) - the same random-walk picking used throughout Move Streams.
BiDatasetInstance has an iterator method
Holds
Read Oct 3
Inside buildMoveStream(...), the MoveIteratorSession resolves each dataset handle to a UniDatasetInstance or BiDatasetInstance, which exposes size(), iterator(RandomGenerator) and exhaustiveIterator(RandomGenerator) - the same random-walk picking used throughout Move Streams.
BiDatasetInstance has an exhaustiveIterator method
Holds
Read Oct 3
The defineNeighborhood method receives a NeighborhoodBuilder instance that you can use to include your move provider:
NeighborhoodProvider has a defineNeighborhood method
HoldsPR #2431 · Sep 30
Read Oct 3
The PhaseCommandContext interface offers the following methods: Object getWorkingSolution():: Returns the working solution of the Solver that is being solved.
PhaseCommandContext has a getWorkingSolution method
Holds
Read Oct 3
Use the isFeasible() method on `BestSolutionChangedEvent's new best Score` to detect such cases.
Score has an isFeasible method
Holds
Read Oct 3
Use the event's isNewBestSolutionInitialized() method to only ignore uninitialized solutions, but also accept infeasible solutions.
BestSolutionChangedEvent has an isNewBestSolutionInitialized method
Holds
Read Oct 3
The PhaseCommand interface appears as follows:
PhaseCommand is a contract
Holds
Read Oct 3
The PhaseCommandContext interface offers the following methods:
PhaseCommandContext is a contract
Holds
Read Oct 3
If you want a fresh score computed, these methods have counterparts, executeAndCalculateScore and executeTemporarilyAndCalculateScore.
PhaseCommandContext has an executeAndCalculateScore method
Holds
Read Oct 3
If you want a fresh score computed, these methods have counterparts, executeAndCalculateScore and executeTemporarilyAndCalculateScore.
PhaseCommandContext has an executeTemporarilyAndCalculateScore method
Holds
Read Oct 3
The getConstraintWeightOverrides() method is required by the SolverModel interface (implemented by AbstractSimpleModel), and setConstraintWeightOverrides() is optional.
AbstractSimpleModel implements SolverModel
Holds
Read Oct 3
The getConstraintWeightOverrides() method is required by the SolverModel interface (implemented by AbstractSimpleModel), and setConstraintWeightOverrides() is optional.
SolverModel has a getConstraintWeightOverrides method
Holds
Read Oct 3
Using the service module, most of the REST Endpoints will be automatically generated thanks to the use of the ModelInput, ModelOutput and SolverModel interfaces. You didn't have to implement those interfaces in this guide, because they were already implemented for you in the AbstractSimpleModel.
AbstractSimpleModel implements ModelInput
Holds
Read Oct 3
Using the service module, most of the REST Endpoints will be automatically generated thanks to the use of the ModelInput, ModelOutput and SolverModel interfaces. You didn't have to implement those interfaces in this guide, because they were already implemented for you in the AbstractSimpleModel.
AbstractSimpleModel implements ModelOutput
Holds
Read Oct 3
The BenchmarkAggregator provides the following public methods: aggregateBenchmarks(PlannerBenchmarkConfig):: Aggregates all benchmark results found in the configured directory.
BenchmarkAggregator has an aggregateBenchmarks method
Holds
Read Oct 3
It does that through the SolutionFileIO interface which has a read and write method:
SolutionFileIO has a read method
Holds
Read Oct 3
It does that through the SolutionFileIO interface which has a read and write method:
SolutionFileIO has a write method
Holds
Read Oct 3
aggregateSelectedBenchmarks(PlannerBenchmarkConfig, List<String>):: Aggregates benchmark results from specific subdirectories by name.
BenchmarkAggregator has an aggregateSelectedBenchmarks method
Holds
Read Oct 3
getAvailableBenchmarkDirectories(PlannerBenchmarkConfig):: Returns list of available benchmark subdirectories with valid results.
BenchmarkAggregator has a getAvailableBenchmarkDirectories method
Holds
Read Oct 3
ModelConfigOverrides is a marker interface from ai.timefold.solver.service.definition.api.
ModelConfigOverrides is a contract, and ModelConfigOverrides is in ai.timefold.solver.service.definition.api
Holds
Read Oct 3
Its accessor is named outputMetrics(), not kpis();
ModelResponse has an outputMetrics method
Holds
60 rules from 11 docs

See these where the change is. The browser extension puts a pull request's doc findings, and a diagram of what it changed, on the GitHub page itself, so the code and what your docs say about it are side by side.