← Back to site

PPRT (Point Perfect)

1. What it is

A read-later tool for people who save articles and never go back to them. Instead of an inbox of unread links sorted by recency, the home screen is a ranked digest of Topics — AI-synthesised clusters of the things you’ve saved that are actually about the same thing, each with a short synthesis of what you’d learn from reading all of them together. (README.md, docs/PPRT_PRD.md §1)

2. Why I built it

Stated directly in the product’s own spec: existing read-later tools (Instapaper, Readwise Reader, Karakeep) are organized around the saved item as the primary object, so you get a growing pile you meant to read and mostly don’t. PPRT’s bet is that inverting the primary object — from “item” to “synthesized topic” — produces something people actually return to. The project is explicitly framed as a validation exercise, not a launch: “Stage: Pre-validation. This is not a funded, scaled product — it’s a solo-built prototype (~15 hrs/week) meant to be shown to a small friends-and-family cohort, iterated on, and killed or continued based on real usage, not a business plan.” (docs/PPRT_PRD.md §1, header)

3. What it does

Based on the README’s own built-vs-not-built accounting and corroborated in code:

Explicitly not built, by decision (not oversight — see docs/ROADMAP.md): billing/Stripe integration, a total daily spend ceiling across all users, member-gets-member, and the Safari/iOS entry points.

4. How it’s built

Pulled directly from package.json files and wrangler.toml, not assumed:

5. Notable decisions

Clustering had to be complete-linkage, and the repo keeps the failure evidence. Two simpler approaches were tried on a real 40-item batch and both collapsed the library into one giant cluster: a centroid representation drifted toward generic shared signal (a 3-item centroid scored 0.59 similarity against a totally unrelated article), and single-linkage chaining transitively merged unrelated items through a shared neighbor (≈19% of all pairs in that batch exceeded the 0.55 threshold — enough edge density to collapse everything). The code comment is blunt: “Do not revert this. It failed twice on real data.” A specific, measured tradeoff, not a textbook clustering choice.

Every LLM call is forced through one function, and the model is data, not code. server/src/llm/index.js is the sole entry point for six task types (summarize, embed, consolidate, classify, tag, format-title); which model handles each is read from a model_config D1 table at call time, so swapping a model is an admin API call, never a redeploy. This is enforced as a hard requirement in the product spec, not a convention — and it’s the first rule listed in the repo’s own contributor guide.

Freemium gating shipped as two independent axes, not one column, specifically to protect a promise made to early users. From 2026-08-20, the first real users are promoted to premium by hand in Backstage on the explicit promise they never pay, including after commercial launch. A single tier field can’t distinguish a comped founder from a paying subscriber once both exist — so the schema splits tier (what an account may do) from subscription_status (whether money is involved), and the code path that grants premium in Backstage deliberately never touches subscription status, so a hand-edit can’t accidentally make the subscriber count untrustworthy.

The invite gate is a toggle, not a rewrite, when it’s time to open up. Registration was invite-only through most of the build; opening it (once Consolidate/Bulk Import were server-side tier-gated so cost was bounded a different way) was a single environment variable read in one place and honored by all three routes that touch account creation, with invites still fully functional underneath — not required, but still looked up, still attributed, still spendable — so the gate can be closed again with zero data loss.

6. Built with Claude Code

This is directly attributable from git history, not inferred. The repo has 413 commits from 2026-05-09 to 2026-08-22 (~3.5 months), effectively solo-authored (399 commits under one name, 14 under a placeholder test identity from early scaffolding). 311 of those 413 commits — about 75% — carry a Co-Authored-By: Claude trailer (188 Opus 5, 119 Sonnet 5, 4 Fable 5), meaning the commit messages themselves record Claude Code as a contributor on three out of every four commits across the project’s life, spanning the full range: the initial Cloudflare migration, the clustering algorithm and its two documented failed attempts, the freemium/tier system, the CMS integration, and ongoing production incident fixes.

The product’s own spec names the tool directly: docs/PPRT_PRD.md’s header states “Prototyping target: Claude Code (Fable 5) for scaffolding and implementation.” — this wasn’t retrofitted for a portfolio narrative; it’s baked into the project’s working documents from the start.

What’s distinctive about the workflow, visible in the repo itself: extensive prose comments throughout the codebase that record why a decision was made and what alternative was tried and rejected, a docs/HISTORY.md file kept explicitly as a reversed-decisions audit trail, and a CLAUDE.md repo-guide file whose stated purpose is routing an AI collaborator to the right document before it starts working — i.e., the project was structured, as it was built, to be legible to an AI agent picking up context cold in a new session. Not clear from the repo: the precise division of “which lines Eduardo typed vs. Claude typed” within any given commit — the co-authorship trailer marks collaboration, not authorship percentage.

7. Current state

Live and working today: save via extension/bookmarklet/web, ingestion and summarization, the Dream Job (clustering, subtopics, merging, auto-split), the Topic digest, two-format export, Community Topics, synthesis ratings, manual topic corrections, magic-link and Google sign-in, bulk import, Backstage, and the git-backed CMS for all editable copy.

GitHub milestones (checked live): “Friends and Family” — 13/13 issues closed, 0 open. “Public Launch” — 10 closed, 8 open (open items include Firefox add-on submission, email notifications on account closure, and cost-per-user measurement).

Deliberately not built: billing/Stripe integration and paid tiers (freemium exists structurally — a tier field every route already checks — but nothing is currently charged), a total cross-user daily spend ceiling, member-gets-member referrals, Safari/iOS.

Usage numbers: not clear from the repo. No analytics integration (PostHog, GA, Mixpanel, Plausible, etc.) was found anywhere in the codebase. The product’s own success metrics are explicitly qualitative and manual — “does a tester describe a Topic synthesis as something they wouldn’t have put together themselves? Test for it directly by asking.” Per-call LLM cost is logged (~$0.0106/saved item), but aggregate usage/user-count reporting is an open, unbuilt issue. Registration is currently open to anyone, but the repo contains no record of how many people have signed up.

8. One-line summary

A solo-built, Cloudflare-hosted read-later app that uses AI to turn your saved links into synthesized topics instead of an unread pile — built hands-on with Claude Code across 400+ commits, three-quarters of them AI-co-authored.