look-ahead-scheduler

Generate rolling look-ahead schedules from the master schedule. Create actionable short-term plans with constraint analysis, crew assignments, and daily coordination.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "look-ahead-scheduler" with this command: npx skills add datadrivenconstruction/ddc_skills_for_ai_agents_in_construction/datadrivenconstruction-ddc-skills-for-ai-agents-in-construction-look-ahead-scheduler

Look-Ahead Scheduler

Overview

Generate rolling look-ahead schedules from the master schedule. Create actionable short-term plans with constraint analysis, crew assignments, and daily coordination.

"Look-ahead planning catches 80% of schedule problems before they occur" — DDC Community

Look-Ahead Hierarchy

┌─────────────────────────────────────────────────────────────────┐ │ LOOK-AHEAD PLANNING │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Master Schedule (12+ months) │ │ ↓ │ │ Phase Schedule (3-6 months) │ │ ↓ │ │ 6-Week Look-Ahead (make-ready) │ │ ↓ │ │ 3-Week Look-Ahead (constraint removal) │ │ ↓ │ │ Weekly Work Plan (commitment) │ │ ↓ │ │ Daily Coordination │ │ │ └─────────────────────────────────────────────────────────────────┘

Technical Implementation

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

class ConstraintType(Enum): PREDECESSOR = "predecessor" LABOR = "labor" MATERIAL = "material" EQUIPMENT = "equipment" INFORMATION = "information" PERMIT = "permit" INSPECTION = "inspection" SPACE = "space" WEATHER = "weather"

class ConstraintStatus(Enum): OPEN = "open" IN_PROGRESS = "in_progress" RESOLVED = "resolved" BLOCKED = "blocked"

class ActivityStatus(Enum): NOT_STARTED = "not_started" IN_PROGRESS = "in_progress" COMPLETE = "complete" DELAYED = "delayed"

@dataclass class Constraint: id: str activity_id: str constraint_type: ConstraintType description: str responsible_party: str needed_by: datetime status: ConstraintStatus = ConstraintStatus.OPEN resolution_notes: str = "" resolved_date: Optional[datetime] = None

@dataclass class LookAheadActivity: id: str name: str trade: str location: str planned_start: datetime planned_finish: datetime duration: int labor_hours: float crew_size: int predecessors: List[str] = field(default_factory=list) constraints: List[Constraint] = field(default_factory=list) status: ActivityStatus = ActivityStatus.NOT_STARTED percent_complete: float = 0.0 notes: str = "" can_start: bool = False

@dataclass class WeeklyWorkPlan: week_start: datetime week_end: datetime activities: List[LookAheadActivity] total_labor_hours: float trades_involved: List[str] constraints_to_resolve: List[Constraint]

@dataclass class DailyPlan: date: datetime activities: List[LookAheadActivity] labor_by_trade: Dict[str, int] equipment_needed: List[str] inspections: List[str] safety_focus: str

class LookAheadScheduler: """Generate rolling look-ahead schedules."""

def __init__(self, project_name: str):
    self.project_name = project_name
    self.activities: Dict[str, LookAheadActivity] = {}
    self.constraints: Dict[str, Constraint] = {}
    self.weekly_plans: List[WeeklyWorkPlan] = []

def import_from_master(self, master_activities: List[Dict],
                      look_ahead_start: datetime,
                      look_ahead_weeks: int = 6) -> int:
    """Import activities from master schedule for look-ahead period."""
    look_ahead_end = look_ahead_start + timedelta(weeks=look_ahead_weeks)
    count = 0

    for act in master_activities:
        start = datetime.fromisoformat(act['planned_start']) if isinstance(act['planned_start'], str) else act['planned_start']
        finish = datetime.fromisoformat(act['planned_finish']) if isinstance(act['planned_finish'], str) else act['planned_finish']

        # Include if overlaps look-ahead period
        if start <= look_ahead_end and finish >= look_ahead_start:
            activity = LookAheadActivity(
                id=act['id'],
                name=act['name'],
                trade=act.get('trade', ''),
                location=act.get('location', ''),
                planned_start=start,
                planned_finish=finish,
                duration=act.get('duration', (finish - start).days),
                labor_hours=act.get('labor_hours', 0),
                crew_size=act.get('crew_size', 0),
                predecessors=act.get('predecessors', [])
            )
            self.activities[activity.id] = activity
            count += 1

    return count

def add_constraint(self, activity_id: str, constraint_type: ConstraintType,
                  description: str, responsible_party: str,
                  needed_by: datetime) -> Constraint:
    """Add constraint to activity."""
    constraint_id = f"CON-{len(self.constraints)+1:04d}"

    constraint = Constraint(
        id=constraint_id,
        activity_id=activity_id,
        constraint_type=constraint_type,
        description=description,
        responsible_party=responsible_party,
        needed_by=needed_by
    )

    self.constraints[constraint_id] = constraint

    if activity_id in self.activities:
        self.activities[activity_id].constraints.append(constraint)

    return constraint

