pg-boss gives a job fifteen minutes unless the send says otherwise, then fails it and retries it while the handler keeps running. Several passes can legitimately need longer and had no protection at all: the seven nightly status passes and the pre-generation (a provider call per account), the period-narrative warm, the record-detection cron (every account's history, every thirty minutes), the key rotation (every encrypted column), the content-index backfill (up to 200 transcriptions) and the on-host backup. Each of them now carries an explicit expiry on every send and schedule, stops between units once three quarters of it is spent (jobBudget), and runs under a session advisory lock keyed on queue and identity (lockedPass), so the retry pg-boss starts after the expiry does nothing beside a run that is still going. The lock is the one the full-history imports already held, moved into job-lock.ts. Where a pass stops early it says so (stopped_early) and the next run continues: the status passes and the narrative warm leave the rest to the next night, the pre-generation and the narratives walk the stalest accounts first, the on-host backup now backs up the oldest copies first, the key rotation reports a partial run in its audit row and skips rotated rows when run again, and the dose-history import fails the delivery so its retry resumes from the committed chunk. The off-host backup, which already stopped on its budget, gets a four-hour expiry and the lock.
src/app/
The Next.js App Router tree: the page surfaces a logged-in user sees and the REST API under api/. Server components are the default; "use client" is added only where a hook, state, or browser API needs it. Routing is file-based — a page.tsx is a page, a route.ts is an API endpoint.
API routes (api/)
Every endpoint wraps its handler in apiHandler (@/lib/api-handler), parses any body with Zod safeParse, narrows userId from the session or Bearer token (never from the body), and returns the { data, error, meta? } envelope. requireAdmin() is cookie-only, so api/admin/ is unreachable by Bearer token. Notable groups:
auth/— login, register, password, passkey (WebAuthn), refresh, Codex device-OAuth.measurements/,mood-entries/,medications/,sleep/— the core health-data CRUD + batch/bulk ingest.ingest/— external Bearer-token ingest surfaces.insights/,analytics/,dashboard/— AI insights, the Coach SSE stream, trend summaries, the first-paint dashboard snapshot.withings/,whoop/,fitbit/,integrations/,sync/— OAuth connect/sync/webhook surfaces and the offline delta feed.fhir/,share-links/,doctor-report/,export/,import/— clinician-facing FHIR R4 read API, shareable records, report + data export.notifications/,devices/,send/,telegram/— channel registration, device tokens, dispatch, webhook intake.admin/— operator panel APIs (cookie-only).health/,version/,meta/,internal/— health check, build-version probe, runtime metadata, worker-internal routes.
Page surfaces
measurements/, medications/, mood/, insights/, achievements/, notifications/, settings/, onboarding/, admin/, plus the public about/, privacy/, bugreport/, the share-record route c/, and the root dashboard (page.tsx). Shared shell + chrome lives in layout.tsx; error.tsx / global-error.tsx / not-found.tsx / loading.tsx cover the framework boundaries. layout.tsx holds the one nonce-bound dangerouslySetInnerHTML (theme bootstrap).
Conventions
See ../../CLAUDE.md. The request-flow walk in .planning/codebase/arch.md annotates each layer with file:line citations. Related dirs: handlers compose ../lib/; pages render ../components/.