Documentation
Current SolverForge release, published package status, completed runtime surface, and roadmap.
Status & Roadmap
Current Status
| Component | Status | Description |
|---|---|---|
| Rust Core | Published; Linux/Windows CI passed, macOS CI failed | Native Rust constraint solver published as solverforge 0.23.0: opt-in parallel acceptor-forager move evaluation, ordered per-phase telemetry, speculative-work diagnostics, and incremental scoring of list-shadow writes; Rust 1.95 |
| CLI Scaffold | Published; CI and release passed | solverforge-cli 3.7.1 targets solverforge 0.23.0, web solverforge-ui 0.9.0 + solverforge-maps 2.1.4, MCP rmcp 3.8.0; generate score rewrites every score-bearing surface and the generated demo data re-renders on destroy (3.3.3), 3.4.0 migrated release tooling to csemver targeting core 0.20.4, and the earlier 3.1.0 package scaffolded solverforge 0.19.5 with rmcp 3.4.0, 3.0.0 scaffolded solverforge 0.19.4 with solverforge-ui 0.7.0 and rmcp 3.3.0 |
| Python | Published; CI and release passed | solverforge-py 0.7.2 compiles dynamic CPython 3.14 models into the solverforge 0.23.0 runtime; PyPI publishes solverforge 0.7.2; opt-in parallel evaluation and ordered per-phase telemetry |
| Worked Use Cases | Released; tag Space syncs and bundle CI passed | solverforge-hospital@2.0.12, solverforge-lessons@2.0.14, solverforge-deliveries@2.0.13, solverforge-fsr@2.0.14, plus the repository-only solverforge-furnace@2.0.7, solverforge-orders@0.2.0, solverforge-fleet@0.1.9, and solverforge-flightcrew@0.2.0; all eight target solverforge 0.23.0 and solverforge-ui 0.9.0 |
| UI | Published | solverforge-ui 0.9.0 exposes framework-neutral embedded assets and moves the bundled agent skill into solverforge-cli; solverforge-cli 3.7.1 scaffolds solverforge-ui 0.9.0 directly, while apps scaffolded by 3.0.0 still pin 0.7.0 |
| Maps | Published | solverforge-maps 2.1.4 carries matrix route-distance access |
Try It Today
- Start with solverforge-cli Getting Started for the generic app shell.
- Use SolverForge Python when you want to define models with Python classes, decorators, and callbacks while using the native SolverForge engine.
- Continue with the worked use-case bundle:
Hospital,
Lessons,
Deliveries, or
FSR. Those guides now
document the released Hospital 2.0.12, Lessons 2.0.14, Deliveries 2.0.13, and
FSR 2.0.14 contracts on
solverforge 0.23.0, while keeping their recordedsolverforge-cli 2.2.2scaffold provenance separate from the currentsolverforge-cli 3.7.1scaffold target. - Use Constraint Node Sharing when a constraint function reuses the same grouped stream across several named terminal constraints.
- Use Projected Scoring Rows when scoring needs retained rows derived from source entities, joined pairs, or oriented relationships between projected rows.
Completed Runtime Surface
- Constraint Streams API: source-aware generated source methods,
for_each,filter, unifiedjoin(...), direct cross-joingroup_by(...), direct cross-join grouped complements,flatten_last,project(...), projected grouped complements,balance,complement(...),if_exists(...),if_not_exists(...),collect_vec(...),indexed_presence(...), explicit score weighting, and.named(...) - Projected Scoring Rows: bounded single-source rows, joined-pair projected
rows, symmetric projected self-joins through
equal(...), and directed projected self-joins throughequal_bi(left_key, right_key) - Constraint Compiler Node Sharing:
#[solverforge_constraints]can share grouped, projected grouped, direct cross grouped, and complemented grouped stream nodes across repeated terminal constraints while preserving terminal names, order, metadata, and score explanation - Score Types: SoftScore, HardSoftScore, HardMediumSoftScore, HardSoftDecimalScore, BendableScore
- Score Analysis: facade-level
ScoreAnalysisandConstraintAnalysis, plus lower-level detailed match/explanation APIs insolverforge-scoring - SERIO Engine: retained incremental scoring for real-time optimization
- Solver Phases: scalar/list construction heuristics, assignment-backed grouped scalar construction, streaming local search, VND as a local-search type, precedence-list neighborhoods, and typed custom, exact, or partitioned search extensions
- Move System: scalar, list, grouped scalar, assignment repair, conflict repair, cartesian, and composite move families
- Runtime Compiler: one immutable resolved graph for native and dynamic construction, selector trees, providers, stable list sources, defaults, and execution errors
- SolverManager API: retained job lifecycle with progress, structurally complete best-solution publication, pause/resume, completion, cancellation, failure, snapshots, snapshot-bound analysis, ordered per-phase telemetry, and explicit candidate-trace detail retrieval
- Configuration: stock
solver.toml, TOML/YAML parsing helpers, bounded scalar candidates, grouped scalar selectors, per-leaf ordering and metrics, weighted unions, seeded score ties, parallel move evaluation, candidate tracing, level-aware simulated annealing, and per-solution config overlays
Python Package
- Install:
python3.14 -m pip install "solverforge==0.7.2"; use the matchingsolverforge-pyv0.7.2tag for source and example development. - Modeling: Python classes, decorators, scalar variables, list variables, explicit assignment metadata, scoped route/savings bundles, named candidate metrics, hard per-row scalar candidate domains, native static same-value conflict fields, and callback constraints.
- Callback failures and interrupts: optional attribute probes treat only
AttributeErroras absence. Other exceptions, includingKeyboardInterrupt, propagate. Direct solves preserve the Python traceback; retained jobs record failure. Recognized callback-error payloads do not print a redundant Rust panic preamble, while unrelated Rust panics reach the previous hook. - Runtime entry points:
Solver.solve(...),Solver.analyze(...), andSolverManager(config=None)over one compiled SolverForge 0.23.0 runtime graph. - Constraint surface: callback-authored unary streams, binary stream-level
joins, grouped counts, balance scoring, fixed or callback-computed weights,
unassigned-list scoring, list precedence/makespan scoring, indexed-presence
grouped collectors, and
joiner.equal(...)/joiner.equal_bi(...). - Dynamic move parity: supported scalar and list local-search selectors, including grouped scalar, conflict repair, compound conflict repair, k-opt, list precedence, list permute, list ruin, union, limited neighborhood, and two-child cartesian composition.
- Parallel evaluation: config dictionaries and
solver.tomlpassmove_thread_countto the core acceptor-forager runtime. Explicit counts of two or more enable evaluator replicas, capped by the Rayon pool and 32;autostays sequential. The synchronous solve releases the GIL so workers can attach; callbacks serialize on GIL-enabled Python and must avoid shared mutable state on free-threaded Python. - Retained diagnostics: ordered
phasestelemetry, run/per-phasemoves_speculative,discarded_speculativetrace outcomes, bounded format-3 candidate traces, atomictelemetry_detail(...), and optional immutableQualifiedCandidateTraceProvenancesupplied per managed job. - Compiled scoring boundary: proven fixed-weight unary, unassigned-list, precedence, and stable string-key equality plans can evaluate natively while callback-dependent shapes preserve the Python path.
- Examples: hospital scheduling with row-filtered scalar candidates and
nearby metadata, plus deliveries routing with unassigned seed routes and
explicit route/savings bundles; both expose retained jobs, exact snapshots,
phase telemetry, analysis, and shared
solverforge-uiassets from the native bridge.
Runtime Notes
- Core runtime line: these manuals describe
solverforge 0.23.0, with Rust1.95. The publishedsolverforge-cli 3.7.1package targetssolverforge 0.23.0for fresh scaffolds; apps scaffolded by earlier lines pin older cores (0.19.7from 3.3.x,0.19.5from 3.1.0,0.19.4from 3.0.0), and an app should move to a newer core line only when that app is deliberately upgraded and validated. - Parallel local search: an explicit
move_thread_countof two or more enables acceptor-forager evaluation windows on independent score directors. Candidate generation and acceptor/forager decisions remain serial and ordered. Each additional evaluator owns a solution copy and independent incremental scoring state.Auto,None, zero, and one remain sequential; construction and VND are not parallelized by this setting. Fixed-work scalar/list selectors without cross-step stream state preserve the sequential trajectory; ruin selectors and lazy providers remain reproducible at a fixed evaluator count. Time-limited runs are not a cross-count trajectory guarantee. - Per-phase telemetry:
SolverTelemetry.phasesretains every started phase in execution order. Run totals sum phase records plus between-phase work.moves_speculativeandDiscardedSpeculativedistinguish discarded trials from consumed candidates.SolverStatscounters are accessor methods. - List-shadow scoring: inverse, index, previous, and next shadow writes retract and reinsert element rows, including clearing elements that leave all lists. Constraints over those rows observe the updated shadows.
- Grouped-scalar foraging: grouped-scalar default local search uses
AcceptedCount(256), the same policy as list models — a step sweeps until the limit is accepted (or the union drains) and applies the best. Throughput no longer depends on how fast the first improvement arrives; explicitly configured foragers keep their pinned behavior. - Joined-tuple grouping: cross-source and same-source
Tri,Quad, andPentastreams exposegroup_by(key, collector). Key closures receive one entity argument per binding; collector mappers receive a borrowed tuple; grouped weights receive(&key, &result). Filters keep their semantic source indexes, and collected payloads need not implementClone. - Guided native scalar search:
search_entity_order_keyandsearch_value_order_keyguide change enumeration; swap uses the entity key for its left traversal. Lower keys come first, missing keys come last, and equal keys retain seeded order. Construction-order hooks remain separate. - Score-authoritative grouped search: candidate ordering steers a bounded round-robin family scan without substituting a heuristic score for the model’s score. Final edits obey declared domains, required rows, capacity, and assignment rules. Sparse hypothetical overlays leave committed indexes untouched; acceptors and foragers decide among scored legal candidates.
- Late acceptance: DLAS compares each evaluated candidate with the step score and the exact worst history score, advancing its ring per candidate; it has no tolerance parameter. Plain late acceptance advances per step and resets the window on a new phase best.
- Lending extensions: move cursor pulls take the live solution; Cartesian
pulls use
next_candidate_with_director(&mut director)to discover right candidates with the left leg applied, then restore the solution and score state. Placers receive the current director on everynext_placementcall. Custom moves expose generic affected-entity visitors and live-preview doability; termination checks take&mut self, and acceptors receive the accepted tabu signature by ownership. - Owned runtime state: compiled selector declarations use indexed arenas, leaf state returns on early cursor drop, assignment bases are retained across local-search steps, and list payloads move through mutation and undo without per-trial payload cloning. Cartesian row storage and undo journals reuse capacity. Pillar swaps report both halves to pin checks, and self-undoing tabu moves enter both configured memories. See the 0.21.x release note.
- Composable joins: each later join uses
equal_on(left_row_key, right_row_key)over the complete prior borrowed row and the new entity’sLeafview, with its own key type. Entity-keyequal_biclosures belong to the first unary join; they do not typecheck on later joins. Named crossBi,Tri,Quad, andPentaadapters use the same typed operator tree;stream::cross::Penta::joincontinues intostream::chain::Chainwith no fixed arity ceiling. That continuation discards an authored penta filter; apply it on the returnedChain. Same-sourcePentaConstraintStreamis terminal. Fluent targets implementCollectionExtract, including filtered unary streams; arbitrary derived producers belong at the low-levelJoinNodeboundary. Grouped results can join another collection. Equality, comparison, overlap, and composed relationships compile into concrete index plans. - Operator-tree boundary:
JoinNodeandOperatorTerminalreplace the historical cross-bi/cross-tri and per-arity self-join engines. Low-level collection, filter, bag-union, owned projection, grouping, semi/anti join, flatten, complement, and self-join producers compose borrowed views with generational identities and source provenance. Specialized stream families still exist; arbitrary derived-consumer sharing is not supplied by these producers. Existing compiler-owned grouped terminal sharing remains supported. - Optional construction progress: keeping an optional assignment unassigned completes that decision, not the whole optional pass. Completed optional roots are skipped before candidate-domain reads and move generation, preserving the candidate budget for later rows; assigned occupants remain available to required augmenting rematches.
- Unary keyed source membership: the first unary keyed equality cross-join applies source filters before key extraction and pair creation, preserving original source indexes and descriptor-local change metadata. Post-join filters operate on joined rows. Left-source pushdown does not extend to comparison, overlap, composed-condition, predicate, or same-source self-join dispatch.
- 0.19.11 construction and scoring fixes: interrupted Clarke-Wright merge work can publish a complete buffered assignment using existing owner-feasibility evidence, without extending the search budget. Interrupted savings generation preserves the committed complete assignment. Relational operators reuse retired buffers and probe scratch; exact typed equality buckets avoid repeating both key extractors for each candidate pair.
- 0.19.13 existence-engine rewrite:
ExistenceNodeanswers semi/anti joins from one occurrence count per right-side key (dense forusizeplanning ids, hash otherwise) with stored key contributions and one key bucket of left rows. Threshold crossings publish or retract every left row carrying that key idempotently per event; right-side children never become rows and never receive handles.JoinerExistenceNodekeeps pair joins for residual predicates, and complements restore through retainedKeyOfkeys, not stale row handles. Incremental notifications append into caller-owned&mut Vec<RowHandle>buffers andRowChangesis gone: one memcpy per hop and no heap allocation on this path. These implementation changes do not imply new benchmark results or measured performance gains. See the 0.19.11 release note, 0.19.12 release note, and 0.19.13 release note. - 0.19.14 dense-key refinement: count additions and removals inline the touched-key update and grow storage only when the key exceeds its current length. Membership semantics stay unchanged. The release also documents the borrowed-row key-shape rule at every later chained join. See the 0.19.14 release note.
- 0.20.1 pooled scratch: precedence-route cycle checks reuse graph-owned buffers and the grouped-scalar construction feasibility path pools its probe scratch, removing per-candidate and per-event allocation. Scores, decisions, and public APIs are unchanged. See the 0.20.x release note.
- 0.19.12 retained bookkeeping: occupied equality buckets and existence link lists are updated in place. Empty buckets still retire their buffers for reuse; generation checks, old-key retraction, residual predicates, and duplicate suppression remain intact.
- Internal zero-regression gate: deterministic allocation counters are checked
exactly without a noise allowance; noisy metric medians have a default 5%
regression tolerance through
BENCH_TOLERANCE_PERCENT. Both sides share a harness package name and equal-length staging paths, and record.textSHA-256 fingerprints. The paired bootstrap upper bound is diagnostic, not an acceptance criterion. This changes internal qualification, not historical site benchmark measurements or their tested versions. - Lost resume wakeup fixed: resume and cancellation clear the pause request and notify while holding the worker’s pause gate, closing the race between its predicate check and condition-variable wait without changing snapshot publication boundaries.
- One sequence model: planning list variables are the canonical model for routes and ordered assignments. They own the sequence directly and retain inverse, index, previous, next, custom, cascading, and piggyback shadows. Scalar variables remain direct single-slot assignments; the former predecessor-chain surface and its chain-only anchor shadow are no longer part of the public runtime.
- One compiled runtime graph: native Rust and dynamic bridge models resolve construction stages, recursive selector trees, providers, stable list-source identities, defaults, and termination into one immutable graph before solving. Declaration, compilation, preparation, and execution errors are explicit; there is no parallel configured-search fallback.
- Resolved selector policy: leaf selectors can use original, seeded random, shuffled, sorted, or probabilistic order; sorted/probabilistic leaves require a registered candidate metric. Unions support sequential, round-robin, rotating-round-robin, random, and default stratified-random scheduling with equal, fixed, or candidate-count weighting. Equal best scores use seeded random tie-breaking by default.
- Cursor-owned candidate execution: shared scalar/list kernels expose stable candidate IDs, let foragers short-circuit without draining a neighborhood, and transfer only the selected winner by value. Generated counts represent actual cursor yields, including speculative parallel pulls, rather than an unrequested logical tail. Speculative trials discarded after a decision point are counted separately from consumed evaluations.
- Construction control and progress: generic and specialized construction share one lifecycle and phase-telemetry path, publish first work promptly, continue roughly once per second under verbose logging, poll control during expensive generation, and commit route counters atomically.
- Required assignment batches: the default hard-first required pass commits dense independent assignments directly, then leaves bounded augmenting rematches to a following cursor that can retain their multi-entity edits.
- Qualified candidate traces:
[candidate_trace]retains a bounded ordered prefix with canonical config, resolved plan, execution policy, input, operation identity, and disposition provenance. Detailed traces are fetched atomically throughSolverManager::get_telemetry_detail(...); routine events and snapshots keep only compact telemetry. - Dynamic capability contracts: bridge models declare scalar legality and nearby sources, list mutation operations, immutable list metadata bundles, and scalar-assignment metadata explicitly. Host compound providers and optional candidate metrics are frozen per solve instead of discovered through mutable schema or thread-local state in cursor execution.
- Lifecycle settlement: pause and cancellation are polled around phases and terminal hooks as well as inside long candidate work. Paused time is excluded from active phase telemetry, and pending control cannot be overwritten by ordinary completion. Configured limits remain binding during mandatory construction; incomplete work fails without publishing an incomplete best/completed snapshot, and a pre-completion pause remains resumable without publishing a partial snapshot.
- Grouped-scalar construction policy: applications drive grouped
construction and repair through
ScalarGroup::assignment(...), grouped constructiongroup_name, andgrouped_scalar_move_selector. The grouped-scalar construction cursors remain solver-internal rather than a public import surface. Required assignment construction streams state instead of relying only on a closed internal batch path. - 0.17.1 route safety fixes: stock CVRP helpers reject unreachable travel-time legs during strict feasibility checks, convert unreachable or malformed distance entries into a large finite cost for construction/search, and clamp route-distance arithmetic used by Clarke-Wright and k-opt.
- CVRP list profile: stock CVRP list variables can declare
domain = "cvrp". The profile supplies the CVRP solution trait, distance meters, strict route-local hooks, relaxed Clarke-Wright savings hooks, and savings metric class. - Route and savings hooks: custom routing list variables can still split
route-local behavior from Clarke-Wright construction explicitly.
route_hooksexportsget,set,depot,distance, andfeasible;savings_hooksexportsdepot,distance, andfeasible;savings_metric_class_fnis the optional construction sharing hook. - Strict K-opt route feasibility: stock CVRP route hooks reject capacity and time-window violations before k-opt commits a route. The stock savings hooks stay relaxed enough for Clarke-Wright construction to assign scoreable capacity or time-window violations when that is better than leaving work unassigned.
- Clarke-Wright completion:
ListClarkeWrightcompletes unmatched route elements instead of dropping work when no saving merge can place them. - Dynamic bridge crate:
solverforge-bridgecarries host-language binding contracts for logical entity/fact/variable IDs, dynamic score families, and descriptor-resolved scalar/list slots. Python remains the first published binding, but the bridge contract is a Rust workspace crate. - Constraint node sharing: annotate reusable constraint factory functions
with
#[solverforge_constraints]. Reused same-binding grouped streams and syntax-proved identical grouped chains share retained incremental node work; separate terminal constraints keep their own names, impact direction, metadata, and explanation rows. - Generated source methods:
#[planning_solution]now exposes collection sources as solution-associated functions such asSchedule::shifts(). Constraint streams use those generated methods throughConstraintFactory::for_each(...). - Assignment-backed scalar groups:
ScalarGroup::assignment(...)declares required nullable scalar slots, capacity keys, sequence/position hooks, and construction ordering; grouped construction andgrouped_scalar_move_selectorconsume the samegroup_namefromsolver.toml. - Collectors:
consecutive_runs(...),collect_vec(...), andindexed_presence(...)cover streaks, owned grouped payloads, and ordinal presence/complement checks. The underlyingCollector<Input>contract is generic over the stream match shape, so unary rows, projected rows, and direct cross-join pairs use the same collector protocol. - Scoring terminals: the current stream surface uses
penalize(score),reward(score), typed dynamic closures,fixed_weight(...), andhard_weight(...); the olderpenalize_hard,penalize_with, andreward_softhelper family is historical. - Public runtime names: direct runtime assembly APIs use
RuntimeModel,VariableSlot,ScalarVariableSlot,ListVariableSlot,ScalarGroup,ScalarAssignmentRule,ConflictRepair,RepairCandidate, andRepairLimits. - Facade configuration exports: app code can import
SolverConfig,PhaseConfig,MoveSelectorConfig,AcceptorConfig,ForagerConfig,SolverConfigOverride, and related enums directly fromsolverforge. - Typed custom search: solutions can compile in search code with
#[planning_solution(search = "...")], register named phases throughSearchContext, and order those names fromsolver.toml. - Joined-pair projected rows: cross joins can use
.project(|left, right| row)to retain one scoring row per joined pair. - Directed projected self-joins: projected rows can use
.join(equal_bi(left_key, right_key))when the left and right side of a same-row-type relationship are semantically different, such as parent-child projected rows. - Direct cross-join grouping: cross joins can group joined pairs with
.group_by(|left, right| key, collector)without projecting first. - Projected grouped complements: projected grouped streams can continue into
complement(...)orcomplement_with_key(...)for missing-key scoring rows. - Direct cross-join grouped complements: direct cross joins can group joined
pairs and then call
complement(...)against a generated fact or entity source for zero-match target rows. - Filtered keyed joins: filtered right-hand join sources, flattened keyed targets, custom keyed extractors, and filtered complement sources retain only rows that still satisfy their source filters.
- Clone-free projected paths: projected outputs, projected self-join keys,
and grouped collector values no longer need
Clonein the0.11.xrelease line. - Joined filter indexes: low-level joined filters receive semantic source indexes for direct, grouped, projected, flattened, and higher-arity joins.
- CVRP profile and custom hook split: stock CVRP routes should use
domain = "cvrp"; custom route domains can wireroute_hooks,savings_hooks, and optionalsavings_metric_class_fnexplicitly. Construction savings can share a metric class without collapsing route-local assignment semantics. - Assignment value-pattern neighborhoods: assignment-backed grouped scalar local search includes bounded value-window swaps, longer value-window swaps, same-sequence run-gap swaps, block reassignments, optional run releases, and three-value rotations. Required assignment construction still has a hard-first fill path for required slots, with public streaming state available for advanced construction integrations.
- Borrowed constraint identity: scoring metadata preserves full
ConstraintRefidentity borrowed from the owning constraint. - Model-owned scalar hooks:
candidate_values,nearby_value_candidates,nearby_entity_candidates,construction_entity_order_key, andconstruction_value_order_keydeclare bounded scalar neighborhoods and construction ordering on the model. - Model-aware defaults: omitted phases run state-aware construction. A default streaming local-search phase requires effective parsed top-level termination; absent termination, an empty termination object, or invalid score-only termination remains construction-only. Capability-matched leaves use seeded random order, multi-family unions use stratified-random scheduling, and assignment-owned scalar slots remain exclusively on their grouped path.
- Exact retained telemetry: generated, evaluated, accepted, not-doable, acceptor-rejected, forager-ignored, hard-delta, conflict-repair, construction-slot, per-phase, speculative-work, move-label, and bounded applied-move counters are retained as authoritative counters; bounded candidate-pull detail is retained separately on explicit request.
Roadmap
Native Solver Complete
The Rust-native constraint solver, derive macros, SERIO scoring engine, retained runtime lifecycle, and stock configuration surface are in place.
Rust API Refinement
Current work focuses on tightening public API contracts, making scoring and runtime paths easier to explain, and keeping source, docs, examples, and CLI scaffolds aligned as releases move.
Python Package
solverforge-py 0.7.2 is the current tagged SolverForge Python source line for
CPython 3.14. It compiles explicit Python model metadata into the
solverforge 0.23.0 runtime, supports opt-in parallel acceptor-forager
trial evaluation, retains every phase’s telemetry, and keeps shared
solverforge-ui 0.9.0 assets. Consumers migrate the single phase dictionary
to the ordered phases list and read the last entry for a running progress
snapshot, guarding an empty list before any phase starts. The automatic release workflow
publishes the source distribution and CPython 3.14 wheels to PyPI for every
tagged patch. 0.6.6 (2026-08-11) enforces each row’s imported candidate set
as the hard domain for every native scalar mutation and adds
same_value_conflict_field for static assignment conflict graphs; the
0.6.7–0.6.10 patches carry the SolverForge 0.19.7 construction and telemetry
behavior, and the 0.6.11–0.6.15 patches retarget to the 0.21.0, 0.21.3, and
0.22.0 registry bases. Its source archive
contains only the package metadata and Python/Rust build inputs; repository
tests, examples, guidance, and tooling remain source-checkout assets.
Additional Language Bindings
Other language bindings remain future-facing. The Rust core remains the source of truth for solver behavior, while Python is the first published dynamic binding.
How You Can Help
- Follow the getting started guides and share feedback.
- Open an issue for bugs or unclear docs.
- Star the GitHub repo and share real planning use cases.