claims-documentation

SKILL.md

Claims Documentation

Overview

Document and manage construction claims for schedule delays, cost impacts, and scope disputes. Track contractual notice requirements, compile supporting evidence, calculate damages, and prepare comprehensive claim packages.

Claims Process

┌─────────────────────────────────────────────────────────────────┐
│                    CLAIMS PROCESS                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Notice  →  Document  →  Quantify  →  Submit  →  Negotiate     │
│  ──────     ────────     ────────     ──────     ─────────     │
│  📋 Identify  📂 Collect   💰 Calculate  📤 Package  🤝 Resolve │
│  📧 Timely    📸 Evidence  ⏱️ Time       📋 Format   ⚖️ Settle  │
│  📝 Written   📄 Chain     📊 Cost       ✓ Review   💵 Payment  │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Technical Implementation

from dataclasses import dataclass, field
from typing import List, Dict, Optional
from datetime import datetime, timedelta
from enum import Enum

class ClaimType(Enum):
    DELAY = "delay"
    DISRUPTION = "disruption"
    ACCELERATION = "acceleration"
    DIFFERING_CONDITIONS = "differing_conditions"
    OWNER_CHANGE = "owner_change"
    SUSPENSION = "suspension"
    TERMINATION = "termination"
    DEFECTIVE_SPECS = "defective_specs"

class ClaimStatus(Enum):
    DRAFT = "draft"
    NOTICE_SENT = "notice_sent"
    DOCUMENTING = "documenting"
    SUBMITTED = "submitted"
    UNDER_REVIEW = "under_review"
    NEGOTIATING = "negotiating"
    SETTLED = "settled"
    DISPUTED = "disputed"
    LITIGATION = "litigation"
    WITHDRAWN = "withdrawn"

class EvidenceType(Enum):
    DAILY_REPORT = "daily_report"
    PHOTO = "photo"
    VIDEO = "video"
    EMAIL = "email"
    LETTER = "letter"
    MEETING_MINUTES = "meeting_minutes"
    SCHEDULE = "schedule"
    COST_RECORD = "cost_record"
    INVOICE = "invoice"
    TIMESHEET = "timesheet"
    WEATHER_DATA = "weather_data"
    DELIVERY_TICKET = "delivery_ticket"
    INSPECTION_REPORT = "inspection_report"
    RFI = "rfi"
    SUBMITTAL = "submittal"

@dataclass
class Evidence:
    id: str
    evidence_type: EvidenceType
    description: str
    date: datetime
    file_path: str
    source: str
    relevance: str
    authenticated: bool = False

@dataclass
class NoticeRequirement:
    notice_type: str
    deadline_days: int
    recipient: str
    method: str  # Written, certified mail, etc.
    contract_reference: str
    sent: bool = False
    sent_date: Optional[datetime] = None
    confirmation: str = ""

@dataclass
class DamageCalculation:
    category: str
    description: str
    amount: float
    basis: str  # How calculated
    supporting_docs: List[str] = field(default_factory=list)

@dataclass
class Claim:
    id: str
    claim_type: ClaimType
    title: str
    description: str
    status: ClaimStatus

    # Event details
    event_date: datetime
    discovery_date: datetime
    responsible_party: str
    contract_references: List[str] = field(default_factory=list)

    # Notice
    notice_requirements: List[NoticeRequirement] = field(default_factory=list)
    notice_compliant: bool = False

    # Documentation
    evidence: List[Evidence] = field(default_factory=list)
    narrative: str = ""

    # Damages
    time_claimed_days: int = 0
    cost_claimed: float = 0.0
    damage_calculations: List[DamageCalculation] = field(default_factory=list)

    # Resolution
    time_awarded_days: int = 0
    amount_awarded: float = 0.0
    settlement_date: Optional[datetime] = None
    settlement_notes: str = ""

