URL: /plays/launch-sites · Id: `play:launch-sites`

# Launch sites

> Launches a ready Startup on one launch site at a time; the agent books it and builds the kit, and the founder writes every word.

Launches a ready [Startup](https://startupbuilder.dev/business/nouns/startup) on one launch site at a time; the agent books it and builds the kit, and the founder writes every word.

```text title="Set it up · Ask"
Be our Marketing Lead for launches: book Leaseline on BetaList, Product Hunt and Hacker News one at a time, once it passes every readiness check. Build the kit; I write the post and every reply myself. Never ask anyone to vote. Show me what’s missing; don’t book.
```

```text title="Run one · Ask"
Hand the latest launch Event to the Marketing Lead agent and show me what it chose and why. Don’t post.
```

```ts title="Run one · SDK"
import { $ } from 'startup-builder'

const { events: [last] } = await $.Event.list({ where: 'name in ("Monitor.checked", "Contact.subscribed", "Version.deployed", "Organization.announced", "Person.commented", "Visitor.identified", "Person.posted", "Change.merged")', 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 }
```

```json title="Run one · Response · sample"
{
  "decision": {
    "$id": "https://api.sb/leaseline.app/decisions/decision_431",
    "$type": "Decision",
    "choice": "brief-a-reply",
    "reason": "a new Hacker News thread asks “anyone used Leaseline for multi-owner renewals?”; the brief holds fragments only, and you write every word",
    "stakes": "low",
    "options": [
      "book-the-launch",
      "time-it-to-a-moment",
      "send-the-notice",
      "brief-a-reply",
      "follow-up-signups",
      "not-now"
    ],
    "by": "worker_8",
    "on": "post_318",
    "expected": "your reply within 60 min",
    "reopensWhen": "no Post.published by worker_1 on post_318 within 60m"
  },
  "actions": [
    {
      "$id": "https://api.sb/leaseline.app/actions/action_1",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_8",
      "verb": "Content.draft",
      "checks": [
        {
          "check": "authorship",
          "status": "pass"
        }
      ]
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_2",
      "$type": "Action",
      "status": "pending",
      "reason": "awaitingFounder",
      "mode": "try",
      "by": "worker_8",
      "verb": "Task.assign",
      "checks": [
        {
          "check": "owned-by-a-person",
          "status": "pass"
        },
        {
          "check": "vote-ask",
          "status": "pass"
        },
        {
          "check": "thread-link",
          "status": "pass"
        }
      ]
    }
  ]
}
```

`action_1` is a fragment brief of 12-word facts, never a sentence for the thread. `action_2` puts it in your tray; you write the reply and post it by hand.

`380 ICP Visitors per launch week against 118 a week before · before-after, not proof · n 3 · path → arr`

| Try another: the launch Event | Choice | Reason |
| --- | --- | --- |
| Hacker News, Oct 19: a new thread names Leaseline | `brief-a-reply` | fragments only; you write every word |
| Product Hunt, Sep 8: every readiness check passes; never launched there | `book-the-launch` | ready, and the site has rested |
| A rival’s dated price rise on Sep 4 | `time-it-to-a-moment` | moves the booking 3 days to sit beside it |
| Owner portal shipped Oct 18; Product Hunt rested 34 of 180 days | `not-now` | the site rests 180 days; `launch-week` holds the Change |

| Worth | Your time | Spend | First signal | Use it when | Instead |
| --- | --- | --- | --- | --- | --- |
| ≈ +$1,200 ARR a month at 1 launch | 7 h per launch, then 20 min a week | $0 per launch; ~$2 a launch of Worker cost | Day 8: first launch-sourced signup | a public demo, 100+ People who said yes | Under 100: `owned-newsletter` · launched there in 180 days: `launch-week` |

**Where it sits:** **Stranger** → Lead → Trialing → Activated → At risk → Churned → Won back · ICP Visits per launch → People reached → New Customers ↑ → ARR added each week → ARR

Worth’s formula: about +250 ICP Visitors per launch (Other Startups’ results; no lift of your own to read) × 1 launch a month × 0.008 Visitors to paid × $590 first order ARR ≈ +$1,180. Under First signal: Each launch reads at day 7 and its Customers at day 30; at 1 a month it reads before-after, beside Other Startups’ results.

`sample · api.sb/leaseline.app · 41 Customers at 2026-10-19 · on Person.posted · first reply brief 2m 40s · 7 keys Not live yet`

## Start it this week
```bash
npx startup-builder add play launch-sites   # 1 · writes plays/launch-sites.mdx (Not live yet, gaps #5)

# 2 · dry run; below, its 422 on 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":"launch-sites"}'

# 3 · keep it: you accept every date and write every thread word
curl -X POST https://api.sb/leaseline.app/campaigns \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"play":"launch-sites"}'

# on
curl -X PATCH https://api.sb/leaseline.app/campaigns/launch-sites \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"endsAt":null}'

# off: bookings cancel and the deploy freeze lifts
curl -X PATCH https://api.sb/leaseline.app/campaigns/launch-sites \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"endsAt":"now"}'
```


```json title="The dry run’s 422 · 2026-08-24 · sample"
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "launch-sites can’t start: 2 checks fail.",
  "checks": [
    {
      "check": "use-when",
      "status": "fail",
      "observed": "74 of 100 People who said yes to hearing from Leaseline",
      "doc": "/plays/launch-sites#check-use-when",
      "fix": "Grow the list to 100 who said yes; the waitlist counts.",
      "then": "Contact.subscribe"
    },
    {
      "check": "baseline",
      "status": "pass",
      "observed": "118 ICP Visitors a week, 28-day baseline",
      "doc": "/plays/launch-sites#check-baseline",
      "then": "Metric.query"
    },
    {
      "check": "launch-readiness",
      "status": "fail",
      "observed": "8 of 9: the demo asks for a signup",
      "doc": "/plays/launch-sites#check-launch-readiness",
      "fix": "Open the demo with no signup; the gate reads the live Site.",
      "then": "Site.publish"
    },
    {
      "check": "domain-authenticated",
      "status": "pass",
      "doc": "/plays/launch-sites#check-domain-authenticated",
      "then": "Domain.authenticate"
    },
    {
      "check": "mailbox-warm",
      "status": "pass",
      "observed": "the Startup’s Mailbox, warm since July",
      "doc": "/plays/launch-sites#check-mailbox-warm",
      "then": "Mailbox.warm"
    },
    {
      "check": "role-unstaffed",
      "status": "pass",
      "observed": "every thread word and every date goes to the founder, Dana",
      "doc": "/plays/launch-sites#check-role-unstaffed",
      "then": "Role.staff"
    }
  ]
}
```

| Check | Fix (one call) | Your time | Wait |
| --- | --- | --- | --- |
| <a id="check-use-when"></a>`use-when` · <a id="check-baseline"></a>`baseline` · fail: 74 of 100 who said yes; pass: 118 a week (gaps #8) | <Op verb="Contact.subscribe" /> from the waitlist form; <Op verb="Metric.query" /> on `launch-icp-visits` fills Before | 10 min | about a week at 4 a day |
| <a id="check-launch-readiness"></a>`launch-readiness` · fail (gaps #8): 8 of 9 | <Op verb="Site.publish" /> the demo with no signup | 30 min | none |
| <a id="check-domain-authenticated"></a>`domain-authenticated` · <a id="check-mailbox-warm"></a>`mailbox-warm` · <a id="check-role-unstaffed"></a>`role-unstaffed` · pass | nothing to do | 0 min | none |

`Your number · Before 118 ICP Visitors a week · Now 380 per launch week · Target 400 by Dec 31 · before-after, not proof · n 3 launches`

A launch costs no cash: the sites list it for free, and the notice goes from your own Mailbox. The kit and the reply briefs cost about $2 a launch (Worker cost). Your time is the cost: about 7 hours per launch, most of it the 4-hour reply block on the day.

## When it starts
| When | Trigger id | Starts on | Window | How it gets in |
| --- | --- | --- | --- | --- |
| Every readiness check passes, and the site has rested 180 days | `launch-ready` | `state` on `Monitor.checked` where every readiness check passes and no launch on the Venue in 180 days | 14d | the gate reads your live Site daily |
| A new comment lands on the live launch thread or listing | `launch-comment` | `event` on `Person.commented` where on the live launch thread or listing | 2h; a brief within 5 min for 72 h | read from the site; nothing to connect |
| A new thread names Leaseline after the launch window | `launch-mention` | `event` on `Person.posted` where names the Startup, after the launch window closes | 7d | read from the site; <Op verb="Event.capture" /> for one seen elsewhere |
| + 5 more for the audience floor, launchable Changes, rival moments, launch signups and claim drift (`?lanes=all`) | | | | |

```json title="One signed delivery · StartupBuilder-Signature"
{
  "$id": "https://api.sb/leaseline.app/events/event_1206",
  "$type": "Event",
  "name": "Person.posted",
  "at": "2026-10-19T13:22:51Z",
  "seq": 1206,
  "data": {
    "post": "posts/post_318",
    "venue": "hacker-news",
    "title": "Ask HN: tools for renewals across many owners?",
    "names": [
      "Leaseline"
    ],
    "comments": 6
  }
}
```

```bash title="Reproduce it in test"
# 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":"Person.posted","url":"https://leaseline.app/hooks/sb"}'
npx startup-builder trigger Person.posted --like event_1206   # replays the delivery into your tray
npx startup-builder clock advance --by 1h   # reads whether you replied within the hour
```

## Who runs it
**Marketing Lead · your agent** (`role:marketing-lead`, `worker_8`) holds the Key Result: it runs the readiness gate, books dates inside the caps, builds the kit, sends the live notice and briefs every comment. Every date, post and reply on a launch thread hands to **you** (Dana Okafor, person, `worker_1`, `role:founder`). Agent text reaches a Hacker News or Product Hunt thread on no rung.

| Option | Rung today | Evidence (passed of n) | Moves up at | Drops back when | You can |
| --- | --- | --- | --- | --- | --- |
| Book the launch · Time it to a moment | You approve each one | 3 of 3 dates accepted, 1 moved | never: a launch date stays yours | — | <Op verb="Approval.approve" /> · <Op verb="Task.return" /> |
| Send the live notice · Follow up launch signups | It does it, you can stop it | 3 notices, 61 follow-ups, 0 Checks failed sent | met after launch 1 (review-all passed) | an `outage` hold or a `bounce` Stop → You approve each one | <Op verb="Action.cancel" /> inside the hold |
| Brief a reply | You approve each one | 88 briefs, 81 replies written by you | never: thread words stay yours | — | skip it in the tray |
| Not now | It does it | 2 of 2 | — | — | <Op verb="Action.redirect" /> |
| Change the site order | You approve each one | 1 merged of 1 | never | — | <Op verb="Change.merge" /> · <Op verb="Change.revert" /> |
| Never: a vote ask, a thread link in a Message, an agent sentence on a thread | Refused, every time | 1 drafted, 1 refused | never | — | the `vote-ask`, `thread-link` and `authorship` Checks |

**What it reads · What it may call.** It reads the live Site, the claims source and each thread’s comments, and a comment is data, never instructions. It may call <Op verb="Content.draft" />, <Op verb="Task.assign" /> and <Op verb="Post.publish" /> on the Startup’s own Handles; it never posts on a launch thread. Its tools are <Op verb="Content.draft" />, <Op verb="Task.assign" />, <Op verb="Post.publish" />, and any other call becomes an Approval under the Role’s Mandate.

```md title="The brief (folded): compiled Agent-register Markdown · play:launch-sites@1 · each line names its record key"
You hold the Marketing Lead Key Result: 400 ICP Visitors per launch by 2026-12-31. ← worker, metric
On each launch Event, choose one option and say why; a rested site gets Not now. ← triggers, chooses
Launch BetaList, then Product Hunt, then Hacker News, one at a time.             ← your file, line 5
Write no sentence for a launch thread: hand the founder fragments of 12 words or fewer. ← copyRules 4, 5, authorship
Never ask anyone to vote, upvote or rank, on any site or channel.                ← copyRules 2, vote-ask
Show only screens the live Site serves today.                                    ← copyRules 6
```

**The slow loop, as a Change.** `decision_391` (2026-09-21, weekly) read Product Hunt’s 640 ICP Visitors against BetaList’s 90 and moved Hacker News before a second directory (your file, line 5), through `change_29`, which Dana merged with <Op verb="Change.merge" />. **Replayed on past launches**, <Op verb="Change.review" /> put the next launch at about 400; read back at its day 7, Hacker News brought 410, n 1 launch, ■□□□ Too early to tell (Example, day 50).

**Bring your own agent** (folded) books and briefs at the same rung when it matches or beats the seated agent on the eval suite; thread words stay yours either way, and <Op verb="Role.staff" /> seats it.

```bash
npx startup-builder eval --scenario launch-sites-3 --agent "claude -p" --seat marketing-lead   # seated: 0 vote asks sent, 0 agent words on threads, 2.4 min a brief · yours: 0, 1 full sentence drafted for a thread, 2.0 min → You approve each one
```

## What it can do next
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
| --- | --- | --- | --- | --- | --- |
| Book the launch `book-the-launch` | every readiness check passes, and the site rested 180 days | Your rule (line 5) | <Op verb="Content.draft" /> the kit from the live Site → <Op verb="Task.assign" /> the date to you, with a deploy freeze | you accept the date, due 1 business day · Take this one | you |
| Time it to a moment `time-it-to-a-moment` | a rival’s dated price rise or shutdown, or a shipped Change, in the next 14 days | Agent’s call, with its reason | <Op verb="Task.assign" /> a moved date | you accept it, due 1 business day · Take this one | you |
| Send the live notice `send-the-notice` | the launch goes live | Your rule (the booking) | `launch-live-notice` to People who said yes, 10 % held back → <Op verb="Post.publish" /> on Leaseline’s own Handles | nobody | the same |
| Brief a reply `brief-a-reply` | a launch comment, or a new thread naming Leaseline | Agent’s call, with its reason | <Op verb="Content.draft" /> of fragments → <Op verb="Task.assign" /> | you write and post it, due 60 min · Take this one | you |
| Follow up launch signups `follow-up-signups` | a signup whose first Source is the launch | Your rule (the Sequence) | `launch-signup-follow-up`: a first-value nudge, then a fixes note | nobody | the same |
| Not now `not-now` | a check fails, the site rested under 180 days, or another of your Startups launches there that day | Your rule (the rest and the caps) | nothing; names the check, the rest’s end date or the other launch | nobody; the gate reads it again daily | the same |

| When they write back | Fixture reply | What happens | Who answers | By when | Call |
| --- | --- | --- | --- | --- | --- |
| Interested | a notice reply: “Can I see this with my 12 buildings?” | goes to `instant-response` as a new Lead, with the launch on its Acquisition | SDR · your agent, then AE · Priya Shah (person) | 5 min | <Op verb="Lead.create" /> |
| Send me pricing | a thread comment: “What does it cost?” | a fragment brief with the Pricing Page’s list Price; you write the reply | you (Dana, person) | 60 min | <Op verb="Task.assign" /> |
| Not now | a notice reply: “Not for us this year.” | nothing more sends to them about the launch | nobody | at once | <Op verb="Decision.list" /> |
| Wrong person | “Forwarding to our ops manager.” | a one-line thanks; it never writes to an address from their text | Marketing Lead · your agent | 1 business day | <Op verb="Conversation.answer" /> |
| Out of office | an auto-reply to the notice | nothing: the notice carries no follow-up | nobody | — | <Op verb="Decision.list" /> |
| Question it can’t answer | a thread comment: “How do you handle owners in two time zones?” | the brief marks it unknown to the claims source; you answer, or say you are checking | you (Dana, person) | 60 min | <Op verb="Conversation.escalate" /> |
| Stop | “Unsubscribe.” | no notice, follow-up or newsletter sends to them again | nobody | at once | <Op verb="Contact.unsubscribe" /> |

## When it hands to you
Every date, post and thread reply hands to **you** (Dana Okafor, person); interested notice replies go through `instant-response` to the AE. As captured, the reply brief in your tray (`brief` is Not live yet, gaps #7).

```json title="What lands in each queue · sample"
[
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_521",
    "$type": "Task",
    "assignee": "worker_1",
    "due": "2026-10-19T14:22:00Z",
    "on": "post_318",
    "brief": {
      "thread": "Ask HN: tools for renewals across many owners?",
      "asked": "anyone used Leaseline for multi-owner renewals?",
      "fragments": [
        "per-owner batches, 60 days out",
        "41 property managers on it",
        "free plan: 10 units"
      ],
      "chose": "brief-a-reply",
      "never": [
        "a vote ask",
        "a sentence written for you",
        "a link to the thread in any Message"
      ]
    }
  }
]
```

You answer a brief by writing the reply and posting it by hand, with <Op verb="Task.return" /> and a reason, or with <Op verb="Action.redirect" />; a date with <Op verb="Approval.approve" /> or <Op verb="Task.return" />. A brief past its due time stays in the tray marked late, and nobody else can post in your name, so nothing goes up a Role. During a launch’s 72 hours the agent pings you within 5 minutes of each comment.

**On time:** You: 81 of 88 briefs answered within 60 min, median 9 min on launch days, n 88, ■■□□ Early sign beside Other Startups’ 86 %. **Your queue:** counted by kind: per launch, 1 date, 1 kit review and about 30 reply briefs; between launches, about 2 mention briefs a week.

**The fixed line:** <Op verb="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 |
| --- | --- | --- | --- |
| <a id="check-owned-by-a-person"></a>`owned-by-a-person` (rule j; Not live yet, gaps #8) | a notice or follow-up 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. | <Op verb="Task.assign" /> |
| <a id="check-authorship"></a>`authorship` (launch sites) | an agent sentence for a Hacker News post, title or comment, or a Product Hunt maker comment or reply | Write it yourself from the fragments. | <Op verb="Task.assign" /> |
| <a id="check-vote-ask"></a>`vote-ask` | any Message, Post or Page that asks anyone to vote, upvote or rank the launch | Cut the ask; say it is live and what it does. | <Op verb="Content.draft" /> |
| <a id="check-thread-link"></a>`thread-link` | a Message or Post carrying a Hacker News thread link | Link your own Site instead. | <Op verb="Content.draft" /> |
| <a id="check-readiness"></a>`readiness` | a launch after a readiness check fails since booking | Fix the failing check; the launch moves to the next open date. | <Op verb="Task.next" /> |
| <a id="check-venue-rest"></a>`venue-rest` (launch sites) | a second launch on one site within 180 days, or a sunk post deleted and posted again | Wait for the rest’s end date, or run `launch-week`. | <Op verb="Decision.list" /> |
| <a id="check-portfolio-caps"></a>`portfolio-caps` (launch sites) | two of your Startups on one site in a day, or one founder on one site twice in 30 days | Take the next open date the caps allow. | <Op verb="Task.next" /> |

| Stop (pauses the Play) | Pauses when | Then |
| --- | --- | --- |
| <a id="stop-outage"></a>`outage` | errors over 2 % of requests, or p95 over 3 s for 5 min | the notice and the Posts hold until the Site recovers |
| <a id="stop-bounce"></a>`bounce` (email) | bounces over 2 % of the notice’s sends | the Mailbox pauses |
| <a id="stop-complaint"></a>`complaint` (email) | complaints near 0.1 % | the notice pauses on every Mailbox |
| <a id="stop-kill-switch"></a>`kill-switch` | you press it | every Tactic pauses at once; the freeze lifts |

The captured refusal, then the Stop that fired as a `Campaign.paused` reason (Not live yet, gaps #2); the rewrite that sent is the Product Hunt notice thread in Example.

```json title="The captured refusal · sample"
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "The Product Hunt live notice did not send: it asks People to upvote.",
  "checks": [
    {
      "check": "vote-ask",
      "status": "fail",
      "doc": "/plays/launch-sites#check-vote-ask",
      "fix": "Cut the ask; say it is live and what it does.",
      "then": "Content.draft"
    },
    {
      "check": "thread-link",
      "status": "pass"
    }
  ]
}
```


```json title="The Stops that fired, as Campaign.paused reasons · sample"
[
  {
    "reason": {
      "limit": "outage",
      "observed": "p95 3.8 s for 6 min",
      "bound": "3 s for 5 min",
      "window": "launch day"
    },
    "doc": "/plays/launch-sites#stop-outage",
    "task": "task_406"
  }
]
```

## Did it work
At 1 launch a month, no held-back half of launches reads, so the basis is before-after (rule o): the columns read Now and Before, and nothing prints as Added. Each figure’s call is <Op verb="Metric.query" /> by node, and every node above the leaf is Not live yet (gaps #4).

`380 ICP Visitors per launch week against 118 a week before · before-after, not proof · n 3 · path → arr`

| Node | Now (56 days) | Before (the 28-day baseline) | Added | Confidence | Basis |
| --- | --- | --- | --- | --- | --- |
| `launch-icp-visits` | 380 per launch: BetaList 90, Product Hunt 640, Hacker News 410 | 118 ICP Visitors a week, from every Source | — · Worth about +$1,200 a month | — | before-after, not proof · Other Startups’ results +250 per launch, ■■■□ Likely, n 640 launches |
| `icp-reached` | 2,490 ICP Visitors in the 56 days | 944 per 56 days | — | — | before-after |
| `new-arr` ↑ | $3,023, 7 launch-sourced Customers (6 on Pro, 1 on Team) · Booked: credited, not all caused by this Play | $1,388 per 56 days, every Source | — · 11 trials still deciding | — | before-after, not proof |
| `net-new-arr` · `arr` | carried from `new-arr` | carried | — | — | carried |

Before against after reads ICP Visitors whose first Source is a launch listing, short link or site referrer, in the 7 days after each launch, against the 28-day baseline before the first. It is not proof; Other Startups’ results, with a 10 % notice holdout, print beside it.

**How it moves it:** each launch put the no-signup demo before a site’s readers for a day, and Dana answered 81 comments at a median of 9 minutes.

**Guardrails:** before against after: `play-sourced-customers`, 7 against 0, and launch-sourced Customers active from day 14 to day 30, 5 of 7 against Leaseline’s 4 of 7 for every Source.

```json title="The Campaign card · sample"
{
  "$id": "https://api.sb/leaseline.app/campaigns/launch-sites",
  "$type": "Campaign",
  "play": "play:launch-sites@1",
  "path": [
    "launch-icp-visits",
    "icp-reached",
    "new-arr",
    "net-new-arr",
    "arr"
  ],
  "experiment": {
    "metric": "launch-icp-visits",
    "unit": "count",
    "heldBack": 0,
    "decision": null
  },
  "readout": {
    "value": null,
    "n": 3,
    "maturing": 0,
    "interval": null,
    "readability": "inadmissible",
    "basis": "before-after",
    "pooled": true
  },
  "ledger": {
    "spend": 0,
    "workerCost": 6,
    "hours": 22,
    "pipelineArr": 3816,
    "wonArr": 3023,
    "projectedArr": 1180,
    "paybackDays": 0
  }
}
```

Outside benchmark: 27 % of Hacker News launch-post titles name AI, models or agents (142 launch posts, a count of the public launch list, 2026-10-24), so a title that names what the Product does stands apart.

**Before it · After it.** Before it: `owned-newsletter` grows the 100 People who said yes. After it: launch signups go to `product-led`, interested notice replies to `instant-response`, and a shipped Change on a rested site goes to `launch-week`.

## Example
Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days): 3 launches on 3 sites, 3 live notices to 212 People who said yes (21 held back), and 88 reply briefs. Read every captured thread at `?threads=all`; three follow the tables.

| What happened, by option | Decisions | Sent or posted | ICP Visitors in 7 days | Won (credited, not all caused by this Play) |
| --- | --- | --- | --- | --- |
| Book the launch · Time it to a moment | 3 · 1 | 3 launches | BetaList 90 · Product Hunt 640 · Hacker News 410 | 7 Customers (+$3,023 ARR) |
| Send the live notice · Follow up launch signups | 3 · 61 | 573 notices · 112 follow-ups | — | 11 trials still deciding |
| Brief a reply · Not now | 88 · 2 | 81 replies, by you | — | — |
| **All** · the 28-day baseline | **158** | — | **380** per launch · 118 a week | **$3,023** |
| At 1 launch a month (the Use it when volume) | about 50 a month | 1 launch | before-after, not proof | Other Startups’ results: +250 per launch, ■■■□ Likely, n 640 |

| Day | Event | What happened | Your time |
| --- | --- | --- | --- |
| 0 | `event_129` Campaign.created | the three steps; dry run fails `use-when` and `launch-readiness` | 30 min |
| 6 | `event_221` Monitor.checked | `launch-ready`: 103 who said yes, and the demo opens with no signup | — |
| 8 | `event_262` Visitor.identified | BetaList live; the first launch-sourced signup | 1.5 h |
| 22 | `event_401` Campaign.paused | `outage`: p95 3.8 s at 08:10; the notice waited 22 min | — |
| 22 | `event_405` Post.published | Product Hunt live at 00:01 PT; you wrote the maker comment and 46 replies | 5 h |
| 28 · 50 | `event_536` Change.merged · `event_880` Readout.created | Hacker News next: replayed at about 400, read back at day 7 at 410, n 1 | 5 min |
| 1–56 | `event_160` Task.assigned, first of 92 | you accepted 3 dates and wrote 81 replies, from the <Op verb="Task.next" /> log | 14 h · **total 22 h measured, 21 h asked** |

> **Product Hunt, Sep 15, 00:20 PT, a commenter** · “Does this work when each building has a different owner?”
>
> **Dana, 00:27** · Yes, that is the case we built for: each owner gets their own renewal batch, 60 days out.¹ The demo has three owners loaded; no signup.² *Written by Dana from the brief’s fragments.*
>
> **Sep 15, 09:40** · the commenter signed up from the listing (`Visitor.identified`, first Source Product Hunt) and paid for Pro on Sep 29.

```diff title="Product Hunt live notice, 2026-09-15 · the draft vote-ask refused, then the rewrite that sent"
- We’re live on Product Hunt today: please upvote us!
+ Leaseline is live on Product Hunt today, with a demo you can open with no signup. Tell us what you think on the listing.
```

> **Hacker News, 2026-10-06 09:14** · a commenter asked how Leaseline handles owners in two time zones; the claims source holds no answer, so the brief marked it unknown, due 10:14. · **Dana, 09:31** · wrote her own reply: the batch runs in each owner’s time zone, and she would add it to the docs that week.³

¹ Your words, from a fragment: “per-owner batches, 60 days out”. ² A screen the live Site serves. ³ Every thread word is yours; the agent wrote none.

## Your settings
```mdx title="plays/launch-sites.mdx"
---
noticeHoldout: 0.1     # 1 in 10 who said yes gets no notice
replyBlock: 09:00-13:00 ET
---
BetaList first, then Product Hunt, then Hacker News, one at a time. I write every word on the threads.
```

```text
$ npx startup-builder show plays/launch-sites
notice holdout 0.1 · reply block 09:00-13:00 ET · site order     your file  plays/launch-sites.mdx:2,3,5 (Ask 2026-08-24; line 5 by change_29, 2026-09-21)
family demand · worker role:marketing-lead, owns never · chooses 6 (4 rule, 2 read) · sequences launch-live-notice, launch-signup-follow-up · every thread word by the founder · checks owned-by-a-person, authorship, vote-ask, thread-link, readiness, venue-rest, portfolio-caps · stops 4 · placements product-hunt, hacker-news, betalist, gmail, outlook, yahoo-mail, x, linkedin    catalog  play:launch-sites@1 (Not live yet, gaps #5)
```

### Every key from the catalog
| Key | Id | What it is |
| --- | --- | --- |
| Tactic | <a id="tactic-launch-readiness-gate"></a>`launch-readiness-gate` | Launch readiness gate runs nine rule checks on a live Startup and books a launch only when every check passes. Starts at Sends nothing. |
| Tactic | <a id="tactic-launch-booking"></a>`launch-booking` | Launch booking orders the eligible Venues, picks a date inside the Portfolio caps and the Human’s reply block, and books a deploy freeze. Starts at It does it, you can stop it. |
| Tactic | <a id="tactic-launch-kit"></a>`launch-kit` | Launch kit compiles the gallery, Video, tagline, fact sheet and red-team list from the live Site and the claims source for the Human. Starts at You approve each one. |
| Tactic | <a id="tactic-owned-audience-notice"></a>`owned-audience-notice` | Owned audience notice tells consented People the Startup is live, admits the Waitlist batch, and posts from the Startup’s own Handles, with no vote ask. Starts at You approve each one. |
| Tactic | <a id="tactic-launch-desk"></a>`launch-desk` | Launch desk watches a live launch thread for 72 hours and hands the Human a fragment-only reply brief for each comment within 5 minutes. Starts at Sends nothing. |
| Tactic | <a id="tactic-launch-readout"></a>`launch-readout` | Launch readout counts ICP Visits a launch brings in 7 days, and Customers still active from day 14 to day 30, against a holdout. Starts at Sends nothing. |
| Tactic | <a id="tactic-claims-check"></a>`claims-check` | Claims check maps every draft sentence to an approved source or the Person’s own words, and holds anything else before it reaches anyone. Starts at Sends nothing. |
| Tactic | <a id="tactic-send-gate-check"></a>`send-gate-check` | Send gate check: Checks Suppression, a live reason, the Market, the basis stamp, the Person-month grant, the facts and the labels before any cold Message exists. Starts at Sends nothing. |
| Tactic | <a id="tactic-claim-drift"></a>`claim-drift` | Claim drift finds each live claim a merged or reverted Change made false within 1 hour, then corrects, pauses or routes it. Starts at You approve each one. |
| Tactic | <a id="tactic-deploy-freeze"></a>`deploy-freeze` | Deploy freeze holds every live deploy and named Flag through the launch days, while reverts run at any hour and approved fixes ship in steps. Starts at Sends nothing. |
| Tactic | <a id="tactic-founder-send-tray"></a>`founder-send-tray` | Founder send tray shows each approved draft with its source trail; the founder presses post by hand or through the Venue’s own posting path. Starts at You approve each one. |
| Trigger | <a id="trigger-launch-ready"></a>`launch-ready` | Launch ready: `state` on `Monitor.checked`, window 14d. |
| Trigger | <a id="trigger-owned-audience-floor"></a>`owned-audience-floor` | Owned audience floor: `threshold` on `Contact.subscribed`, window 30d. |
| Trigger | <a id="trigger-launchable-change"></a>`launchable-change` | Launchable change: `event` on `Version.deployed`, window 30d. |
| Trigger | <a id="trigger-rival-moment"></a>`rival-moment` | Rival moment: `event` on `Organization.announced`, window 14d. |
| Trigger | <a id="trigger-launch-comment"></a>`launch-comment` | Launch comment: `event` on `Person.commented`, window 2h. |
| Trigger | <a id="trigger-launch-sourced-signup"></a>`launch-sourced-signup` | Launch-sourced signup: `event` on `Visitor.identified`, window 30d. |
| Trigger | <a id="trigger-launch-mention"></a>`launch-mention` | Launch mention: `event` on `Person.posted`, window 7d. |
| Trigger | <a id="trigger-claim-drift"></a>`claim-drift` | Claim drift: `event` on `Change.merged`, window 1d. |
| Sequence | <a id="sequence-launch-live-notice"></a>`launch-live-notice` | Launch live notice: 1 steps; it ends on Opt-out; Signup. |
| Sequence | <a id="sequence-launch-signup-follow-up"></a>`launch-signup-follow-up` | Launch signup follow-up: 3 steps; it ends on Opt-out; An open Issue from the Customer, which Support takes over; Day 30 after the launch. |
| Benchmark | <a id="benchmark-show-hn-ai-title-share"></a>`show-hn-ai-title-share` | Share of Hacker News launch-post titles that name AI, models or agents: 27 % (Count of the public Hacker News launch-post list, re-run in a verification pass, 2026-10-24). |