Use the generated project as the starting point, then grow solver behavior in the
application that depends on solverforge.
- Add or refine constraints when the default business rules are incomplete.
- Tune
solver.tomlwhen the search strategy, termination, or runtime settings need to change. - Use
#[planning_solution(config = "...")]when a retained solve needs a per-solution override; the callback should decorate the loadedsolver.tomlconfig rather than replace it. - Introduce custom phases, selectors, or acceptors when the built-in search flow is not a fit for the problem shape.
- Keep domain modeling in Rust and solver policy in configuration separate;
use per-solution config callbacks only to layer runtime-specific adjustments on
top of
solver.toml. - Preserve structured solver events and telemetry so the UI and service layer
remain accurate. Generated moves, evaluated moves, accepted moves, and
generation/evaluation
Durations now flow through retained telemetry exactly; only human-facing rates are derived at the edge. - Prefer small, app-specific extensions over forking the scaffold templates.
Construction heuristics now route by validated model capability rather than ad hoc special cases:
first_fitandcheapest_insertionstay generic for mixed or list-bearing targets, but scalar-only construction targets use the descriptor boundary.first_fit_decreasing,weakest_fit*,strongest_fit*,allocate_entity_from_queue, andallocate_to_value_from_queueare scalar-only. The targeted scalar variable must declare the requiredconstruction_entity_order_keyand/orconstruction_value_order_key. Generated model assembly provides the same hooks at runtime; the scalar route resolves one binding set from descriptor metadata plus runtime scalar-variable hooks by descriptor index and variable name, then uses that same resolved set for validation and execution. The compact scalarvariable_indexremains the generated getter/setter dispatch index. Those construction hooks are not selector-order hints for local search; local-search scalar selectors keep canonical bounded candidate order.list_round_robin,list_cheapest_insertion,list_regret_insertion,list_clarke_wright, andlist_k_optare list-only. The runtime validates the required list hooks before phase build instead of failing deep inside the algorithm.group_nameselects a namedScalarGroupfor grouped scalar construction. Candidate-backed groups apply arbitrary compound scalar candidates atomically. Assignment-backed groups generate stock nullable scalar candidates and feed the same grouped construction heuristics: required entities first, optional entities only when score-improving, and capacity blockers repaired through bounded augmenting paths. Decreasing and strength-based assignment heuristics require the correspondingwith_entity_orderandwith_value_orderhooks.
If move_selector is omitted, the runtime keeps one streaming-first default
story:
- scalar-only models default to
ChangeMoveSelectorplusSwapMoveSelector - list-only models default to
NearbyListChangeMoveSelector(20),NearbyListSwapMoveSelector(20), andListReverseMoveSelector - mixed models use the list defaults first, then the scalar defaults
Nearby scalar selectors are explicit model capabilities. If the search policy
uses nearby_change_move_selector or nearby_swap_move_selector, the matching
scalar variable must provide the corresponding nearby candidate hook; distance
meters may rank or filter those bounded candidates, but the runtime does not use
them as candidate-discovery hooks.
Canonical local search uses the monomorphized variable plan published by macro runtime assembly. The descriptor boundary remains explicit for scalar-only construction and callers that intentionally assemble descriptor selectors; it is not a fallback path for normal local search.
limited_neighborhood applies a fixed move cap to one configured neighborhood
when that cap is part of the search policy. The canonical defaults are already
explicit streaming neighborhoods, so use the cap deliberately instead of
wrapping broad generation by default.
Scalar value generation is separate from scalar legality. value_candidate_limit
caps candidate values for scalar construction, scalar change, pillar change, and
scalar ruin-recreate. Nearby scalar selectors require nearby_*_candidates;
distance meters only rank or filter those bounded candidates. Scalar
cheapest_insertion is accepted only when a bounded candidate source is present.
Cartesian neighborhoods compose selectors sequentially. The left child is previewed first, the right child is opened against that preview state, and the runtime only materializes the selected winning composite move by ownership after forager choice. Left children that require full score evaluation during preview are rejected up front.
- Start from the scaffolded project.
- Change the domain model or constraints that encode your business rules.
- Adjust solver configuration for search strategy and termination.
- Add custom solver code only when configuration is not enough.