Probabilistic project scheduling using Monte Carlo simulation, confidence intervals, risk-adjusted timelines, and throughput-based forecasting.
Runs Monte Carlo simulations on historical throughput data to forecast project completion dates with confidence intervals. Use it when stakeholders ask "when will it be done?" to provide probabilistic answers based on team velocity rather than single-point estimates.
/plugin marketplace add melodic-software/claude-code-plugins/plugin install estimation-planning@melodic-softwareThis skill is limited to using the following tools:
Probabilistic scheduling techniques using Monte Carlo simulation for realistic project forecasting with confidence intervals.
| Confidence | Meaning | Use Case |
|---|---|---|
| 50% | Median estimate, even odds | Internal planning |
| 70% | Reasonably confident | Team commitments |
| 85% | High confidence | Customer commitments |
| 95% | Very high confidence | Contractual deadlines |
| Data Type | Minimum Sample | Ideal Sample |
|---|---|---|
| Throughput | 4 sprints | 10+ sprints |
| Cycle Time | 30 items | 100+ items |
| Story Points | 6 sprints | 12+ sprints |
monte_carlo_process:
1_gather_historical_data:
throughput:
definition: "Items completed per time period"
granularity: "Sprint, week, or day"
example: "[8, 12, 10, 7, 11, 9, 13, 8, 10, 11]"
cycle_time:
definition: "Time from start to completion per item"
units: "Days or hours"
example: "[3, 5, 2, 8, 4, 3, 6, 4, 5, 3, 7, 4]"
2_define_scope:
backlog_size:
known_items: "Count of defined stories"
estimated_discovery: "Expected additional items"
uncertainty_range: "Low to high item count"
3_run_simulation:
iterations: 10000 # Minimum recommended
for_each_iteration:
- "Randomly sample throughput from historical data"
- "Accumulate until backlog complete"
- "Record completion date"
4_analyze_results:
outputs:
- "Probability distribution of completion dates"
- "Confidence intervals (50%, 85%, 95%)"
- "Risk of missing target date"
namespace EstimationPlanning.MonteCarlo;
/// <summary>
/// Monte Carlo schedule simulation using throughput-based forecasting.
/// </summary>
public sealed class MonteCarloScheduler
{
private readonly Random _random = new();
/// <summary>
/// Run Monte Carlo simulation for project completion forecasting.
/// </summary>
public SimulationResult Simulate(
int[] historicalThroughput,
int backlogSize,
int iterations = 10_000,
DateOnly? startDate = null)
{
if (historicalThroughput.Length < 4)
{
throw new ArgumentException(
"Need at least 4 historical data points for meaningful simulation");
}
startDate ??= DateOnly.FromDateTime(DateTime.Today);
var completionPeriods = new int[iterations];
for (var i = 0; i < iterations; i++)
{
completionPeriods[i] = SimulateOneRun(historicalThroughput, backlogSize);
}
Array.Sort(completionPeriods);
return new SimulationResult(
Iterations: iterations,
BacklogSize: backlogSize,
StartDate: startDate.Value,
Percentile50: GetPercentile(completionPeriods, 50),
Percentile70: GetPercentile(completionPeriods, 70),
Percentile85: GetPercentile(completionPeriods, 85),
Percentile95: GetPercentile(completionPeriods, 95),
MinPeriods: completionPeriods[0],
MaxPeriods: completionPeriods[^1],
AllResults: completionPeriods);
}
private int SimulateOneRun(int[] historicalThroughput, int remainingWork)
{
var periods = 0;
while (remainingWork > 0)
{
// Randomly sample from historical throughput
var throughput = historicalThroughput[_random.Next(historicalThroughput.Length)];
remainingWork -= throughput;
periods++;
}
return periods;
}
private static int GetPercentile(int[] sorted, int percentile)
{
var index = (int)Math.Ceiling(percentile / 100.0 * sorted.Length) - 1;
return sorted[Math.Max(0, index)];
}
/// <summary>
/// Calculate probability of completing by target date.
/// </summary>
public double ProbabilityOfCompletion(
SimulationResult simulation,
DateOnly targetDate)
{
var targetPeriods = (targetDate.ToDateTime(TimeOnly.MinValue) -
simulation.StartDate.ToDateTime(TimeOnly.MinValue)).Days / 7; // Assuming weekly periods
var completedBeforeTarget = simulation.AllResults.Count(p => p <= targetPeriods);
return (double)completedBeforeTarget / simulation.Iterations;
}
}
public sealed record SimulationResult(
int Iterations,
int BacklogSize,
DateOnly StartDate,
int Percentile50,
int Percentile70,
int Percentile85,
int Percentile95,
int MinPeriods,
int MaxPeriods,
int[] AllResults)
{
public DateOnly GetDateForPercentile(int percentile, int daysPerPeriod = 7)
{
var periods = percentile switch
{
50 => Percentile50,
70 => Percentile70,
85 => Percentile85,
95 => Percentile95,
_ => throw new ArgumentException($"Unsupported percentile: {percentile}")
};
return StartDate.AddDays(periods * daysPerPeriod);
}
}
throughput_collection:
daily:
granularity: "Items completed per day"
use_when:
- "Kanban teams"
- "Continuous delivery"
- "High throughput teams"
data_points_needed: "30+ days"
weekly:
granularity: "Items completed per week"
use_when:
- "Mixed Kanban/Scrum"
- "Medium throughput"
data_points_needed: "12+ weeks"
sprint:
granularity: "Items completed per sprint"
use_when:
- "Scrum teams"
- "Fixed cadence"
data_points_needed: "6+ sprints"
what_to_count:
good:
- "User stories completed"
- "Features deployed"
- "Tickets resolved"
avoid:
- "Sub-tasks (too granular)"
- "Bugs only (not representative)"
- "Points (use count for Monte Carlo)"
namespace EstimationPlanning.MonteCarlo;
/// <summary>
/// Analyze historical throughput data for simulation.
/// </summary>
public static class ThroughputAnalyzer
{
/// <summary>
/// Calculate throughput statistics.
/// </summary>
public static ThroughputStats Analyze(int[] throughput)
{
if (throughput.Length == 0)
throw new ArgumentException("Throughput data required");
var sorted = throughput.OrderBy(t => t).ToArray();
var mean = throughput.Average();
var stdDev = Math.Sqrt(throughput.Average(t => Math.Pow(t - mean, 2)));
var cv = stdDev / mean; // Coefficient of variation
return new ThroughputStats(
Count: throughput.Length,
Mean: mean,
StandardDeviation: stdDev,
CoefficientOfVariation: cv,
Min: sorted[0],
Max: sorted[^1],
Median: GetMedian(sorted),
Percentile25: GetPercentile(sorted, 25),
Percentile75: GetPercentile(sorted, 75),
Stability: cv < 0.3 ? "Stable" : cv < 0.5 ? "Moderate" : "Volatile");
}
/// <summary>
/// Detect trends in throughput (improving, stable, declining).
/// </summary>
public static TrendAnalysis AnalyzeTrend(int[] throughput)
{
if (throughput.Length < 4)
return new TrendAnalysis("Insufficient data", 0);
// Simple linear regression
var n = throughput.Length;
var sumX = Enumerable.Range(0, n).Sum();
var sumY = throughput.Sum();
var sumXY = Enumerable.Range(0, n).Zip(throughput, (x, y) => x * y).Sum();
var sumX2 = Enumerable.Range(0, n).Sum(x => x * x);
var slope = (n * sumXY - sumX * sumY) / (double)(n * sumX2 - sumX * sumX);
var percentChange = slope / throughput.Average() * 100;
var trend = percentChange switch
{
> 5 => "Improving",
< -5 => "Declining",
_ => "Stable"
};
return new TrendAnalysis(trend, percentChange);
}
private static double GetMedian(int[] sorted) =>
sorted.Length % 2 == 0
? (sorted[sorted.Length / 2 - 1] + sorted[sorted.Length / 2]) / 2.0
: sorted[sorted.Length / 2];
private static int GetPercentile(int[] sorted, int percentile)
{
var index = (int)Math.Ceiling(percentile / 100.0 * sorted.Length) - 1;
return sorted[Math.Max(0, index)];
}
}
public sealed record ThroughputStats(
int Count,
double Mean,
double StandardDeviation,
double CoefficientOfVariation,
int Min,
int Max,
double Median,
int Percentile25,
int Percentile75,
string Stability);
public sealed record TrendAnalysis(string Trend, double PercentChangePerPeriod);
namespace EstimationPlanning.MonteCarlo;
/// <summary>
/// Forecast individual item completion using cycle time data.
/// </summary>
public sealed class CycleTimeForecaster
{
private readonly Random _random = new();
/// <summary>
/// Forecast when a single item starting today will complete.
/// </summary>
public CycleTimeForecast ForecastItem(
double[] historicalCycleTimes,
DateOnly startDate,
int iterations = 10_000)
{
var completionDays = new double[iterations];
for (var i = 0; i < iterations; i++)
{
completionDays[i] = historicalCycleTimes[
_random.Next(historicalCycleTimes.Length)];
}
Array.Sort(completionDays);
return new CycleTimeForecast(
StartDate: startDate,
Percentile50Days: completionDays[(int)(iterations * 0.50)],
Percentile70Days: completionDays[(int)(iterations * 0.70)],
Percentile85Days: completionDays[(int)(iterations * 0.85)],
Percentile95Days: completionDays[(int)(iterations * 0.95)]);
}
/// <summary>
/// Analyze cycle time distribution.
/// </summary>
public CycleTimeAnalysis AnalyzeCycleTimes(double[] cycleTimes)
{
var sorted = cycleTimes.OrderBy(c => c).ToArray();
var mean = cycleTimes.Average();
// Calculate percentiles
var p50 = GetPercentile(sorted, 50);
var p85 = GetPercentile(sorted, 85);
var p95 = GetPercentile(sorted, 95);
// Identify outliers (items taking > 2x the 85th percentile)
var outlierThreshold = p85 * 2;
var outlierCount = cycleTimes.Count(c => c > outlierThreshold);
return new CycleTimeAnalysis(
ItemCount: cycleTimes.Length,
Mean: mean,
Median: p50,
Percentile85: p85,
Percentile95: p95,
OutlierCount: outlierCount,
OutlierThreshold: outlierThreshold);
}
private static double GetPercentile(double[] sorted, int percentile)
{
var index = (int)Math.Ceiling(percentile / 100.0 * sorted.Length) - 1;
return sorted[Math.Max(0, index)];
}
}
public sealed record CycleTimeForecast(
DateOnly StartDate,
double Percentile50Days,
double Percentile70Days,
double Percentile85Days,
double Percentile95Days)
{
public DateOnly GetCompletionDate(int percentile) => percentile switch
{
50 => StartDate.AddDays((int)Math.Ceiling(Percentile50Days)),
70 => StartDate.AddDays((int)Math.Ceiling(Percentile70Days)),
85 => StartDate.AddDays((int)Math.Ceiling(Percentile85Days)),
95 => StartDate.AddDays((int)Math.Ceiling(Percentile95Days)),
_ => throw new ArgumentException($"Unsupported percentile: {percentile}")
};
}
public sealed record CycleTimeAnalysis(
int ItemCount,
double Mean,
double Median,
double Percentile85,
double Percentile95,
int OutlierCount,
double OutlierThreshold);
backlog_uncertainty:
known_backlog:
description: "Well-defined items"
handling: "Count directly"
estimated_discovery:
description: "Expected additional items during project"
handling: "Add percentage or range"
typical_ranges:
low_uncertainty: "10-20% additional"
medium_uncertainty: "20-40% additional"
high_uncertainty: "40-80% additional"
greenfield: "100%+ additional"
simulation_approach:
simple:
method: "Use range: simulate with low, likely, high item counts"
report: "Separate forecasts for each scenario"
advanced:
method: "Randomize backlog size within range for each iteration"
report: "Single combined distribution"
namespace EstimationPlanning.MonteCarlo;
/// <summary>
/// Monte Carlo simulation with uncertain backlog size.
/// </summary>
public sealed class UncertainBacklogSimulator
{
private readonly Random _random = new();
/// <summary>
/// Simulate with backlog size uncertainty.
/// </summary>
public SimulationResult SimulateWithUncertainty(
int[] historicalThroughput,
BacklogEstimate backlog,
int iterations = 10_000,
DateOnly? startDate = null)
{
startDate ??= DateOnly.FromDateTime(DateTime.Today);
var completionPeriods = new int[iterations];
for (var i = 0; i < iterations; i++)
{
// Randomize backlog size using triangular distribution
var backlogSize = SampleTriangular(
backlog.LowEstimate,
backlog.LikelyEstimate,
backlog.HighEstimate);
completionPeriods[i] = SimulateOneRun(historicalThroughput, backlogSize);
}
Array.Sort(completionPeriods);
return new SimulationResult(
Iterations: iterations,
BacklogSize: backlog.LikelyEstimate,
StartDate: startDate.Value,
Percentile50: GetPercentile(completionPeriods, 50),
Percentile70: GetPercentile(completionPeriods, 70),
Percentile85: GetPercentile(completionPeriods, 85),
Percentile95: GetPercentile(completionPeriods, 95),
MinPeriods: completionPeriods[0],
MaxPeriods: completionPeriods[^1],
AllResults: completionPeriods);
}
private int SampleTriangular(int low, int likely, int high)
{
var u = _random.NextDouble();
var fc = (double)(likely - low) / (high - low);
if (u < fc)
{
return (int)(low + Math.Sqrt(u * (high - low) * (likely - low)));
}
else
{
return (int)(high - Math.Sqrt((1 - u) * (high - low) * (high - likely)));
}
}
private int SimulateOneRun(int[] historicalThroughput, int remainingWork)
{
var periods = 0;
while (remainingWork > 0)
{
var throughput = historicalThroughput[_random.Next(historicalThroughput.Length)];
remainingWork -= throughput;
periods++;
}
return periods;
}
private static int GetPercentile(int[] sorted, int percentile)
{
var index = (int)Math.Ceiling(percentile / 100.0 * sorted.Length) - 1;
return sorted[Math.Max(0, index)];
}
}
public sealed record BacklogEstimate(
int LowEstimate,
int LikelyEstimate,
int HighEstimate,
string Assumptions);
forecast_presentation:
title: "Project Completion Forecast"
date_generated: ""
data_period: ""
key_findings:
target_date: ""
probability_of_hitting_target: ""
recommended_commitment_date: ""
confidence_levels:
fifty_percent:
date: ""
meaning: "50% chance of completing by this date"
recommendation: "Use for internal planning only"
eighty_five_percent:
date: ""
meaning: "85% chance of completing by this date"
recommendation: "Use for customer commitments"
ninety_five_percent:
date: ""
meaning: "95% chance of completing by this date"
recommendation: "Use for contractual deadlines"
assumptions:
- "Team composition remains stable"
- "No major scope changes"
- "Historical throughput is representative"
risks_not_modeled:
- "Dependencies on external teams"
- "Major technical unknowns"
- "Resource constraints"
recommendations:
- ""
../estimation-techniques/ for underlying estimation methods../risk-assessment/ for risk quantificationestimation-techniques - Story points and function pointsrisk-assessment - Risk identification and analysisfeasibility-analysis - Schedule feasibility evaluationLast Updated: 2025-12-26
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.