Changelog
Version history for the FieldGenius VRP API, drawn from the project's internal history log. Dates reflect when each change shipped during initial development (April-June 2026). Earlier phases (core solver bootstrap, adapter architecture, CI/CD packaging) are summarized briefly; later releases are described in more detail because they map directly to documented, externally visible behavior.
FieldGenius development spans two repositories: fieldgenius-api (the core VRP solver service, the subject of this documentation site) and derudder-adapter (a thin client-specific adapter that is not part of this public API surface). Entries below are tagged by which repository they affect.
v2.4.0 — Recurring Jobs: Seconds-Only Wire Format (7 September 2026)
fieldgenius-api
The /api/v1/recurring-jobs endpoints previously converted service_time and tw (time window) values between minutes on the wire and seconds internally. That conversion has been removed. service_time and tw.start/tw.end are now sent and received in seconds, matching every other timing field in the API (time_limit_seconds, a regular job's service_time, and so on). A request that previously sent "service_time": 20 to mean 20 minutes must now send 1200. Any integration still sending minutes-scaled values will register patterns with durations and time windows 60 times shorter than intended.
- Affects
POST /api/v1/recurring-jobs,PATCH /api/v1/recurring-jobs/{id},GET /api/v1/recurring-jobs/{id}, andPOST /api/v1/recurring-jobs/expand. - Fleet shift/time-window values supplied via a pattern's
resourcesarray are also interpreted as seconds now, consistent with the rest of the change. - This removes a class of bug where a value was silently converted twice (once in the recurring-jobs router, once elsewhere) or not at all, depending on the code path.
- The Recurring Jobs endpoint reference has been updated to document seconds throughout, including corrected example payloads.
v2.4.0 — Gateway Authentication (August 2026)
fieldgenius-api
All requests to the VRP, async jobs, and travel-matrix routers (/api/v1/optimize/*, /api/v1/jobs/*, /api/v1/travel) must now include both X-API-Key (identifying the calling application) and X-Client-Key (identifying that application's specific end customer, scoped to it). Previously these routes had no authentication at all. A request missing either header now receives 401 Unauthorized; a request with an invalid key receives 403 Forbidden. /health is unaffected and remains open. Existing API keys were migrated so already-deployed integrations did not need to be reissued new credentials, but every caller now needs to send both headers where previously none were required.
- Caller identity is verified against a shared
callerstable, with each end-customer key scoped to the specific application key that owns it — a leakedX-Client-Keycannot be replayed on its own. - Both headers are re-verified independently by the API on every request; a request is never trusted on an upstream application's say-so alone.
- Async job records created via
/optimize/asyncand/optimize/replan/asyncnow carry aclient_id, recording which caller submitted each job. - This authentication layer does not apply to the separate
/api/v1/recurring-jobsendpoints, which continue to use their ownX-API-Key-based tenant scheme (see the Recurring Jobs endpoint reference).
v2.4.0 — Request Result Cache (July 2026)
fieldgenius-api
- Identical requests to
/optimize/sync,/optimize/async,/optimize/reoptimize,/optimize/evaluate, and/optimize/replan/asyncare now recognized and served from a cache instead of re-solving from scratch. Two textually-identical requests (same body, same solver logic version) return the same result; the second call is effectively instant. - An async request that duplicates one already in progress is attached to that same job instead of starting a redundant solve.
- Controlled by
RESULT_CACHE_ENABLED(on by default) andRESULT_CACHE_TTL_SECONDS(default one hour, after which a repeat request re-solves). Requires aREDIS_URLto be configured. If the cache is ever unreachable or misbehaving, requests simply solve normally — caching failures never surface as errors to callers. /optimize/replan(synchronous) is not covered by this cache; its async counterpart,/optimize/replan/async, is.
v2.4.0 — Live Solve Progress Tracking (31 July 2026)
fieldgenius-api
- Async solves can now be watched while they run. Set
options.live_tracking = trueon aPOST /optimize/asyncrequest, and subsequentGET /jobs/{job_id}polls return aprogressfield (current best cost, served/unserved job counts) while the job is stillrunning. Default isfalse; requests that don't opt in are unaffected. POST /jobs/{job_id}/cancelon a live-tracked job now returns the best solution found so far asresult, instead of always returningnull, provided the solver had reached at least one snapshot before the cancel request arrived. The salvaged result is marked with a warning identifying it as a partial, mid-solve solution.- Not available for the synchronous endpoints (
/optimize/sync,/optimize/replan,/optimize/reoptimize), which hold a single open connection with no cancel mechanism to attach progress or salvage to.
v2.4.0 — Durable Job Queue (16 July 2026)
fieldgenius-api
- Async solve jobs (
/optimize/async,/optimize/replan/async) are now persisted to a database as they are created, started, and completed, instead of living only in the API process's memory. If the API process restarts or crashes while jobs are queued or running, those jobs are automatically recovered and resubmitted on startup, and a client pollingGET /jobs/{job_id}continues to get a valid answer under the same job ID — no client-side retry logic needed. - Off by default, controlled by
JOB_PERSISTENCE_ENABLED. When disabled, behavior is unchanged from before this feature existed. - A job recovered after a crash is retried automatically, capped by
JOB_MAX_AUTO_RETRIES(default 1), so a request that reliably crashes the process cannot loop forever across restarts. - This feature covers the async endpoints only; synchronous requests (
/optimize/sync,/optimize/replan,/optimize/reoptimize) hold one HTTP connection for the life of the request and have no "come back later" step for a durable record to serve.
v2.3.0 — Multi-Phase Solver (9 June 2026)
fieldgenius-api
- Added sequential solver phases:
options.phasesaccepts a list ofSolverPhaseobjects, each with its owntime_limit_seconds,objective, andlocal_search_metaheuristic. Each phase warm-starts from the previous phase's solution. - Phase failure resilience: if a phase returns no solution, the solver retains the previous best and continues. The final response always contains the best solution found across all phases.
- Backward compatible:
phasesdefaults tonull; requests that do not set it are unaffected. - This is the mechanism the auto-optimizer (
options.auto_configure) builds on when it generates compaction/intensification/diversification phase plans.
v2.1.0 — Mandatory Job Disjunction Fix and Out-of-Region Validation (8 June 2026)
fieldgenius-api
The solver requires every node in the routing model to be registered with a disjunction call so constraint propagation can correctly reason about reachable nodes. Mandatory jobs were registered without a disjunction, which blocked propagation under tight time windows and caused the solver to incorrectly report the problem as infeasible. Reproducible on Solomon and Homberger R/RC benchmark families.
- Fix: all jobs, mandatory and optional alike, now receive a disjunction registration. Mandatory jobs use a fixed penalty of 100,000,000 so the solver never voluntarily drops them, while propagation still functions correctly.
- Added a post-solve guard in the orchestrator: if any mandatory job is missing from the solution, the response status is escalated to
infeasible. - Benchmark impact: five previously infeasible (0% served) instances now solve correctly.
- Out-of-region coordinate validation: a bounding box check (Belgium: latitude 49.4-51.6, longitude 2.3-6.5) now runs before every matrix computation. Any out-of-region coordinate returns a structured 422 validation error identifying the offending location index, coordinates, and label. Implemented in
app/services/vrp/zone.py. This check only applies to GPS-coordinate (profile-based) requests; requests that supply explicitdistance_matrix/time_matrixare unaffected. - CI/CD: Docker image pruning (
docker image prune -af) and disk-usage logging (docker system df) added to the deploy job to prevent disk exhaustion from accumulated unused images.
v1.1.0 – v1.3.0 — CVRPLIB Benchmark Expansion (2 June 2026)
fieldgenius-api
- Added a 55-instance CVRPLIB benchmark suite across four families: Solomon 25, Solomon 100, Homberger 200, Homberger 400.
- Known-optimal values recorded per instance; gap percentage reported in both terminal output and generated HTML reports.
- Version progression from v1.0.0 to v1.3.0 was driven entirely by solver tuning informed by this benchmark suite.
- Git LFS configured for large benchmark JSON fixture files.
v1.0.0 — First Stable Release (1 June 2026)
fieldgenius-api derudder-adapter
A client reported that jobs with a scheduled_start time always came back infeasible, even when the time window looked correct. The adapter had translated scheduled_start into a hard time_windows entry; the API then computed travel time and tried to meet the pinned arrival exactly, which was usually impossible. The correct semantics are that scheduled_start is a departure time, not an arrival time. This is the same departure-time mechanism documented for the Job.departure_time field in the OptimizationRequest schema.
The adapter returned HTTP 200 with an empty body when the solver found no solution. Fixed by adding a status field that propagates through to callers, so an unsolvable request now visibly returns "status": "infeasible" instead of an empty success response.
- Version bumped to 1.0.0.
- Matrix service limits restored to support travel matrices of 100+ locations.
- The solver timeout grace period (the extra time the API waits beyond
options.time_limit_secondsbefore giving up, currently 120 seconds) was restored after having been accidentally reduced to 10 seconds. - Benchmark README auto-update wired into CI.
Pre-1.0.0 — Initial Development (23 April - 27 May 2026)
fieldgenius-api derudder-adapter
Summarized; see the project's internal history log for full detail on each phase.
- 23 April: Core solver bootstrap. Appointments, driving/work breaks, break visibility in responses.
- 24 April: Extended constraints.
max_wait_before_service, job cancellation, auto-matrix computation from GPS coordinates. - 27 April: Adapter bootstrap.
derudder-adapterarchitecture established, separated from the core solver, initial documentation. - 30 April: Deployment prep. Dual-scenario CI/CD pipeline, Docker packaging.
- 11-18 May: Logging and departure-time handling.
departure_timemechanism introduced, FG request/response dumps for debugging, a missing intervention-type price bug fixed (client-reported: caused an HTTP 500, fixed by defaulting to zero price plus a warning log). - 19-20 May: Contract tests. Adapter contract test suite added to CI as a regression gate.
- 20-26 May: Auth and identifiers. Dual ID scheme,
parent_idsupport, API key authentication, dedicatedunservedlist in the response (client-reported: unserved jobs were previously silently discarded with no way for the caller to re-queue them). - 26 May: Benchmarks. Solomon C101 baseline established.
- 27 May: Two more client-reported bugs fixed: a UTC offset bug that caused shifts to end at 18:00 Brussels time instead of the intended 16:00 (shift moved to 05:00-14:00 UTC to compensate), and mandatory jobs silently vanishing when two jobs shared identical GPS coordinates (fixed by assigning a unique location index per job, i.e. location deduplication was masking distinct jobs as one).
Documentation note
This documentation site is generated primarily from the project's internal documentation set, cross-checked against the actual request/response models and route handlers in fieldgenius-api/app/. Where the two disagreed (for example, the API reference document names error codes NOT_FOUND/CONFLICT that do not exist in app/core/errors.py, where the real names are JOB_ID_NOT_FOUND and JOB_ALREADY_TERMINAL), this site follows the source code. See Errors for the verified list.