Owly Post
About Owly PostHow it was madePhase reports

Phase 3: polish and onboarding

What phase 3 delivered, with acceptance evidence, deviations and open items

Status: ✅ complete. Phase 4 started per Esmee's instruction (review items collected, not blocking).

What was built

  • Design system: Tailwind 4, warm stone/amber owl brand, shared component set (components/ui.tsx); owl personality confined to empty states, error states and the digest header; billing and error-recovery copy stays professional. Auth/billing/legal pages restyled without content changes.
  • Onboarding wizard (/onboarding, SPEC §6): step 1 sources (starter-bundle cards, add-by-URL via core feed detection, OPML import — the wizard was rebuilt to five steps in July and OPML dropped in D-33), step 2 rhythm (day/time, default Sunday 17:00, tenant timezone), step 3 instant first digest with a patient pending state. Home redirects new tenants (zero sources) into the wizard. Bundle selection also seeds the preference profile with the bundle's editorial direction.
  • Starter bundles (lib/onboarding/starter-bundles.ts, [REVIEW]): 5 bundles × 6 feeds, every URL fetched and verified as live RSS/Atom on 2026-07-08; rejected candidates documented (First Round: no public feed; Paul Graham: HTML not RSS; Andrew Chen & Brand New: stale; Designer News: HTTP 500; De Correspondent: members-only; Stratechery: paywalled).
  • Dashboard: home (lifecycle banners, stats, next digest, run-now), sources management (grouped per category, pause/resume/delete, add/import), digest archive + web view (narratives, verdict badges, skips collapsed, 👍/👎 per item and section — "More/Less like this"), settings (profile editor, schedule, billing, sign-out).
  • Email path: digest mail wired end-to-end (Resend → owner's verified account email, sender owl@owlypost.com overridable via env). Delivery itself is intentionally unverified until the Phase 4 domain verification — a mail failure never fails a digest run.
  • Legal: /terms + /privacy drafts with all mandated clauses ([REVIEW] — drafts only; Esmee reviewed and adjusted both before go-live).
  • Core perf contribution (public repo): summarizePendingItems now runs a worker pool (concurrency 16, was sequential) — a backlogged first ingest dropped from ~9 minutes to ~1–2. Reverted a day later with the rest of the Phase 0 core work (P4-6): the cloud does not get to optimise the open source project for its own onboarding metric. Accepted consequence — the first digest went back to ~9 minutes until the core lands the change on its own merits.

Acceptance evidence (SPEC §12, Phase 3)

Run end-to-end in the browser as the Phase 2 e2e user (fresh tenant, zero sources):

StepEvidence
Wizard renders + bundle applyDesign bundle → 6 sources, category, profile seeded (638 chars) in one click
ScheduleSaved (Sunday 17:00 Europe/Amsterdam)
Instant first digestReal run: 125 items ingested from 6 live feeds, 100 summarized (cap), digest ready via claude-sonnet-4-6 (16k in / 5k out)
Digest web viewRenders sections with genuinely editorial narratives (real NN/g/Smashing/CSS-Tricks content), verdict badges, collapsed skips
Feedback loopSection 👍 clicked in UI → section_feedback row (Design, up)
TimingWizard interaction itself takes well under a minute; the first digest runs behind the patient pending state while ingest and summaries complete.

Deviations → DECISIONS_LOG P3-1…P3-4

[REVIEW] items touched

  • Starter-bundle contents + profile texts (SPEC §14.4) — lib/onboarding/starter-bundles.ts.
  • /terms + /privacy wording incl. price-change clause (SPEC §14.5) — placeholders flagged inline: contact email mail@owlypost.com, 18+ requirement, invoice-retention note, SCC mention.
  • Trial LLM budget headroom: the heavy first digest cost ~41 cents against the 75-cent trial budget ([REVIEW] §5 numbers) — a second manual digest in the trial would flirt with the cap. Consider raising trial_overrides.monthly_llm_budget_cents (e.g. 150) or accepting the block message.

Open items

  • Email delivery verification (needs owlypost.com DNS + Resend verification — Phase 4).
  • Feedback can be flipped 👍↔👎 but not cleared (the core's feedback write has no delete — minor UX gap; a core change is the maintainer's call, not the cloud's).
  • runDigestNow links to /digests rather than deep-linking the fresh digest (needs a small return-value change).

On this page