def update_constraint(self, constraint_id: str, status: ConstraintStatus,
                     notes: str = "") -> Constraint:
    """Update constraint status."""
    if constraint_id not in self.constraints:
        raise ValueError(f"Constraint {constraint_id} not found")

    constraint = self.constraints[constraint_id]
    constraint.status = status
    constraint.resolution_notes = notes

    if status == ConstraintStatus.RESOLVED:
        constraint.resolved_date = datetime.now()

    return constraint

def analyze_make_ready(self) -> Dict[str, List[str]]:
    """Analyze which activities are 'make-ready' (constraints resolved)."""
    ready = []
    not_ready = []
    blocked = []

    for act in self.activities.values():
        # Check predecessors complete
        pred_complete = all(
            self.activities.get(p, {}).status == ActivityStatus.COMPLETE
            for p in act.predecessors if p in self.activities
        )

        # Check constraints resolved
        open_constraints = [c for c in act.constraints
                          if c.status != ConstraintStatus.RESOLVED]

        if not pred_complete:
            blocked.append(act.id)
            act.can_start = False
        elif open_constraints:
            not_ready.append(act.id)
            act.can_start = False
        else:
            ready.append(act.id)
            act.can_start = True

    return {
        "ready": ready,
        "not_ready": not_ready,
        "blocked": blocked
    }

def generate_weekly_plan(self, week_start: datetime) -> WeeklyWorkPlan:
    """Generate weekly work plan."""
    week_end = week_start + timedelta(days=6)

    # Get activities for this week
    week_activities = [
        act for act in self.activities.values()
        if act.planned_start <= week_end and act.planned_finish >= week_start
    ]

    # Calculate totals
    total_hours = sum(act.labor_hours for act in week_activities)
    trades = list(set(act.trade for act in week_activities if act.trade))

    # Get constraints needing resolution this week
    constraints_due = [
        c for c in self.constraints.values()
        if c.status == ConstraintStatus.OPEN and c.needed_by <= week_end
    ]

    plan = WeeklyWorkPlan(
        week_start=week_start,
        week_end=week_end,
        activities=week_activities,
        total_labor_hours=total_hours,
        trades_involved=trades,
        constraints_to_resolve=constraints_due
    )

    self.weekly_plans.append(plan)
    return plan

def generate_daily_plan(self, date: datetime) -> DailyPlan:
    """Generate daily coordination plan."""
    # Get activities for this day
    day_activities = [
        act for act in self.activities.values()
        if act.planned_start <= date <= act.planned_finish
        and act.can_start
    ]

    # Labor by trade
    labor_by_trade = defaultdict(int)
    for act in day_activities:
        labor_by_trade[act.trade] += act.crew_size

    # Equipment needed
    equipment = []
    for act in day_activities:
        for c in act.constraints:
            if c.constraint_type == ConstraintType.EQUIPMENT:
                equipment.append(c.description)

    # Inspections
    inspections = [
        c.description for c in self.constraints.values()
        if c.constraint_type == ConstraintType.INSPECTION
        and c.needed_by.date() == date.date()
    ]

    # Safety focus based on activities
    safety_focus = self._determine_safety_focus(day_activities)

    return DailyPlan(
        date=date,
        activities=day_activities,
        labor_by_trade=dict(labor_by_trade),
        equipment_needed=equipment,
        inspections=inspections,
        safety_focus=safety_focus
    )

def _determine_safety_focus(self, activities: List[LookAheadActivity]) -> str:
    """Determine daily safety focus based on activities."""
    # Keywords to safety topics
    keywords = {
        "excavation": "Excavation Safety - Shoring, sloping, access",
        "steel": "Steel Erection - Fall protection, crane safety",
        "concrete": "Concrete Pour - Silica, formwork, PPE",
        "roof": "Fall Protection - 100% tie-off, guardrails",
        "electrical": "Electrical Safety - LOTO, qualified personnel",
        "crane": "Crane Safety - Rigging, load charts, signaling",
        "welding": "Hot Work - Fire watch, permits, ventilation"
    }

    for act in activities:
        name_lower = act.name.lower()
        for keyword, safety in keywords.items():
            if keyword in name_lower:
                return safety

    return "General Site Safety - PPE, housekeeping, awareness"

