Build in public
Publishes a weekly build log compiled from your own records: what you decided, tested, killed and earned, with no typed number, after your OK.
- Channel
- Social
- Kind
- Social & video
Be our Content Lead for a build log: every Friday after office hours, write up what we decided, tested, killed and earned from our records, with every number from the record and revenue only from Stripe. I approve and redact each one and press post on my own accounts. Show me what’s missing; don’t publish.
# Not live yet (gaps #2)
curl -X POST "https://api.sb/leaseline.app/campaigns?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"play":"build-in-public"}'import { $ } from 'startup-builder'
return await $.Campaign.create({ play: 'build-in-public' }, { mode: 'try' }) // dry run on the day-0 Startup, 2026-08-24 · Not live yet, gaps #2
npx startup-builder do Campaign.create --json '{"play":"build-in-public"}' --try # Not live yet (gaps #2){
"name": "do",
"arguments": {
"code": "return await $.Campaign.create({ play: 'build-in-public' }, { mode: 'try' }) // dry run on the day-0 Startup, 2026-08-24 · Not live yet, gaps #2",
"mode": "try"
}
}application/json{
"type": "https: //startupbuilder.dev/ errors/ refused",
"title": "Refused",
"status": 422,
"detail": "build-in-public can’t start yet: 1 check fails and 1 warns.",
"checks": [
{
"check": "owned-by-a-person",
"status": "pass",
"observed": "0 subscribers are Leads a person owns",
"doc": "/plays/ build-in-public#check-owned-by-a-person",
"then": "Task.assign"
},
{
…
Hand this week’s records to the Content Lead agent and show me what it chose and why. Don’t publish.
curl "https://api.sb/leaseline.app/events?where=name%20in%20(%22Decision.recorded%22%2C%20%22Experiment.concluded%22%2C%20%22Hypothesis.dropped%22%2C%20%22Invoice.collected%22%2C%20%22Decision.superseded%22%2C%20%22Incident.resolved%22%2C%20%22Consent.granted%22%2C%20%22Metric.queried%22%2C%20%22Prospect.engaged%22)&limit=1" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
# Not live yet (gaps #7)
curl -X POST "https://api.sb/leaseline.app/triggers/fire?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"like":"event_1388"}'
# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/decisions?where=on%20%3D%20%22content_88%22" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/actions?where=on%20%3D%20%22content_88%22" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"import { $ } from 'startup-builder'
const { events: [last] } = await $.Event.list({ where: 'name in ("Decision.recorded", "Experiment.concluded", "Hypothesis.dropped", "Invoice.collected", "Decision.superseded", "Incident.resolved", "Consent.granted", "Metric.queried", "Prospect.engaged")', limit: 1 }) // { play } compiled to this Play’s Trigger Events
const { subject } = await $.Trigger.fire({ like: last.$id }, { mode: 'try' }) // the same Event again, in try mode · Not live yet, gaps #7
const { decisions: [decision] } = await $.Decision.list({ where: `on = "${subject}"` }) // what it chose, and why · Not live yet, gaps #7
const { actions } = await $.Action.list({ where: `on = "${subject}"` }) // what it did, with its checks · Not live yet, gaps #7
return { decision, actions }
npx startup-builder do Event.list --json '{"where":"name in (\"Decision.recorded\", \"Experiment.concluded\", \"Hypothesis.dropped\", \"Invoice.collected\", \"Decision.superseded\", \"Incident.resolved\", \"Consent.granted\", \"Metric.queried\", \"Prospect.engaged\")","limit":1}'
npx startup-builder do Trigger.fire --json '{"like":"event_1388"}' --try # Not live yet (gaps #7)
npx startup-builder do Decision.list --json '{"where":"on = \"content_88\""}' # Not live yet (gaps #7)
npx startup-builder do Action.list --json '{"where":"on = \"content_88\""}' # Not live yet (gaps #7){
"name": "do",
"arguments": {
"code": "const { events: [last] } = await $.Event.list({ where: 'name in (\"Decision.recorded\", \"Experiment.concluded\", \"Hypothesis.dropped\", \"Invoice.collected\", \"Decision.superseded\", \"Incident.resolved\", \"Consent.granted\", \"Metric.queried\", \"Prospect.engaged\")', limit: 1 }) // { play } compiled to this Play’s Trigger Events\nconst { subject } = await $.Trigger.fire({ like: last.$id }, { mode: 'try' }) // the same Event again, in try mode · Not live yet, gaps #7\nconst { decisions: [decision] } = await $.Decision.list({ where: `on = \"${subject}\"` }) // what it chose, and why · Not live yet, gaps #7\nconst { actions } = await $.Action.list({ where: `on = \"${subject}\"` }) // what it did, with its checks · Not live yet, gaps #7\nreturn { decision, actions }",
"mode": "try"
}
}application/json{
"decision": {
"$id": "https: //api.sb/ leaseline.app/ decisions/ decision_499",
"$type": "Decision",
"choice": "lead-with-an-outcome",
"reason": "logs 5 to 7 drew 7, 6 and 5 contacts against a peak of 14, under half, and Maple Court said yes to showing its renewal result",
"stakes": "low",
"options": [
"publish-the-log",
"lead-with-an-outcome",
"ask-for-a-result",
"correct-the-number",
"welcome-the-reader",
"not-now"
…
sample · 4 Not live yetPreviewOn POST /{namespace}/processes/{process}/enableOff POST /{namespace}/processes/{process}/pauseTake this one POST /{namespace}/tasks/{task}/claim
action_3 is log 8, waiting for your redactions and OK (You approve each one); the issue to subscribers and the posts on your own accounts wait for your press.
67 contacts in 8 logs against 11 before · before-after,
| Try another: the week | Choice | Reason |
|---|---|---|
| Week of Oct 12: contacts fell to 5, Maple Court’s result consented | lead-with-an-outcome | 3 logs under half the peak, and a result you may show |
| Week of Sep 7: Decision “iterate”, the reminder test read | publish-the-log | a Decision and a test read to report |
| Sep 21: a Stripe refund changed log 4’s 9 to 8 | correct-the-number | a recompute differs from the published number |
| Wrenfield: 14 renewals sent on time in September, no yes to show it | ask-for-a-result | a verified result with no display Consent |
- 30 min week 1, then 20 a week
- Your time
- Week 1: first reader subscribes from a log
- First signal
Where it sits: Stranger → Lead → Trialing → Activated → At risk → Churned → Won back · Build log contacts → New Leads → New Customers ↑ → ARR added each week → ARR
Worth’s formula: about +4.1 contacts per log (Other Startups’ results; no lift of your own yet) × 4.3 logs a month × 0.21 contacts to Leads × 0.14 Leads to paid × $347 first order ARR ≈ +$180. Under First signal: One log a week can’t be split at random, so it reads before-after, and against its own first 4 logs for decay; 8 logs tell a fall to half the early rate.
sample · api.sb/
Start it this week
npx startup-builder add play build-in-public # 1 · writes plays/build-in-public.mdx (Not live yet, gaps #5)
# 2 · dry run; below, its 422 on the day-0 Startup (clock at 2026-08-24)
curl -X POST "https://api.sb/leaseline.app/campaigns?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"play":"build-in-public"}'
# 3 · keep it, at You approve each one
curl -X POST https://api.sb/leaseline.app/campaigns \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"play":"build-in-public"}'
# on
curl -X POST https://api.sb/leaseline.app/processes/content-marketing/enable \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
# off: no log publishes; corrections keep running
curl -X POST https://api.sb/leaseline.app/processes/content-marketing/pause \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"| Check | Fix | Then |
|---|---|---|
owned-by-a-person pass · Not live yet, gaps #8 | 0 subscribers are Leads a person owns | POST /{namespace}/tasks/{task}/assign |
keep-first pass · Not live yet, gaps #8 | 0 Customers with a result to ask about are at risk | POST /{namespace}/tasks/next |
customer-ask-cap pass · Not live yet, gaps #3 | 0 open asks to those Customers | GET /{namespace}/decisions |
use-when pass · Not live yet, gaps #8 | Friday office hours recorded 6 Decisions in 6 weeks; a weekly test each week | POST /{namespace}/metrics/query |
billing-connected fail · Not live yet, gaps #8 | Connect Stripe in live mode; revenue shows only from it. | POST /{namespace}/integrations/connect |
baseline warn · Not live yet, gaps #8 | n 11: it reads before-after, not proof, beside Other Startups’ results. | POST /{namespace}/metrics/query |
role-unstaffed pass · Not live yet, gaps #8 | Content Lead is your agent; each log comes to you | POST /{namespace}/roles/{role}/staff |
JSON
{
"type": "https://startupbuilder.dev/errors/refused",
"title": "Refused",
"status": 422,
"detail": "build-in-public can’t start yet: 1 check fails and 1 warns.",
"checks": [
{
"check": "owned-by-a-person",
"status": "pass",
"observed": "0 subscribers are Leads a person owns",
"doc": "/plays/build-in-public#check-owned-by-a-person",
"then": "Task.assign"
},
{
"check": "keep-first",
"status": "pass",
"observed": "0 Customers with a result to ask about are at risk",
"doc": "/plays/build-in-public#check-keep-first",
"then": "Task.next"
},
{
"check": "customer-ask-cap",
"status": "pass",
"observed": "0 open asks to those Customers",
"doc": "/plays/build-in-public#check-customer-ask-cap",
"then": "Decision.list"
},
{
"check": "use-when",
"status": "pass",
"observed": "Friday office hours recorded 6 Decisions in 6 weeks; a weekly test each week",
"doc": "/plays/build-in-public#check-use-when",
"then": "Metric.query"
},
{
"check": "billing-connected",
"status": "fail",
"doc": "/plays/build-in-public#check-billing-connected",
"fix": "Connect Stripe in live mode; revenue shows only from it.",
"then": "Integration.connect"
},
{
"check": "baseline",
"status": "warn",
"observed": "11 contacts from your own posts in 90 days",
"doc": "/plays/build-in-public#check-baseline",
"fix": "n 11: it reads before-after, not proof, beside Other Startups’ results.",
"then": "Metric.query"
},
{
"check": "role-unstaffed",
"status": "pass",
"observed": "Content Lead is your agent; each log comes to you",
"doc": "/plays/build-in-public#check-role-unstaffed",
"then": "Role.staff"
}
]
}It says no, says why, and names the fix.
| Check | Fix (one call) | Your time | Wait |
|---|---|---|---|
owned-by-a-person · keep-first · customer-ask-cap · pass · pass · pass (gaps #3, #8) | — | 0 min | none |
use-when · billing-connected · pass: 6 Decisions in 6 weeks · fail | POST /{namespace}/integrations/connect with system recurring-billing (Stripe), live mode | 5 min | the first sync, about 10 min |
baseline · warn: 11 in 90 days | POST /{namespace}/metrics/query; it fills Before in Your number | 0 min | none |
role-unstaffed · pass (gaps #8) | — | 0 min | none |
Your number · Before 11 per 90 days · Now 67 contacts in 8 logs · Target 120 by Dec 31 · before-after,
Each log costs about $0.40 to compile and check from your Domain (Worker cost), and the issue to about 150 subscribers sends from your Domain at no extra charge. That comes to about $2 a month at 4 logs.
When it starts
| When | Trigger id | Starts on | Window | How it gets in |
|---|---|---|---|---|
| Friday office hours record the week’s Decision | friday-decision | event on Decision.recorded where the weekly office hours' Decision: ship, kill, iterate, persevere or pivot | the draft within 1 h; expires after 3 d | POST /{namespace}/decisions in office hours |
| The weekly test reaches its read | weekly-test-read | event on Experiment.concluded where the weekly test's Readout recorded, or its read date reached | 7 d | POST /{namespace}/experiments/{experiment}/conclude |
| A recompute changes a published number | number-restated | threshold on Metric.queried where a recompute differs from the published value | the correction within 1 h | the nightly POST /{namespace}/metrics/query recompute |
+ 6 more for kills, Stripe milestones, overrules, incidents, consented results and replies (?lanes=) |
{
"$id": "https://api.sb/leaseline.app/events/event_1388" ,
"$type": "Event",
"name": "Decision.recorded",
"at": "2026-10-16T16:40:10Z" ,
"seq": 1388,
"data": {
"decision": {
"$id": "https://api.sb/leaseline.app/decisions/decision_497" ,
"choice": "iterate",
"on": "the 60-day reminder test"
},
"week": 8,
"log": {
"$id": "https://api.sb/leaseline.app/contents/content_88"
}
}
}# each delivery is signed with the Watch’s secret
curl -X POST https://api.sb/leaseline.app/watches \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"selector":"Decision.recorded","url":"https://leaseline.app/hooks/sb"}'
npx startup-builder trigger Decision.recorded --like event_1388 # replays Friday’s Decision
npx startup-builder clock advance --by 3d # runs the draft’s expiry if you never approved itWho runs it
Content Lead · your agent (role:, worker_7) holds the Key Result and compiles each log. Every log comes to you (Dana Okafor, person, worker_1) to redact and approve, and posts on your own accounts go out only when you press them. Your number links to Did it work.
| Option | Rung today | Evidence (passed of n) | Moves up at | Drops back when | You can |
|---|---|---|---|---|---|
| Publish this week’s log · Lead with a Customer’s result | You approve each one, on every rung | 8 of 8 approved; 3 with a redaction | never | — | POST /{namespace}/approvals/{approval}/approve · POST /{namespace}/approvals/{approval}/refuse |
| Correct the number | It does it | 1 of 1 within 1 h | — | — | POST /{namespace}/changes/{change}/revert |
| Ask a Customer for their result · Welcome the new reader | It does it, you can stop it | 2 of 2 · 61 of 61 | met on day 14 | a held Customer is asked anyway → You approve each one | POST /{namespace}/actions/{action}/cancel inside the hold |
| Skip this week | It does it | 0 weeks skipped | — | — | POST /{namespace}/actions/{action}/redirect |
| Never: a typed number, revenue not from Stripe, a Customer named without their yes, a raise or a forecast | To a person, every time | 2 refused, 0 published | never | — | POST /{namespace}/processes/{process}/pause |
What it reads · What it may call. It reads your Decisions, Experiments, Changes, Incidents and Stripe, and every number in a log is a token that recomputes at render; no number is typed, and a Customer item without display Consent is redacted before you see it. Its tools are POST /{namespace}/content/draft, POST /{namespace}/content/{content}/publish, POST /{namespace}/messages/{message}/broadcast, PATCH /{namespace}/pages/{id}, and any other call becomes an Approval under the Role’s Mandate.
You hold the Content Lead Key Result: 120 contacts from the build log by 2026-12-31. ← worker, metric
Each Friday, compile the week’s Decision, test, kills and numbers into one log and say what leads it. ← triggers, chooses
Every number is a record token; revenue reads only from Stripe. ← number-token, stripe-revenue
Show subscribers, Customers and revenue as counts; no Customer by name without their yes. ← your file, line 5; display-consent
Posts on Dana’s accounts wait for her press. ← your file, line 6 The slow loop, as a Change. decision_489 (2026-10-12, weekly) read contacts per log falling from a peak of 14 to 5 by log 7, under half for 3 logs, so change_31 moved the lead slot to a consented Customer result and Dana merged it. Replayed on past logs, POST /{namespace}/changes/{change}/review put contacts for a result-led log at 8.5; read back on log 8 at +7d it is 9, n 1 log, ■□□□ Too early to tell (Example, day 53).
Bring your own agent (folded) compiles beside the seated one; its log reaches you only if it passes the same number, leak and consent checks.
npx startup-builder eval --scenario build-in-public-8 --agent "claude -p" --seat content-lead # seated: 8.4 contacts a log, 0 typed numbers, 0 leaks · yours: 7.9, 1 typed number refused What it can do next
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
|---|---|---|---|---|---|
Publish this week’s log publish-the-log | a Decision, test read or kill this week | Your rule (line 4) | POST /{namespace}/content/draft → POST /{namespace}/content/{content}/publish the page → POST /{namespace}/messages/{message}/broadcast the issue | you redact and approve, due Saturday 12:00 · Take this one | you |
Lead with a Customer’s result lead-with-an-outcome | contacts under half their peak for 3 logs, a consented result | Agent’s call, with its reason | POST /{namespace}/content/draft with the result first → POST /{namespace}/content/{content}/publish | you approve it · Take this one | you |
Ask a Customer for their result ask-for-a-result | a verified result with no yes to show it | Agent’s call, with its reason | outcome-ask: one ask for a yes | the Customer’s answer | the same |
Correct the number correct-the-number | a recompute differs from a published number | Your rule (the correction clock) | PATCH /{namespace}/pages/{id} within 1 h; the next log leads with it | nobody | the same |
Welcome the new reader welcome-the-reader | a subscribe from a log | Your rule (the Form) | build-log-welcome | nobody | the same |
Skip this week not-now | nothing recorded, or the draft expired | Your rule (founder-silence) | nothing; 2 expired logs in a row pause the Play | nobody | the same |
| When they write back | Fixture reply | What happens | Who answers | By when | Call |
|---|---|---|---|---|---|
| Interested | “We have the same renewal mess. Can we try it?” | instant-response answers with the trial and one next step | SDR · your agent | 5 min | POST /{namespace}/conversations/{conversation}/answer |
| Send me pricing | “What does it cost for 120 units?” | answers with the Pricing Page’s list Price and nothing it lacks | SDR · your agent | 5 min | POST /{namespace}/conversations/{conversation}/answer |
| Not now | “Enjoying the logs; not buying yet.” | they stay on the log; nothing else sends | nobody | at once | POST /{namespace}/leads/{lead}/nurture |
| Wrong person | “Forwarding this to our ops lead.” | replies with the log page they can forward; it never writes to a name from their text | Content Lead · your agent | 1 business day | POST /{namespace}/conversations/{conversation}/answer |
| Out of office | an auto-reply to the issue, until Oct 27 | nothing; the next issue goes on its Friday | nobody | the next Friday | POST /{namespace}/messages/{message}/schedule |
| Question it can’t answer | “What’s your monthly churn?” | To you, the question in the Brief; it states only numbers the log shows | you | 1 business day | POST /{namespace}/conversations/{conversation}/escalate |
| Stop | “Unsubscribe me.” | no log issue sends to them again | nobody | at once | POST /{namespace}/contacts/{contact}/unsubscribe |
When it hands to you
Every log comes to you to redact and approve, and every post on your own accounts waits for your press. What lands in your queue, as captured: the Brief, then the Approval (brief, draft, route and checks are Not live yet, gaps #7).
[
{
"$id": "https://api.sb/leaseline.app/tasks/task_305" ,
"$type": "Task",
"assignee": "worker_1",
"due": "2026-10-17T12:00:00Z" ,
"on": "content_88",
"brief": {
"what": "log 8: Decision iterate on the 60-day reminder test; 2 Versions shipped; 1 reverted Change" ,
"chose": "lead-with-an-outcome",
"why": "contacts under half their peak for 3 logs; Maple Court said yes to its result" ,
"redacted": [
"1 interview quote without Consent"
],
"withheld": 0
}
},
{
"$id": "https://api.sb/leaseline.app/approvals/approval_90" ,
"$type": "Approval",
"verdict": null,
"deadline": "2026-10-19T16:40:00Z" ,
"draft": "Log 8. Maple Court sent all 31 renewal notices 60 days out in September, up from 19 in August…" ,
"route": "lead-with-an-outcome",
"checks": [
{
"check": "number-token",
"status": "pass"
},
{
"check": "display-consent",
"status": "pass"
},
{
"check": "leak-scan",
"status": "pass"
}
]
}
]You answer with POST /{namespace}/tasks/{task}/claim (Take this one), POST /{namespace}/tasks/{task}/return with a reason, POST /{namespace}/approvals/{approval}/approve or POST /{namespace}/approvals/{approval}/refuse, each a one-call window, and a log unanswered by its deadline expires and never publishes. Two expired logs in a row pause the Play (founder-silence).
On time: you, 8 of 8 logs approved by their deadline, median 1 h 40 min, ■■□□ Early sign, n 8. Your queue: counted by kind: each week, 1 log to redact and approve and 2 posts to press; about 2 replies a week.
The fixed line: POST /{namespace}/deals/{deal}/assign sets owner to the receiving person, the agent is never a Deal’s owner, and Meetings land on the owner’s calendar under the owner’s name.
When it won’t send
| Check (refuses one send) | Refuses | Fix | Then |
|---|---|---|---|
owned-by-a-person (rule j; Not live yet, gaps #8) | a welcome or ask to a Contact whose Lead or open Deal a person owns | Leave it to the person who owns it: the draft waits in their queue. | POST /{namespace}/tasks/{task}/assign |
keep-first (rule j; Not live yet, gaps #8) | a result ask to a Customer who is at risk, past due or leaving | Let their CSM reach them first, then ask again once they are healthy. | POST /{namespace}/tasks/next |
customer-ask-cap (rule j; Not live yet, gaps #3) | a result ask to a Customer with another open ask | Wait for the open ask to close; GET /{namespace}/decisions names the Play that holds them. | GET /{namespace}/decisions |
number-token | a quantity in a draft or your edit that is not a record token | Use the record’s token, or cut the number. | POST /{namespace}/content/draft |
recompute | a log where 2 tokens recompute differently at render | Re-render; the log waits for the records to settle. | POST /{namespace}/metrics/query |
stripe-revenue | a revenue figure from anything but live-mode Stripe | Read it from Stripe, or cut it. | POST /{namespace}/metrics/query |
display-consent | a Customer or Person item with no yes to show it | It is redacted; ask once with outcome-ask. | POST /{namespace}/consents |
leak-scan | an email address, Customer domain or name from your records in the final render | Remove it; the scan reruns before publishing. | POST /{namespace}/content/draft |
cherry-pick | a log missing a public number without its Withheld count | Show it, or print it as withheld. | POST /{namespace}/content/draft |
raise | any mention of a raise, a round, a valuation, an investment ask or a revenue forecast | Cut the line. | POST /{namespace}/content/draft |
spam-brake | an issue past 3 times the trailing 7-day Message rate in an hour | Send to a smaller List, or wait an hour for the rate to fall. | POST /{namespace}/messages/{message}/broadcast |
The captured refusal, then the Stop that fired as a Campaign.paused reason (Not live yet, gaps #2); the line that published instead is the second thread in Example.
| Check | Fix | Then |
|---|---|---|
number-token fail | Use the record’s token, or cut the number. | POST /{namespace}/content/draft |
leak-scan pass | — | POST /{namespace}/content/draft |
JSON
{
"type": "https://startupbuilder.dev/errors/refused",
"title": "Refused",
"status": 422,
"detail": "Log 5 did not publish: your edit typed a number no record holds.",
"checks": [
{
"check": "number-token",
"status": "fail",
"observed": "“about 50 property managers” in line 1",
"doc": "/plays/build-in-public#check-number-token",
"fix": "Use the record’s token, or cut the number.",
"then": "Content.draft"
},
{
"check": "leak-scan",
"status": "pass"
}
]
}It says no, says why, and names the fix.
[
{
"reason": {
"limit": "correction",
"observed": "log 4 said 9 annual switches; a refund made it 8" ,
"bound": "0 restated numbers uncorrected",
"window": "1 h"
},
"doc": "/plays/build-in-public#stop-correction" ,
"fixed": "2026-09-21T16:52:00Z"
}
]Did it work
One log a week cannot be split at random, so the basis is before-after (rule o): the columns read Now and Before, and nothing prints as Added. Decay reads against the first 4 logs. Each figure’s call is POST /{namespace}/metrics/query by node; every node above the leaf is Not live yet (gaps #4).
67 contacts in 8 logs against 11 before · before-after,
- Chose
- lead-with-an-outcome
- Why
- logs 5 to 7 drew 7, 6 and 5 contacts against a peak of 14, under half, and Maple Court said yes to showing its renewal result
- From
- publish-the-log · lead-with-an-outcome · ask-for-a-result · correct-the-number · welcome-the-reader · not-now
- Expects
- 7 or more contacts from log 8 within 7d
- Reopens when
- under 4 contacts from content_88 by day 7
- By
- worker_7
JSON
{
"$id": "decisions/decision_499",
"$type": "Decision",
"choice": "lead-with-an-outcome",
"reason": "logs 5 to 7 drew 7, 6 and 5 contacts against a peak of 14, under half, and Maple Court said yes to showing its renewal result",
"stakes": "low",
"options": [
"publish-the-log",
"lead-with-an-outcome",
"ask-for-a-result",
"correct-the-number",
"welcome-the-reader",
"not-now"
],
"by": "worker_7",
"on": "content_88",
"expected": "7 or more contacts from log 8 within 7d",
"reopensWhen": "under 4 contacts from content_88 by day 7"
}| Node | Now (8 logs) | Before (the 90 days, per 56) | Added | Confidence | Basis |
|---|---|---|---|---|---|
build-log-contacts | 67 consented contacts: 10 a log for logs 1 to 4, 6.8 for logs 5 to 8 | 7 from your own posts | — · Worth about +$180 a month | — | before-after, not proof · Other Startups’ results +4.1 a log, ■■□□ Early sign, n 640 logs |
new-leads | 14 Leads from those contacts | 3 | — | — | before-after |
new-arr ↑ | $694, 2 Customers on Pro · Booked: credited, not all caused by this Play | $347 | — | — | before-after · 3 trials still deciding |
net-new-arr · arr | carried from new-arr | carried | — | — | carried |
The leaf counts consented contacts whose first Consent came through a log page, issue or post within 7 days; before against after reads 56 days against the 90 before, scaled. It is not proof, and Other Startups’ results print beside it.
How it moves it: each week’s Decision, test and Stripe numbers became one log on leaseline.app, in the issue to about 150 subscribers and on LinkedIn and X, where before the week left no public trace.
Guardrails: before against after: organic-share of visits, 32 % against 30 %, and unsubscribes per issue, 0.6 % against 0.5 %.
{
"$id": "https://api.sb/leaseline.app/campaigns/build-in-public" ,
"$type": "Campaign",
"play": "play:build-in-public@1" ,
"path": [
"build-log-contacts",
"new-leads",
"new-arr",
"net-new-arr",
"arr"
],
"experiment": {
"metric": "build-log-contacts",
"unit": "count",
"heldBack": 0,
"decision": null
},
"readout": {
"value": null,
"n": 8,
"maturing": 0,
"interval": null,
"readability": "inadmissible",
"basis": "before-after",
"pooled": true
},
"ledger": {
"spend": 0,
"workerCost": 3,
"hours": 3.2,
"wonArr": 694,
"projectedArr": 180,
"paybackDays": 1
}
}Outside benchmark: posts about a real result drew about twice the replies of posts about progress in one 2026 study of founder accounts (a social-analytics firm, 12,000 posts).
Before it · After it. Before it: weekly-review records the week, and founder-voice writes the founder’s other posts. After it: owned-newsletter keeps the readers it brings, and instant-response answers anyone who writes in.
Example
Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days): 8 logs, each compiled the Friday after office hours and approved by Dana. Revenue showed as counts from Stripe; Customers showed by name only with their yes. Read every captured log at ?threads=; three follow the tables.
| What happened, by option | Decisions | Done | Contacts (what happened) |
|---|---|---|---|
| Publish this week’s log · Lead with a Customer’s result | 7 · 1 | 7 published · 1 published | 58 · 9 |
| Ask a Customer for their result · Correct the number | 2 · 1 | 1 yes, 1 no · fixed in 12 min | — |
| Welcome the new reader · Skip this week | 61 · 0 | 61 welcomed · — | — |
| All 8 logs, against before | 8 logs | 8 approved, 3 with a redaction | 67 · before: 11 in 90 days |
| At 1 log a week (the Use it when volume) | about 8 in 56 days | before-after, not proof | Other Startups’ results: +4.1 contacts a log, ■■□□ Early sign, n 640 logs |
| Day | Event | What happened | Your time |
|---|---|---|---|
| 0 | event_1123 Campaign.created | the three steps; the dry run fails billing-connected | 20 min |
| 0 | event_1124 Integration.connected | Stripe connected in live mode | 5 min |
| 4 | event_1146 Content.published | log 1: 6 contacts in 7 days | 15 min |
| 25 | event_1262 Action.refused | number-token held log 5 for a typed number; you cut it | 3 min |
| 28 | event_1276 Campaign.paused | correction: a refund made log 4’s 9 an 8; corrected in 12 min | — |
| 53 | event_1381 Change.merged | a Customer’s result leads: replayed at 8.5 contacts, log 8 drew 9 | 5 min |
| 1–56 | event_1147 Task.created, first of 8 | one log a week, from the POST /{namespace}/tasks/next log | 2.3 h · total 3.2 h measured, 3 h asked |
Log 8, Oct 17 · Maple Court sent all 31 of September’s renewal notices 60 days out, up from 19 in August.¹ This week we decided to iterate on the reminder test: 41 of 188 managers opened it, under the 25 % our kill rule set.² Revenue: 41 paying Customers, from Stripe.³
Oct 17 to 24 · 9 contacts: 5 from Dana’s LinkedIn post, 3 from the log page’s Form, 1 forwarded issue; one started a trial on Oct 18.
- About 50 property managers tried the reminder this week.
+ 47 property managers tried the reminder this week.Log 6, 2026-10-02 16:40 · the draft went to Dana (
task_279, due Saturday 12:00) with 1 interview quote already redacted, because the speaker gave no yes. Dana withheld the week’s agent costs, which printed as “1 number withheld this week”, approved at 09:15 Saturday, and pressed post on her LinkedIn at 09:20.⁴
¹ A Customer’s result, shown with their yes. ² Every number is a record token that recomputed at render. ³ Revenue reads only from live-mode Stripe. ⁴ A withheld number prints as withheld, never as gone.
Your settings
---
revenue: counts # show Customers and revenue as counts from Stripe
---
One log a week, from Friday office hours.
No Customer by name without their yes.
Posts on my accounts wait for my press.$ npx startup-builder show plays/build-in-public
revenue counts · weekly · the name rule · your press your file plays/build-in-public.mdx:2,4,5,6 (Ask 2026-08-24)
family demand · process content-marketing · worker role:content-lead, owns never · chooses 6 (4 rule, 2 read) · sequences build-log-welcome, outcome-ask · approval every-send · checks owned-by-a-person, keep-first, customer-ask-cap, number-token, recompute, stripe-revenue, display-consent, leak-scan, cherry-pick, raise, spam-brake · stops 6 catalog play:build-in-public@1 (Not live yet, gaps #5) Every key from the catalog
Compiled, not written: edit the Play record build-in-public and its Leaseline run, then regenerate. Every step runs from a sentence or a line of code. In your words: the CRM words you know, and where each lives here.