You are currently viewing The Delay Was in the Data Three Weeks Ago: Building a Construction Early Warning Dashboard

The Delay Was in the Data Three Weeks Ago: Building a Construction Early Warning Dashboard

The monthly schedule update lands on a Thursday. The planner has re-run the logic, and substantial completion has moved three weeks to the right. The project manager asks the obvious question in the next meeting: why is this the first time anyone is hearing about it?

The uncomfortable answer is that people did know. The superintendent knew the drywall crew had been short for two weeks. The project engineer knew a structural RFI had been sitting with the design team well past its due date. The foreman knew the weekly commitments kept slipping. None of it was hidden. It just never reached one screen, in one place, while there was still float left to spend.

This post walks through building a construction early warning dashboard that catches those signals while they are still cheap to act on. It covers why schedule delay shows up late, the one data decision that makes early warning possible, the signals worth tracking, how to pull data out of Primavera P6, Microsoft Project and Procore, and how to alert without training everyone to ignore the alerts.

Why Construction Delay Shows Up Three Weeks Late

Delay rarely arrives as one event. It accumulates in small amounts across activities that do not look critical yet. Three things keep it invisible.

  1. The schedule is updated in batches. If the CPM schedule is statused monthly, or even biweekly, the finish date is only recalculated at that cadence. Everything that happened in between lands at once.
  2. Progress is self-reported. Percent complete is an estimate from whoever statuses the activity. Optimism is not dishonesty, but it compounds. An activity that sits at a comfortable-looking percentage for three updates is telling you something.
  3. The data is overwritten in place. This is the one that bites hardest. Most schedule tools, and most dashboards built on top of them, show the current value of float, the current forecast finish and the current status. Last month’s values are gone unless someone saved the file.

That third point is why so many project dashboards look fine right up until they don’t. A current-state dashboard tells you where you are. Delay warnings live in the rate of change. An activity with twelve days of float looks healthy. The same activity with twelve days of float, down from thirty over the last two updates, is the next critical path.

The Design Decision That Makes Early Warning Possible: Snapshot Everything

Before any chart or alert, decide that every schedule update, every RFI log pull and every weekly plan gets stored as a dated snapshot. You append. You never overwrite. Storage for this is trivial; a few hundred activities per update is nothing for any database.

PostgreSQL is what I reach for first here. It runs happily on a small managed instance like Amazon RDS or DigitalOcean Managed Databases, or on a modest VPS from Hetzner, Contabo or Interserver if you are comfortable handling backups yourself. A minimal activity snapshot table looks like this:

CREATE TABLE activity_snapshot (
    project_code     text        NOT NULL,
    data_date        date        NOT NULL,
    task_code        text        NOT NULL,
    task_name        text,
    total_float_days numeric,
    early_start      date,
    early_finish     date,
    is_complete      boolean     NOT NULL DEFAULT false,
    loaded_at        timestamptz NOT NULL DEFAULT now(),
    PRIMARY KEY (project_code, data_date, task_code)
);

The primary key does the important work. Each activity appears once per data date, so loading the same export twice fails loudly instead of silently doubling the history. The data_date is the schedule’s status date, not the day you loaded the file, because trends must be plotted against when the schedule says it was true.

Keying on task_code, the activity ID the planner sees, rather than an internal database ID matters too. Internal IDs can change when a schedule is re-imported. Activity IDs usually survive, and when they don’t, you want to know (more on that in troubleshooting).

Early Warning Signals That Move Before the Finish Date Does

Each of these is a leading indicator of schedule delay. None is perfect alone. Together they give you weeks of notice instead of a surprise in the monthly update.

Float Erosion on Near-Critical Paths

Watching the critical path is necessary but late. By the time an activity is critical, its float is already gone. The useful signal is near-critical work losing float quickly. This query compares each open activity to its own previous snapshot:

WITH history AS (
    SELECT
        project_code,
        task_code,
        task_name,
        data_date,
        total_float_days,
        LAG(total_float_days) OVER (
            PARTITION BY project_code, task_code
            ORDER BY data_date
        ) AS prev_float_days
    FROM activity_snapshot
    WHERE NOT is_complete
)
SELECT
    project_code,
    task_code,
    task_name,
    prev_float_days,
    total_float_days,
    prev_float_days - total_float_days AS float_lost_days
FROM history
WHERE data_date = (SELECT max(data_date) FROM activity_snapshot)
  AND prev_float_days IS NOT NULL
  AND total_float_days <= 20
  AND prev_float_days - total_float_days >= 5
ORDER BY float_lost_days DESC;

The thresholds (twenty days remaining, five days lost) are starting points, not rules. Tune them to your update cadence and project length. What matters is that the query looks at movement, which a current-state report cannot.

Constraint Aging: RFIs and Submittals Against Need-By Dates

An overdue RFI on its own is noise. Every project has them. An overdue RFI that blocks an activity starting in ten days is a delay you can see coming.

