Commit Graph

11 Commits

Author SHA1 Message Date
0b128f6639 nook: claim lifecycle sends so concurrent runs cannot double-mail
The lifecycle job marked rows with an unconditional UPDATE before
sending, so two overlapping invocations (cron tick + manual hit, or
retry) both passed the SELECT, both ran the UPDATE — re-writing the
same value — and both sent the same email. Real duplicates went out to
licensees. Rows are now claimed with a conditional UPDATE (... AND
asked_at IS NULL), and the send happens only when the claim reports
rowsAffected 1; a racing invocation claims nothing and sends nothing.

Tests: lifecycle suite gains a lost-claim case; 36 nook API tests pass.

Issue: FRE-11
2026-10-05 09:38:48 -04:00
f66fd43f62 nook: capture trial email and run the two lifecycle emails
Trials were anonymous — a 14-day trial you cannot talk to. trials gains
an email column (idempotent ALTER), POST /api/the-nook/trial-email
attaches the optional first-launch prompt's answer, and the address
lands in a Brevo list when NOOK_TRIAL_LIST_ID is set. The lifecycle
emails follow: a D+11 trial expiry reminder with a buy link, a purchase
thank-you from the Stripe webhook, and scheduled testimonial (D+3) and
review (D+14) asks via the daily /api/the-nook/lifecycle cron. Rows are
marked before sending so retries never double-send; trial addresses
that already bought get no buy reminder; the endpoint requires a
Bearer secret (CRON_SECRET / NOOK_LIFECYCLE_SECRET) and fails closed
when unconfigured — an open trigger would email real users. All sends
are best-effort and logged, never thrown.

Tests: trial-email (3) and lifecycle (6) suites; full run 539 pass.

Issues: FRE-10, FRE-11
2026-10-04 23:22:24 -04:00
421fb946fc nook: add /agents, /changelog and /faq
Three organic surfaces, each answering an objection at the moment it
bites. /agents lists all twelve agents with each hook's exact event
surface (mirrors AgentSpec.all + hook/setup.go + the plugins) and what
a card can do — allow/deny or observe. /changelog renders the appcast
HTML the Sparkle updater already publishes, so release notes have one
source of truth. /faq answers privacy (scoped honestly: no prompt or
code content leaves the device), the notch question, battery,
permissions, the macOS 14 floor and offline use — no refund question,
per decision: the trial is the answer. Nav gains the three links.

Tests: appcast parser suite (5 tests) covers CDATA, escaped HTML,
Version-prefix titles and garbage input.

Issues: FRE-12, FRE-13, FRE-14
2026-10-04 23:22:09 -04:00
96e401ecbb nook marketing - utm, analytics, etc 2026-10-03 11:03:26 -04:00
fd68efdb78 nook bug reporting handler 2026-09-01 17:44:20 -04:00
cb584f8b68 license resend, license pretified 2026-08-27 23:03:33 -04:00
8c5fe25c2b fix email trigger 2026-08-27 20:42:17 -04:00
a161c96b05 nook: surface per-license maxDevices in activate + status responses 2026-08-27 09:26:14 -04:00
082e503d4f nook: per-license device caps + grant CLI for free licenses 2026-08-27 09:16:31 -04:00
0031055315 nook: fix idempotent reactivation SQL bind (row object -> id) 2026-08-27 08:55:51 -04:00
e4a9be4899 prep for the nook 2026-08-26 17:41:57 -04:00