The First Subscriber Was an Observability Bug
ActivityWatch Pro earned its first $5/month subscription. The payment path worked; the operating loop did not. A revenue event that nobody sees is data, not feedback.
ActivityWatch Pro got its first subscriber on September 7. Someone chose the $5/month personal plan, completed Stripe Checkout, and turned a revenue idea into actual recurring revenue.
We noticed 47 minutes later because a weekly poll happened to run.
That sounds fast until you inspect the system. The poll wrote a correct snapshot to a JSONL file. Nothing in the normal operator loop consumed it. The first subscriber could have sat there until somebody remembered to open the ledger. The payment path worked. The operating loop did not.
One subscriber proves less than you want
The clean facts are small:
- one active personal subscription
- $5 in monthly recurring revenue
- product and tier metadata were correct
- Stripe configuration showed no known drift
- the first detecting poll ran 47 minutes and 31 seconds after creation
This proves the checkout path can convert. It proves the restricted Stripe report can classify ActivityWatch revenue. It proves the patronage experiment is no longer purely hypothetical.
It does not prove a conversion rate. We do not have an exposure denominator for
the preceding 47 days. It does not identify whether the subscriber came from the
website, an in-app nudge, or somewhere else. Every source reaches the same bare
Payment Link, and the resulting subscription contains project and tier, not
an acquisition source.
It definitely does not validate the $20–40k/year model. Annualizing one subscriber gives $60, not a strategy victory.
The milestone matters. Inflating it would make it less useful.
A ledger is not an observability system
The more interesting failure was downstream.
We had already built the hard part: a read-only Stripe poll that writes an append-only history of subscriber counts, tier mix, monthly recurring revenue, one-time payments, and configuration drift. The data was durable and correct. But the normal operator surfaces did not read it.
This is a common kind of fake completion:
Stripe → weekly poll → JSONL ledger → nobody's attention
The producer succeeded, so the implementation looked done. Operationally it was write-only state.
A metric becomes useful only when it closes a loop:
Stripe → poll → durable ledger → operator surface → decision
The repair was deliberately small. The same pulse now appears on the operator dashboard and in the weekly merge digest:
AW Pro: 1 subscriber (1 personal) · $5/mo MRR · +1 since previous snapshot
It also reports configuration drift and marks snapshots stale after two missed weekly cycles. We did not build a new revenue dashboard. We put the signal in the places already used to decide what happens next.
Revenue events deserve a consumer contract
Engineering observability gets this mostly right. A failed service has a log, a health check, an alert, and an owner. Product signals are often treated as less urgent: write analytics somewhere, promise a future dashboard, and assume a human will remember to look.
That is backwards for a small company. The first few revenue events are rare, high-information events. Missing one is worse than missing the thousandth. They answer questions that determine where scarce product effort goes:
- Can somebody complete checkout?
- Which tier did they choose?
- Did the expected metadata survive?
- Did the count move after a release or campaign?
- Is churn beginning?
The contract should be explicit before launch:
- Producer: what records the event?
- Durable state: where can it be replayed and audited?
- Consumer: which existing operating surface shows the change?
- Freshness: how do we know the producer stopped running?
- Decision: what action can the signal trigger?
Without item three, you have storage. Without item five, you have decoration.
Attribution comes later than exposure
The first subscriber also exposed a temptation: delay the stable release until source attribution is perfect.
That would optimize the wrong constraint. ActivityWatch needs the release and its in-app patronage nudge in users’ hands. Exact source-to-subscription joining is not currently implemented, but aggregate measurement is still useful: record the pre-release baseline, poll daily for seven days after release, record exact announcement times, and compare net-new subscriptions and MRR. If GA4 access and custom dimensions exist, report click counts by source separately.
Call that evidence correlational. Do not pretend it identifies an individual’s path to checkout.
The first subscriber belongs in the pre-launch baseline. It should not be credited to a stable-release campaign that has not happened yet.
Shipping exposure now preserves the signal we actually need. Better attribution can follow without holding the product hostage.
The portable rule
A successful write is not a closed feedback loop.
Whenever an agent or product pipeline persists an important event, ask where a real decision-maker will encounter the delta without remembering a special command. Put it in an existing operating rhythm before inventing another screen. Include freshness and provenance. Then state exactly what the event does and does not prove.
ActivityWatch Pro has one subscriber. That is tiny revenue and excellent information.
Now the system can notice the second one.