def generate_look_ahead_report(self, weeks: int = 3) -> str:
    """Generate look-ahead schedule report."""
    self.analyze_make_ready()
    today = datetime.now()

    lines = [
        f"# {weeks}-Week Look-Ahead Schedule",
        f"",
        f"**Project:** {self.project_name}",
        f"**Generated:** {today.strftime('%Y-%m-%d')}",
        f"**Period:** {today.strftime('%Y-%m-%d')} to {(today + timedelta(weeks=weeks)).strftime('%Y-%m-%d')}",
        f"",
    ]

    # Summary
    make_ready = self.analyze_make_ready()
    lines.extend([
        f"## Summary",
        f"",
        f"- **Total Activities:** {len(self.activities)}",
        f"- **Ready to Start:** {len(make_ready['ready'])}",
        f"- **Awaiting Constraints:** {len(make_ready['not_ready'])}",
        f"- **Blocked:** {len(make_ready['blocked'])}",
        f""
    ])

    # Open constraints
    open_constraints = [c for c in self.constraints.values()
                      if c.status == ConstraintStatus.OPEN]
    if open_constraints:
        lines.extend([
            f"## Open Constraints ({len(open_constraints)})",
            f"",
            f"| Activity | Type | Description | Responsible | Needed By |",
            f"|----------|------|-------------|-------------|-----------|"
        ])
        for c in sorted(open_constraints, key=lambda x: x.needed_by):
            lines.append(
                f"| {c.activity_id} | {c.constraint_type.value} | {c.description} | "
                f"{c.responsible_party} | {c.needed_by.strftime('%Y-%m-%d')} |"
            )
        lines.append("")

    # Weekly breakdown
    for week_num in range(weeks):
        week_start = today + timedelta(weeks=week_num)
        week_start = week_start - timedelta(days=week_start.weekday())  # Monday

        plan = self.generate_weekly_plan(week_start)

        lines.extend([
            f"## Week {week_num + 1}: {plan.week_start.strftime('%b %d')} - {plan.week_end.strftime('%b %d')}",
            f"",
            f"**Activities:** {len(plan.activities)} | **Labor:** {plan.total_labor_hours:.0f} hrs | **Trades:** {', '.join(plan.trades_involved)}",
            f""
        ])

        if plan.activities:
            lines.append("| Activity | Trade | Location | Start | Duration | Status |")
            lines.append("|----------|-------|----------|-------|----------|--------|")
            for act in sorted(plan.activities, key=lambda a: a.planned_start):
                status = "Ready" if act.can_start else "Constraint"
                lines.append(
                    f"| {act.name} | {act.trade} | {act.location} | "
                    f"{act.planned_start.strftime('%m/%d')} | {act.duration}d | {status} |"
                )
            lines.append("")

    return "\n".join(lines)

def get_constraint_log(self) -> str:
    """Generate constraint log."""
    lines = [
        "# Constraint Log",
        "",
        "| ID | Activity | Type | Description | Responsible | Status | Needed By |",
        "|----|----------|------|-------------|-------------|--------|-----------|"
    ]

    for c in sorted(self.constraints.values(), key=lambda x: x.needed_by):
        lines.append(
            f"| {c.id} | {c.activity_id} | {c.constraint_type.value} | "
            f"{c.description} | {c.responsible_party} | {c.status.value} | "
            f"{c.needed_by.strftime('%Y-%m-%d')} |"
        )

    return "\n".join(lines)

Quick Start

from datetime import datetime, timedelta

Initialize scheduler

scheduler = LookAheadScheduler("Office Tower Construction")

Import activities from master schedule

master_activities = [ { "id": "A100", "name": "Level 5 MEP Rough-in", "trade": "Mechanical", "location": "Level 5", "planned_start": datetime.now(), "planned_finish": datetime.now() + timedelta(days=10), "labor_hours": 400, "crew_size": 5 }, { "id": "A101", "name": "Level 5 Framing", "trade": "Carpentry", "location": "Level 5", "planned_start": datetime.now() + timedelta(days=5), "planned_finish": datetime.now() + timedelta(days=15), "labor_hours": 320, "crew_size": 4, "predecessors": ["A100"] } ]

scheduler.import_from_master(master_activities, datetime.now(), look_ahead_weeks=6)

Add constraints

scheduler.add_constraint( "A100", ConstraintType.MATERIAL, "Ductwork delivery", "ABC Mechanical", datetime.now() + timedelta(days=2) )

scheduler.add_constraint( "A101", ConstraintType.INFORMATION, "Framing layout drawings", "Architect", datetime.now() + timedelta(days=4) )

Analyze make-ready status

status = scheduler.analyze_make_ready() print(f"Ready: {len(status['ready'])}, Not Ready: {len(status['not_ready'])}")

Generate look-ahead report

print(scheduler.generate_look_ahead_report(weeks=3))

Generate daily plan

daily = scheduler.generate_daily_plan(datetime.now()) print(f"Today's safety focus: {daily.safety_focus}")

Requirements

pip install (no external dependencies)

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Research

pandas-construction-analysis

No summary provided by upstream source.

Repository SourceNeeds Review
Research

weather-impact-analysis

No summary provided by upstream source.

Repository SourceNeeds Review
Research

data-evolution-analysis

No summary provided by upstream source.

Repository SourceNeeds Review
Research

bid-analysis-comparator

No summary provided by upstream source.

Repository SourceNeeds Review