PtahCast
← Back to blog

The Cold-Start Problem: Forecasting Delivery Dates Before You Have Any History

Bar chart titled 'Weekly closed tickets (12 week sample)', showing ticket counts per week with a dashed line marking lambda equals 35

Every forecast PtahCast produces is built from one thing: how many tickets a team actually finished, period over period. Feed that history into a Monte Carlo simulation and you get a delivery window with real confidence behind it — not a gut-feel date, but a distribution built from what this team has actually shown it can do.

That works beautifully once an engagement has a track record. It falls apart on day one.

The problem with day one

A brand-new client engagement has zero completed tickets. No throughput history means no distribution to sample from, which means no forecast — or, worse, a forecast quietly built on nothing, presented with the same confidence as one built on six months of real delivery data.

For most software, an empty state on day one is a minor annoyance. For PtahCast, it's closer to a contradiction of the entire pitch. The whole point of the product is replacing a gut-feel date with something an agency can put its name behind. If the very first thing a new user sees is a blank dashboard — or worse, a forecast that looks authoritative but is actually built on nothing — we've undermined the one thing we're selling before the relationship even starts.

So the cold-start problem isn't really a data problem. It's a trust problem wearing a data problem's clothes.

The first version of a solution

The fix we shipped generates synthetic throughput data to seed the simulator, so a forecast exists from the moment a client is created — no waiting weeks for real history to accumulate before the tool becomes useful.

The generator draws from a Poisson distribution, which is the right tool for this specific job: per-period ticket counts are a classic counting process — non-negative integers, no natural ceiling, variance that grows with the mean — and Poisson needs just one parameter, lambda, which maps directly onto something an agency owner already thinks in: roughly how many tickets does this team close a week.

Where that lambda comes from determines how much the resulting forecast should be trusted, so every batch of synthetic data is tagged with one of three provenance tiers, and the tier travels with the forecast rather than being thrown away after use:

System default. No history, no estimate from the agency — the simulator falls back to a generic baseline rate. This is the lowest-confidence tier, and it's labeled as such. It exists so the product never shows a broken screen, not so it can pass as a real answer.

User-estimated. The agency enters their own expected weekly throughput — based on a similar past engagement, a team's known capacity, whatever they'd otherwise be guessing in their head anyway — and that number drives the synthetic series instead of the generic default. Still a guess, but an informed one, and it's labeled at that confidence level too.

Actual. Real, measured throughput from tickets that actually got completed. The only tier that isn't synthetic, and the only one a forecast's confidence can eventually rest on.

The onboarding checklist reflects this directly: create your first client, and either connect throughput data or accept the cold-start dataset — so the very first forecast a new user sees is never empty, and never pretending to be something it isn't.

What we got wrong the first time

Shipping the generator wasn't the same as shipping a correct cold-start experience, and two real bugs made that gap obvious.

The first showed up as a support report that the simulation "doesn't seem to be using the starter data." It traced back to an ordering problem: the bootstrap step that seeds synthetic history was written to no-op if any history already existed for that client — reasonable, so it wouldn't clobber real data on every forecast run. But the portfolio dashboard's "Run forecast" button was reachable before a user ever reached the onboarding checklist. Click it first, and the generic system-default data got silently written and locked in. Enter a real weekly estimate on the checklist afterward, and it was just as silently discarded — the bootstrap step saw history already existed and walked away. The fix was a second function purpose-built for this case: submitting an estimate always takes effect, replacing whatever synthetic placeholder was sitting there, no matter what order things happened in. It still refuses outright to overwrite real history — a guess should never replace something measured, even a stale one — but it stopped treating "something already exists" as a reason to ignore a better number.

The second bug was harder to see, because nothing crashed and nothing looked obviously wrong — it just never got better. The direct question that surfaced it: does the cold-start number actually get replaced by real numbers as tickets get completed? The honest answer was no. Nothing in the system had ever been wired to promote synthetic periods to real ones. A client could run for months, rack up a full history of completed tickets, and its forecast confidence would still be sitting at "this is a guess" — because every period behind it was still, mechanically, a Poisson draw from week one. The fix runs on every forecast: recompute real completed-ticket counts for each period, and wherever a real count exists, replace whatever synthetic filler was occupying that period with the measured one. Confidence climbs as a natural side effect, not because anyone flips a switch. One deliberate exception: a period that predates the client's own start date has no real signal to report at all — treating it as a real, measured zero would be its own kind of lie, so that gap stays filled with synthetic data rather than a fabricated actual.

The actual lesson

Neither of these was a bug in the Poisson math, or in the choice of provenance tiers. Both were about what happens after the synthetic data is created — whether a better number is allowed to replace a worse one, and whether a guess is ever mechanically retired once the real answer shows up.

That's the real shape of the cold-start problem: it isn't solved by picking a defensible fake distribution once and moving on. It's a lifecycle question. Label the guess honestly. Let a real estimate always beat a blind default. And once real data exists, make sure it actually takes over — automatically, on every run, without anyone needing to remember to switch anything on.

Get that lifecycle right, and a forecast's confidence label — Low, Medium, High — tells the truth at every point along it, from the day a client is created with nothing but a Poisson guess, through to months later when it's built entirely on what a team actually delivered.

Every PtahCast forecast carries its own confidence label, honestly, from day one.

Start free 30-day trial

Get new articles by email

One email when we publish something new. No spam, unsubscribe anytime.