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:
- Three save paths into one pipeline: a browser extension (Chrome + Firefox, one source tree), a bookmarklet, and direct save in the web app.
- Ingestion and summarization: fetched content is extracted (
@mozilla/readability) and summarized through a provider-agnostic LLM layer. - The “Dream Job”: a nightly Cron Trigger (and an on-demand premium trigger) that embeds new items, clusters related ones by embedding similarity, writes a bullet-point synthesis per cluster, and ranks results by salience — including subtopic splitting, cluster merging, and reinforcement of existing Topics as new related items arrive.
- The Digest: the ranked-Topics home screen.
- Export: a ZIP of everything a user owns in plain text/CSV, always free — deliberately not a paid lever.
- Community Topics: opt-in curators can publish individual Topics into a shared, anonymous pool that other users see as suggestions — one-way, editorial, explicitly not a social/follow layer.
- Quality feedback loops: per-topic ratings (was the synthesis good?) and manual corrections (was the clustering wrong — should this have merged or split?) — both write-only at runtime, meant to be read by hand rather than feed an automatic retraining loop.
- Auth: self-built passwordless magic-link sign-in plus Google OAuth, both routed through the same invite/registration gate.
- Bilingual: English and Portuguese, in both interface and generated content.
- Backstage: a separate admin app for user/tier management, cost dashboards, and a git-backed CMS (Sveltia, committing straight to GitHub) for every piece of editable copy in the product — a copy or email-template edit in Backstage is a live deploy about two minutes later.
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:
- Backend (
server/): Hono on Cloudflare Workers, D1 (SQLite) for relational data, Vectorize for embedding similarity search, Queues for async ingestion, Cron Triggers for the nightly Dream Job. Content extraction via@mozilla/readability+linkedom. - LLM providers: Google Gemini (native) and OpenRouter (everything else), dispatched behind one internal abstraction — a mock provider exists for offline dev.
- Web app (
web/): React 19 + React Router 8, built with Vite 8, linted with oxlint. Installable PWA with a service-worker app-shell cache. - Admin app (
admin/, “Backstage”): React 19 + Vite, embeds Sveltia CMS as a git-backed content editor. - Extension (
extension/): Manifest V3, one source tree, two manifests (Chrome/Firefox). - Hosting: Cloudflare end to end — Workers (API), Pages ×2 (web app, admin), D1, Vectorize, Queues.
- Mail: Resend, for magic links and transactional email.
- CI/CD: GitHub Actions — three path-filtered deploy workflows (web/admin/worker) that push to Cloudflare on merge to
main, plus a gitleaks secret-scan workflow on every push and PR. - No test suite. Confirmed in the repo’s own contributor guide (“There are no automated tests”) and no test runner/config found in any
package.json. Correctness is enforced by a CI syntax check, a CORS-completeness script, and manual verification — a real, stated gap, not hidden.
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.