Repository navigation
v0.1.4 — Sprint capacity calculation fix
Sprint capacity calculation fix
What changed
Three bugs in ado_sync.py caused future sprints (any sprint with no capacity yet configured in Azure DevOps) to report zero capacity, breaking Monte Carlo forecasts.
Bug 1 — fabricated 1.0 h/day capacity for zero-activity rows
When Azure returned capacity rows where capacityPerDay was 0 or activities was empty, the code silently substituted 1.0 h/day per member. A team of 8 over a 10-day sprint showed team_capacity_hours = 80 / capacity_factor = 1.0 while Azure recorded 0 hours. The fallback is removed; this state is now reported honestly as zero_capacity.
Bug 2 — future-sprint working days treated as non-working by simulation
When no capacity rows were returned, a max(1.0, …) baseline fallback caused per_date_ratio = 0.0 for every working day in the sprint. The Monte Carlo simulation treated those days as non-working days — making all forecasts end sooner than they should. Fixed: unconfigured sprints now contribute per_date_ratio = 1.0 (full capacity assumed) until real data is available.
Bug 3 — no carry-forward for future sprints
Future sprints with no Azure capacity configuration had no fallback. They now inherit the last configured sprint's per-member daily capacity (carried_forward source). The target sprint's own team days off and individual days off are still applied on top.
Also fixed:
iteration_summary_team_days_off_countrenamed toado_team_total_days_off_count— it is a raw Azure diagnostic field that counts all member days off (team + individual), not team-wide days only. It is never used to reduceplanned_working_days.- New
team_days_off_countcolumn — counts only team-wide days off (from the team days-off endpoint). Always correct. - New
capacity_sourcecolumn with four explicit states:azure_configured,missing_capacity,zero_capacity,carried_forward. per_user_capacityis now a JSON string, readable in CSV exports (was[object Object]).calculate_sprint_capacity()extracted as a pure, I/O-free function — fully testable without Azure credentials or Streamlit.
Why it matters
If your team uses AgileForecasting and has sprints beyond the current sprint that are not yet capacity-configured in Azure DevOps (which is normal — teams configure capacity sprint-by-sprint as they approach), previous versions would show 0.0 for all future sprint capacity and the simulation would treat every future working day as non-working, producing forecasts that are far too pessimistic or collapse entirely.
Before / after
| Sprint state | team_capacity_hours before |
team_capacity_hours after |
|---|---|---|
| Configured sprints | ✅ Correct | ✅ Correct (unchanged) |
| Future sprint (no Azure config) | ❌ 0.0 |
✅ Carried forward from last configured sprint |
| Sprint with 0-hour activities | ❌ 80.0 (fallback artifact) |
✅ 0.0 (zero_capacity) |
Upgrade / clone instructions
Clone fresh:
git clone https://github.com/XeonNAS/agileforecasting.git
cd agileforecasting
pip install -r requirements.lock # Linux/macOS
# or
pip install -r requirements.txt # Windows
pip install -e ".[dev]"
streamlit run streamlit_app/app.pyUpdate existing clone:
git pull origin main
pip install -e ".[dev]"No configuration changes or database migrations are required. All fixes are in the data layer (ado_sync.py); the Streamlit UI and CSV export will automatically show the corrected values on the next Azure DevOps refresh.
Testing summary
- 158 unit tests pass on Ubuntu (Python 3.12) and Windows (Python 3.12).
- 22 new tests cover:
missing_capacityandzero_capacitystates, carry-forward (single and multi-sprint), carry-forward not applied tozero_capacity, target-sprint team/individual days off applied after carry-forward,ado_team_total_days_off_countisolation,team_days_off_countcorrectness, JSON serialisation ofper_user_capacity. - CI (
ubuntu-latest+windows-latest) passed on this PR before merge. ruff format --checkandruff checkpass on all files.
Follow-up recommendations
- Team composition on carry-forward: if a team member joins or leaves between the last configured sprint and a future sprint, the carried capacity will include the wrong headcount until Azure is configured for that sprint. This is unavoidable without real Azure data, and the
carried_forwardsource makes it auditable. ado_team_total_days_off_countis retained as a diagnostic column. It can be hidden from the UI in a future cleanup if the raw Azure value causes confusion (it will always differ fromteam_days_off_countwhen any member has individual days off).