Skip to content

feat(#233): extract scheduler engine into pure lib (Phase 2 of Resource Optimiser)#235

Merged
NickMonrad merged 2 commits intomainfrom
feature/optimiser-phase2-scheduler-extraction
Apr 29, 2026
Merged

feat(#233): extract scheduler engine into pure lib (Phase 2 of Resource Optimiser)#235
NickMonrad merged 2 commits intomainfrom
feature/optimiser-phase2-scheduler-extraction

Conversation

@NickMonrad
Copy link
Copy Markdown
Owner

Summary

Phase 2 of 4 for Resource Optimiser (#233). Extracts the timeline scheduling engine from the Express route handler into a pure, testable library (server/src/lib/scheduler.ts) so the Phase 3 optimiser can call it in a tight loop with different resource configurations — no DB round-trips per iteration.

Behaviour-preserving refactor. All existing tests pass without modification. The HTTP API response shape is unchanged.

Changes

server/src/lib/scheduler.ts (NEW, 799 lines)

  • Pure runScheduler(input: SchedulerInput): SchedulerOutput — no Prisma, no I/O, no module-level state
  • All scheduling logic moved here: topological sort (Kahn's), MinHeap priority queue, resource-levelling simulation, story proportional scheduling, cross-epic anti-cycle guard, named-resource start/end week constraints
  • Exposed types: SchedulerInput, SchedulerOutput, SchedulerEpic, SchedulerFeature, SchedulerStory, SchedulerTask, SchedulerResourceType, SchedulerNamedResource, ParallelWarning
  • Helpers also exported: effectiveAllocationPct, getWeeklyCapacity, computeParallelWarnings

server/src/routes/timeline.ts (−730 lines net)

  • POST /schedule now: load Prisma data → map to SchedulerInput → call runScheduler → write results to DB → respond
  • Route shrank from ~1240 lines to ~510 lines
  • getWeeklyCapacity re-exported for backwards compat with existing timeline.test.ts

server/src/test/scheduler.test.ts (NEW, 22 tests)

Covers happy path, multi-feature parallelism, epic dependencies, feature dependencies, resource constraints, named-resource start/end weeks, manual story overrides, empty input, zero-hour features, cross-epic dependency anti-cycle (hasCrossEpicDep guard), epic timelineStartWeek anchor.

Review fixes applied (sub-agent review cycle)

  • 🟡 Perfepics.find() in epic-dep loop replaced with Map<id, epic> lookup (O(n×d) → O(d))
  • 🟡 PerfadjList changed from Map<string, string[]> with .includes() dedup to Map<string, Set<string>> with .has() (O(e²) → O(e))
  • 🟢 Coverage — added 2 missing test cases (cross-epic anti-cycle, timelineStartWeek anchor)
  • ✅ Verified: computeParallelWarnings async→sync conversion is safe (had no awaits), manual upsert guards preserved, MinHeap actually used, zero non-determinism in pure path

Tests

  • npx tsc --noEmit (server) — ✅ clean
  • npx tsc --noEmit (client) — ✅ clean
  • npm test (server) — ✅ 164/164 passing (142 existing + 22 new scheduler tests)

Phases

Refs #233

NickMonrad and others added 2 commits April 29, 2026 16:47
…ce Optimiser)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… coverage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@NickMonrad NickMonrad merged commit d1ac4e7 into main Apr 29, 2026
1 check 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.

1 participant