Self-running daily reading and reflection app (two versions)
A daily reading and reflection app that assembles each day before anyone wakes, never shows half-built content, degrades visibly on failure, is designed around free-tier allowances, and was rebuilt when the first platform could no longer carry it.
Problem
A small private group of readers wanted a short daily reading, a written reflection and an audio version waiting every morning, with nobody paying for hosting or doing anything by hand. The site that publishes each day's reading schedule has no data API, and on some mornings a plain request is refused. Partway through, the preferred hosted AI provider began refusing requests without warning. With a single failure path, any of this meant a blank day.
Goal
Assemble the day automatically before anyone wakes, never show wrong or half-built content, degrade visibly when something fails, design around free-tier compute, AI and storage allowances, and decide honestly when the first platform could no longer carry the app.
My role
Product owner and builder of version one, working with an AI coding assistant, including independent model reviews of the codebase and the decision record that led to the rebuild. Sole designer and engineer of version two: architecture, fetching and parsing, AI integration, audio pipeline, security model, test suite and gated deployment.
Solution
Version one ran on a serverless script platform with a spreadsheet as its data store, a custom signed-token login and routed question answering. Version two is a serverless edge application whose scheduled jobs build the day in stages, with a separate fallback chain at every fragile point (fetching, AI generation and audio), strict all-or-nothing rules for the essentials, and a private front end behind a shared access code. Longer fixed audio is recorded ahead of time by local tools and kept in object storage.
How it works
Version one. A nightly job fetched the day's schedule page with a browser-like client and backoff, fell back to the publisher's feed when a request was refused, parsed irregular multi-part reference strings into structured references, fetched the texts from an open API, and asked a language model for a short reflection under strict length and tone rules while avoiding recently used stories. Every external call was counted against configured limits. The written decision record compared staying put against four families of platforms, using two independent AI reviews of the real code, and chose an edge worker with a small SQL database for real local testing, one-step deploys and scheduled triggers.
Version two.
A few scheduled runs fire each day. The first builds the day; later runs are cheap retries that fill in only what failed.
The schedule is found through three isolated routes in order: a plain request, a headless-browser fetch that renders the publisher's public daily page, and a syndication feed that publishes later. A route succeeds only if its output parses into a valid structure. Parsers are pure functions tested against saved real pages.
The reflection comes from a model ladder for provider redundancy: the preferred hosted model, then a second provider, then an open-weights model hosted on the edge platform itself, then a small model from a third hosted provider as a last resort, so one provider's outage never leaves the day without a reflection. A rung without credentials is skipped, and a refusal that a retry cannot change is not retried. The whole ladder runs inside one time budget: every call has a deadline, a retry that could not finish in time is not started, and each failure is logged with a named reason.
The essentials are all-or-nothing. If any of them fails, no record is written, and the page shows the most recent complete day under a clear notice.
Everything else degrades on its own: secondary readings, a curated short story matched to the day's theme, a screened current-events link threaded across the week, and audio synthesised at the edge within a character budget sized to the free daily allowance, stored as a real audio track that plays from a lock screen.
A reader for the full reference text offers every chapter as a pre-recorded track. Synthesising that much speech on the platform would have used up its free daily allowance for months, so a local tool records it once through the speech provider's own API: chunks break at sentence ends and are joined as raw audio and encoded once, the text comes from the same cache the reader renders from, chapters already stored are skipped, and a spending ceiling stops a run before a chapter that would cross it. The tracks live in object storage, and each address carries a hash of the stored bytes, so a re-recorded chapter is never hidden behind a stale browser cache.
A guided recitation screen walks a fixed call-and-response sequence one step at a time, remembers the place on the device, and lets the household switch optional additions on or off, checked on the server. A two-voice player leads and answers, and is offered only when every piece of the chosen sequence is recorded in both voices.
Built and tested but not yet live: text sent to the voice is first rewritten so references, dates and numerals are spoken the way a reader would say them, from an editable pronunciation list in which every entry must state its reason.
Access is a shared code that mints a signed bearer token, with constant-time comparisons and brute-force limits that group IPv6 addresses by their routed prefix, so one network cannot spread attempts across many addresses.
Technical challenges
Fetching from a source with no API that intermittently refuses plain requests, without ever showing wrong content.
Redesigning the AI step around a ladder that depends on no single provider after the preferred provider began refusing requests.
A locale-data quirk in month abbreviations that would silently have disabled the feed fallback for a whole month.
HTML entity edge cases that silently dropped a section of the page.
Fitting text-to-speech into a free daily budget by measuring real content lengths.
Stopping a model from inventing plausible but unsupported details.
A default connection timeout shorter than a slow host's handshake, which looked exactly like rate limiting and cost an unattended recording run many chapters, fixed with a longer connection limit, retries with a growing wait and a deadline on every outward call.
Fitting a complete spoken library into free storage by measuring bytes per character and choosing a compact audio format, then detecting the older devices that cannot play it and telling them plainly instead of showing a button that fails.
In version one: no local testing on the platform, and deployed code that had drifted from the repository because deploys bypassed version control, which became evidence for the migration.
Capabilities
- Automated daily content pipeline
- Three-route fetching with a last-known-good fallback
- Multi-provider language model ladder
- Structured reference parsing
- Screened, threaded current-events links
- Curated story matching
- Edge text-to-speech with a budget cap
- Grounded question answering from an approved reference set
- Private access-code login
- Usage view against free allowances
- Pre-recorded audio library in object storage
- Guided two-voice call-and-response player
- Light and dark themes
- No third-party requests on page load
- A documented, reviewed platform migration
Technology
Impact
Version one ran daily at zero hosting cost and produced the evidence and written reasoning for the rebuild. Version two delivers the day unattended, survives refused requests and provider outages without showing wrong content. It also serves every chapter of its reference text as pre-recorded audio, each file checked end to end after recording, using a small share of the free storage allowance.
Skills demonstrated
- End-to-end product delivery
- Resilience engineering and graceful degradation
- Robust parsing of irregular markup
- Failover across language model providers
- Prompt governance
- Serverless edge architecture
- Fixture-based parser testing
- Security hardening
- Designing around quotas
- Cost-bounded batch media generation
- CI/CD with test gates
- Architecture decision records
- Knowing when to re-platform