mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-07 04:12:04 +00:00
The page reads one window anchored on today, ninety days back and half a year forward, and the month grid navigates freely past it. A month outside the window came back with no days: the cells still rendered (they come from the month, not the read) and still opened the log sheet, and the write behind it still landed, because the period route is built for back-dating and re-anchors the neighbouring cycles for it. What did not happen is anything visible. Saving an April flow in August closed the sheet onto a month the read never carried, so the entry looked like it had not been made, and entering it again did not help. The grid now reports the month it moved to and gets its own bounded read for it. The anchored window is untouched, because every other surface on the page means today and the route caps a span at 400 days, so a union with a month two years back would simply be refused. A write also says so now. Saving, deleting and both period boundaries raise a confirmation; the sheet's inline strip already named a failure. Between the invisible month and the silent success there was no way to tell a save that worked from one that never happened. The sheet header carries the date it is writing to, and the one-tap boundaries drop the word today when the day is not today. It always wrote to the selected date; saying otherwise made a correction read as a mistake. The gear beside the log button is gone. It was a second door to the tab directly below it and no other page carries one in that corner. - calendar days carry isCycleStart, so the delete dialog can say that removing this day removes the period start with it - window arithmetic is a pure module with its own tests, including the 400-day span the anchor cannot be widened past - integration: a day 111 back is written, is absent from the anchored read, and comes back complete on a window over its month - new strings in all six languages
HealthLog API Spec
openapi.yaml is the canonical OpenAPI 3.1 description for the HealthLog
Next.js API. It is the source of truth for the native iOS client's
DTO codegen.
The MCP server is documented separately — it is a distinct contract over JSON-RPC, not part of the REST OpenAPI spec:
mcp-capabilities.md— the tool, resource, and prompt catalogue, the grounding contract, and the write model.mcp-skills.md— building a connector or skill on top of it.../self-hosting/mcp.md— enabling and connecting the surface (operator guide).
Preview locally
Open an interactive Redoc preview in your browser:
npx @redocly/cli preview docs/api/openapi.yaml
Generate static HTML for sharing:
npx @redocly/cli build-docs docs/api/openapi.yaml --output docs/api/index.html
Validate
npx @redocly/cli lint docs/api/openapi.yaml
The spec is expected to be lint-clean (errors = 0). Some warnings are intentional:
no-server-example.com—http://localhost:3000is the dev server.operation-2xx-response/operation-4xx-response— webhook + redirect endpoints intentionally omit one or the other.no-unused-components—ReminderPhaseis exposed for client codegen even though no path response references it directly.
Layout
info— title, version (mirrored frompackage.json), description.servers— productionhttps://healthlog.bombeck.ioplushttp://localhost:3000for local development.tags— domain groupings (Auth,Measurements,Medications,Mood,Dashboard,Insights,Achievements,DoctorReport,Integrations,User,Tokens,Notifications,Onboarding,Health,Settings,Analytics,Export,AuditLog,Monitoring,Webhooks,Ingest,Admin).securitySchemes:bearerAuth— long-livedhlk_*API tokens issued viaPOST /api/tokens. Required on/api/ingest/medication.sessionCookie— HttpOnlyhealthlog_sessioncookie set by the password and passkey login flows. Used by the web app and any iOS client running inside a web view.
components.schemas— DTOs for all routes plus the underlying Prisma models (e.g.Measurement,Medication,MedicationIntakeEvent,MoodEntry,User,DashboardLayout,ApiEnvelope,ApiError).paths— all routes fromsrc/app/api/**/route.ts, including the/api/admin/*family which is taggedAdminand marked internal.
Conventions
- Successful responses use the envelope
{ data, error: null }. - Error responses use the envelope
{ data: null, error: '<message>' }. - Pagination uses
limit+offsetquery parameters and ameta: { total, limit, offset }block on the response. - All timestamps are ISO 8601 / RFC 3339 (
format: date-time) in UTC. - IDs are CUIDs (opaque strings).
Updating the spec
When adding or changing routes:
- Update the relevant route handler under
src/app/api/**/route.ts. - Mirror the change in
openapi.yaml. KeepoperationIdcamelCase and stable — the iOS codegen keys off it. - Run
npx @redocly/cli lint docs/api/openapi.yamland resolve all errors. Warnings are tolerated when documented above. - Bump
info.versionif the change is breaking for clients.