SkinAtlas, part 1: explainability over magic
SkinAtlas is a personal atlas of your skin: your products, their ingredients, your routines, and a daily journal, connected into one model that can answer questions like “which product correlates with the bad weeks.” The founding rule is written into every design doc: explainability over magic. Every benefit, risk, and conflict the app shows traces back to a concrete record in a hand-curated ingredient knowledge base. Nothing is a black-box model opinion, including the AI copilot, which is only allowed to answer from your own data and cites the records it used.
It’s the youngest of my projects — first commit June 21 — and the fastest moving: 762 commits in under two weeks, through five roadmap phases. Paste an ingredient list and get an analysis; build AM/PM routines and get conflict detection; keep a skin journal and get correlations; photograph your skin and get a vision-model read, grounded in the same knowledge base.

The discover page. Every card on it traces back to an ingredient record.
Boring choices, on purpose
The architecture decisions are recorded as ADRs, and most of them choose the
boring option. SQLite (via Turso) instead of Postgres, which forces every
list field to be serialized JSON and produced a small in-house rule: all
serialization goes through one helper module, no raw JSON.parse in feature
code. Hand-written CSS, no UI framework. Anthropic models as the single
intelligence layer, with one chokepoint file so cost and behavior are tunable
in one place — the skin analyzer runs on a mid-tier model after a commit
titled “swap Opus → Sonnet” made the cost math work.
One decision was legal rather than technical: the obvious data source for ingredient information is a popular ingredient-decoding site, and SkinAtlas deliberately never scrapes it. The decision doc names the terms-of-service risk to a future paid tier and lists the alternatives that got built instead — open product databases, the EU’s 24,000-entry cosmetic ingredient registry, retailer pages verified case by case.
The outage that improved the build
Three days after launch, production went down. Not one cause — three, stacked: a missing auth env var, a hosting quota, and a database schema that had never been migrated for the new auth tables. Each was invisible locally because dev-mode fallbacks masked them. The fix that mattered wasn’t any of the three patches; it was a build-time schema-drift check that now fails the deploy if the live database doesn’t match the code’s expectations. Fail at build, not at 2am.
Parts 2 and 3: how the knowledge base grows by a hundred ingredients a night, and the memory system that lets a new session pick up where the last one died.