feat: capacity-aware scheduling (#8) — real focus factors drive every forecast #50

Merged
christian merged 1 commits from feat/capacity into main 2026-07-09 05:00:20 +00:00
Owner

The last backlog item. Turns the single-serial-worker scheduler into a capacity-aware, multi-lane one. Configured members become lanes; an issue runs on its assignee's lane (or the earliest-free lane), its duration scaled by that lane's throughput (focusFactor × allocation). Every forecast — Focus cone, Runway, milestone drill-in — is now capacity-aware.

Core (@commitea/core)

  • capacity-v0: CapacityMember, capacityPerWorkday, parseCapacityConfig (clamps, drops invalid → []).
  • scheduler-capacity-v0: scheduleWithCapacity reuses the v0 topo order + critical path, re-lays across lanes (layoutOnLanes/resolveLanes/makespan). Empty workers → the single serial plan verbatim (v0 untouched, lower risk).
  • forecast(..., { workers }): each Monte Carlo trial lays sampled durations across the lanes and takes the makespan; the serial path is unchanged. SchedulableIssue gains assignee, ScheduledItem gains worker.
  • 11 new tests: parse/clamp, parallelism halves the makespan, speed scaling, assignee routing, cross-lane deps, forecast makespan shrinks with lanes.

App

  • pm-state capacity/members.json read (readCapacity + pmstate:capacity bridge); useCapacity → workers; forecastBacklog/runwayView/milestoneView pass workers. Runway Capacity card shows the real config.

Live result

Capacity card is real — christian (focus 0.8 · 0.80 pd/day), stephen (0.6 × 50% · 0.30 pd/day) from pm-state. The P2 forecast shifts 32d → 37dmore honest: v0 assumed a mythical focus-1.0 worker; real focus factors (<1) make work realistically longer, and adding full-focus people speeds it up (unit-proven).

Config

Lives in pm-state (D4); seeded. Degrades to fixture/serial when absent. Estimates are ideal person-days (pm-state.md).

Verified

128 core tests green · desktop typecheck clean · 14 fixture e2e green · live: real capacity card + capacity-aware forecast shift.

The backlog is complete

With this, every screen and every forecast runs on real, capacity-aware data.

🤖 Generated with Claude Code

The last backlog item. Turns the single-serial-worker scheduler into a **capacity-aware, multi-lane** one. Configured members become lanes; an issue runs on its assignee's lane (or the earliest-free lane), its duration scaled by that lane's throughput (`focusFactor × allocation`). **Every forecast** — Focus cone, Runway, milestone drill-in — is now capacity-aware. ## Core (`@commitea/core`) - **capacity-v0**: `CapacityMember`, `capacityPerWorkday`, `parseCapacityConfig` (clamps, drops invalid → `[]`). - **scheduler-capacity-v0**: `scheduleWithCapacity` reuses the v0 topo order + critical path, re-lays across lanes (`layoutOnLanes`/`resolveLanes`/`makespan`). Empty workers → the single serial plan verbatim (v0 untouched, lower risk). - **`forecast(..., { workers })`**: each Monte Carlo trial lays sampled durations across the lanes and takes the makespan; the serial path is unchanged. `SchedulableIssue` gains `assignee`, `ScheduledItem` gains `worker`. - **11 new tests**: parse/clamp, parallelism halves the makespan, speed scaling, assignee routing, cross-lane deps, forecast makespan shrinks with lanes. ## App - pm-state `capacity/members.json` read (`readCapacity` + `pmstate:capacity` bridge); `useCapacity` → workers; `forecastBacklog`/`runwayView`/`milestoneView` pass workers. Runway Capacity card shows the real config. ## Live result Capacity card is real — christian (focus 0.8 · 0.80 pd/day), stephen (0.6 × 50% · 0.30 pd/day) from pm-state. The P2 forecast shifts **32d → 37d** — *more honest*: v0 assumed a mythical focus-1.0 worker; real focus factors (<1) make work realistically longer, and adding full-focus people speeds it up (unit-proven). ## Config Lives in pm-state (D4); seeded. Degrades to fixture/serial when absent. Estimates are ideal person-days (pm-state.md). ## Verified 128 core tests green · desktop typecheck clean · 14 fixture e2e green · live: real capacity card + capacity-aware forecast shift. ## The backlog is complete With this, every screen and every forecast runs on real, capacity-aware data. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
christian added 1 commit 2026-07-09 04:59:28 +00:00
Turns the single-serial-worker scheduler into a capacity-aware, multi-lane one.
Configured team members become lanes; an issue runs on its assignee's lane (or the
earliest-free lane), its duration scaled by that lane's throughput
(focusFactor × allocation). Every forecast — Focus cone, Runway, milestone
drill-in — is now capacity-aware.

core (@commitea/core):
- capacity/capacity-v0: CapacityMember + capacityPerWorkday + parseCapacityConfig
  (clamps, drops invalid; degrades to []).
- scheduler/scheduler-capacity-v0: scheduleWithCapacity reuses the v0 topo order +
  critical path, re-lays work across lanes (layoutOnLanes, resolveLanes, makespan).
  Empty workers → the single serial plan verbatim.
- forecast() gains options.workers: each MC trial lays sampled durations across the
  lanes and takes the makespan; serial path unchanged. SchedulableIssue gains
  assignee; ScheduledItem gains worker.
- 11 new tests (parse/clamp, parallelism halves makespan, speed scaling, assignee
  routing, cross-lane deps, forecast makespan shrinks with lanes).

app:
- pm-state capacity/members.json read (readCapacity + pmstate:capacity bridge);
  useCapacity hook → workers; forecastBacklog/runwayView/milestoneView pass workers.
- Runway Capacity card shows the real config (person · focus · alloc · pd/day).

Config lives in pm-state (D4); seeded christian(0.8)/stephen(0.6×0.5). Degrades to
the fixture/serial when absent.

Verified: 128 core tests green, desktop typecheck clean, 14 fixture e2e green. Live:
the capacity card is real, and the P2 forecast shifts 32d→37d — honest, since real
focus factors (<1) replace the v0 focus-1.0 assumption.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
christian merged commit 770c253625 into main 2026-07-09 05:00:20 +00:00
Sign in to join this conversation.
No description provided.