Work log — 2026-04-04
What shipped today
The biggest session in Prakta’s history — 11 issues closed in a single day, nearly all security hardening and error-handling improvements. The codebase went from having several unvalidated API inputs and unprotected database operations to having consistent input validation, error boundaries, and defensive checks across every route.
The security work was the highlight. The Stripe checkout route was accepting arbitrary price IDs from the client — now it validates against an allowlist of the two known Stripe prices. The checkout redirect URLs were built from a client-controlled origin header — now they use the server-side NEXT_PUBLIC_APP_URL. The Stripe webhook handler was casting customer fields to string without null checks, meaning a malformed Stripe event could silently query with “null” as the customer ID. The @anthropic-ai/sdk was upgraded from 0.80 to 0.82 to patch a sandbox escape vulnerability. The onboarding route was accepting unvalidated message arrays and passing them to the Claude API without catching failures.
Beyond security, the serve route got two performance improvements: redundant Supabase client creation was consolidated into a single instance per request, and the grain-chain and prereq queries were parallelized with Promise.all. The config PATCH route now filters against a 7-field allowlist so learning-model-only parameters can’t be overwritten via the API. Error handling was added to the task CRUD route (update, start, delete) and the defer route. The energy checkin’s radio buttons now report correct aria-checked state to screen readers. And the terms, privacy, and blog pages were unblocked from the auth middleware — they were behind login because PUBLIC_ROUTES didn’t include them.
Two scout runs identified all of this work. The first swept error-handling, security, dead-code, performance, feature-gaps, dependency-health, and accessibility. The second focused on the public site (finding the terms/privacy auth issue) and remaining route-level gaps. Zero dead code was found — clean codebase.
Also updated CLAUDE.md and DECISIONS.md: Vercel auto-deploy is disabled — push to main normally, deploy manually with vercel --prod --yes to control costs.
Completed
- #235 — Validate config PATCH updates against allowlist
- #237 — Reduce redundant Supabase client creation in serve route
- #238 — Validate Stripe priceId against allowlist in checkout route
- #239 — Upgrade @anthropic-ai/sdk to fix sandbox escape vulnerability
- #240 — Add try-catch to task CRUD operations in tasks/[id]/route.ts
- #241 — Parallelize sequential queries in serve route
- #242 — Fix hardcoded aria-checked={false} in energy checkin radio buttons
- #243 — Make terms and privacy pages publicly accessible
- #244 — Add try-catch to deferTask in defer route
- #245 — Validate Stripe webhook customer_id fields before use
- #246 — Validate onboarding messages array before passing to Claude API
Release progress
- March 2026 milestone closed (was empty — 0 issues attached, vestigial).
Carry-over
- #247 (needs-clarification) — Show annual pricing on homepage. Waiting on Paul’s input: display format, price confirmation, trial messaging on homepage.
- #86 (backlog) — Replace in-memory rate limiter with persistent solution for production
- #101 (blocked) — Branded transactional emails (requires Supabase dashboard work)
- #108 (backlog) — BCC founder on all transactional emails
Risks
- Rate limiter (#86) is still in-memory. Fine for dev/low-traffic, will silently fail under multi-instance Vercel deployment. Should be elevated before any meaningful user traffic.
- No test suite exists. All changes were type-checked and self-reviewed but there are zero automated tests. Every issue closed today was verified by
tsc --noEmitonly.
Flags and watch-outs
supabase/.gitignore,supabase/config.toml, andweb/.gitignoreremain untracked — still waiting on a decision to commit or .gitignore them.- Several screenshot files from the public site scout are sitting in the repo root (homepage.png, login.png, etc.) — these are ephemeral and should be deleted or gitignored.
- Vercel auto-deploy is now disabled. Deployments require manual
vercel --prod --yesfrom monorepo root.
Next session
- Answer #247 (annual pricing) — Paul needs to confirm price and display format
- Consider elevating #86 (rate limiter) from backlog if launch is approaching
- Run another
/scout— the queue is empty, need fresh work - Consider adding a basic test suite — 11 issues shipped with zero test coverage is unsustainable
Why customer tools are organized wrong
This article reveals a fundamental flaw in how customer support tools are designed—organizing by interaction type instead of by customer—and explains why this fragmentation wastes time and obscures the full picture you need to help users effectively.
Infrastructure shapes thought
The tools you build determine what kinds of thinking become possible. On infrastructure, friction, and building deliberately for thought rather than just throughput.
Server-side dashboard architecture: Why moving data fetching off the browser changes everything
How choosing server-side rendering solved security, CORS, and credential management problems I didn't know I had.
The work of being available now
A book on AI, judgment, and staying human at work.
The practice of work in progress
Practical essays on how work actually gets done.
The bottleneck moved and nobody noticed
When execution becomes nearly free, the bottleneck shifts from doing the work to deciding what work to do. Most organizations are optimized for the wrong constraint.
The inbox nobody reads is the one that matters
Every organization has a monitoring system that works perfectly and reports to nobody. The gap between having information and acting on it is where most failures actually live.
The best customers are the first ones you turn against
Every subscription makes a bet that most customers won't use what they're paying for. The customer who closes that gap becomes a problem to be managed.