diff --git a/CLAUDE.md b/CLAUDE.md index 4eded2583..349aca477 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ Working notes for anyone (human or otherwise) editing this repository. Skim befo ## What HealthLog is -Self-hosted personal-health-tracking PWA: weight, blood pressure, pulse, body composition, glucose, sleep, mood, medication compliance. Withings + Apple Health sync, multi-provider AI Insights (BYOK or local), doctor-report PDF, native iOS client in public beta. Single `docker compose up`, Postgres-backed, AES-256-GCM at rest. AGPL-3.0. Current line: v1.5.x. +Self-hosted personal-health-tracking PWA: weight, blood pressure, pulse, body composition, glucose, sleep, mood, medication compliance. Withings + Apple Health sync, multi-provider AI Insights (BYOK or local), doctor-report PDF, native iOS client in public beta. Single `docker compose up`, Postgres-backed, AES-256-GCM at rest. AGPL-3.0. Current line: v1.12.x. ## Voice and privacy @@ -38,7 +38,7 @@ Prefer patch bumps. A release dominated by bug fixes plus additive features with | Framework | Next.js 16.2.6 — App Router, `output: "standalone"` | SWC strips `console.*` in prod except `error/warn`. | | Language | TypeScript 6, `strict: true`, `moduleResolution: "bundler"` | Path alias `@/*` → `./src/*`. `scripts/` excluded from typecheck. | | UI | React 19.2.5 (exact), Tailwind 4, shadcn/ui (new-york style, zinc base), Radix, Lucide | Recharts 3 for charts — stays; replacement requires explicit approval. | -| Persistence | PostgreSQL 16 + Prisma 7.8 — 47 models | Generated client at `src/generated/prisma`. Production image ships a separate `/opt/prisma-cli` install + `/opt/pg-boss` runtime install (see `Dockerfile:74-83`). | +| Persistence | PostgreSQL 16 + Prisma 7.8 — 61 models | Generated client at `src/generated/prisma`. Production image ships a separate `/opt/prisma-cli` install + `/opt/pg-boss` runtime install (see `Dockerfile:74-83`). | | Queue | pg-boss 12.18 | Cron + retry semantics live in Postgres tables; workers under `src/lib/jobs/`. | | Auth | `@simplewebauthn/server` 13, `@node-rs/argon2` 2 | Passkey + Argon2id password, server-side sessions in Postgres. | | Notifications | `@parse/node-apn` 8 (APNs), `web-push` 3 (VAPID), raw fetch for Telegram + ntfy | Per-channel `recordPushAttempt` row, hard-reject classification. | @@ -124,7 +124,7 @@ The architecture map in `.planning/codebase/arch.md` walks each layer with file: | Notification dispatcher | `src/lib/notifications/dispatcher.ts` + `src/lib/notifications/senders/` | APNs → Telegram → ntfy → Web Push cascade, hard-reject classification, `push_attempts` ledger with 90-day retention. | | Coach | `src/app/api/insights/chat/route.ts` + `src/lib/ai/coach/` | SSE stream, budget gate + per-user rate gate, refusal detector, snapshot builder, message persistence with `encryptedContent` Bytes column. | | OpenAPI | `src/lib/openapi/registry.ts` + `src/lib/openapi/routes.ts` | Source of truth for `docs/api/openapi.yaml`. | -| Schema | `prisma/schema.prisma` (47 models) | `cuid()` PKs, `snake_case` columns via `@map`, encrypted columns mostly under `*Encrypted` (search the file). | +| Schema | `prisma/schema.prisma` (61 models) | `cuid()` PKs, `snake_case` columns via `@map`, encrypted columns mostly under `*Encrypted` (search the file). | | Compose | `docker-compose.yml` | `app` + `db`. Env-var whitelist under `environment:` — vars not listed never reach the container. `pull_policy: always` is load-bearing. | `.planning/codebase/arch.md` carries the full annotated walk for every section. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eea5b4d01..20eaafeb2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -90,37 +90,34 @@ Always include the migration file in your PR. ## Branch Model -HealthLog uses a long-lived `develop` branch for daily work and reserves `main` for releases. +HealthLog is trunk-based: `main` is the single long-lived branch and is always releasable. There is no `develop` branch. ``` feature/* ──┐ - ├──► develop ──► (release-merge) ──► main ──► tag vX.Y.Z ──► GHCR build ──► Coolify deploy - fix/* ──┘ │ - └──► hotfix/* ──► main + tag, then merge back to develop + fix/* ──┼──► PR ──► main ──► tag vX.Y.Z ──► GHCR build ──► deploy + │ + release/vX.Y.Z ──► PR (merge-commit) ──► main ──► tag vX.Y.Z ──► GHCR build ──► deploy ``` -Two simple rules: +The rules: -- **`develop` is the daily target.** Open feature, fix, test, and docs PRs against `develop`. Builds do not run on `develop` pushes — the branch is free of release ceremony. -- **`main` is release-only.** It receives a single release-merge per version, gets tagged (`v1.4.20`, etc.), and that tag is what triggers the Docker image build, the GHCR push, and the Coolify deploy. Pushes to `main` outside a release are reserved for hotfixes that cannot wait for the next cycle. - -If a critical bug needs a hotfix: - -1. Branch from `main`: `git checkout -b hotfix/something main` -2. Land the fix; tag a patch release on `main` -3. Merge `main` back into `develop` so the next release inherits the fix +- **`main` always builds, passes CI, and is deployable.** Every commit on it is a release commit (`chore(release): vX.Y.Z`), a release-branch merge, or a maintenance commit (docs, gitignore, audit findings) applied directly. +- **A release** is built on a short-lived `release/vX.Y.Z` branch cut off `main`, then opened as a PR `release/vX.Y.Z` → `main` with green CI, merged as a merge-commit, and tagged `vX.Y.Z`. The tag push triggers the GHCR multi-arch image build (and `:latest`). Deploy is manual. Delete the release branch after merge. +- **Small standalone changes** (a single fix, a docs touch) go through a short-lived `fix/X` / `feat/X` branch + PR targeting `main`. +- **Hotfixes** branch from `main` and merge straight back to `main`. There is no second long-lived line to forward-port to. +- Never force-push `main` or any release branch. End users: track the latest `v*` tag (or `:latest` on GHCR). The `main` branch always equals the latest release. -Contributors: track `develop`. PRs target `develop`. +Contributors: branch off `main`; PRs target `main`. ## Pull Requests 1. Fork the repository -2. Create a feature branch off `develop` (`git checkout -b feature/your-feature develop`) +2. Create a feature branch off `main` (`git checkout -b feat/your-feature main`) 3. Make your changes 4. Ensure all checks pass (`pnpm typecheck && pnpm lint && pnpm test && pnpm format:check && pnpm build`) -5. Submit a PR against `develop` with a clear description of the changes +5. Submit a PR against `main` with a clear description of the changes ## Translations