The hard part is the link. RFI and submittal logs in Procore or Autodesk Construction Cloud rarely point at schedule activity IDs. Someone has to maintain a small mapping table from RFI or submittal to activity, or at least to area and spec section. I would not skip it. Without the link you get a list of late paperwork; with it you get “this answer is needed in eight days and the ball has been in the architect’s court for three weeks.”

The signal to chart is days of slack between the expected response and the need-by date of the linked activity. When it goes negative, the RFI is now on the schedule’s path whether the schedule knows it or not.

Commitment Reliability: PPC and Reasons for Variance

If the field runs the Last Planner System, you already have a strong signal. Percent Plan Complete (PPC) is the share of weekly commitments finished as promised. Each week’s PPC is a lagging number, but the trend across weeks leads the CPM schedule, because missed promises show up in the field long before they show up as recalculated dates.

Store reasons for variance as categories, not free text. A PPC dip caused by weather tells you something different from three straight weeks of “material not on site.” The second one is a procurement problem that will keep producing delay until someone fixes it.

Look-Ahead Churn

Count how many times an activity’s planned start has moved across snapshots of the three- or six-week look-ahead. An activity pushed out once is normal planning. One that has been pushed four updates running is being deferred, not planned, and the reason is usually a constraint nobody has named yet.

Earned Value, Read Carefully

If you track earned value, the Schedule Performance Index (SPI = EV / PV) belongs on the dashboard, with a caveat many dashboards skip. Classic SPI drifts back toward 1.0 as the project nears completion, even when the job is late, because earned value and planned value both end at the budget at completion. A late-stage SPI that looks like recovery may just be arithmetic.

Earned Schedule fixes this by measuring in time rather than money. It asks at what point in the baseline your current earned value was supposed to be reached, then divides that by the actual time elapsed to get SPI(t). It needs no new data collection, only different math on the same numbers. If you only plot one schedule index late in a job, plot SPI(t).

Claimed Progress Versus Manpower

Daily logs record who was on site. When an activity’s reported percent complete keeps rising while the manpower logged against that trade stays well below the plan, one of those numbers is wrong. Usually it is the percent complete. This comparison is crude, and it needs a consistent trade mapping, but it is one of the few checks that uses data nobody is trying to make look good.

Getting the Data Out of P6, Microsoft Project and Procore

A construction project dashboard lives or dies on boring extraction work. Plan for it.

Primavera P6 XER Exports

An XER file is plain tab-delimited text. Lines beginning with %T name a table, %F lists its fields, and %R lines are rows. Activities live in the TASK table. This reader pulls one table into a list of dictionaries:

def read_xer_table(path, table_name):
    rows, fields, current = [], None, None
    # XER exports often use a Windows code page, not UTF-8
    with open(path, encoding="cp1252", errors="replace") as fh:
        for line in fh:
            parts = line.rstrip("rn").split("t")
            tag = parts[0]
            if tag == "%T":
                current = parts[1]
            elif tag == "%F" and current == table_name:
                fields = parts[1:]
            elif tag == "%R" and current == table_name:
                rows.append(dict(zip(fields, parts[1:])))
    return rows

tasks = read_xer_table("update.xer", "TASK")
for t in tasks[:5]:
    print(t["task_code"], t["task_name"], t["total_float_hr_cnt"])

Two details catch people. Float and durations in XER are stored in hours (total_float_hr_cnt), so converting to days means dividing by the hours per day of each activity’s calendar, not a blanket eight. And an activity with a value in act_end_date has an actual finish, which is a clean way to set is_complete without relying on status labels.

Use the float P6 stored in the export rather than recomputing the critical path yourself. P6’s result reflects its scheduling options, constraints and calendars. Your recompute probably won’t.

Microsoft Project

Microsoft Project can save a schedule as XML, which carries tasks, dates and total slack. The same snapshot logic applies. Save one file per status date, load it with its data date, and never replace the previous one.

Procore and Other Project Management Platforms

Procore’s REST API exposes RFIs and submittals per project, including status, due dates and ball-in-court. Two warnings. Most useful endpoints are scoped to a single project, so a portfolio view means iterating over the project list. And submittal status values can be configured per company, so map them explicitly rather than hard-coding what you saw on one account. Autodesk Construction Cloud has its own RFI API with a similar shape.

The Spreadsheet Nobody Wants to Admit Exists

The weekly work plan is very often an Excel file or a Smartsheet. Accept that. A scheduled job that reads a fixed-layout sheet from a shared folder is less elegant than an API and far more likely to get used. Fighting the field’s tools is how early warning systems die.

Building the Dashboard and Alerts People Don’t Mute

For visualization I lean toward Grafana, self-hosted or on Grafana Cloud, because its alert rules can run SQL directly against PostgreSQL and route results to email, Slack or Microsoft Teams. You can see the style of dashboard in this live Grafana demo.

Power BI deserves a fair hearing. Many contractors already license it, project controls teams know it, and its reporting and drill-down are excellent for monthly reviews. Where it is weaker is rule-based alerting on arbitrary queries. If your organization lives in Microsoft 365, a sensible split is Power BI for the reporting pack and a small scheduled job or Grafana instance for the alerts. Metabase is another reasonable middle ground for smaller teams.

