Bottom Line Studio

14 dates. No receipts. Zero counted at go-live.

The CRM had 14 Stage 1 dates the evidence didn’t support. I caught them in testing, before a client’s new forecast went live. The rule was simple: show the receipt or stay out of the forecast.

No receipt, no Stage 1.

TL;DR — the short version

Do any of these sound familiar?

  • The forecast and the calendar disagree about how many first meetings happened this quarter.
  • A deal hit Stage 1 the same afternoon it was created.
  • Someone cleaned up the CRM before the board meeting, and the quarter looked better overnight.
  • Every pipeline report has an asterisk, and nobody remembers what it means.

This summer a client’s sales team moved to a stricter definition of Stage 1: a meeting that actually happened, with someone from the buyer’s decision team. A new forecast was going live on top of it. Before it did, I put a data contract between the CRM and the reports: a short list of rules every deal had to pass before the forecast was allowed to count it.

  • A date is a claim. Dates in a CRM get typed, defaulted and bulk-edited as often as they get earned.
  • Check it against the receipt: a stage change in the history and a meeting that was actually held.
  • Don’t fix misses quietly. They go to a queue a person clears. The system can suggest a date. It can’t write one.
  • Do it before go-live, when a miss is a test result instead of a board-deck correction.
A data contract checks stage history and completed meetings before the forecast counts a date. Unsupported dates counted fall from 14 in testing to zero at go-live; exceptions go to human review.

14 unsupported dates in the first test. Zero counted at go-live. Here’s the gate that made the difference, and the source choice I got backwards first.

A Stage 1 date is a claim, not a fact

A date field in a CRM gets filled in a lot of ways. A rep types it. A workflow stamps it when a stage changes. A bulk update sets it during a cleanup. On this engagement we had just moved 129 deals back from Stage 1 to Stage 0 when we checked whether their meetings had actually happened, so a lot of dates had been touched recently, by a lot of hands.

Only some of those ways mean a meeting took place. The forecast couldn’t tell the difference. It read the field, and the field always had a value.

I didn’t need to know how each bad date got there. I needed the forecast to stop counting them.

A date is a claim. Find the receipt.Illustrative record: the CRM field and the evidence tell different stories. Illustrative example, not a client record.A date is a claim. Find the receipt.Illustrative record: the CRM field and the evidence tell different stories.WHAT THE FIELD SAYSStage 1Date: Jul 7Looks complete.A value is not proof.WHAT THE EVIDENCE SAYSNo move to Stage 1 in the history.Meeting never marked completed.No buying-committee contact.Route to human review.Illustrative example, not a client record.BOTTOM LINE STUDIObottomlinestudio.ai
Illustrative. The field looks fine on its own. The stage history and the meeting record are what tell you whether to believe it.

Put the evidence check before the forecast

A data contract sounds heavier than it is. It’s an agreed list of rules about what the data should look like and what it means, checked by code at the point where one system hands data to another. Nothing reaches the forecast without passing it. This one checked four things:

  • Source. The Stage 1 date comes from the stage history: the record of when a deal actually moved, not the field that says when it did.
  • Receipt. There’s a completed meeting behind it, with someone from the buyer’s decision team on it.
  • Shape. The columns the forecast reads can’t be renamed or dropped without failing a check before the change is merged.
  • Exceptions. Anything that fails goes to a queue. Not into the forecast, and not into the trash.

I trusted the field first. That was backwards.

My first version read the CRM field and used the stage history only as a fallback when the field was empty. It seemed sensible. The field was what everyone looked at.

It was the wrong way round. The field was almost never empty, so the fallback almost never ran. And a filled-in field was exactly the problem. A few days before go-live I reversed it: stage history first, the field only as a cross-check. That one change is most of the reason the number reached zero.

The lesson I keep relearning: the source that’s always filled in isn’t the source that’s always right.

Misses go to a person, never back into the CRM on their own

It would have been easy to have the system correct the dates itself. The meeting record usually suggests the right one. I didn’t, for the same reason I don’t let an AI agent merge its own code: propose freely, merge never.

So the S0 meeting-exception queue shows each failing deal with what the field says, what the history says, and a suggested date. A person approves it before anything is written back. The suggestions save time. The approval keeps the CRM honest.

The test found what the forecast couldn’t.Results reported in the development test before go-live. Development test, before go-live. De-identified client engagement.The test found what the forecast couldn’t.Results reported in the development test before go-live.UNSUPPORTED STAGE 1 DATES14 → 0Counted by the forecastEvery miss went to a person.AI-SCORED ACCOUNTS IN PIPELINE233 → 378A second measure,recorded in the same test run.Development test, before go-live. De-identified client engagement.BOTTOM LINE STUDIObottomlinestudio.ai
From the dev test run a week before go-live. Every miss went to the queue for a person to clear.

The same test run recorded another change: AI-scored accounts showing up in pipeline went from 233 to 378. That result sits alongside the date check; it does not, by itself, establish what caused the increase.

A failed check needs an owner

There are three common places to enforce a rule like “Stage 1 needs a real meeting.” Only one of them puts the miss in front of a person.

In the dashboard filterIn a CRM validation ruleIn a data contract
Who sees a missNobody. It’s filtered out.The rep, at data entryA named person, in a queue
Bad dates already in the CRMHidden, if the filter is rightUntouchedChecked on every run
Survives a report rebuildNo. It lives in one report.YesYes. Every report reads through it.
A renamed columnBreaks the report on the dayNot coveredFails before the change is merged
CostCheap, and quietly copied wrongFriction for every repA few days of engineering, once

Test it with the person who will own it

The contract ran in a development copy first, with the analytics engineer who was taking the platform over at the keyboard. In order:

  • First test run. 14 Stage 1 dates with nothing behind them. Found in dev, a week before anyone saw the forecast.
  • Exception queue shipped. Every miss became a row a person could work, not a log line.
  • Suggested dates gated. Nothing written back to the CRM without an approval.
  • Go-live. The forecast counted zero dates without a receipt.

Doing it with the next owner driving mattered as much as the code. When I left a few weeks later, the person who understood the contract best wasn’t me.

Five checks before your next forecast goes live

  • Pick the one date your forecast leans on hardest. Usually Stage 1, or whatever your team calls the first real meeting.
  • Find its receipt. The stage history, the meeting record, the signed form. Something a person didn’t just type.
  • Count the misses in a test copy before anyone sees a forecast built on them.
  • Send misses to a person. Suggest the fix, never apply it silently.
  • Fail on shape changes before they merge, not in Monday’s pipeline review.

Which field in your forecast gets treated as fact because it’s always filled in?

If your forecast and your calendar disagree, start with the date field they both depend on. I build the definitions, evidence checks, and exception workflows behind those numbers. Book a Consultation.
Ian Kessler · Bottom Line Studio · bottomlinestudio.ai
From production GTM data work, de-identified to the pattern level.