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 recorded solverforge-cli 2.2.2 scaffold provenance separate from the current solverforge-cli 3.7.1 scaffold 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, unified join(...), direct cross-join group_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 through equal_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 ScoreAnalysis and ConstraintAnalysis, plus lower-level detailed match/explanation APIs in solverforge-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 matching solverforge-py v0.7.2 tag 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 AttributeError as absence. Other exceptions, including KeyboardInterrupt, 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(...), and SolverManager(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.toml pass move_thread_count to the core acceptor-forager runtime. Explicit counts of two or more enable evaluator replicas, capped by the Rayon pool and 32; auto stays 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 phases telemetry, run/per-phase moves_speculative, discarded_speculative trace outcomes, bounded format-3 candidate traces, atomic telemetry_detail(...), and optional immutable QualifiedCandidateTraceProvenance supplied 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-ui assets from the native bridge.

Runtime Notes

  • Core runtime line: these manuals describe solverforge 0.23.0, with Rust 1.95. The published solverforge-cli 3.7.1 package targets solverforge 0.23.0 for fresh scaffolds; apps scaffolded by earlier lines pin older cores (0.19.7 from 3.3.x, 0.19.5 from 3.1.0, 0.19.4 from 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_count of 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.phases retains every started phase in execution order. Run totals sum phase records plus between-phase work. moves_speculative and DiscardedSpeculative distinguish discarded trials from consumed candidates. SolverStats counters 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, and Penta streams expose group_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 implement Clone.
  • Guided native scalar search: search_entity_order_key and search_value_order_key guide 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 every next_placement call. 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’s Leaf view, with its own key type. Entity-key equal_bi closures belong to the first unary join; they do not typecheck on later joins. Named cross Bi, Tri, Quad, and Penta adapters use the same typed operator tree; stream::cross::Penta::join continues into stream::chain::Chain with no fixed arity ceiling. That continuation discards an authored penta filter; apply it on the returned Chain. Same-source PentaConstraintStream is terminal. Fluent targets implement CollectionExtract, including filtered unary streams; arbitrary derived producers belong at the low-level JoinNode boundary. Grouped results can join another collection. Equality, comparison, overlap, and composed relationships compile into concrete index plans.
  • Operator-tree boundary: JoinNode and OperatorTerminal replace 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: ExistenceNode answers semi/anti joins from one occurrence count per right-side key (dense for usize planning 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. JoinerExistenceNode keeps pair joins for residual predicates, and complements restore through retained KeyOf keys, not stale row handles. Incremental notifications append into caller-owned &mut Vec<RowHandle> buffers and RowChanges is 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 .text SHA-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 through SolverManager::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 construction group_name, and grouped_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_hooks exports get, set, depot, distance, and feasible; savings_hooks exports depot, distance, and feasible; savings_metric_class_fn is 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: ListClarkeWright completes unmatched route elements instead of dropping work when no saving merge can place them.
  • Dynamic bridge crate: solverforge-bridge carries 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 as Schedule::shifts(). Constraint streams use those generated methods through ConstraintFactory::for_each(...).
  • Assignment-backed scalar groups: ScalarGroup::assignment(...) declares required nullable scalar slots, capacity keys, sequence/position hooks, and construction ordering; grouped construction and grouped_scalar_move_selector consume the same group_name from solver.toml.
  • Collectors: consecutive_runs(...), collect_vec(...), and indexed_presence(...) cover streaks, owned grouped payloads, and ordinal presence/complement checks. The underlying Collector<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(...), and hard_weight(...); the older penalize_hard, penalize_with, and reward_soft helper family is historical.
  • Public runtime names: direct runtime assembly APIs use RuntimeModel, VariableSlot, ScalarVariableSlot, ListVariableSlot, ScalarGroup, ScalarAssignmentRule, ConflictRepair, RepairCandidate, and RepairLimits.
  • Facade configuration exports: app code can import SolverConfig, PhaseConfig, MoveSelectorConfig, AcceptorConfig, ForagerConfig, SolverConfigOverride, and related enums directly from solverforge.
  • Typed custom search: solutions can compile in search code with #[planning_solution(search = "...")], register named phases through SearchContext, and order those names from solver.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(...) or complement_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 Clone in the 0.11.x release 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 wire route_hooks, savings_hooks, and optional savings_metric_class_fn explicitly. 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 ConstraintRef identity borrowed from the owning constraint.
  • Model-owned scalar hooks: candidate_values, nearby_value_candidates, nearby_entity_candidates, construction_entity_order_key, and construction_value_order_key declare 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