Whatever the tool, the alert design matters more than the charts:

  • Send a weekly digest, not a page. Schedule risk is not an outage. A Monday summary of every signal that crossed a threshold gets read. Twenty individual notifications get filtered.
  • Name the owner in the alert. “RFI 142 blocks level 3 slab pour, need-by in 8 days, ball in court: structural engineer” is actionable. “Constraint risk: high” is not.
  • Compare the project to itself. Portfolio-wide thresholds punish complex jobs and let simple ones coast. A drop from its own recent average is the better trigger.
  • Alert on stale data. If no schedule update has loaded in longer than your cadence, that is itself a warning. An early warning dashboard showing old data is worse than none.

Troubleshooting

Float values look wrong compared to P6

Check the hours-to-days conversion first. Then check which float definition the schedule uses. A project-level “must finish by” date or activity constraints can produce negative float that looks like a bug but is correct. Also confirm whether the planner defines critical as longest path or as float below a threshold.

Trend lines break after a re-baseline or renumbering

If activity IDs change, LAG() finds no previous row and the activity appears brand new. Add a check that counts activities present in the previous snapshot but missing from the current one. A sudden jump means the history needs a mapping table, not a fresh start.

PPC sits at or near 100 percent every week

That usually means commitments are being made at the end of the week, or tasks are so loose they cannot fail. A PPC that never moves carries no signal. Look at how commitments are recorded before trusting the number.

The dashboard is green but the field says you are behind

Check how percent complete is calculated. P6 supports duration, physical and units percent complete types, and they can tell very different stories about the same activity. Believe the field, then find which input is out of step.

Common Mistakes

  • Building on the live schedule only, with no history, so every trend has to be reconstructed by hand.
  • Watching only the critical path and missing the near-critical work that becomes critical next month.
  • Trusting classic SPI in the final third of a project, when it drifts toward 1.0 regardless of performance.
  • Tracking overdue RFIs without linking them to the activities they block.
  • Recomputing float yourself instead of using what the scheduling tool calculated.
  • Sending so many alerts that the project team creates an inbox rule to hide them.

Best Practices

  • Append every export with its data date. Never overwrite history.
  • Start with two signals, float erosion and constraint aging, and add more once those are trusted.
  • Agree with the planner on update cadence. The dashboard cannot be fresher than its inputs.
  • Keep the RFI-to-activity mapping small and owned by one named person.
  • Review thresholds after the first month. Early numbers are guesses.
  • Show each signal with its source and data date, so nobody argues about whether it is current.

Frequently Asked Questions

What are leading indicators of construction delay?

The most useful are float erosion on near-critical activities, RFIs and submittals aging toward the start of the work they block, a falling PPC trend, activities repeatedly pushed out of the look-ahead, and reported progress outpacing logged manpower.

How often should the schedule be updated for early warning to work?

More often than monthly if you can manage it. Weekly or biweekly updates give the trend queries enough points to show movement. Signals from RFIs and weekly plans can refresh daily regardless of schedule cadence.

Is SPI a good early warning metric?

Early and mid-project, yes, alongside float and critical path analysis. Late in a project, classic SPI drifts toward 1.0 even on late jobs, so the time-based SPI(t) from Earned Schedule is more trustworthy.

Can I build this with Power BI instead of Grafana?

Yes for the reporting side, and it is often the better choice where the team already uses it. The snapshot database and trend logic are the same either way. Rule-based alerting is where you may want a separate tool.

Do I need Primavera P6 for a construction early warning dashboard?

No. Any scheduling tool that exports activities with dates and float works, including Microsoft Project. What you need is a consistent export on every update and somewhere to keep the history.

How do I link RFIs to schedule activities?

Usually with a small mapping table maintained by the project engineer: RFI or submittal number, activity ID, and need-by date. Mapping by area and spec section is a workable first step if activity-level links are too much at the start.

Conclusion

The project manager who learns about delay three weeks late usually isn’t short of data. The problem is that the data is spread across a schedule, an RFI log and a weekly plan, and each one only remembers its latest state. A construction early warning dashboard works because it keeps the history and watches the movement: float draining from work that isn’t critical yet, answers arriving after the work that needs them, promises slipping week after week. Snapshot first. Everything else is a query on top.


Need Help Building a Construction Early Warning Dashboard?

I build and host the data side of this for construction teams and project controls groups. Here is where I can help:

  • Setting up a snapshot database that stores every P6 or Microsoft Project update with its data date.
  • Writing XER and XML loaders that handle hours-based float, calendars and renumbered activities.
  • Pulling RFIs and submittals from Procore or Autodesk Construction Cloud and linking them to activities.
  • Building Grafana dashboards for float erosion, constraint aging, PPC and SPI(t) trends.
  • Designing weekly digest alerts with named owners, sent to email, Slack or Teams.
  • Hosting and maintaining the dashboard so your team reads it instead of running it.

If you want to scope it first, the Engagement Builder lays out phases and effort. Or send me one recent XER export and your RFI log, and I will tell you which signals your data can already support.