phase 4 · learn
Funnel and metrics
Go deeper
The funnel is the ladder a person climbs from "found" to "paid". This page defines both ladders, the one rule for picking the constraint, every metric once (the formula, the window and the event it reads), how a sign-up or a payment is traced back to the message that caused it, and the three rhythms that read it all: live, daily and the Monday report.
sequence · the steps that went out → funnel · how far each person got → loop · what to change next
rule 1 — Every metric is defined once · One id, one formula, one window, one source event. A number in the Monday report and the same number on a dashboard can't disagree.
rule 2 — Fix the weakest rung, one at a time · The constraint is the rung that leaks most. Work on anything else and the extra people pile up in front of it.
rule 3 — No number without a trail · Every sign-up and payment carries the ids back to the message, the lead and the detector that found the person. Without that, a metric can only be watched, not acted on.
what it is, in industry terms
Two ladders: a pre-launch one, and the full one.
A conversion funnel counts how many people reach each rung and what share move to the next. The constraint (bottleneck) is the rung with the worst drop.
found
a person caught by a detector and kept as a lead
→ met.rung_conversion (engaged ÷ found)
engaged
you replied, or they replied to you
→ met.rung_conversion (pain stated ÷ engaged)
pain stated
they said the problem in their own words
→ met.rung_conversion
brief offered
you offered to read their product and send a brief
→ met.rung_conversion
waitlist offered
you pointed them at the waitlist
→ met.offer_to_join
signed up
they joined the waitlist
The general ladder any product, live mode
reached
a message or post landed in front of them
→ met.reply_rate_7d, met.link_ctr
visited
they opened the site, carrying a touch id
→ met.visit_to_signup
signed up
an account or a waitlist entry
→ met.activation_7d
activated
they did the first thing that predicts paying
→ met.trial_to_paid
paid
Stripe reports a paid invoice
picking the constraint, every Monday
- For each rung, take the conversion to the next rung over the last 7 days, and the same over the 4 weeks before.
- Ignore any rung with fewer than 30 people entering it this week. That sample can't tell a real drop from noise.
- The constraint is the rung with the lowest conversion against its own recent past. When nothing has history yet, it's simply the lowest rate.
- Write it down as the review's
constraint_chosen, with one hypothesis and one change. Everything else waits a week.
Pace is a straight line from the starting count to the target, not a forecast. The Monday report shows joins against that line.
the metric object
Every metric is one record with the same fields.
This is the shape a metric takes in the catalog and in code. A metric reads events from the GTM tracking plan; it never reads a raw table someone can edit. Change a formula and its version goes up, so old readouts stay comparable.
| Field | Type | What it holds |
|---|---|---|
id | string | Stable, readable: met.<name>. Readouts, alerts and experiments refer to it. |
version | integer | Bumped whenever the formula, window or events change. |
question | sentence | What it answers, in plain words. If you can't write one, drop the metric. |
formula | numerator / denominator | Both sides named as events, with the join between them. |
unit | rate | count | hours | dollars | How it's shown. |
grain | per touch, lead, post, detector, week… | What one row of the computation is. |
window | duration | How long after the denominator event the numerator still counts (a reply within 7 days). |
kind | primary | guardrail | diagnostic | Primary is what a test tries to move; a guardrail must not get worse; a diagnostic explains a change. |
direction | up | down | Which way is better. |
judges | stage | The pipeline stage this metric holds to account. |
rhythm | live | daily | weekly | When it's read. |
min_sample | integer | Below this, the report shows the count and no rate. |
one metric, as data
{
"id": "met.reply_rate_7d",
"version": 1,
"question": "Do the messages get answered?",
"formula": {
"numerator": "touch_sent with a reply_received on the same touch_id within window",
"denominator": "touch_sent"
},
"unit": "rate",
"grain": "touch",
"window": "7d",
"kind": "primary",
"direction": "up",
"judges": "sequence",
"rhythm": "weekly",
"min_sample": 30,
"split_by": [
"playbook_id",
"channel",
"trigger_id",
"template_id"
],
"guardrails": [
"met.stop_rate"
],
"reads_events": [
"touch_sent",
"reply_received"
]
}the catalog
Every metric worth reading, defined once.
primary · what tests try to move — 12
primary
Waitlist joins per week
Are we on pace for the goal?
- unit
- count · per week · higher is better
- window
- 7 days · read weekly
- events
signup_created (waitlist)
primary
Waitlist offer to join
When we point someone at the waitlist, do they join?
- formula
- waitlist sign-ups ÷ leads offered the waitlist
- unit
- rate · per lead · higher is better
- window
- 14 days · read weekly
- events
waitlist offered, signup_created
primary — needs
Reply rate per touch
Do the messages get answered?
- formula
- touches with a reply within 7 days ÷ touches sent
- unit
- rate · per touch · higher is better
- window
- 7 days · read weekly
- events
touch_sent, reply_received
primary — needs
Positive reply rate
Are the answers good ones?
- formula
- replies classed positive or question ÷ touches sent
- unit
- rate · per touch · higher is better
- window
- 7 days · read weekly
- events
reply_received (class)
primary
Drafts shipped unedited
How often is the draft good enough as written?
- formula
- drafts decided shipped ÷ drafts decided (shipped, shipped edited, discarded)
- unit
- rate · per draft · higher is better
- window
- at decision · read weekly
- events
draft.decided
primary
Profile clicks per 100 impressions
Did the post make people want to know who wrote it?
- formula
- profile clicks ÷ impressions × 100, at the 1,440-minute snapshot
- unit
- rate · per post · higher is better
- window
- 24 hours · read weekly
primary
Visit to sign-up
Does the page convert the people we send?
- formula
- sign-ups ÷ visits that carry a touch id
- unit
- rate · per visit · higher is better
- window
- 7 days · read weekly
- events
visit_started, signup_created
primary — needs
Signal precision
Of what a detector catches, how much is real?
- formula
- matches the founder labels real ÷ matches labeled
- unit
- rate · per detector · higher is better
- window
- 30 days · read weekly
- events
match_labeled
primary — needs
Yield per detector dollar
Which detectors earn their cost?
- formula
- leads (or replies) from a detector's signals ÷ its run cost in dollars
- unit
- rate · per detector · higher is better
- window
- 30 days · read weekly
- events
detector_run_completed, lead events
primary — needs
Activation within 7 days
Do new sign-ups do the thing that predicts paying?
- formula
- sign-ups with the activation action within 7 days ÷ sign-ups
- unit
- rate · per account · higher is better
- window
- 7 days · read weekly
- events
signup_created, activation_reached
primary
Trial to paid
Do trials become payments?
- formula
- accounts with invoice.paid within 14 days of trial start ÷ trials started
- unit
- rate · per account · higher is better
- window
- 14 days · read weekly
- events
invoice.paid
primary — needs
Campaign lift
What did the campaign cause?
- formula
- conversion in the treatment arm − conversion in the holdout
- unit
- rate · per campaign · higher is better
- window
- campaign window · read weekly
- events
lead_assigned, signup_created
guardrail · must not get worse — 3
guardrail — needs
Stop requests per 100 touches
Are we annoying people?
- formula
- stop, block or report events ÷ touches sent × 100
- unit
- rate · per touch · lower is better
- window
- 7 days · read daily
- events
negative_feedback
guardrail
Monthly churn
Are customers leaving?
- formula
- subscriptions canceled in the month ÷ active at the start of the month
- unit
- rate · per month · lower is better
- window
- 1 month · read weekly
- events
customer.subscription.deleted
guardrail
Founder minutes per join
What does each join cost in your time?
- formula
- founder minutes spent (reviewing, editing, sending) ÷ joins
- unit
- minutes · per week · lower is better
- window
- 7 days · read weekly
- events
draft.decided, lead activity
diagnostic · explains a change — 11
diagnostic
Rung conversion
What share of people move from one rung to the next?
- formula
- people reaching rung n+1 ÷ people reaching rung n, among those entering rung n in the window
- unit
- rate · per rung, week · higher is better
- window
- 14 days · read weekly
- events
ladder rung events
diagnostic — needs
Time to first reply
How fast do people answer?
- formula
- median hours from touch sent to its first reply
- unit
- hours · per touch · lower is better
- window
- 7 days · read weekly
- events
touch_sent, reply_received
diagnostic
Median edit distance
How much does the founder rewrite?
- formula
- median normalized edit distance of drafts shipped edited, split by edit locus (hook, body, ending)
- unit
- rate · per draft · lower is better
- window
- at decision · read weekly
- events
draft.decided
diagnostic
Time to decide
How long does a draft wait on the founder?
- formula
- median seconds from draft generated to decided
- unit
- seconds · per draft · lower is better
- window
- at decision · read weekly
- events
draft.generated, draft.decided
diagnostic
Impressions at 24 hours
How far did a post travel?
- formula
- impressions from the 1,440-minute snapshot
- unit
- count · per post · higher is better
- window
- 24 hours · read daily
diagnostic
30-minute forecast error
Does the 30-minute number predict the 7-day number?
- formula
- error of a 7-day impressions forecast made from the 30-minute snapshot
- unit
- rate · per post · lower is better
- window
- 7 days · read weekly
diagnostic
Short-link clicks per touch
Did the message make them click?
- formula
- short-link clicks ÷ touches that carried a link
- unit
- rate · per touch · higher is better
- window
- 7 days · read weekly
- events
link_clicked
diagnostic — needs
Judge agreement
Does the match judge agree with the founder?
- formula
- matches where judge verdict = founder label ÷ labeled matches
- unit
- rate · per judge version · higher is better
- window
- 30 days · read weekly
- events
match_judged, match_labeled
diagnostic — needs
Detection lag
How fresh is what we catch?
- formula
- median hours from the source post's own timestamp to capture
- unit
- hours · per signal · lower is better
- window
- at capture · read daily
- events
item_matched
diagnostic
Lead accept rate
Does the founder agree the lead fits?
- formula
- leads accepted ÷ leads reviewed (accept, skip, never)
- unit
- rate · per lead · higher is better
- window
- at review · read weekly
- events
lead_reviewed
diagnostic
Failed payment recovery
Do failed payments get recovered?
- formula
- failed invoices later paid ÷ failed invoices
- unit
- rate · per invoice · higher is better
- window
- 14 days · read weekly
- events
invoice.payment_failed, invoice.paid
attribution
Trace every join and payment back to what caused it.
Attribution answers "which detector, playbook and message produced this sign-up?" It only works if one chain of ids runs from the rule that found the person to the payment.
detector — detector_id + v
→
signal — signal_id
→
lead — lead_id
→
move — move_id
→
touch — touch_id
→
short link click — link id
→
visit — touch_id from the link
→
sign-up — first + last touch
→
payment — Stripe customer
model 1 — Last touch · Credit goes to the last message before the sign-up. The default weekly read: simple, and it rewards the step that closed.
model 2 — First touch · Credit goes to the detector and message that first reached the person. Tells you which triggers open doors.
model 3 — Self-reported · "How did you hear about us?" on the sign-up form, free text. Catches what links miss: a friend, a podcast, a post they saw but didn't click.
model 4 — Holdout lift · The only causal one. Leads held out of a campaign get nothing; the difference in sign-ups is what the campaign caused.
payments come from Stripe webhooks
checkout.session.completed | checkout finished: link the Stripe customer to the account and its last touch |
invoice.paid | a payment, first or renewal: the "paid" rung |
invoice.payment_failed | a failed payment: a warm trigger for the recovery play |
customer.subscription.updated | plan change: upgrade or downgrade |
customer.subscription.deleted | a cancellation: churn, with tenure and the reason if given |
reading the two together
- Report last touch weekly, first touch monthly, and put self-reported answers beside both.
- Where link attribution and the self-reported answer disagree, the answer usually names the earlier, unclickable touch. Keep both; don't overwrite one with the other.
- A channel with lots of last-touch credit and no first-touch credit closes but doesn't find. One with the reverse finds but doesn't close.
- Never claim a campaign "caused" sign-ups without a holdout. Say "came after".
three rhythms
Live, daily, and the Monday report.
Different questions need different clocks. Live alerts are for things that rot in hours; the daily snapshot catches breakage; the Monday report is where decisions get made.
as it happens
Alerts that need a person now
- a reply or a question waiting on you
- a new Hot lead
- a failed payment
- a stop request
- a source down past its max staleness
daily · one snapshot
Counts, frozen once a day
- people on each rung, and new arrivals
- new signals and leads, per trigger and per detector
- drafts generated, shipped, edited, discarded
- post metrics from the 10 min, 30 min, 1 h, 1 day and 7 day snapshots
- stale or failing sources
weekly · Monday
The review that changes one thing
- pace against the goal
- the ladder, with the constraint marked
- what joined or paid, and where it came from
- detector and draft quality
- experiment readouts due
- the one change for this week
1 · pace — Waitlist joins this week: {n}. Straight-line pace needs {n} a week from here.
2 · ladder and constraint
found · {n} · engaged · {n} · pain stated · {n} · brief offered · constraint? · waitlist offered · {n} · signed up · {n}
3 · where joins came from
- last touch: {playbook} via {channel}, {n} joins
- first touch: {trigger} caught by {detector_id}
- self-reported: {answers}
4 · quality
- signal precision per detector: {rate}, from founder labels
- drafts shipped unedited: {share}; median edit distance {x}
- stop requests per 100 touches: {n}
5 · experiments
- readouts due this week, with their pre-set win rules
- tests still collecting, and how far from their sample size
6 · the one change — Constraint: {rung}. Hypothesis: {why it leaks}. Change: {knob}, judged on {metric} by {date}.
Placeholders in braces are filled from the week's data. No numbers here are real.
how it connects
Where the funnel reads from, and what it feeds.
GTM tracking plan — READS FROM · the event catalog every metric is computed from
Sequence and step library — FED BY · the steps and touches that the reply and click metrics judge
Playbook Library — FED BY · each playbook's own success number and stop rule
Detector catalog — JUDGES · signal precision and yield per dollar, per detector
Experimentation engine — FEEDS · every test names a primary metric and its guardrails from this catalog
Pipeline — MAP · stage 11 on the canvas, with the funnel ladder
data to capture
What this stage records, and what that data trains.
From the stage 11 line on the pipeline overview, extended. Recorded in this shape from the first day, every rung becomes a label.
record
- replies with full text and a class (positive, question, not now, stop), joined to the
touch_id - clicks per short-link id, and visits carrying the
touch_idfrom the link - sign-up with first and last touch, plus the self-reported "how did you hear"
- activation: the first action that predicts paying, with time since sign-up
- Stripe: paid, failed, plan changed, canceled, with amount, plan and tenure
- each post's metrics at 10 minutes, 30 minutes, 1 hour, 1 day and 7 days
- the daily snapshot itself, as rows, so any past day can be re-read
label · trains · test
Label: each rung reached, with its timestamp, so time-to-event models work (who joins, and how soon).
Trains: conversion and churn models; attribution weights; an early forecast (does a post's 30-minute number predict its 7-day number?) that lets a weak post be boosted or left alone within the hour.
Test: landing and waitlist copy, and the call to action, A/B by visitor; the rest of the funnel's knobs belong to the stages that feed it.
Add next: carry the touch_id through short links into sign-up.