Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Track subcontractor payments, lien waivers, and compliance. Manage payment schedules and documentation.
.claude/skills/datadrivenconstruction-subcontractor-payment-tracker/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 127% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 634% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 191% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 146% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 52% | 0% |
Subcontractor payments require careful management:
Comprehensive subcontractor payment tracking with lien waiver management, compliance monitoring, and payment scheduling.
pythonimport pandas as pd from datetime import datetime, date, timedelta from typing import Dict, Any, List, Optional from dataclasses import dataclass, field from enum import Enum class PaymentStatus(Enum): SCHEDULED = "scheduled" INVOICED = "invoiced" APPROVED = "approved" PAID = "paid" HELD = "held" DISPUTED = "disputed" class WaiverType(Enum): CONDITIONAL_PROGRESS = "conditional_progress" UNCONDITIONAL_PROGRESS = "unconditional_progress" CONDITIONAL_FINAL = "conditional_final" UNCONDITIONAL_FINAL = "unconditional_final" @dataclass class LienWaiver: waiver_id: str waiver_type: WaiverType through_date: date amount: float received_date: Optional[date] file_path: str = "" @dataclass class SubcontractorPayment: payment_id: str subcontractor_id: str invoice_number: str invoice_date: date amount: float retention_held: float status: PaymentStatus scheduled_date: date paid_date: Optional[date] = None check_number: str = "" lien_waiver: Optional[LienWaiver] = None notes: str = "" @dataclass class Subcontractor: sub_id: str company_name: str contact_name: str email: str phone: str contract_amount: float retention_percent: float trade: str payments: List[SubcontractorPayment] = field(default_factory=list) insurance_expiry: Optional[date] = None license_number: str = "" @property def total_paid(self) -> float: return sum(p.amount for p in self.payments if p.status == PaymentStatus.PAID) @property def total_retention(self) -> float: return sum(p.retention_held for p in self.payments) @property def balance_remaining(self) -> float: return self.contract_amount - self.total_paid - self.total_retention class SubcontractorPaymentTracker: """Track subcontractor payments and compliance.""" def __init__(self, project_name: str): self.project_name = project_name self.subcontractors: Dict[str, Subcontractor] = {} self._payment_counter = 0 def add_subcontractor(self, company_name: str, contact_name: str, email: str, phone: str, contract_amount: float, trade: str, retention_percent: float = 0.10) -> Subcontractor: sub_id = f"SUB-{len(self.subcontractors) + 1:03d}" sub = Subcontractor( sub_id=sub_id, company_name=company_name, contact_name=contact_name, email=email, phone=phone, contract_amount=contract_amount, retention_percent=retention_percent, trade=trade ) self.subcontractors[sub_id] = sub return sub def record_invoice(self, sub_id: str, invoice_number: str, invoice_date: date, gross_amount: float, scheduled_date: date = None) -> SubcontractorPayment: if sub_id not in self.subcontractors: raise ValueError(f"Subcontractor {sub_id} not found") sub = self.subcontractors[sub_id] self._payment_counter += 1 retention = gross_amount * sub.retention_percent net_amount = gross_amount - retention payment = SubcontractorPayment( payment_id=f"PAY-{self._payment_counter:05d}", subcontractor_id=sub_id, invoice_number=invoice_number, invoice_date=invoice_date, amount=net_amount, retention_held=retention, status=PaymentStatus.INVOICED, scheduled_date=scheduled_date or invoice_date + timedelta(days=30) ) sub.payments.append(payment) return payment def approve_payment(self, payment_id: str, sub_id: str): sub = self.subcontractors.get(sub_id) if not sub: return for payment in sub.payments: if payment.payment_id == payment_id: payment.status = PaymentStatus.APPROVED break def record_payment(self, payment_id: str, sub_id: str, check_number: str, paid_date: date = None): sub = self.subcontractors.get(sub_id) if not sub: return for payment in sub.payments: if payment.payment_id == payment_id: payment.status = PaymentStatus.PAID payment.paid_date = paid_date or date.today() payment.check_number = check_number break def attach_lien_waiver(self, payment_id: str, sub_id: str, waiver_type: WaiverType, through_date: date, amount: float, received_date: date = None): sub = self.subcontractors.get(sub_id) if not sub: return for payment in sub.payments: if payment.payment_id == payment_id: waiver = LienWaiver( waiver_id=f"LW-{payment_id}", waiver_type=waiver_type, through_date=through_date, amount=amount, received_date=received_date or date.today() ) payment.lien_waiver = waiver break def get_pending_payments(self) -> List[Dict[str, Any]]: pending = [] for sub in self.subcontractors.values(): for payment in sub.payments: if payment.status in [PaymentStatus.INVOICED, PaymentStatus.APPROVED]: pending.append({ 'payment_id': payment.payment_id, 'subcontractor': sub.company_name, 'invoice': payment.invoice_number, 'amount': payment.amount, 'scheduled': payment.scheduled_date, 'status': payment.status.value, 'has_waiver': payment.lien_waiver is not None }) return sorted(pending, key=lambda x: x['scheduled']) def get_missing_waivers(self) -> List[Dict[str, Any]]: missing = [] for sub in self.subcontractors.values(): for payment in sub.payments: if payment.status == PaymentStatus.PAID and not payment.lien_waiver: missing.append({ 'subcontractor': sub.company_name, 'payment_id': payment.payment_id, 'amount': payment.amount, 'paid_date': payment.paid_date }) return missing def get_summary(self) -> Dict[str, Any]: total_contract = sum(s.contract_amount for s in self.subcontractors.values()) total_paid = sum(s.total_paid for s in self.subcontractors.values()) total_retention = sum(s.total_retention for s in self.subcontractors.values()) return { 'project': self.project_name, 'total_subcontractors': len(self.subcontractors), 'total_contract_value': total_contract, 'total_paid': total_paid, 'total_retention_held': total_retention, 'remaining_to_pay': total_contract - total_paid - total_retention, 'pending_payments': len(self.get_pending_payments()), 'missing_waivers': len(self.get_missing_waivers()) } def export_report(self, output_path: str): with pd.ExcelWriter(output_path, engine='openpyxl') as writer: # Summary by subcontractor sub_data = [{ 'ID': s.sub_id, 'Company': s.company_name, 'Trade': s.trade, 'Contract': s.contract_amount, 'Paid': s.total_paid, 'Retention': s.total_retention, 'Balance': s.balance_remaining } for s in self.subcontractors.values()] pd.DataFrame(sub_data).to_excel(writer, sheet_name='Subcontractors', index=False) # All payments pay_data = [] for sub in self.subcontractors.values(): for p in sub.payments: pay_data.append({ 'Payment ID': p.payment_id, 'Subcontractor': sub.company_name, 'Invoice': p.invoice_number, 'Amount': p.amount, 'Retention': p.retention_held, 'Status': p.status.value, 'Scheduled': p.scheduled_date, 'Paid': p.paid_date, 'Waiver': p.lien_waiver.waiver_type.value if p.lien_waiver else 'Missing' }) if pay_data: pd.DataFrame(pay_data).to_excel(writer, sheet_name='Payments', index=False) return output_path
pythontracker = SubcontractorPaymentTracker("Office Tower") # Add subcontractor sub = tracker.add_subcontractor( company_name="ABC Electrical", contact_name="John Smith", email="john@abcelectric.com", phone="555-1234", contract_amount=500000, trade="Electrical" ) # Record invoice payment = tracker.record_invoice(sub.sub_id, "INV-001", date.today(), 50000) # Approve and pay tracker.approve_payment(payment.payment_id, sub.sub_id) tracker.record_payment(payment.payment_id, sub.sub_id, "CHK-12345") # Attach waiver tracker.attach_lien_waiver(payment.payment_id, sub.sub_id, WaiverType.UNCONDITIONAL_PROGRESS, date.today(), 50000)
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 15,741 | 7,221 | -54% | 1 | 1 | 0% | 1,893 | 4,300 | +127% | 0 | 0 | — |
case-02 | fail→fail | 5,880 | 12,715 | +116% | 1 | 1 | 0% | 1,028 | 5,411 | +426% | 0 | 0 | — |
case-03 | fail→fail | 28,431 | 27,611 | -3% | 1 | 1 | 0% | 6,226 | 8,902 | +43% | 0 | 0 | — |
case-04 | fail→fail | 14,826 | 15,167 | +2% | 1 | 1 | 0% | 2,849 | 5,707 | +100% | 0 | 0 | — |
case-05 | fail→fail | 9,264 | 25,986 | +181% | 1 | 1 | 0% | 1,788 | 8,848 | +395% | 0 | 0 | — |
case-06 | fail→fail | 13,851 | 23,890 | +72% | 1 | 1 | 0% | 2,273 | 7,898 | +247% | 0 | 0 | — |
case-07 | pass→pass | 5,896 | 2,100 | -64% | 1 | 1 | 0% | 903 | 3,049 | +238% | 0 | 0 | — |
case-08 | pass→pass | 2,995 | 2,813 | -6% | 1 | 1 | 0% | 530 | 3,254 | +514% | 0 | 0 | — |
case-09 | pass→pass | 6,064 | 3,717 | -39% | 1 | 1 | 0% | 998 | 3,460 | +247% | 0 | 0 | — |
case-10 | fail→pass | 2,913 | 2,200 | -24% | 1 | 1 | 0% | 426 | 3,128 | +634% | 0 | 0 | — |
case-11 | pass→pass | 8,017 | 5,863 | -27% | 1 | 1 | 0% | 1,495 | 3,937 | +163% | 0 | 0 | — |
case-12 | fail→pass | 6,428 | 2,233 | -65% | 1 | 1 | 0% | 1,044 | 3,042 | +191% | 0 | 0 | — |
case-13 | pass→pass | 9,616 | 2,666 | -72% | 1 | 1 | 0% | 1,636 | 3,228 | +97% | 0 | 0 | — |
case-14 | pass→pass | 9,626 | 3,139 | -67% | 1 | 1 | 0% | 1,448 | 3,254 | +125% | 0 | 0 | — |
case-15 | pass→pass | 10,160 | 3,364 | -67% | 1 | 1 | 0% | 1,619 | 3,277 | +102% | 0 | 0 | — |
case-16 | fail→pass | 8,228 | 3,016 | -63% | 1 | 1 | 0% | 1,321 | 3,245 | +146% | 0 | 0 | — |
case-17 | fail→pass | 11,919 | 1,544 | -87% | 1 | 1 | 0% | 1,941 | 2,943 | +52% | 0 | 0 | — |
case-18 | fail→pass | 8,911 | 3,487 | -61% | 1 | 1 | 0% | 1,377 | 3,188 | +132% | 0 | 0 | — |
case-19 | fail→pass | 12,359 | 5,093 | -59% | 1 | 1 | 0% | 2,093 | 3,632 | +74% | 0 | 0 | — |
case-20 | pass→pass | 11,248 | 4,905 | -56% | 1 | 1 | 0% | 1,902 | 3,529 | +86% | 0 | 0 | — |
case-21 | fail→pass | 3,534 | 2,631 | -26% | 1 | 1 | 0% | 473 | 3,113 | +558% | 0 | 0 | — |
case-22 | pass→pass | 6,959 | 2,189 | -69% | 1 | 1 | 0% | 1,024 | 2,980 | +191% | 0 | 0 | — |
case-23 | fail→pass | 5,048 | 2,060 | -59% | 1 | 1 | 0% | 693 | 3,030 | +337% | 0 | 0 | — |
case-24 | fail→pass | 13,574 | 2,941 | -78% | 1 | 1 | 0% | 2,132 | 3,255 | +53% | 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. 24 cases were attempted. The headline lift of +42 percentage points is the difference between those two pass rates over the 24 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.