class ClaimsDocumentor:
    """Document and manage construction claims."""

    # Common notice requirements
    DEFAULT_NOTICE_REQUIREMENTS = {
        ClaimType.DELAY: [
            {"notice_type": "Intent to Claim", "deadline_days": 21, "method": "Written"},
            {"notice_type": "Detailed Claim", "deadline_days": 45, "method": "Written"},
        ],
        ClaimType.DIFFERING_CONDITIONS: [
            {"notice_type": "Immediate Notice", "deadline_days": 2, "method": "Written/Verbal"},
            {"notice_type": "Written Notice", "deadline_days": 7, "method": "Written"},
        ],
        ClaimType.OWNER_CHANGE: [
            {"notice_type": "Notice of Impact", "deadline_days": 14, "method": "Written"},
        ],
    }

    def __init__(self, project_name: str, contract_date: datetime):
        self.project_name = project_name
        self.contract_date = contract_date
        self.claims: Dict[str, Claim] = {}

    def create_claim(self, claim_type: ClaimType, title: str,
                    description: str, event_date: datetime,
                    responsible_party: str) -> Claim:
        """Create new claim."""
        claim_id = f"CLM-{datetime.now().strftime('%Y%m%d%H%M%S')}"

        claim = Claim(
            id=claim_id,
            claim_type=claim_type,
            title=title,
            description=description,
            status=ClaimStatus.DRAFT,
            event_date=event_date,
            discovery_date=datetime.now(),
            responsible_party=responsible_party
        )

        # Add default notice requirements
        for req in self.DEFAULT_NOTICE_REQUIREMENTS.get(claim_type, []):
            notice = NoticeRequirement(
                notice_type=req["notice_type"],
                deadline_days=req["deadline_days"],
                recipient=responsible_party,
                method=req["method"],
                contract_reference=""
            )
            claim.notice_requirements.append(notice)

        self.claims[claim_id] = claim
        return claim

    def record_notice_sent(self, claim_id: str, notice_type: str,
                          confirmation: str = "") -> NoticeRequirement:
        """Record that notice was sent."""
        if claim_id not in self.claims:
            raise ValueError(f"Claim {claim_id} not found")

        claim = self.claims[claim_id]

        for notice in claim.notice_requirements:
            if notice.notice_type == notice_type:
                notice.sent = True
                notice.sent_date = datetime.now()
                notice.confirmation = confirmation

                # Check overall notice compliance
                claim.notice_compliant = all(n.sent for n in claim.notice_requirements)

                if claim.status == ClaimStatus.DRAFT:
                    claim.status = ClaimStatus.NOTICE_SENT

                return notice

        raise ValueError(f"Notice type {notice_type} not found")

    def check_notice_deadlines(self, claim_id: str) -> List[Dict]:
        """Check status of notice deadlines."""
        if claim_id not in self.claims:
            raise ValueError(f"Claim {claim_id} not found")

        claim = self.claims[claim_id]
        status = []

        for notice in claim.notice_requirements:
            deadline = claim.event_date + timedelta(days=notice.deadline_days)
            days_remaining = (deadline - datetime.now()).days

            status.append({
                "notice_type": notice.notice_type,
                "deadline": deadline,
                "days_remaining": days_remaining,
                "sent": notice.sent,
                "overdue": days_remaining < 0 and not notice.sent,
                "status": "Sent" if notice.sent else ("OVERDUE" if days_remaining < 0 else f"{days_remaining} days left")
            })

        return status

    def add_evidence(self, claim_id: str, evidence_type: EvidenceType,
                    description: str, date: datetime, file_path: str,
                    source: str, relevance: str) -> Evidence:
        """Add evidence to claim."""
        if claim_id not in self.claims:
            raise ValueError(f"Claim {claim_id} not found")

        evidence_id = f"EVD-{len(self.claims[claim_id].evidence)+1:04d}"

        evidence = Evidence(
            id=evidence_id,
            evidence_type=evidence_type,
            description=description,
            date=date,
            file_path=file_path,
            source=source,
            relevance=relevance
        )

        self.claims[claim_id].evidence.append(evidence)

        if self.claims[claim_id].status == ClaimStatus.NOTICE_SENT:
            self.claims[claim_id].status = ClaimStatus.DOCUMENTING

        return evidence

    def add_damage_calculation(self, claim_id: str, category: str,
                              description: str, amount: float,
                              basis: str, supporting_docs: List[str] = None) -> DamageCalculation:
        """Add damage calculation to claim."""
        if claim_id not in self.claims:
            raise ValueError(f"Claim {claim_id} not found")

        calc = DamageCalculation(
            category=category,
            description=description,
            amount=amount,
            basis=basis,
            supporting_docs=supporting_docs or []
        )

        claim = self.claims[claim_id]
        claim.damage_calculations.append(calc)

        # Update total claimed
        claim.cost_claimed = sum(c.amount for c in claim.damage_calculations)

        return calc

    def calculate_delay_damages(self, claim_id: str, delay_days: int,
                               daily_rate: float,
                               include_escalation: bool = True) -> Dict:
        """Calculate delay damages using Eichleay formula or daily rate."""
        if claim_id not in self.claims:
            raise ValueError(f"Claim {claim_id} not found")

        claim = self.claims[claim_id]

        # Direct costs
        extended_general_conditions = delay_days * daily_rate

        # Add standard categories
        self.add_damage_calculation(
            claim_id, "Extended General Conditions",
            f"{delay_days} days × ${daily_rate:,.2f}/day",
            extended_general_conditions,
            "Daily rate method"
        )

        # Escalation (if applicable)
        escalation = 0
        if include_escalation:
            escalation = extended_general_conditions * 0.03  # 3% escalation
            self.add_damage_calculation(
                claim_id, "Material/Labor Escalation",
                "Cost increase due to extended duration",
                escalation,
                "3% escalation factor"
            )

        claim.time_claimed_days = delay_days

        return {
            "delay_days": delay_days,
            "daily_rate": daily_rate,
            "extended_gc": extended_general_conditions,
            "escalation": escalation,
            "total": claim.cost_claimed
        }

    def write_narrative(self, claim_id: str, narrative: str):
        """Write claim narrative."""
        if claim_id not in self.claims:
            raise ValueError(f"Claim {claim_id} not found")

        self.claims[claim_id].narrative = narrative

    def submit_claim(self, claim_id: str) -> Claim:
        """Submit claim."""
        if claim_id not in self.claims:
            raise ValueError(f"Claim {claim_id} not found")

        claim = self.claims[claim_id]
        claim.status = ClaimStatus.SUBMITTED
        return claim

    def record_settlement(self, claim_id: str, time_awarded: int,
                         amount_awarded: float, notes: str = "") -> Claim:
        """Record claim settlement."""
        if claim_id not in self.claims:
            raise ValueError(f"Claim {claim_id} not found")

        claim = self.claims[claim_id]
        claim.status = ClaimStatus.SETTLED
        claim.time_awarded_days = time_awarded
        claim.amount_awarded = amount_awarded
        claim.settlement_date = datetime.now()
        claim.settlement_notes = notes

        return claim

    def generate_evidence_index(self, claim_id: str) -> str:
        """Generate evidence index."""
        if claim_id not in self.claims:
            return "Claim not found"

        claim = self.claims[claim_id]

        lines = [
            "# Evidence Index",
            "",
            f"**Claim:** {claim.title}",
            f"**Claim ID:** {claim.id}",
            "",
            "| # | Type | Date | Description | Source | Relevance |",
            "|---|------|------|-------------|--------|-----------|"
        ]

        for i, ev in enumerate(sorted(claim.evidence, key=lambda e: e.date), 1):
            lines.append(
                f"| {i} | {ev.evidence_type.value} | {ev.date.strftime('%Y-%m-%d')} | "
                f"{ev.description[:30]} | {ev.source} | {ev.relevance[:30]} |"
            )

        return "\n".join(lines)

    def generate_claim_package(self, claim_id: str) -> str:
        """Generate complete claim package."""
        if claim_id not in self.claims:
            return "Claim not found"

        claim = self.claims[claim_id]

        lines = [
            "# CLAIM PACKAGE",
            "",
            f"## Claim: {claim.title}",
            "",
            f"**Claim ID:** {claim.id}",
            f"**Type:** {claim.claim_type.value.replace('_', ' ').title()}",
            f"**Status:** {claim.status.value}",
            f"**Event Date:** {claim.event_date.strftime('%Y-%m-%d')}",
            f"**Responsible Party:** {claim.responsible_party}",
            "",
            "---",
            "",
            "## 1. Executive Summary",
            "",
            claim.description,
            "",
            f"**Time Claimed:** {claim.time_claimed_days} days",
            f"**Amount Claimed:** ${claim.cost_claimed:,.2f}",
            "",
            "## 2. Factual Narrative",
            "",
            claim.narrative if claim.narrative else "*Narrative pending*",
            "",
            "## 3. Contract References",
            "",
        ]

        for ref in claim.contract_references:
            lines.append(f"- {ref}")

        lines.extend([
            "",
            "## 4. Notice Compliance",
            "",
            "| Notice Type | Deadline | Status | Sent Date |",
            "|-------------|----------|--------|-----------|"
        ])

        for notice in claim.notice_requirements:
            deadline = claim.event_date + timedelta(days=notice.deadline_days)
            status = "✓ Sent" if notice.sent else "Pending"
            sent = notice.sent_date.strftime('%Y-%m-%d') if notice.sent_date else "-"
            lines.append(f"| {notice.notice_type} | {deadline.strftime('%Y-%m-%d')} | {status} | {sent} |")

        lines.extend([
            "",
            "## 5. Damage Calculations",
            "",
            "| Category | Description | Amount | Basis |",
            "|----------|-------------|--------|-------|"
        ])

        for calc in claim.damage_calculations:
            lines.append(f"| {calc.category} | {calc.description} | ${calc.amount:,.2f} | {calc.basis} |")

        lines.extend([
            "",
            f"**Total Claimed: ${claim.cost_claimed:,.2f}**",
            "",
            "## 6. Evidence Summary",
            "",
            f"Total Documents: {len(claim.evidence)}",
            ""
        ])

        # Group evidence by type
        by_type = {}
        for ev in claim.evidence:
            t = ev.evidence_type.value
            by_type[t] = by_type.get(t, 0) + 1

        for t, count in sorted(by_type.items()):
            lines.append(f"- {t.replace('_', ' ').title()}: {count}")

        return "\n".join(lines)

