seam: worker solve handler must dispatch by mode to engine/modes/* #93

Open
opened 2026-09-11 14:01:32 +00:00 by bart · 0 comments
Collaborator

Problem

worker/handlers/solve.py (T-E, docs/12-implementation-plan.md T-E row) calls the generic polaris.engine.solve.solve() for every solve task, regardless of SolveTaskPayload.mode. There is no dispatch by mode.

PH5.3 (issue #35, feature/engine-manual-mode) adds polaris.engine.modes.manual.solve_manual(snapshot, ManualScope) — the real implementation of the manual re-plan mode, including the pass-through behaviour that keeps a re-optimised plan_version a complete picture of the horizon (out-of-scope crew-days carried forward unchanged, not silently dropped from the new version). POST /plans/versions/{id}/reoptimise already enqueues a solve task with mode=manual and a narrowed scope_shift_ids/horizon_start/horizon_end, so out-of-scope shifts are still excluded from the model today (via build_snapshot's existing, mode-agnostic narrowing) — but because the worker never calls solve_manual, the resulting plan_version does not get the pass-through rows for other crews' untouched confirmed work on the same dates. Publishing such a version would supersede those crews' assignments (per-date, not per-crew, per publish_version) with nothing in the new version to replace them.

The same gap will recur for event mode (PH5.2, engine/modes/event.py) and override mode (PH3.3/PH5.4, engine/modes/override.py) once those land — none of them will actually run through the worker either without this change.

Proposed fix

worker/handlers/solve.py dispatches by task_payload.mode to the matching polaris.engine.modes.* entry point (falling back to the generic polaris.engine.solve.solve() for daily, which has no mode-specific module) instead of unconditionally calling the generic engine.

Why a seam request instead of widening PH5.3

worker/handlers/solve.py is owned by T-E (docs/12-implementation-plan.md: domain/planning/**, worker/handlers/{solve,validate}.py, ...), not by PH5.3's row (engine/modes/manual.py, api/routers/plans/reoptimise.py). Per CLAUDE.md: "If a ticket turns out to need a change outside its scope, open a seam request issue that blocks it, instead of widening the ticket."

Acceptance criteria

  • worker/handlers/solve.py dispatches solve tasks to polaris.engine.modes.manual.solve_manual when mode=manual (and to daily's generic solve() otherwise, until event/override modules exist)
  • A manual-mode solve task run through the worker end-to-end produces a plan_version whose assignments include pass-through rows for out-of-scope crew-days

Blocks

## Problem `worker/handlers/solve.py` (T-E, `docs/12-implementation-plan.md` T-E row) calls the generic `polaris.engine.solve.solve()` for every `solve` task, regardless of `SolveTaskPayload.mode`. There is no dispatch by mode. PH5.3 (issue #35, `feature/engine-manual-mode`) adds `polaris.engine.modes.manual.solve_manual(snapshot, ManualScope)` — the real implementation of the `manual` re-plan mode, including the pass-through behaviour that keeps a re-optimised plan_version a complete picture of the horizon (out-of-scope crew-days carried forward unchanged, not silently dropped from the new version). `POST /plans/versions/{id}/reoptimise` already enqueues a `solve` task with `mode=manual` and a narrowed `scope_shift_ids`/`horizon_start`/`horizon_end`, so out-of-scope *shifts* are still excluded from the model today (via `build_snapshot`'s existing, mode-agnostic narrowing) — but because the worker never calls `solve_manual`, the resulting plan_version does **not** get the pass-through rows for other crews' untouched confirmed work on the same dates. Publishing such a version would supersede those crews' assignments (per-date, not per-crew, per `publish_version`) with nothing in the new version to replace them. The same gap will recur for `event` mode (PH5.2, `engine/modes/event.py`) and `override` mode (PH3.3/PH5.4, `engine/modes/override.py`) once those land — none of them will actually run through the worker either without this change. ## Proposed fix `worker/handlers/solve.py` dispatches by `task_payload.mode` to the matching `polaris.engine.modes.*` entry point (falling back to the generic `polaris.engine.solve.solve()` for `daily`, which has no mode-specific module) instead of unconditionally calling the generic engine. ## Why a seam request instead of widening PH5.3 `worker/handlers/solve.py` is owned by T-E (`docs/12-implementation-plan.md`: `domain/planning/**, worker/handlers/{solve,validate}.py, ...`), not by PH5.3's row (`engine/modes/manual.py`, `api/routers/plans/reoptimise.py`). Per `CLAUDE.md`: "If a ticket turns out to need a change outside its scope, open a seam request issue that blocks it, instead of widening the ticket." ## Acceptance criteria - [ ] `worker/handlers/solve.py` dispatches `solve` tasks to `polaris.engine.modes.manual.solve_manual` when `mode=manual` (and to `daily`'s generic `solve()` otherwise, until `event`/`override` modules exist) - [ ] A `manual`-mode `solve` task run through the worker end-to-end produces a plan_version whose assignments include pass-through rows for out-of-scope crew-days ## Blocks - #35
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
patrick/Polaris#93
No description provided.