Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Analyze labor productivity by trade, activity, and location. Track efficiency and identify improvement opportunities.
.claude/skills/datadrivenconstruction-labor-productivity-analyzer/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | 45% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 34% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 151% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 53% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 54% | 0% |
pythonimport pandas as pd from datetime import date from typing import Dict, Any, List from dataclasses import dataclass, field from enum import Enum class ProductivityStatus(Enum): EXCEEDING = "exceeding" ON_TARGET = "on_target" BELOW_TARGET = "below_target" CRITICAL = "critical" @dataclass class ProductivityEntry: entry_id: str date: date trade: str activity_code: str activity_description: str location: str crew_size: int hours_worked: float quantity_installed: float unit: str target_productivity: float # units per hour @property def actual_productivity(self) -> float: if self.hours_worked == 0: return 0 return self.quantity_installed / self.hours_worked @property def productivity_factor(self) -> float: if self.target_productivity == 0: return 0 return self.actual_productivity / self.target_productivity @property def status(self) -> ProductivityStatus: pf = self.productivity_factor if pf >= 1.1: return ProductivityStatus.EXCEEDING elif pf >= 0.9: return ProductivityStatus.ON_TARGET elif pf >= 0.7: return ProductivityStatus.BELOW_TARGET return ProductivityStatus.CRITICAL class LaborProductivityAnalyzer: def __init__(self, project_name: str): self.project_name = project_name self.entries: List[ProductivityEntry] = [] self.targets: Dict[str, float] = {} # activity_code: target_productivity self._counter = 0 def set_target(self, activity_code: str, target_productivity: float): self.targets[activity_code] = target_productivity def add_entry(self, entry_date: date, trade: str, activity_code: str, activity_description: str, location: str, crew_size: int, hours_worked: float, quantity_installed: float, unit: str) -> ProductivityEntry: self._counter += 1 entry_id = f"PROD-{self._counter:05d}" target = self.targets.get(activity_code, 1.0) entry = ProductivityEntry( entry_id=entry_id, date=entry_date, trade=trade, activity_code=activity_code, activity_description=activity_description, location=location, crew_size=crew_size, hours_worked=hours_worked, quantity_installed=quantity_installed, unit=unit, target_productivity=target ) self.entries.append(entry) return entry def get_productivity_by_trade(self) -> Dict[str, Dict[str, Any]]: by_trade = {} for entry in self.entries: if entry.trade not in by_trade: by_trade[entry.trade] = {'hours': 0, 'quantity': 0, 'entries': 0} by_trade[entry.trade]['hours'] += entry.hours_worked by_trade[entry.trade]['quantity'] += entry.quantity_installed by_trade[entry.trade]['entries'] += 1 for trade in by_trade: hours = by_trade[trade]['hours'] qty = by_trade[trade]['quantity'] by_trade[trade]['avg_productivity'] = qty / hours if hours > 0 else 0 return by_trade def get_productivity_by_activity(self) -> Dict[str, Dict[str, Any]]: by_activity = {} for entry in self.entries: code = entry.activity_code if code not in by_activity: by_activity[code] = { 'description': entry.activity_description, 'hours': 0, 'quantity': 0, 'target': entry.target_productivity } by_activity[code]['hours'] += entry.hours_worked by_activity[code]['quantity'] += entry.quantity_installed for code in by_activity: hours = by_activity[code]['hours'] qty = by_activity[code]['quantity'] by_activity[code]['actual'] = qty / hours if hours > 0 else 0 by_activity[code]['factor'] = ( by_activity[code]['actual'] / by_activity[code]['target'] if by_activity[code]['target'] > 0 else 0 ) return by_activity def get_low_performers(self) -> List[ProductivityEntry]: return [e for e in self.entries if e.status in [ProductivityStatus.BELOW_TARGET, ProductivityStatus.CRITICAL]] def get_summary(self) -> Dict[str, Any]: if not self.entries: return {'total_entries': 0} total_hours = sum(e.hours_worked for e in self.entries) factors = [e.productivity_factor for e in self.entries] avg_factor = sum(factors) / len(factors) return { 'total_entries': len(self.entries), 'total_hours': total_hours, 'average_productivity_factor': round(avg_factor, 2), 'exceeding': sum(1 for e in self.entries if e.status == ProductivityStatus.EXCEEDING), 'on_target': sum(1 for e in self.entries if e.status == ProductivityStatus.ON_TARGET), 'below_target': sum(1 for e in self.entries if e.status == ProductivityStatus.BELOW_TARGET), 'critical': sum(1 for e in self.entries if e.status == ProductivityStatus.CRITICAL) } def export_report(self, output_path: str): data = [{ 'Date': e.date, 'Trade': e.trade, 'Activity': e.activity_code, 'Location': e.location, 'Crew': e.crew_size, 'Hours': e.hours_worked, 'Quantity': e.quantity_installed, 'Unit': e.unit, 'Target': e.target_productivity, 'Actual': round(e.actual_productivity, 2), 'Factor': round(e.productivity_factor, 2), 'Status': e.status.value } for e in self.entries] pd.DataFrame(data).to_excel(output_path, index=False)
pythonanalyzer = LaborProductivityAnalyzer("Office Tower") # Set targets analyzer.set_target("CONC-001", 2.5) # m3 per hour # Add entry entry = analyzer.add_entry( entry_date=date.today(), trade="Concrete", activity_code="CONC-001", activity_description="Pour concrete slab", location="Level 3", crew_size=8, hours_worked=80, quantity_installed=180, unit="m3" ) print(f"Productivity factor: {entry.productivity_factor:.2f}") print(f"Status: {entry.status.value}")
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | pass→pass | 11,912 | 12,539 | +5% | 1 | 1 | 0% | 2,775 | 4,145 | +49% | 0 | 0 | — |
case-02 | fail→fail | 23,726 | 12,360 | -48% | 1 | 1 | 0% | 1,497 | 4,722 | +215% | 0 | 0 | — |
case-03 | fail→pass | 8,958 | 4,706 | -47% | 1 | 1 | 0% | 1,905 | 2,761 | +45% | 0 | 0 | — |
case-08 | fail→pass | 11,432 | 2,831 | -75% | 1 | 1 | 0% | 1,769 | 2,367 | +34% | 0 | 0 | — |
case-04 | fail→pass | 6,125 | 6,022 | -2% | 1 | 1 | 0% | 1,249 | 3,141 | +151% | 0 | 0 | — |
case-05 | fail→pass | 8,805 | 4,282 | -51% | 1 | 1 | 0% | 1,796 | 2,743 | +53% | 0 | 0 | — |
case-06 | fail→pass | 9,258 | 4,367 | -53% | 1 | 1 | 0% | 1,783 | 2,752 | +54% | 0 | 0 | — |
case-07 | pass→pass | 21,055 | 4,297 | -80% | 1 | 1 | 0% | 2,203 | 2,824 | +28% | 0 | 0 | — |
case-09 | fail→pass | 12,420 | 8,396 | -32% | 1 | 1 | 0% | 2,263 | 3,388 | +50% | 0 | 0 | — |
case-10 | fail→pass | 8,796 | 6,350 | -28% | 1 | 1 | 0% | 1,538 | 2,901 | +89% | 0 | 0 | — |
case-11 | pass→pass | 3,437 | 3,427 | -0% | 1 | 1 | 0% | 629 | 2,554 | +306% | 0 | 0 | — |
case-12 | pass→pass | 5,745 | 2,765 | -52% | 1 | 1 | 0% | 1,162 | 2,390 | +106% | 0 | 0 | — |
case-13 | pass→pass | 8,667 | 4,754 | -45% | 1 | 1 | 0% | 1,909 | 2,841 | +49% | 0 | 0 | — |
case-14 | fail→pass | 10,323 | 2,585 | -75% | 1 | 1 | 0% | 1,728 | 2,336 | +35% | 0 | 0 | — |
case-15 | pass→pass | 5,116 | 3,395 | -34% | 1 | 1 | 0% | 1,057 | 2,514 | +138% | 0 | 0 | — |
case-16 | fail→pass | 7,569 | 3,756 | -50% | 1 | 1 | 0% | 1,470 | 2,636 | +79% | 0 | 0 | — |
case-17 | pass→pass | 7,111 | 4,674 | -34% | 1 | 1 | 0% | 1,612 | 2,949 | +83% | 0 | 0 | — |
case-18 | fail→pass | 3,678 | 3,994 | +9% | 1 | 1 | 0% | 722 | 2,617 | +262% | 0 | 0 | — |
case-19 | pass→pass | 2,875 | 3,985 | +39% | 1 | 1 | 0% | 588 | 2,619 | +345% | 0 | 0 | — |
case-20 | pass→pass | 11,236 | 13,585 | +21% | 1 | 1 | 0% | 2,056 | 4,444 | +116% | 0 | 0 | — |
case-21 | pass→pass | 6,006 | 7,037 | +17% | 1 | 1 | 0% | 1,294 | 3,327 | +157% | 0 | 0 | — |
case-22 | pass→pass | 4,424 | 3,856 | -13% | 1 | 1 | 0% | 953 | 2,652 | +178% | 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, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +45 percentage points is the difference between those two pass rates over the 21 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.