Quick Start

from datetime import datetime, timedelta

# Initialize documentor
documentor = ClaimsDocumentor("Office Tower", datetime(2024, 1, 1))

# Create claim
claim = documentor.create_claim(
    claim_type=ClaimType.DELAY,
    title="Owner-Caused Delay - Design Changes",
    description="Multiple design changes to structural system caused 45-day delay",
    event_date=datetime(2024, 6, 15),
    responsible_party="Owner"
)

# Check notice deadlines
deadlines = documentor.check_notice_deadlines(claim.id)
for d in deadlines:
    print(f"{d['notice_type']}: {d['status']}")

# Record notice sent
documentor.record_notice_sent(claim.id, "Intent to Claim", "Certified Mail #12345")

# Add evidence
documentor.add_evidence(
    claim.id,
    EvidenceType.RFI,
    "RFI-042 requesting structural clarification",
    datetime(2024, 6, 10),
    "/docs/RFI-042.pdf",
    "Project Files",
    "Shows owner's delayed response"
)

# Calculate damages
damages = documentor.calculate_delay_damages(
    claim.id,
    delay_days=45,
    daily_rate=5000.0
)
print(f"Total damages: ${damages['total']:,.2f}")

# Write narrative
documentor.write_narrative(claim.id, """
On June 15, 2024, the Owner issued a design change directive requiring
modifications to the structural steel at Levels 5-8. This change...
""")

# Generate claim package
print(documentor.generate_claim_package(claim.id))

Requirements

pip install (no external dependencies)
Weekly Installs
3
GitHub Stars
55
First Seen
11 days ago
Installed on
opencode3
antigravity3
claude-code3
github-copilot3
codex3
kimi-cli3