Skip to content

v0.1.4 — Sprint capacity calculation fix

Choose a tag to compare

@XeonNAS XeonNAS released this 02 Jun 00:50
· 4 commits to main since this release
b1b5054

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_count renamed to ado_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 reduce planned_working_days.
  • New team_days_off_count column — counts only team-wide days off (from the team days-off endpoint). Always correct.
  • New capacity_source column with four explicit states: azure_configured, missing_capacity, zero_capacity, carried_forward.
  • per_user_capacity is 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.py

Update 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_capacity and zero_capacity states, carry-forward (single and multi-sprint), carry-forward not applied to zero_capacity, target-sprint team/individual days off applied after carry-forward, ado_team_total_days_off_count isolation, team_days_off_count correctness, JSON serialisation of per_user_capacity.
  • CI (ubuntu-latest + windows-latest) passed on this PR before merge.
  • ruff format --check and ruff check pass 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_forward source makes it auditable.
  • ado_team_total_days_off_count is 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 from team_days_off_count when any member has individual days off).