releases
solverforge 0.26.x: Replica Directors Without Descriptor Types, Proportional Debug Info, and Late Acceptance That Keeps Its History
The 0.26.x line builds replica-forking score directors with ScoreDirector::with_replicas, keeps stream closure types proportional to the constraint set so large apps link with full debug info, and documents every public item the planning macros generate. 0.26.1 keeps the late-acceptance score history across soft-level improvements and draws scalar ruin batches without an entity-sized permutation.
solverforge 0.26.x follows solverforge 0.25.1; the line was published on
crates.io from 2026-11-10 through 2026-11-11 and closes with
0.26.1, with a Rust 1.95
floor. At publication cargo add solverforge installed 0.26.1, and docs.rs
served the 0.26.1 API. The public modeling, stream, configuration, and solving
APIs are unchanged across the line; the one breaking change, in 0.26.0, is in the
lower-level solverforge-scoring score-director constructors. 0.26.1 changes
no public API: late acceptance keeps its score history unless a hard or medium
level of the best score changes, and scalar ruin-and-recreate draws its batches
without an entity-sized permutation. solverforge-cli 4.0.4
scaffolds 0.26.1, and all eight worked use cases pin it.
What Changed
Late acceptance keeps its score history (0.26.1)
late_acceptance keeps its score history across a new best score unless a
hard or medium level of the best score changes: the hard level of a hard/soft
score, the hard and medium levels of a hard/medium/soft score, or a hard level
of a bendable score. Only then is the history refilled with the new best, so
entries from before a feasibility change cannot admit moves that keep the new
hard level but give up the soft levels. An improvement in soft levels alone
leaves the history as it is, and a score with only soft levels never refills
it.
Through 0.26.0 the acceptor refilled the whole history on every new best score.
After the first improvement every entry equalled the best, so only moves at
least as good as the best were accepted: from then on the phase ran as hill
climbing with sideways moves, and searches that rely on accepting worse moves
to leave a local optimum settled early. The configuration is unchanged:
late_acceptance_size still sets the history length, and
diversified_late_acceptance is unaffected.
The fix was contributed by slapers in #242, together with a follow-up that refills the history of a bendable score only on its hard levels.
Scalar ruin batches without an entity-sized permutation (0.26.1)
ruin_recreate_move_selector draws each batch of entities to clear from a
partial shuffle that records only the positions it displaces, so a batch costs
time and memory proportional to the ruin count instead of the entity count, and
opening the move cursor no longer resets an entity-sized permutation for every
batch. The batches and the random draws they consume are unchanged, so a seed
selects the same entities as before. In the zero-regression gate’s ruin
case, a 15-trial run against 0.26.0 measured 41.8% less wall time and 55.5%
fewer instructions
(#249).
Replica-forking directors — breaking change (0.26.0)
solverforge-scoring removes ReplicaDescriptor. A score director that forks
evaluator replicas is built with ScoreDirector::with_replicas,
with_replicas_weighted, or with_replicas_in_mode, which take the
constraint-set constructor beside a borrowed descriptor:
ScoreDirector::with_replicas(
solution,
constraints, // normally constraint_builder(&solution)
constraint_builder, // fn(&S) -> C
&descriptor, // &SolutionDescriptor
entity_counter, // fn(&S, usize) -> usize
)
The constructor is a field of the director instead of part of its descriptor
type, so a replica-capable director is ScoreDirector<S, C, &SolutionDescriptor>
and names its constraint set once. DirectorDescriptor has no type
parameters, and DirectorDescriptor::replica() is replaced by share().
Forking with Director::fork_replica() and Director::fork_for_solution(),
and rebuilding the constraint set on reset(), behave as before; a director
not built by with_replicas* returns None from both forks.
Macro-generated solves and the configured entry points build their directors
themselves, so application code that does not construct a ScoreDirector
directly needs no change. See
Extend the Solver.
Debug info stays proportional to the constraint set
The stream combinators build the closures they store (filters, weights,
extractors, joined-row adapters) in functions generic only over what each
closure captures. A closure built inside a generic method used to name every
upstream stream type, so each chained filter, penalize, or named repeated
the whole stream type, and debug-info type names grew with the length of every
constraint chain. The public stream API is unchanged.
On a 30-constraint application test build, the line-tables debug strings drop from 2.82 GB to 0.37 GB and the longest type name from 8.0 MB to 1.0 MB, and the test binary links and passes with full debug info.
Generated items are documented or hidden
Every public item the planning macros generate carries documentation or is
#[doc(hidden)]: descriptor and entity-count plumbing is hidden, and the
collection source methods and the generated <Solution>ConstraintStreams
trait and its methods are documented. A crate that enables missing_docs gets
no warnings from the macros
(#235).
Module roots hold declarations only
Every mod.rs and lib.rs in the workspace holds only module declarations and
re-exports, and each crate’s lint levels live in its Cargo [lints] table.
Public paths and behaviour are unchanged.
Published Version Boundaries
| Surface | Version | Notes |
|---|---|---|
| Last 0.26 release | solverforge 0.26.1 |
Rust 1.95; late acceptance keeps its history across soft-level improvements; scalar ruin batches without an entity-sized permutation; replica directors built by with_replicas* |
| First 0.26 release | solverforge 0.26.0 |
Replica directors built by with_replicas*; proportional stream closure types; documented generated items |
| Previous core line | solverforge 0.25.1 |
Bounded nearby list candidates; see the 0.25.x release note |
| CLI | solverforge-cli 4.0.4 |
Fresh scaffolds target core 0.26.1 (4.0.2 and 4.0.3 target 0.26.0); see the 4.0.x CLI release note |
| Python | solverforge-py 0.7.2 / PyPI solverforge 0.7.2 |
Compiled onto core 0.23.0; upgrading the Rust core does not retarget these wheels |
| Worked use cases | November 11 app releases | All eight pin core 0.26.1 and UI 0.9.0; see the use-case 3.0 release note |
| UI | solverforge-ui 0.9.1 |
Unchanged |
| Maps | solverforge-maps 2.3.3 |
Unchanged |
All nine workspace crates (solverforge, solverforge-core,
solverforge-config, solverforge-scoring, solverforge-solver,
solverforge-macros, solverforge-bridge, solverforge-console, and
solverforge-cvrp) publish 0.26.0 and 0.26.1.
The registry records the facade crate’s publication at 2026-11-10T10:33:40Z.
Its artifact SHA-256 is
62181b0bb54c94833633b2b69cb0592d98d4532e0ba204f7cce828966cc0f773;
.cargo_vcs_info.json binds it to
720fdaded42f16a131cdb8820b624b407b275d5f, the v0.26.0 tag commit. The
GitHub Release
was published at 2026-11-10T10:33:56Z.
On that commit the CI run passed. The Release workflow re-ran the CI matrix on Linux, macOS, and Windows with the Miri, MSRV, docs, and package jobs, verified the release, published to crates.io, and created the GitHub Release; every job succeeded.
The registry records the 0.26.1 facade publication at 2026-11-11T02:49:52Z.
Its artifact SHA-256 is
a00f06d8db244d619f556a25165115015bda50f01c811db0d515c9e6e3c28458;
.cargo_vcs_info.json binds it to
f3b41788e7519f3b707f8a9aa450fc545e0d5c08, the v0.26.1 tag commit. The
GitHub Release
was published at 2026-11-11T02:50:11Z. On that commit the
CI run
passed, and every job of the
Release workflow
succeeded: the Linux, macOS, Windows, Miri, MSRV, docs, and package jobs,
release verification, crates.io publication, and the GitHub Release. The
0.26.1 range also changes the zero-regression gate’s own tooling (timed trials
rotate through the code layouts, and hardware branch misses are reported
rather than graded); no published crate behaviour depends on it.
Patch History
| Version | Date | Notes |
|---|---|---|
0.26.1 |
2026-11-11 | Late acceptance refills its history only when a hard or medium level of the best score changes (contributed by slapers in #242); scalar ruin batches drawn without an entity-sized permutation; no public API change. |
0.26.0 |
2026-11-10 | ReplicaDescriptor removed: replica-forking directors are built by ScoreDirector::with_replicas*, and DirectorDescriptor::replica() becomes share(); stream closures built in narrowly generic functions; generated public items documented or hidden; declaration-only module roots. |
Upgrade Checklist
- From 0.26.0, move the
solverforgepin to0.26.1; no code changes. Alate_acceptancephase now keeps accepting worse moves after its first improvement, so re-check solve quality and any tunedlate_acceptance_sizeor termination budget on models that use it. - Replace
ScoreDirector::with_descriptor(solution, constraints, ReplicaDescriptor::new(&descriptor, constraint_builder), entity_counter)withScoreDirector::with_replicas(solution, constraints, constraint_builder, &descriptor, entity_counter), and the weighted or mode-generic forms withwith_replicas_weightedorwith_replicas_in_mode. - Spell a replica-capable director’s type as
ScoreDirector<S, C, &SolutionDescriptor>(plus the weight mode, if not the default) instead of namingReplicaDescriptor. - Drop the type parameters from
DirectorDescriptorbounds, and callshare()where a custom descriptor implementation or caller usedreplica(). - Application code that only uses the facade, the planning macros, and the
configured solve entry points upgrades by moving the
solverforgepin.
Where to read next
Read the core manual, Local Search, and Extend the Solver. The 0.26.0 compare covers its 8 commits and the 0.26.1 compare its 8 commits; the CHANGELOG lists the breaking change and both releases.