Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Calculate construction labor rates with overhead, benefits, and productivity factors. Regional rate databases and crew composition.
.claude/skills/datadrivenconstruction-labor-rate/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-09 | ✗→✓ | ▲ Improved | 92% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 49% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 84% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 55% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 251% | 0% |
Labor costs account for 30-50% of construction costs. This skill calculates all-in labor rates including wages, benefits, overhead, and regional adjustments.
pythonimport pandas as pd from typing import Dict, Any, List, Optional from dataclasses import dataclass, field from enum import Enum class LaborCategory(Enum): """Labor skill categories.""" LABORER = "laborer" CARPENTER = "carpenter" ELECTRICIAN = "electrician" PLUMBER = "plumber" IRONWORKER = "ironworker" MASON = "mason" OPERATOR = "equipment_operator" FOREMAN = "foreman" SUPERINTENDENT = "superintendent" class WorkType(Enum): """Work type for productivity.""" NEW_CONSTRUCTION = "new" RENOVATION = "renovation" DEMOLITION = "demolition" MAINTENANCE = "maintenance" @dataclass class LaborRate: """Complete labor rate breakdown.""" category: str base_wage: float benefits: float taxes: float insurance: float overhead: float profit: float total_rate: float unit: str = "hour" @dataclass class CrewComposition: """Crew composition for work.""" name: str workers: List[Dict[str, Any]] total_hourly_cost: float output_per_hour: float unit: str class LaborRateCalculator: """Calculate construction labor rates.""" # Default burden rates (percent of base wage) DEFAULT_BURDENS = { 'benefits': 0.30, # Health, pension, vacation 'taxes': 0.10, # FICA, unemployment 'insurance': 0.08, # Workers comp, liability 'overhead': 0.15, # General conditions 'profit': 0.10 # Contractor profit } # Base wages by category (USD/hour, US average) BASE_WAGES = { LaborCategory.LABORER: 22, LaborCategory.CARPENTER: 32, LaborCategory.ELECTRICIAN: 38, LaborCategory.PLUMBER: 36, LaborCategory.IRONWORKER: 35, LaborCategory.MASON: 34, LaborCategory.OPERATOR: 40, LaborCategory.FOREMAN: 45, LaborCategory.SUPERINTENDENT: 55 } # Regional factors REGIONAL_FACTORS = { 'US_National': 1.00, 'New_York': 1.45, 'San_Francisco': 1.40, 'Chicago': 1.15, 'Houston': 0.95, 'Atlanta': 0.90, 'Germany_Berlin': 1.20, 'UK_London': 1.35 } def __init__(self, burden_rates: Dict[str, float] = None): self.burdens = burden_rates or self.DEFAULT_BURDENS def calculate_rate(self, category: LaborCategory, region: str = 'US_National', custom_wage: float = None) -> LaborRate: """Calculate all-in labor rate.""" # Get base wage base = custom_wage or self.BASE_WAGES.get(category, 25) # Apply regional factor regional_factor = self.REGIONAL_FACTORS.get(region, 1.0) base *= regional_factor # Calculate burden components benefits = base * self.burdens['benefits'] taxes = base * self.burdens['taxes'] insurance = base * self.burdens['insurance'] # Subtotal before markup subtotal = base + benefits + taxes + insurance # Overhead and profit overhead = subtotal * self.burdens['overhead'] profit = (subtotal + overhead) * self.burdens['profit'] total = subtotal + overhead + profit return LaborRate( category=category.value, base_wage=round(base, 2), benefits=round(benefits, 2), taxes=round(taxes, 2), insurance=round(insurance, 2), overhead=round(overhead, 2), profit=round(profit, 2), total_rate=round(total, 2) ) def calculate_crew_cost(self, composition: Dict[LaborCategory, int], region: str = 'US_National') -> float: """Calculate hourly cost for crew composition.""" total = 0 for category, count in composition.items(): rate = self.calculate_rate(category, region) total += rate.total_rate * count return round(total, 2) def get_rate_table(self, region: str = 'US_National') -> pd.DataFrame: """Generate rate table for all categories.""" rates = [] for category in LaborCategory: rate = self.calculate_rate(category, region) rates.append({ 'category': rate.category, 'base_wage': rate.base_wage, 'benefits': rate.benefits, 'taxes': rate.taxes, 'insurance': rate.insurance, 'overhead': rate.overhead, 'profit': rate.profit, 'total_rate': rate.total_rate }) return pd.DataFrame(rates) class ProductivityFactor: """Calculate productivity factors for labor.""" # Base productivity factors WORK_TYPE_FACTORS = { WorkType.NEW_CONSTRUCTION: 1.0, WorkType.RENOVATION: 0.75, WorkType.DEMOLITION: 0.90, WorkType.MAINTENANCE: 0.65 } # Condition factors CONDITION_FACTORS = { 'ideal': 1.0, 'normal': 0.90, 'difficult': 0.75, 'hazardous': 0.60, 'confined_space': 0.50 } # Weather factors WEATHER_FACTORS = { 'clear': 1.0, 'hot': 0.85, 'cold': 0.80, 'rain': 0.60, 'wind': 0.75 } def calculate_factor(self, work_type: WorkType, condition: str = 'normal', weather: str = 'clear', overtime_hours: int = 0) -> float: """Calculate combined productivity factor.""" base = self.WORK_TYPE_FACTORS.get(work_type, 1.0) cond = self.CONDITION_FACTORS.get(condition, 0.9) weath = self.WEATHER_FACTORS.get(weather, 1.0) # Overtime degradation (productivity drops after 8 hours) overtime_factor = 1.0 if overtime_hours > 0: # Each OT hour is ~15% less productive overtime_factor = 1 - (overtime_hours * 0.015) combined = base * cond * weath * overtime_factor return round(max(combined, 0.3), 2) # Minimum 30% productivity def adjust_labor_hours(self, base_hours: float, work_type: WorkType, condition: str = 'normal', weather: str = 'clear') -> float: """Adjust labor hours for conditions.""" factor = self.calculate_factor(work_type, condition, weather) return round(base_hours / factor, 1) class CrewBuilder: """Build and optimize crew compositions.""" # Standard crew compositions STANDARD_CREWS = { 'concrete_pour': { LaborCategory.FOREMAN: 1, LaborCategory.CARPENTER: 2, LaborCategory.LABORER: 4, LaborCategory.OPERATOR: 1 }, 'framing': { LaborCategory.FOREMAN: 1, LaborCategory.CARPENTER: 4, LaborCategory.LABORER: 2 }, 'electrical_rough': { LaborCategory.FOREMAN: 1, LaborCategory.ELECTRICIAN: 3, LaborCategory.LABORER: 1 }, 'plumbing_rough': { LaborCategory.FOREMAN: 1, LaborCategory.PLUMBER: 2, LaborCategory.LABORER: 1 }, 'masonry': { LaborCategory.FOREMAN: 1, LaborCategory.MASON: 4, LaborCategory.LABORER: 4 } } def __init__(self, rate_calculator: LaborRateCalculator): self.calc = rate_calculator def get_crew(self, work_type: str, region: str = 'US_National') -> CrewComposition: """Get standard crew composition with costs.""" if work_type not in self.STANDARD_CREWS: raise ValueError(f"Unknown work type: {work_type}") composition = self.STANDARD_CREWS[work_type] total_cost = self.calc.calculate_crew_cost(composition, region) workers = [] for category, count in composition.items(): rate = self.calc.calculate_rate(category, region) workers.append({ 'category': category.value, 'count': count, 'hourly_rate': rate.total_rate, 'subtotal': rate.total_rate * count }) return CrewComposition( name=work_type, workers=workers, total_hourly_cost=total_cost, output_per_hour=1.0, # Placeholder unit='hour' ) def custom_crew(self, workers: Dict[LaborCategory, int], region: str = 'US_National') -> CrewComposition: """Build custom crew composition.""" total_cost = self.calc.calculate_crew_cost(workers, region) worker_list = [] for category, count in workers.items(): rate = self.calc.calculate_rate(category, region) worker_list.append({ 'category': category.value, 'count': count, 'hourly_rate': rate.total_rate, 'subtotal': rate.total_rate * count }) return CrewComposition( name='custom', workers=worker_list, total_hourly_cost=total_cost, output_per_hour=1.0, unit='hour' )
pythoncalc = LaborRateCalculator() # Get single rate rate = calc.calculate_rate(LaborCategory.CARPENTER, region='New_York') print(f"Carpenter rate NYC: ${rate.total_rate}/hr") # Rate table rates = calc.get_rate_table('US_National') print(rates)
pythonbuilder = CrewBuilder(calc) concrete_crew = builder.get_crew('concrete_pour', 'Chicago') print(f"Crew cost: ${concrete_crew.total_hourly_cost}/hr")
pythonproductivity = ProductivityFactor() factor = productivity.calculate_factor( WorkType.RENOVATION, condition='difficult', weather='hot' ) adjusted_hours = productivity.adjust_labor_hours(100, WorkType.RENOVATION)
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-09 | fail→pass | 14,999 | 11,686 | -22% | 1 | 1 | 0% | 2,855 | 5,483 | +92% | 0 | 0 | — |
case-01 | fail→pass | 16,718 | 12,891 | -23% | 1 | 1 | 0% | 3,042 | 4,519 | +49% | 0 | 0 | — |
case-02 | fail→fail | 11,687 | 15,655 | +34% | 1 | 1 | 0% | 2,188 | 6,707 | +207% | 0 | 0 | — |
case-03 | fail→pass | 11,961 | 4,763 | -60% | 1 | 1 | 0% | 2,095 | 3,862 | +84% | 0 | 0 | — |
case-08 | fail→pass | 15,883 | 7,594 | -52% | 1 | 1 | 0% | 2,908 | 4,512 | +55% | 0 | 0 | — |
case-04 | pass→pass | 5,061 | 5,259 | +4% | 1 | 1 | 0% | 884 | 3,909 | +342% | 0 | 0 | — |
case-05 | pass→pass | 5,681 | 7,198 | +27% | 1 | 1 | 0% | 1,129 | 4,467 | +296% | 0 | 0 | — |
case-06 | pass→pass | 2,775 | 3,773 | +36% | 1 | 1 | 0% | 491 | 3,479 | +609% | 0 | 0 | — |
case-07 | fail→fail | 34,903 | 8,292 | -76% | 1 | 1 | 0% | 3,293 | 4,850 | +47% | 0 | 0 | — |
case-10 | fail→pass | 9,474 | 11,356 | +20% | 1 | 1 | 0% | 1,572 | 5,513 | +251% | 0 | 0 | — |
case-11 | fail→fail | 14,188 | 11,592 | -18% | 1 | 1 | 0% | 2,397 | 5,628 | +135% | 0 | 0 | — |
case-12 | fail→fail | 12,108 | 10,438 | -14% | 1 | 1 | 0% | 2,050 | 5,404 | +164% | 0 | 0 | — |
case-13 | fail→fail | 11,407 | 15,428 | +35% | 1 | 1 | 0% | 1,918 | 6,347 | +231% | 0 | 0 | — |
case-14 | fail→pass | 14,039 | 6,637 | -53% | 1 | 1 | 0% | 2,535 | 4,247 | +68% | 0 | 0 | — |
case-15 | fail→pass | 14,245 | 5,206 | -63% | 1 | 1 | 0% | 2,413 | 3,892 | +61% | 0 | 0 | — |
case-16 | fail→pass | 9,047 | 4,602 | -49% | 1 | 1 | 0% | 1,436 | 3,832 | +167% | 0 | 0 | — |
case-17 | fail→pass | 11,327 | 4,942 | -56% | 1 | 1 | 0% | 2,125 | 3,979 | +87% | 0 | 0 | — |
case-18 | fail→pass | 13,249 | 7,773 | -41% | 1 | 1 | 0% | 2,611 | 4,668 | +79% | 0 | 0 | — |
case-19 | fail→pass | 14,997 | 9,485 | -37% | 1 | 1 | 0% | 2,769 | 5,075 | +83% | 0 | 0 | — |
case-20 | fail→pass | 28,089 | 10,476 | -63% | 1 | 1 | 0% | 2,532 | 5,351 | +111% | 0 | 0 | — |
case-21 | fail→pass | 11,486 | 4,919 | -57% | 1 | 1 | 0% | 2,020 | 3,938 | +95% | 0 | 0 | — |
case-22 | fail→pass | 11,579 | 8,213 | -29% | 1 | 1 | 0% | 2,256 | 4,716 | +109% | 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 +64 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.