Work · Mobile app · Consumer app founder · via agency partner
Newborn-care companion app
A folder of flat mockups, a store deadline, and 3am logging on bad wifi
A cross-platform infant-care tracking app built from a folder of flat client mockups — turned into a token-driven design system, then a shipping mobile application with an offline-first logging path, a mascot-driven guidance engine, and multi-caregiver data isolation proven by tests.
- Expo
- React Native
- Supabase
- TypeScript
- TanStack Query
- Role · sole designer & builder
- 8 min
- route screens across six flows
- 31Scope
- row-level-security policies, proven by a test suite
- 27Scope
- client assets recovered from flat mockups
- ~60Scope
- System size
- System flow · 10 stages · 40 components reconstructed from the build record
The situation
The client had an idea for an infant-care tracking app and a folder of designs. The designs were flat images. No measurable values, no tokens, nothing a developer could build from without guessing. Seven near-identical background creams and five different navigation bars sat inside the same set.
Underneath that was a harder problem than the visuals. The core use case is a parent logging a feed at 3am, one-handed, in the dark, on bad wifi. The chosen backend has no offline sync of its own. So the offline strategy had to be settled before the schema was written, because the primary-key strategy is the schema.
There was also a store submission deadline, and the app did not exist yet. I came in through an agency partner to turn the folder into a shipping mobile application.
What was broken
- Designs arrived as flat images with no measurable values, so every spacing, colour and radius would have been a guess.
- Seven near-identical background creams and five different navigation bars meant the same element rendered differently on different screens.
- Three tab destinations in the delivered designs pointed nowhere at all.
- The client's own artwork existed only embedded inside PDFs and mockups, so a rebuilt logo would have been an approximation rather than their logo.
- The backend offered no offline path, and the highest-traffic action in the product happens on the worst connection a parent will ever have.
- Two store-compliance gaps were absent from the designs and the locked stack, and either would have been a submission rejection.
- Store credential lead times sat on the critical path with nothing tracking them.
Constraints
- The app is multi-caregiver, so one household's data must never be reachable by another, and the client is untrusted by definition.
- The chosen backend has no offline sync, so durability had to be built on the device.
- A notification on a lock screen is readable by anyone holding the phone, which caps what a nudge may say.
- Store enrolment lead time gated the submission date, and no amount of engineering shortens it.
- Push delivery and the AI chat both depended on client-side accounts and approvals outside my control.
- The design system had to be derived by measuring the client's own files, because there was no other source.
FIG 01 · System Flow
How the system actually runs
Every component below is reconstructed from the project’s own build records. Nothing is added to make it look bigger.
- components
- 40
- components
- stages
- 10
- stages
- decisions & guards
- 11
- decisions & guards
- AI
- 1
- AI
- human handoffs
- 0
- human handoffs
- platforms
- 5
- platforms
- Stage 1, User action: The screens, the timers, and the tap that starts a write.
- Log screens (Capture, React Native). The logging routes — bottle, nursing, pumping, solids, diaper, sleep, nap, fussy timer and bedtime routine — inside a five-tab shell. Connects to Validate before queueing when save tapped.
- Route tree (Source, Expo Router). Route groups for auth, onboarding and the tab shell, plus stacks for logging, history, settings, babies, foods, growth, milestones and the mascot. Connects to Log screens when caregiver navigates.
- Live session timers (Trigger, React Native). Nursing, pumping and nap sessions run live timers that survive closing the app. Connects to Validate before queueing when session ended.
- Stage 2, Local state: Validation, the client-generated id, the optimistic row and the encrypted cache.
- Validate before queueing (Guard, TypeScript). The payload is parsed against its per-type schema before it can reach the queue. Connects to Client-generated row id when payload parses; Log screens when invalid · refused before queueing.
- Client-generated row id (Data store, Expo Crypto). The row’s primary key is generated on the device at save time, not by the server. Connects to Optimistic pending row when row marked pending; Enqueue job.
- Optimistic pending row (Data store, TanStack Query). The row is written into the query cache immediately with a pending marker, before any network call. Connects to Encrypted read cache when throttled persist; Today view when on screen immediately; Six-state mascot when log saved → celebrating.
- Encrypted read cache (Data store, AES + secure storage). The query cache is persisted as ciphertext with the key in secure storage, on a seven-day trust window and throttled writes; removal drops the ciphertext and the key. Connects to Today view when warm start from disk.
- Split persistence rule (Guard, Storage policy). Pending writes live in plain storage; only the long-lived read cache is encrypted. Connects to One queue, five lanes when pending writes · plaintext; Encrypted read cache when long-lived cache · encrypted.
- Stage 3, Offline outbox: The durable FIFO queue, its drain, its verdicts and its failure tray.
- Enqueue job (Trigger, Async storage). A save appends a typed job to a per-user FIFO queue with a queue-local id, a timestamp and an attempt count. Connects to One queue, five lanes when FIFO append.
- One queue, five lanes (Data store, Async storage). Five job kinds share one queue — event create, event update, event delete, milestone create, measurement create. Connects to FIFO drain when fire-and-forget after every save.
- FIFO drain (Scheduled job, React Native). Walks the queue in order, calls the sender for each lane, and stops the pass at any job it cannot move rather than stepping over it. Connects to One connectivity source when asks the synchronous check; Duplicate-ignoring write when per job, in order.
- Couldn’t-be-saved tray (Outcome, Async storage). A job the server will never accept moves to a visible tray instead of being dropped; History shows a row and the parent chooses Retry or let it go. Connects to One queue, five lanes when parent taps Retry; Today view when a row, never an error code.
- Post-drain invalidation (Scheduled job, TanStack Query). Invalidates the user’s cached scope only if the queue actually moved, turning the optimistic pending row into the server’s own row. Connects to Today view when pending marker → server row.
- Stage 4, Network edge: One source of truth about connectivity, and the reconnect edge that wakes the drain.
- One connectivity source (API / service, React Native). One subscription publishing a synchronous last-known online check, a subscribe hook for the banner, and a reconnect edge. Connects to Reconnect edge trigger when offline → online.
- Reconnect edge trigger (Trigger, React Native). Fires only on the offline-to-online transition, not on every network event. Connects to FIFO drain when network came back.
- Stage 5, Backend write: How a queued job reaches the backend, and how it is made replay-safe.
- Duplicate-ignoring write (API / service, Supabase). The send path upserts on the client-generated id with duplicate-ignoring semantics, in the event, milestone and measurement lanes alike. Connects to Three-way verdict when server responds; Row-level security when request reaches the backend.
- Three-way verdict (Decision, TypeScript). A send resolves to ok, transient, or permanent — where permanent means the server will never accept this job. Connects to Couldn’t-be-saved tray when permanent; Post-drain invalidation when ok · queue moved; FIFO drain when transient · job stays put.
- Stage 6, Row-level security: The policies and constraints that decide what a client may touch.
- Row-level security (Guard, Postgres). Policies on every table, expressed through three security-definer predicates: is this user a member of that household, an owner of it, or a member of the household that owns this baby. Connects to Core tables when membership predicate passes; Three-way verdict when refused · permanent.
- Database backstops (Guard, Postgres). Immutable attribution — the caregiver who logged an event cannot be forged on insert or re-attributed on update — plus payload checks, a time-order constraint and immutable columns on the food library. Connects to Row-level security when attribution + constraints.
- Stage 7, Tables: The schema the app and the cloud sweep share.
- Core tables (Data store, Postgres). Profiles, households, role-typed memberships, babies, typed events with a details payload and a time-order constraint, device tokens and guidance content — later foods, plan rules, nudge log, app config, milestones, measurements, invites and chat quota. Connects to Prediction engine when recent events read.
- Household bootstrap (API / service, Postgres function). One server-side function creates the household, the owner membership and the first baby together. Connects to Core tables when household + membership + baby, one call.
- Draft + mirror discipline (Guard, Repo convention). New schema changes start as drafts in a folder the migration push tool cannot reach, each paired with a plain-language mirror document saying what it does and what the app does when it is absent. Connects to Core tables when after sign-off and explicit apply.
- Stage 8, Guidance & nudges: The prediction engine, its two runtimes, and claiming a send exactly once.
- Prediction engine (Scheduled job, TypeScript). Pure functions computing the next feed, nap, diaper and bedtime windows from recent events and a published rule library, taking an optional timezone. Connects to Today view when device runtime; Cron sweep when byte-identical copy, script-synced; Six-state mascot when overdue past 30 minutes.
- Cron sweep (Scheduled job, Edge function + cron). Every fifteen minutes: authenticate the caller, read the kill switch, load the rule library, find babies with an event in the last 72 hours, and run the same engine over fourteen days of events per baby, each in its own try/catch. Connects to Three-condition timezone gate when bedtime prediction; Claim a send once when prediction inside the lead window.
- Two credentials per request (Guard, Edge function + vault). The platform’s JWT gate only proves the caller holds a project key, so the function verifies a second shared secret of its own; the scheduler sends both, pulling its half from the database vault. Connects to Cron sweep when both credentials present.
- Dark-ship kill switch (Guard, App config table). A config flag is read first; anything other than exactly true and the sweep returns skipped before touching a device token, and the push sender refuses again on the same condition. Connects to Cron sweep when flag is exactly true.
- Three-condition timezone gate (Guard, Edge function). Bedtime is promoted to the cloud only when the household’s timezone column exists, holds a non-null value, and this runtime’s internationalization layer accepts it. Connects to Device-scheduled nudges when any condition fails · stays on device.
- Device-scheduled nudges (Scheduled job, React Native). The device half of the pipeline, which keeps bedtime for every household the cloud cannot safely schedule for. Connects to Today view when device-scheduled bedtime.
- Claim a send once (Guard, Postgres unique key). A send is claimed by inserting into a log table with do-nothing-on-conflict, keyed on baby plus kind plus the anchoring event, behind a 90-minute per-baby-per-kind cooldown checked first. Connects to Household push fan-out when claim inserted, not a conflict; Cron sweep when conflict or cooldown · skip.
- Household push fan-out (Message, Push service). Batched push to every household member’s device tokens with low-PII copy — no baby name, no times, no schedule, just the kind. Connects to Today view when caregiver opens the app.
- Guarded notification import (Guard, React Native). One module is the only place the notification library is loaded, and it is loaded dynamically; consumers ask for the namespace and get null where it cannot be loaded. Connects to Household push fan-out when namespace resolves; Device-scheduled nudges when namespace resolves.
- Stage 9, Today view: What the caregiver actually sees — plan, mascot, history.
- Today view (Outcome, React Native). The home surface — a next-events countdown strip, a quick-log grid, the mascot and a rotating tip card, all reacting live to the logs. Connects to History and day view when caregiver navigates.
- Six-state mascot (AI, React Native). A pure six-state resolver — idle, happy, comforting, encouraging, celebrating, sleepy — driven by a log save, an overdue prediction, the night window, and a streak or first log.
- History and day view (Outcome, React Native). A full timeline with filters, a day view over a half-open range so adjacent days never claim the same entry, a naps list, and entry detail, edit and delete. Connects to One queue, five lanes when edit or delete, online or not.
- Stage 10, Identity & proof: Auth, the click-through prototype, the design report and the test layers.
- Auth and onboarding (API / service, Supabase Auth). Welcome, sign-up, sign-in and password-reset screens, then baby profile, photo, a short quiz and a completion screen; a profile row is created on signup by a database trigger. Connects to Household bootstrap when first baby saved.
- Design defect report (Report, Docs). A written report back to the agency partner: the delivered set grew from 25 screens to 73, tokens from 47 to 53, plus defects found by measurement, three tab destinations that pointed nowhere, and two store-compliance gaps closed. Connects to Click-through prototype when measured tokens and screens.
- Click-through prototype (Report, Static web). A deployed, shareable vanilla prototype of the full screen set, treated as the source of truth for screen copy and design tokens. Connects to Route tree when source of truth for copy and tokens.
- Cross-household isolation suite (Report, Node + hosted project). Creates two throwaway users, has one onboard a household and baby, then attempts every cross-household attack a client could make — read another household’s babies, insert an event on their baby, forge attribution, re-attribute an event, join their household, read unpublished content — then cleans up after itself. Connects to Row-level security when proves what the policies deny.
- Unit and parity suites (Report, Jest + Deno). A local gate of lint, unit tests and strict typecheck, plus an export-per-platform boot gate proving the router tree and every import resolve; engine fixtures run under both runtimes and a parity test asserts the two engine copies match. Connects to Prediction engine when fixtures + parity assertion.
- Weekly written updates (Report, Docs). Client-facing updates separating what is built from what is blocked on the client’s own accounts, and listing the specific decisions the build is waiting on. Connects to Auth and onboarding when names credential blockers.
A parent logs a bottle at 03:12 in a basement nursery with no wifi. The connection comes back twenty minutes later.
0/17A parent logs a bottle at 03:12 in a basement nursery with no wifi. The connection comes back twenty minutes later.
Read this system as text
Stage 01
User action
The screens, the timers, and the tap that starts a write.
- Log screens · Capture · React Native — The logging routes — bottle, nursing, pumping, solids, diaper, sleep, nap, fussy timer and bedtime routine — inside a five-tab shell. → Validate before queueing (save tapped)
- Route tree · Source · Expo Router — Route groups for auth, onboarding and the tab shell, plus stacks for logging, history, settings, babies, foods, growth, milestones and the mascot. → Log screens (caregiver navigates)
- Live session timers · Trigger · React Native — Nursing, pumping and nap sessions run live timers that survive closing the app. → Validate before queueing (session ended)
Stage 02
Local state
Validation, the client-generated id, the optimistic row and the encrypted cache.
- Validate before queueing · Guard · TypeScript — The payload is parsed against its per-type schema before it can reach the queue. → Client-generated row id (payload parses), Log screens (invalid · refused before queueing)
- Client-generated row id · Data store · Expo Crypto — The row’s primary key is generated on the device at save time, not by the server. → Optimistic pending row (row marked pending), Enqueue job
- Optimistic pending row · Data store · TanStack Query — The row is written into the query cache immediately with a pending marker, before any network call. → Encrypted read cache (throttled persist), Today view (on screen immediately), Six-state mascot (log saved → celebrating)
- Encrypted read cache · Data store · AES + secure storage — The query cache is persisted as ciphertext with the key in secure storage, on a seven-day trust window and throttled writes; removal drops the ciphertext and the key. → Today view (warm start from disk)
- Split persistence rule · Guard · Storage policy — Pending writes live in plain storage; only the long-lived read cache is encrypted. → One queue, five lanes (pending writes · plaintext), Encrypted read cache (long-lived cache · encrypted)
Stage 03
Offline outbox
The durable FIFO queue, its drain, its verdicts and its failure tray.
- Enqueue job · Trigger · Async storage — A save appends a typed job to a per-user FIFO queue with a queue-local id, a timestamp and an attempt count. → One queue, five lanes (FIFO append)
- One queue, five lanes · Data store · Async storage — Five job kinds share one queue — event create, event update, event delete, milestone create, measurement create. → FIFO drain (fire-and-forget after every save)
- FIFO drain · Scheduled job · React Native — Walks the queue in order, calls the sender for each lane, and stops the pass at any job it cannot move rather than stepping over it. → One connectivity source (asks the synchronous check), Duplicate-ignoring write (per job, in order)
- Couldn’t-be-saved tray · Outcome · Async storage — A job the server will never accept moves to a visible tray instead of being dropped; History shows a row and the parent chooses Retry or let it go. → One queue, five lanes (parent taps Retry), Today view (a row, never an error code)
- Post-drain invalidation · Scheduled job · TanStack Query — Invalidates the user’s cached scope only if the queue actually moved, turning the optimistic pending row into the server’s own row. → Today view (pending marker → server row)
Stage 04
Network edge
One source of truth about connectivity, and the reconnect edge that wakes the drain.
- One connectivity source · API / service · React Native — One subscription publishing a synchronous last-known online check, a subscribe hook for the banner, and a reconnect edge. → Reconnect edge trigger (offline → online)
- Reconnect edge trigger · Trigger · React Native — Fires only on the offline-to-online transition, not on every network event. → FIFO drain (network came back)
Stage 05
Backend write
How a queued job reaches the backend, and how it is made replay-safe.
- Duplicate-ignoring write · API / service · Supabase — The send path upserts on the client-generated id with duplicate-ignoring semantics, in the event, milestone and measurement lanes alike. → Three-way verdict (server responds), Row-level security (request reaches the backend)
- Three-way verdict · Decision · TypeScript — A send resolves to ok, transient, or permanent — where permanent means the server will never accept this job. → Couldn’t-be-saved tray (permanent), Post-drain invalidation (ok · queue moved), FIFO drain (transient · job stays put)
Stage 06
Row-level security
The policies and constraints that decide what a client may touch.
- Row-level security · Guard · Postgres — Policies on every table, expressed through three security-definer predicates: is this user a member of that household, an owner of it, or a member of the household that owns this baby. → Core tables (membership predicate passes), Three-way verdict (refused · permanent)
- Database backstops · Guard · Postgres — Immutable attribution — the caregiver who logged an event cannot be forged on insert or re-attributed on update — plus payload checks, a time-order constraint and immutable columns on the food library. → Row-level security (attribution + constraints)
Stage 07
Tables
The schema the app and the cloud sweep share.
- Core tables · Data store · Postgres — Profiles, households, role-typed memberships, babies, typed events with a details payload and a time-order constraint, device tokens and guidance content — later foods, plan rules, nudge log, app config, milestones, measurements, invites and chat quota. → Prediction engine (recent events read)
- Household bootstrap · API / service · Postgres function — One server-side function creates the household, the owner membership and the first baby together. → Core tables (household + membership + baby, one call)
- Draft + mirror discipline · Guard · Repo convention — New schema changes start as drafts in a folder the migration push tool cannot reach, each paired with a plain-language mirror document saying what it does and what the app does when it is absent. → Core tables (after sign-off and explicit apply)
Stage 08
Guidance & nudges
The prediction engine, its two runtimes, and claiming a send exactly once.
- Prediction engine · Scheduled job · TypeScript — Pure functions computing the next feed, nap, diaper and bedtime windows from recent events and a published rule library, taking an optional timezone. → Today view (device runtime), Cron sweep (byte-identical copy, script-synced), Six-state mascot (overdue past 30 minutes)
- Cron sweep · Scheduled job · Edge function + cron — Every fifteen minutes: authenticate the caller, read the kill switch, load the rule library, find babies with an event in the last 72 hours, and run the same engine over fourteen days of events per baby, each in its own try/catch. → Three-condition timezone gate (bedtime prediction), Claim a send once (prediction inside the lead window)
- Two credentials per request · Guard · Edge function + vault — The platform’s JWT gate only proves the caller holds a project key, so the function verifies a second shared secret of its own; the scheduler sends both, pulling its half from the database vault. → Cron sweep (both credentials present)
- Dark-ship kill switch · Guard · App config table — A config flag is read first; anything other than exactly true and the sweep returns skipped before touching a device token, and the push sender refuses again on the same condition. → Cron sweep (flag is exactly true)
- Three-condition timezone gate · Guard · Edge function — Bedtime is promoted to the cloud only when the household’s timezone column exists, holds a non-null value, and this runtime’s internationalization layer accepts it. → Device-scheduled nudges (any condition fails · stays on device)
- Device-scheduled nudges · Scheduled job · React Native — The device half of the pipeline, which keeps bedtime for every household the cloud cannot safely schedule for. → Today view (device-scheduled bedtime)
- Claim a send once · Guard · Postgres unique key — A send is claimed by inserting into a log table with do-nothing-on-conflict, keyed on baby plus kind plus the anchoring event, behind a 90-minute per-baby-per-kind cooldown checked first. → Household push fan-out (claim inserted, not a conflict), Cron sweep (conflict or cooldown · skip)
- Household push fan-out · Message · Push service — Batched push to every household member’s device tokens with low-PII copy — no baby name, no times, no schedule, just the kind. → Today view (caregiver opens the app)
- Guarded notification import · Guard · React Native — One module is the only place the notification library is loaded, and it is loaded dynamically; consumers ask for the namespace and get null where it cannot be loaded. → Household push fan-out (namespace resolves), Device-scheduled nudges (namespace resolves)
Stage 09
Today view
What the caregiver actually sees — plan, mascot, history.
- Today view · Outcome · React Native — The home surface — a next-events countdown strip, a quick-log grid, the mascot and a rotating tip card, all reacting live to the logs. → History and day view (caregiver navigates)
- Six-state mascot · AI · React Native — A pure six-state resolver — idle, happy, comforting, encouraging, celebrating, sleepy — driven by a log save, an overdue prediction, the night window, and a streak or first log.
- History and day view · Outcome · React Native — A full timeline with filters, a day view over a half-open range so adjacent days never claim the same entry, a naps list, and entry detail, edit and delete. → One queue, five lanes (edit or delete, online or not)
Stage 10
Identity & proof
Auth, the click-through prototype, the design report and the test layers.
- Auth and onboarding · API / service · Supabase Auth — Welcome, sign-up, sign-in and password-reset screens, then baby profile, photo, a short quiz and a completion screen; a profile row is created on signup by a database trigger. → Household bootstrap (first baby saved)
- Design defect report · Report · Docs — A written report back to the agency partner: the delivered set grew from 25 screens to 73, tokens from 47 to 53, plus defects found by measurement, three tab destinations that pointed nowhere, and two store-compliance gaps closed. → Click-through prototype (measured tokens and screens)
- Click-through prototype · Report · Static web — A deployed, shareable vanilla prototype of the full screen set, treated as the source of truth for screen copy and design tokens. → Route tree (source of truth for copy and tokens)
- Cross-household isolation suite · Report · Node + hosted project — Creates two throwaway users, has one onboard a household and baby, then attempts every cross-household attack a client could make — read another household’s babies, insert an event on their baby, forge attribution, re-attribute an event, join their household, read unpublished content — then cleans up after itself. → Row-level security (proves what the policies deny)
- Unit and parity suites · Report · Jest + Deno — A local gate of lint, unit tests and strict typecheck, plus an export-per-platform boot gate proving the router tree and every import resolve; engine fixtures run under both runtimes and a parity test asserts the two engine copies match. → Prediction engine (fixtures + parity assertion)
- Weekly written updates · Report · Docs — Client-facing updates separating what is built from what is blocked on the client’s own accounts, and listing the specific decisions the build is waiting on. → Auth and onboarding (names credential blockers)
Platforms · Expo · React Native · Supabase · TypeScript · TanStack Query
What I built
A token system measured out of the mockups
I mined the client's own mockups and PDFs for embedded full-resolution artwork and recovered the native assets, including six mascot states, so the logo in the app is the client's logo exactly rather than a redraw.
Every screen was rebuilt on shared tokens, so a global change is one edit rather than twenty-five. The route tree then extended well past the mockups into the auth, settings, legal and empty-state screens the brief never mentioned.
One queue, five lanes
The save path is the heart of the product. A payload is validated against its per-type schema before it can reach the queue, because a payload the database would refuse must never enter the outbox where it would fail permanently after the parent already saw a tick.
The row's primary key is generated on the device at save time, not by the server. That is the whole idempotency trick: the id exists before the network does, so a replay can be recognised as the same row. The send path then upserts on that id with duplicate-ignoring semantics, so replaying a queue after a crash, or after a response lost on a flaky connection, can never double-log a feed.
Five job kinds share one queue: event create, update and delete, milestone create, measurement create. It was generalised from a queue of events to a queue of typed jobs deliberately, because two queues would let a delete overtake the create it deletes. Ordering is correctness here, not tidiness. The drain walks the queue in order and stops the pass at any job it cannot move rather than stepping over it.
Persistence split on purpose
Pending writes live in plain storage. The long-lived read cache is persisted as ciphertext with its key in secure storage, on a seven-day trust window and throttled writes. That cache is a full record of a baby's feeds, sleeps and diapers sitting on a phone that can be lost.
Isolation proven rather than asserted
Policies sit on every table, expressed through three security-definer predicates: is this user a member of that household, an owner of it, or a member of the household that owns this baby. The foundation migration alone declares twenty policies, and later migrations add roughly two dozen more across foods, milestones, measurements, invites, memberships, profiles, preferences and chat. Four more scope photo objects to the owning household, because baby photos are household data rather than user data.
Beneath those sit database backstops. The caregiver who logged an event cannot be forged on insert or re-attributed on update. In a multi-caregiver household, who logged this is the one field a client must not be able to rewrite. The client validates before queueing, and the database refuses anyway.
Guidance, in two runtimes, sending once
Pure functions compute the next feed, nap, diaper and bedtime windows from recent events and a published rule library.
The cloud sweep runs every fifteen minutes: authenticate the caller, read the kill switch, load the rules, find babies with an event in the last 72 hours, and run the engine over fourteen days of events per baby, each in its own try-catch. A send is claimed by inserting into a log table with do-nothing-on-conflict, keyed on baby plus kind plus the anchoring event, behind a 90-minute cooldown checked first. Two independent mechanisms on purpose: a cooldown alone is not enough against a prediction whose time drifts, and a unique key alone gives no rate limit.
Edge cases and failure handling
- A job the server will never accept moves to a visible tray rather than being dropped, and History shows a row so the parent chooses retry or let it go. Retrying a permanent failure forever would wedge the queue behind a row that cannot move.
- Unknown connectivity deliberately counts as online, because refusing a write while a probe is still resolving fails a save that would have worked.
- Cloud bedtime is promoted only when the household timezone column exists, holds a non-null value, and the runtime's internationalization layer accepts it. There is no default and no backfill, because a guessed zone nudges at the wrong hour of the night and a null zone nudges not at all.
- The notification library throws at import time on one runtime and platform pair, taking down the whole module graph with an unrelated-looking error. Every consumer had already guarded its calls, which was useless because the import is what detonates. One module now loads it dynamically and hands back null where it cannot be loaded.
- The push fan-out carries low-PII copy only: no baby name, no times, no schedule, just the kind. Tokens the push service reports as unregistered are pruned at the end of each sweep.
How I verified it
A cross-household isolation suite creates two throwaway users, has one onboard a household and baby, then attempts every cross-household attack a client could make: read another household's babies, insert an event on their baby, forge attribution, re-attribute an event, join their household, read unpublished content.
The local gate is lint, unit tests and strict typecheck, plus the export-per-platform boot gate proving the router tree and every import resolve. Engine fixtures run under both runtimes and a parity test asserts the two engine copies match byte for byte. There were roughly 390 automated tests green at the multi-caregiver milestone, and the suite has grown substantially since.
What is not tested is documented as plainly as what is. The cloud sweep's own handler has no automated test and the runner cannot reach it, with the reasons enumerated, so the README lists exactly what is proven and names the log fields that stand in as observability for the rest.
Before any app code existed, the client had a deployed click-through prototype of the full screen set to tap, treated as the source of truth for screen copy and tokens. Specific questions could then be asked against a running thing.
What changed
The client owns a shipping cross-platform application built on one token system, with the screen set grown from the 25 the mockups covered to 73, and their own logo and six mascot states extracted at native resolution rather than approximated.
Crash replay cannot double-log an event, by construction rather than by retry policy. Multi-caregiver privacy is proven by a suite that attacks it, not asserted in a document. Core tracking was code-complete ahead of the milestone, and multi-baby support was pulled forward by a month.
Two things ship dark on purpose. The push pipeline is behind a kill switch that returns skipped before touching a device token, and flipping it needs store credentials, a build proving delivery, and a human's approval.
The engagement ran on weekly written updates separating what was built from what was blocked on the client's own accounts. Store enrolment lead time gated the submission date, and naming that weeks ahead was the only lever anyone had on it. The design defect report went back to the agency partner in writing, including the three tab destinations that pointed nowhere and the two store-compliance gaps that were closed before they became rejections.
Before / after
- Before
- ✕ Designs as flat images with seven different creams and five nav bars
- ✕ No app, no schema, no offline answer for 3am logging
- ✕ Client artwork existing only inside PDFs
- ✕ A store deadline with no repo behind it
- After
- ✓ 31 screens live on one token system
- ✓ Offline-first logging that survives crashes without double entries
- ✓ The client’s actual logo and mascot, extracted at native resolution
- ✓ ~390 tests green and milestones tracked in weekly written updates
ScopeFigures on this page describe what was built, covered or verified — not a measured lift.
Related systems
Not a one-off.
Digital publishing product · sibling of CS-04
AI book-writing application
Both are shipped products with real test suites, not automations.
100API endpoints across the product’s two servicesScope
High-ticket education business · sibling of CS-03
Ads & sales reporting dashboard
Both use Postgres with row-level security proven by tests.
13purpose-built screens, live on the client’s domainScope
Author-coaching program
AI Book-Summary Writer
Both are custom builds where a workflow tool was the wrong instrument.
1 sittingblank page → finished summaryScope
Have something close to this? Book a call. I’ll tell you if I’ve already built it.
One Manila business day to reply. All client work is under NDA; what you see here is redrawn architecture with real metrics.