All projects

Apps, health tooling, offline PWA

Shift Sleep Planner

  • 64 automated tests
  • not medical advice

A Korean-language phone web app for people on night and rotating shifts. You enter shifts, times you cannot sleep and the sleep you actually got, and it plans today's and tomorrow's sleep around them. It installs to the home screen and works offline.

Open the live demo (fictional night-shift schedule; AI summary is a fixed example) ↗Code on GitHub: hyh4867/shift-sleep-planner ↗

Today view for a fictional 22:00–06:00 night shift: timeline, caffeine cutoff at 00:30 flagged as falling mid-shift, and dim-light advice with source tags.
Night shift · cutoff falls mid-shift and is flagged
Weekly view: daily sleep bars with unlogged days hatched, and the cumulative deficit of 3 h 30 min counted from logged days only.
Week · deficit counts logged days only
Today view for a fictional mixed rotation: main sleep shortened by a training block, with a 90-minute nap before the next night shift.
Mixed rotation · sleep around a training block
AI summary preview listing exactly what will be sent: per-day totals for logged days, nap counts, the deficit and the number of unlogged days.
AI preview · aggregates shown before sending

The question

Night and rotating shifts push sleep into whatever gaps are left. Can a planner turn published sleep guidance into concrete times for one person's roster, show where each rule comes from, and do it without the schedule ever leaving the phone?

What I built

A pure-function planner puts the main sleep in the longest free gap (or, after a night shift, the first gap after a 30-minute wind-down), adds naps before night shifts, tops the day up to the target, and derives a caffeine cutoff and a dim-light window from each main sleep. Every default can be changed in settings.

Shift, unavailable-time and actual-sleep blocks can cross midnight; overlapping blocks are rejected before they are saved. A today/tomorrow timeline and a weekly chart show the plan, and every recommendation carries its source tag.

An optional one-paragraph weekly summary from Claude (claude-haiku-5-5) runs through a Vercel serverless function. It is off by default and asks for consent when switched on. Before every request it shows the exact JSON that will be sent. Calls are limited per device, per IP and per day overall. On the live demo no API key is configured, so the summary there is a fixed example sentence labelled “데모 예시” (demo example); it does not analyse the data sent.

Rules and sources

Defaults shown; each can be changed in the app. Parts marked “app definition” are my operational choices, not claims from the source.
RuleDefaultSource
Sleep targetAt least 7 h per day, main sleep and naps combined; a sleep counts toward the day it endsAASM–SRS joint consensus (Watson et al., 2015). Applying it as a 24-hour total is an app definition
Nap before a night shiftFree time ≥ 2 h before the shift → 90 min; ≥ 45 min → 20 min; the nap ends 30 min before the shiftNIOSH 2015-115, Module 7 (“Before Nights”, “Nap Duration”). Night shift = overlaps 00:00–06:00 (app definition)
Main sleepLongest free gap at the usual bedtime; after a night shift, the first gap after a 30-min wind-down; 30 min to wake up before a shiftApp definition
Caffeine cutoff6 h before the planned main sleep; caffeine early in the shift; flagged when the cutoff falls mid-shiftDrake et al., J Clin Sleep Med 2013; NIOSH Module 6
Dim the lights1–2 h before the main sleepNIOSH Module 6
Cumulative deficitSum of each logged day's shortfall below the target; surplus does not offset; unlogged days shown separately as plan estimatesApp definition

Privacy design

Shift and sleep times are stored only in the browser's IndexedDB on the phone. There is no account and no server-side copy; backup is a JSON file the user exports and imports.

The AI summary receives aggregates only: total sleep minutes and nap counts for the days the user actually logged (numbered 1–7 instead of dates), the deficit over those days, the target and the number of unlogged days. It never receives dates, clock times, notes or plan estimates.

The browser and the server run the same validator. It allows only listed keys and values in range, requires the totals to match the per-day values, and rejects anything that looks like a time or a date. The API key is kept in a server environment variable. If a public deployment has no API key or no rate-limit store, the function does not call the model and returns a fixed summary labelled as a demo example. The live demo runs in this mode.

Evaluation

64 Vitest tests check: shifts that cross midnight; overlap rejection; nap placement and the 30-minute margin; caffeine-cutoff arithmetic; 24-hour totals and deficit accumulation, including the on-screen “surplus does not offset” rule; that the AI payload contains no time, date or note from a sample week; server rate limits and demo mode; and the serverless function's import paths.

The AI summary handler is tested only with a fake model. It has never been run with a real API key, so the quality of a real summary is untested.

Three fictional schedules (day shifts only, a run of night shifts, a mixed rotation) are checked for overlaps and used for every screenshot. On the production deployment, a valid payload gets the labelled example summary and a payload containing a time is rejected with HTTP 400.

Key findings

For a fictional 22:00–06:00 night shift, the plan is: main sleep 06:30–13:30, a 90-minute nap 20:00–21:30 before the next shift, and a caffeine cutoff at 00:30, which the app flags because it falls in the middle of the shift.

In the fictional night-shift week the logged-days deficit is 3 h 30 min: 30 min on a short night before the night run, then 2 h and 1 h on the first two daytime sleeps after night shifts. A 9-hour day does not cancel any of it.

Writing the rules down exposed a conflict in my own brief: a 20-minute nap that must end 30 minutes before the shift cannot fit a gap of 45–49 minutes. The app keeps the margin, skips the nap and says why.

The first deployment found a bug the local tests had missed: the serverless function failed with ERR_MODULE_NOT_FOUND, because Node's native ES modules need explicit file extensions. A test now walks the function's import graph.

My role and the agent workflow

I wrote the brief: which rules to apply and their sources, the privacy constraints, what the AI may receive, and the review changes (logged-days-only deficit, wind-down margin, call caps).

Claude implemented the app, tests and deployment in bounded steps. I reviewed its output and keep responsibility for the claims on this page.

Limits

Not medical advice. The app diagnoses nothing; it tells users to seek medical advice if insomnia or severe sleepiness persists.

I have not tested whether using the app improves sleep, alertness or safety. The tests show that the rules are applied as written, not that the rules help.

The rules are simplified readings of the cited guidance. The 24-hour total, the wind-down and wake-up margins and the placement heuristics are my own choices, labelled as such in the app.

Every schedule in the code, tests, demo and screenshots is fictional. No real roster was used.

The live demo's AI summary is a fixed example sentence, not an analysis, and the summary handler has only been tested with a fake model. A real summary could be wrong. The feature is optional, and the app works fully without it.

Evidence and reproducibility

Back to all projects