Skip to content

Allow partial optimization for over-capacity routes - #229

Merged
markboenigk merged 6 commits into
benevolentbandwidth:mainfrom
Gill87:fix/edit-capacity-warning-pr
Jul 30, 2026
Merged

Allow partial optimization for over-capacity routes#229
markboenigk merged 6 commits into
benevolentbandwidth:mainfrom
Gill87:fix/edit-capacity-warning-pr

Conversation

@Gill87

@Gill87 Gill87 commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Route managers can continue optimization when total delivery quantity exceeds available vehicle capacity. A warning dialog offers Optimize Anyway, then the shared depot prompt, so VROOM can build a feasible partial route without treating the case as a hard error.

Motivation

Blocking the entire optimization forces operators to manually guess which deliveries to remove before they can obtain a usable route. The solver already supports partial assignment, so the interface should let users make that choice explicitly. Approval for that choice must also stay scoped to a single attempt so a failed solve cannot silently skip the warning later.

Changes

  • Replaced the aggregate-capacity blocking error with a warning dialog and an Optimize Anyway path.
  • Moved partial-optimization approval into useOptimize so Optimize Anyway only approves and opens the depot overlay; the solver starts on depot save.
  • Clears approval on fresh Optimize, dismiss, or after a finished or failed solve attempt.
  • Renamed ErrorOverlay to AlertPopup with explicit error and warning variants, and moved --edit-btn-warning onto :root with the other button tokens.
  • Added in-flight guards on Optimize Anyway and depot save, plus unit tests for the approval lifecycle helpers.

Validation

  • npm --prefix app/ui run lint
  • npm --prefix app/ui run typecheck
  • npm --prefix app/ui run test
  • Intentionally skipped: browser-based visual testing was not run for this focused frontend behavior change.

Risk

The solver can leave deliveries unassigned when capacity or other constraints prevent scheduling them. The warning sets that expectation, but the current results view lists assigned stops only and does not enumerate the omitted deliveries.

Rollout and Recovery

No migration or configuration change is required. Verify an over-capacity route shows the warning, use Optimize Anyway to reach the depot prompt, then confirm a partial route is created; also confirm a failed solve shows the capacity warning again on the next Optimize. Revert this PR to restore the blocking behavior and prior popup name if needed.

Gill87 and others added 4 commits July 18, 2026 17:23
Let route managers continue with a clearly labeled warning while VROOM selects the feasible deliveries.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the new warning token aligned with the frontend formatter output.

Co-authored-by: Cursor <cursoragent@cursor.com>
Make error and warning intent explicit without duplicating dialog behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the edit page aligned with the frontend formatter output.

Co-authored-by: Cursor <cursoragent@cursor.com>
@markboenigk

Copy link
Copy Markdown
Collaborator

Review: solid direction, one correctness bug worth fixing before merge

The warn-instead-of-block approach is the right call here, and the AlertPopup refactor is a nice a11y improvement over ErrorOverlay (stable useId()s, aria-describedby, alertdialog role when an action is present).

One bug I'd want fixed before merging:

allowPartialOptimization can go stale and silently skip the capacity warning (page.tsx:74, 231-234, 236-249; useOptimize.ts:134, 180-185)
This flag lives in page.tsx, separate from the hook, and is only reset to false from dismissCapacityWarning and the AddressOverlay's onClose. But every failure path inside optimize()'s try block (depot geocode failure, unsupported region, delivery geocode failures, job submit/poll failures, network errors) returns early without touching either reset path — and needsDepotAddress is unconditionally reset to false at the top of every optimize() call, so the overlay just disappears on failure. If a user hits "Optimize Anyway," the depot save succeeds, but the flow then fails downstream, allowPartialOptimization stays true with no visible trace. If the address-overlay flow gets re-entered later, that stale true gets forwarded into optimize() again and silently bypasses the capacity check — no warning shown, even though the user never opted into partial optimization that time.

Suggestion: reset this flag at the start of every fresh "Optimize" click (not just the two dismiss paths), or better, move its lifecycle into the hook itself so it resets alongside everything else optimize() already clears at the top.

Smaller items, non-blocking:

  • --edit-btn-warning is declared on .root in edit.module.css:11, but the file's own header comment (and every sibling --edit-btn-* token) says these belong in globals.css's :root so portaled content can resolve them. Works today since AlertPopup isn't portaled, but would silently lose its background if the warning variant is ever reused from a portaled modal (e.g. on /results). Cheap fix: move it to globals.css.
  • No in-flight guard on "Optimize Anyway" / the depot-save button — the other optimize entry points disable on isOptimizing, these don't. Low risk, but a rapid double-click could fire two concurrent optimize() calls.
  • No test coverage for the new AlertPopup variant/action or the partial-optimization flow — worth at least one test given the bug above lives in exactly this new logic.

Gill87 and others added 2 commits July 25, 2026 14:34
Keep Optimize Anyway approval in the optimize hook for a single attempt, open the depot overlay without starting the solver, and clear stale approval on dismiss or failed runs.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@Gill87

Gill87 commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

Hey @markboenigk,

Nice catch on the edge case where if the optimization fails, the popup no longer appears again, I didn't see that when I was first implementing it. I went ahead and addressed that by putting the allowPartialOptimization into the useOptimize lifecycle, and I also made changes to resolve the smaller, non-blocking items that you mentioned.

@markboenigk

Copy link
Copy Markdown
Collaborator

Code review

No issues found. Checked for bugs and CLAUDE.md compliance across 5 independent review passes (CLAUDE.md audit, bug scan, git history, prior PR comment history, code comment compliance).

One candidate issue was investigated and ruled out as low-impact: the actionDisabled={isOptimizing} guard on the "Optimize Anyway" button in app/ui/src/app/edit/page.tsx is effectively a no-op, since isOptimizing only becomes true after the capacity-warning branch has already returned early in useOptimize.ts. This does not cause a real double-submission bug because optimizeAnyway() is a synchronous, idempotent state update with no network call — the actual solve is separately gated by the depot-save guard. Not flagged as a blocking issue.

Based on this review, the PR appears ready to merge.

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@markboenigk
markboenigk merged commit 969da83 into benevolentbandwidth:main Jul 30, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants