fix(shifts): reject over-long jobs under allow_out_of_hours_depot_travel - #14
Merged
Merged
Conversation
When `allow_out_of_hours_depot_travel` is set, the depot-start window is relaxed to
`{earliest: None, latest: None}` so the vehicle can leave the depot out of hours and
reach the first job at the shift start. That relaxation becomes `Actor.detail.time.start
= 0` in `Fleet::new`, so the transport time-window constraint evaluated insertions from a
depot departure of t=0 — effectively a `[0, end.latest]` operating window instead of the
real `[shift_start, end.latest]`. The `FirstJobArrivalFloor` correction that pushes the
first-job arrival back to the true shift start was only applied later in
`apply_first_job_arrival_floor` during scheduling, never in the feasibility check, so a
job longer than the whole shift (e.g. a 12h task on a 9-5 shift) was wrongly scheduled and
finished outside the tech's work hours.
Floor the first-job arrival to `FirstJobArrivalFloor` inside the constraint's
`evaluate_activity` (only when `prev` is the depot and the floor is set), mirroring the
schedule computed by `apply_first_job_arrival_floor`. The shift-end overrun is now detected
during insertion, so the over-long job is left unassigned — matching behaviour with the
flag off.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reject over-long jobs under
allow_out_of_hours_depot_travelhttps://uptickhq.slack.com/archives/C08UUNL1L6R/p1784264505054759
Size: small (2 files, +83/−0 — a one-branch guard in the transport constraint plus a regression test)
TransportConstrainthot path; guarded so non-out-of-hours actors are unaffectedvrp-core/src/construction/features/transport.rs— the flooredarr_time_at_targetMotivation
When
allow_out_of_hours_depot_travelis set, a job whose service duration exceeds the whole shift span was being scheduled anyway, finishing well outside the tech's work hours. Example: a tech on a 9–5 (8h) shift, single 12h task — should be unassigned with and without the flag, but the flag let it through.Root cause. With the flag,
fleet_readerrelaxes the depot-start window to{earliest: None, latest: None}so the vehicle can leave the depot before the shift starts and reach the first job at the shift start. That relaxation becomesActor.detail.time.start = 0inFleet::new(unwrap_or(0.)). The transport time-window constraint (evaluate_activity) then evaluates insertions from a depot departure oft = 0— effectively a[0, end.latest]operating window instead of the real[shift_start, end.latest]. TheFirstJobArrivalFloorcorrection that pushes the first-job arrival back to the true shift start is only applied later inapply_first_job_arrival_floorduring scheduling — never in the feasibility check — so the shift-end overrun was never detected and the over-long job was accepted.The standalone checker (
checker/limits.rs::check_shift_time) already computes the effective window correctly (first-job arrival → depot return) and flags such a tour as"tour time is outside shift time". So the solver and the checker disagreed; the solver was too permissive.Changes
vrp-core/.../features/transport.rs— inTransportConstraint::evaluate_activity, whenprevis the depot and the actor declares aFirstJobArrivalFloor, floorarr_time_at_targetto that floor before the shift-end feasibility check. This mirrors the schedule thatapply_first_job_arrival_floorwill produce, so the constraint sees the true (floored) arrival and rejects jobs that would overrun the shift. Guarded byget_first_job_arrival_floor()beingSome(only out-of-hours actors set it) andprev.job.is_none(), so all other problems are unaffected.vrp-pragmatic/.../features/limits/max_duration.rs— regression testallow_out_of_hours_depot_travel_rejects_job_longer_than_shift: a 150-unit job on a[100, 200](span 100) shift must be unassigned with the flag OFF and ON.Verification
cargo test -p vrp-core --lib→ 709 passed, 0 failedcargo test -p vrp-pragmatic --lib→ 342 passed, 0 failed (9 pre-existing ignored)start.latest) is preserved:allow_out_of_hours_depot_travel_moves_shift_bounds_onto_first_and_last_jobsallow_out_of_hours_depot_travel_still_enforces_shift_start_latest🤖 Generated with Claude Code