Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Multi-project portfolio analytics dashboard. Aggregate KPIs across projects, track portfolio health, compare performance, and support executive decision-making.
.claude/skills/datadrivenconstruction-portfolio-dashboard/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 114% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 159% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 246% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 753% | 0% |
| case-20 | ✗→✓ | ▲ Improved | 497% | 0% |
Aggregate and analyze data across multiple construction projects for portfolio-level visibility. Track KPIs, identify trends, compare project performance, and support strategic resource allocation decisions.
┌─────────────────────────────────────────────────────────────────┐
│ PORTFOLIO DASHBOARD │
├─────────────────────────────────────────────────────────────────┤
│ │
│ PROJECT A PROJECT B PROJECT C PROJECT D │
│ ↓ ↓ ↓ ↓ │
│ ┌─────────────────────────────────────────────┐ │
│ │ DATA AGGREGATION │ │
│ │ Cost | Schedule | Safety | Quality | Risk │ │
│ └─────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────┐ │
│ │ PORTFOLIO KPIs │ │
│ │ 📊 Total Value 📈 On-Schedule % │ │
│ │ 💰 On-Budget % 🛡️ Safety Rate │ │
│ │ ⚠️ Risk Score 📋 Resource Util │ │
│ └─────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘pythonfrom dataclasses import dataclass, field from typing import List, Dict, Optional, Tuple from datetime import datetime, timedelta from enum import Enum import statistics class ProjectStatus(Enum): PLANNING = "planning" ACTIVE = "active" ON_HOLD = "on_hold" COMPLETE = "complete" CANCELLED = "cancelled" class HealthStatus(Enum): GREEN = "green" # On track YELLOW = "yellow" # At risk RED = "red" # Critical GREY = "grey" # Not started/on hold @dataclass class ProjectMetrics: project_id: str project_name: str status: ProjectStatus contract_value: float percent_complete: float # Schedule planned_start: datetime planned_end: datetime actual_start: Optional[datetime] forecast_end: datetime schedule_variance_days: int = 0 # Cost budget: float actual_cost: float forecast_cost: float cost_variance: float = 0.0 cpi: float = 1.0 spi: float = 1.0 # Safety recordable_incidents: int = 0 total_hours: float = 0 trir: float = 0.0 # Quality defects_open: int = 0 rework_cost: float = 0.0 # Risk risk_score: float = 0.0 critical_risks: int = 0 @property def health(self) -> HealthStatus: """Determine overall project health.""" if self.status in [ProjectStatus.ON_HOLD, ProjectStatus.CANCELLED]: return HealthStatus.GREY # Critical if significantly over budget/schedule if self.cpi < 0.85 or self.spi < 0.85 or self.critical_risks > 3: return HealthStatus.RED # At risk if moderately off track if self.cpi < 0.95 or self.spi < 0.95 or self.critical_risks > 0: return HealthStatus.YELLOW return HealthStatus.GREEN @dataclass class PortfolioSummary: report_date: datetime total_projects: int active_projects: int total_contract_value: float total_budget: float total_actual_cost: float total_forecast_cost: float # Performance avg_cpi: float avg_spi: float on_budget_pct: float on_schedule_pct: float # Safety portfolio_trir: float total_incidents: int # Health distribution green_count: int yellow_count: int red_count: int # Trends cost_trend: str schedule_trend: str @dataclass class ProjectComparison: metric: str projects: Dict[str, float] avg: float best: Tuple[str, float] worst: Tuple[str, float] class PortfolioDashboard: """Multi-project portfolio analytics.""" # Health thresholds THRESHOLDS = { "cpi_warning": 0.95, "cpi_critical": 0.85, "spi_warning": 0.95, "spi_critical": 0.85, "trir_warning": 2.0, "risk_score_warning": 7.0 } def __init__(self, portfolio_name: str): self.portfolio_name = portfolio_name self.projects: Dict[str, ProjectMetrics] = {} self.snapshots: List[Dict] = [] # Historical data def add_project(self, metrics: ProjectMetrics): """Add or update project in portfolio.""" self.projects[metrics.project_id] = metrics def import_projects(self, projects_data: List[Dict]) -> int: """Import multiple projects from data.""" count = 0 for p in projects_data: metrics = ProjectMetrics( project_id=p['id'], project_name=p['name'], status=ProjectStatus(p.get('status', 'active')), contract_value=p['contract_value'], percent_complete=p.get('percent_complete', 0), planned_start=p['planned_start'], planned_end=p['planned_end'], actual_start=p.get('actual_start'), forecast_end=p.get('forecast_end', p['planned_end']), budget=p['budget'], actual_cost=p.get('actual_cost', 0), forecast_cost=p.get('forecast_cost', p['budget']), cpi=p.get('cpi', 1.0), spi=p.get('spi', 1.0), recordable_incidents=p.get('incidents', 0), total_hours=p.get('total_hours', 0), risk_score=p.get('risk_score', 0), critical_risks=p.get('critical_risks', 0) ) # Calculate derived metrics metrics.cost_variance = metrics.budget - metrics.actual_cost metrics.schedule_variance_days = (metrics.planned_end - metrics.forecast_end).days if metrics.total_hours > 0: metrics.trir = (metrics.recordable_incidents * 200000) / metrics.total_hours self.add_project(metrics) count += 1 return count def get_active_projects(self) -> List[ProjectMetrics]: """Get list of active projects.""" return [p for p in self.projects.values() if p.status == ProjectStatus.ACTIVE] def calculate_portfolio_summary(self) -> PortfolioSummary: """Calculate portfolio-level summary metrics.""" active = self.get_active_projects() all_projects = list(self.projects.values()) if not all_projects: return None # Totals total_contract = sum(p.contract_value for p in all_projects) total_budget = sum(p.budget for p in all_projects) total_actual = sum(p.actual_cost for p in all_projects) total_forecast = sum(p.forecast_cost for p in all_projects) # Performance averages (weighted by budget) if total_budget > 0: avg_cpi = sum(p.cpi * p.budget for p in active) / sum(p.budget for p in active) if active else 1.0 avg_spi = sum(p.spi * p.budget for p in active) / sum(p.budget for p in active) if active else 1.0 else: avg_cpi = avg_spi = 1.0 # On budget/schedule percentages on_budget = len([p for p in active if p.cpi >= 0.95]) on_schedule = len([p for p in active if p.spi >= 0.95]) on_budget_pct = (on_budget / len(active) * 100) if active else 100 on_schedule_pct = (on_schedule / len(active) * 100) if active else 100 # Safety metrics total_incidents = sum(p.recordable_incidents for p in all_projects) total_hours = sum(p.total_hours for p in all_projects) portfolio_trir = (total_incidents * 200000 / total_hours) if total_hours > 0 else 0 # Health distribution green = len([p for p in active if p.health == HealthStatus.GREEN]) yellow = len([p for p in active if p.health == HealthStatus.YELLOW]) red = len([p for p in active if p.health == HealthStatus.RED]) # Trends (compare to previous snapshot if available) cost_trend = "stable" schedule_trend = "stable" if self.snapshots: prev = self.snapshots[-1] if avg_cpi > prev.get('avg_cpi', 1.0): cost_trend = "improving" elif avg_cpi < prev.get('avg_cpi', 1.0): cost_trend = "declining" if avg_spi > prev.get('avg_spi', 1.0): schedule_trend = "improving" elif avg_spi < prev.get('avg_spi', 1.0): schedule_trend = "declining" return PortfolioSummary( report_date=datetime.now(), total_projects=len(all_projects), active_projects=len(active), total_contract_value=total_contract, total_budget=total_budget, total_actual_cost=total_actual, total_forecast_cost=total_forecast, avg_cpi=avg_cpi, avg_spi=avg_spi, on_budget_pct=on_budget_pct, on_schedule_pct=on_schedule_pct, portfolio_trir=portfolio_trir, total_incidents=total_incidents, green_count=green, yellow_count=yellow, red_count=red, cost_trend=cost_trend, schedule_trend=schedule_trend ) def compare_projects(self, metric: str) -> ProjectComparison: """Compare projects by specific metric.""" active = self.get_active_projects() if not active: return None metric_map = { "cpi": lambda p: p.cpi, "spi": lambda p: p.spi, "percent_complete": lambda p: p.percent_complete, "cost_variance": lambda p: p.cost_variance, "trir": lambda p: p.trir, "risk_score": lambda p: p.risk_score } if metric not in metric_map: raise ValueError(f"Unknown metric: {metric}") getter = metric_map[metric] values = {p.project_name: getter(p) for p in active} avg = statistics.mean(values.values()) # Best/worst depends on metric (higher CPI good, lower TRIR good) if metric in ["trir", "risk_score"]: best = min(values.items(), key=lambda x: x[1]) worst = max(values.items(), key=lambda x: x[1]) else: best = max(values.items(), key=lambda x: x[1]) worst = min(values.items(), key=lambda x: x[1]) return ProjectComparison( metric=metric, projects=values, avg=avg, best=best, worst=worst ) def get_projects_at_risk(self) -> List[ProjectMetrics]: """Get projects that need attention.""" return [p for p in self.get_active_projects() if p.health in [HealthStatus.YELLOW, HealthStatus.RED]] def get_top_risks(self, limit: int = 10) -> List[Dict]: """Get top risks across portfolio.""" risks = [] for p in self.get_active_projects(): if p.risk_score > 0: risks.append({ "project": p.project_name, "risk_score": p.risk_score, "critical_risks": p.critical_risks, "cpi": p.cpi, "spi": p.spi }) return sorted(risks, key=lambda x: -x['risk_score'])[:limit] def forecast_cash_needs(self, months: int = 6) -> List[Dict]: """Forecast cash needs across portfolio.""" forecasts = [] for month in range(1, months + 1): month_date = datetime.now() + timedelta(days=month * 30) month_spend = 0 for p in self.get_active_projects(): # Simple linear projection based on remaining work remaining = p.forecast_cost - p.actual_cost months_remaining = max(1, (p.forecast_end - datetime.now()).days / 30) monthly_burn = remaining / months_remaining month_spend += monthly_burn forecasts.append({ "month": month_date.strftime("%Y-%m"), "projected_spend": month_spend }) return forecasts def save_snapshot(self): """Save current state for trend analysis.""" summary = self.calculate_portfolio_summary() if summary: self.snapshots.append({ "date": datetime.now(), "avg_cpi": summary.avg_cpi, "avg_spi": summary.avg_spi, "on_budget_pct": summary.on_budget_pct, "on_schedule_pct": summary.on_schedule_pct, "total_forecast": summary.total_forecast_cost }) def generate_report(self) -> str: """Generate portfolio dashboard report.""" summary = self.calculate_portfolio_summary() if not summary: return "No projects in portfolio" lines = [ "# Portfolio Dashboard", "", f"**Portfolio:** {self.portfolio_name}", f"**Report Date:** {summary.report_date.strftime('%Y-%m-%d')}", "", "## Executive Summary", "", f"| Metric | Value |", f"|--------|-------|", f"| Total Projects | {summary.total_projects} ({summary.active_projects} active) |", f"| Total Contract Value | ${summary.total_contract_value:,.0f} |", f"| Total Budget | ${summary.total_budget:,.0f} |", f"| Actual Cost to Date | ${summary.total_actual_cost:,.0f} |", f"| Forecast at Completion | ${summary.total_forecast_cost:,.0f} |", "", "## Performance Indicators", "", f"| KPI | Value | Trend |", f"|-----|-------|-------|", f"| Avg CPI | {summary.avg_cpi:.2f} | {summary.cost_trend} |", f"| Avg SPI | {summary.avg_spi:.2f} | {summary.schedule_trend} |", f"| On Budget | {summary.on_budget_pct:.0f}% | |", f"| On Schedule | {summary.on_schedule_pct:.0f}% | |", f"| Portfolio TRIR | {summary.portfolio_trir:.2f} | |", "", "## Health Distribution", "", f"🟢 Green: {summary.green_count} | 🟡 Yellow: {summary.yellow_count} | 🔴 Red: {summary.red_count}", "" ] # Projects at risk at_risk = self.get_projects_at_risk() if at_risk: lines.extend([ "## Projects Requiring Attention", "", "| Project | Health | CPI | SPI | Critical Risks |", "|---------|--------|-----|-----|----------------|" ]) for p in sorted(at_risk, key=lambda x: x.cpi): health_icon = "🟡" if p.health == HealthStatus.YELLOW else "🔴" lines.append( f"| {p.project_name} | {health_icon} | {p.cpi:.2f} | {p.spi:.2f} | {p.critical_risks} |" ) lines.append("") # Project comparison lines.extend([ "## Project Comparison - CPI", "", "| Project | CPI |", "|---------|-----|" ]) cpi_compare = self.compare_projects("cpi") if cpi_compare: for name, value in sorted(cpi_compare.projects.items(), key=lambda x: -x[1]): lines.append(f"| {name} | {value:.2f} |") return "\n".join(lines)
pythonfrom datetime import datetime, timedelta # Initialize dashboard dashboard = PortfolioDashboard("Regional Construction Portfolio") # Import project data projects = [ { "id": "PRJ-001", "name": "Downtown Office Tower", "status": "active", "contract_value": 50000000, "budget": 48000000, "actual_cost": 25000000, "forecast_cost": 49000000, "percent_complete": 55, "planned_start": datetime(2024, 1, 1), "planned_end": datetime(2025, 6, 30), "forecast_end": datetime(2025, 7, 15), "cpi": 0.92, "spi": 0.95, "incidents": 2, "total_hours": 150000, "risk_score": 7.5, "critical_risks": 2 }, { "id": "PRJ-002", "name": "Hospital Expansion", "status": "active", "contract_value": 80000000, "budget": 75000000, "actual_cost": 30000000, "forecast_cost": 74000000, "percent_complete": 40, "planned_start": datetime(2024, 3, 1), "planned_end": datetime(2026, 2, 28), "forecast_end": datetime(2026, 2, 28), "cpi": 1.02, "spi": 1.00, "incidents": 0, "total_hours": 100000, "risk_score": 4.0, "critical_risks": 0 } ] dashboard.import_projects(projects) # Get portfolio summary summary = dashboard.calculate_portfolio_summary() print(f"Portfolio Value: ${summary.total_contract_value:,.0f}") print(f"Avg CPI: {summary.avg_cpi:.2f}") print(f"On Budget: {summary.on_budget_pct:.0f}%") # Find projects at risk at_risk = dashboard.get_projects_at_risk() print(f"Projects at risk: {len(at_risk)}") # Compare projects cpi_comparison = dashboard.compare_projects("cpi") print(f"Best CPI: {cpi_comparison.best[0]} ({cpi_comparison.best[1]:.2f})") # Generate report print(dashboard.generate_report())
bashpip install (no external dependencies)
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 20,846 | 19,609 | -6% | 1 | 1 | 0% | 4,393 | 9,396 | +114% | 0 | 0 | — |
case-02 | fail→pass | 16,201 | 18,024 | +11% | 1 | 1 | 0% | 3,499 | 9,073 | +159% | 0 | 0 | — |
case-03 | pass→pass | 14,115 | 5,495 | -61% | 1 | 1 | 0% | 2,354 | 6,078 | +158% | 0 | 0 | — |
case-04 | pass→pass | 6,883 | 4,735 | -31% | 1 | 1 | 0% | 1,086 | 5,843 | +438% | 0 | 0 | — |
case-05 | fail→pass | 9,967 | 4,020 | -60% | 1 | 1 | 0% | 1,678 | 5,813 | +246% | 0 | 0 | — |
case-06 | pass→pass | 8,150 | 3,990 | -51% | 1 | 1 | 0% | 1,552 | 5,855 | +277% | 0 | 0 | — |
case-07 | pass→pass | 12,033 | 5,196 | -57% | 1 | 1 | 0% | 2,308 | 6,173 | +167% | 0 | 0 | — |
case-08 | pass→pass | 4,193 | 3,047 | -27% | 1 | 1 | 0% | 781 | 5,554 | +611% | 0 | 0 | — |
case-09 | pass→pass | 4,635 | 3,945 | -15% | 1 | 1 | 0% | 895 | 5,789 | +547% | 0 | 0 | — |
case-10 | pass→pass | 4,485 | 3,891 | -13% | 1 | 1 | 0% | 947 | 5,778 | +510% | 0 | 0 | — |
case-11 | pass→pass | 4,405 | 4,633 | +5% | 1 | 1 | 0% | 857 | 5,903 | +589% | 0 | 0 | — |
case-12 | pass→pass | 3,498 | 5,078 | +45% | 1 | 1 | 0% | 567 | 6,105 | +977% | 0 | 0 | — |
case-13 | pass→pass | 13,294 | 4,606 | -65% | 1 | 1 | 0% | 2,769 | 5,994 | +116% | 0 | 0 | — |
case-14 | fail→pass | 4,003 | 3,614 | -10% | 1 | 1 | 0% | 666 | 5,679 | +753% | 0 | 0 | — |
case-15 | pass→pass | 3,331 | 2,916 | -12% | 1 | 1 | 0% | 674 | 5,604 | +731% | 0 | 0 | — |
case-16 | pass→pass | 4,997 | 3,884 | -22% | 1 | 1 | 0% | 823 | 5,711 | +594% | 0 | 0 | — |
case-17 | pass→pass | 2,648 | 3,454 | +30% | 1 | 1 | 0% | 442 | 5,598 | +1167% | 0 | 0 | — |
case-18 | pass→pass | 4,352 | 3,647 | -16% | 1 | 1 | 0% | 798 | 5,737 | +619% | 0 | 0 | — |
case-19 | pass→pass | 8,812 | 4,237 | -52% | 1 | 1 | 0% | 1,427 | 5,885 | +312% | 0 | 0 | — |
case-20 | fail→pass | 4,566 | 4,464 | -2% | 1 | 1 | 0% | 1,000 | 5,973 | +497% | 0 | 0 | — |
case-21 | pass→pass | 5,149 | 3,216 | -38% | 1 | 1 | 0% | 890 | 5,613 | +531% | 0 | 0 | — |
case-22 | pass→pass | 2,872 | 2,869 | -0% | 1 | 1 | 0% | 501 | 5,552 | +1008% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +23 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.