Document core-model decisions in CLAUDE.md and ARCHITECTURE

Records the landed data model and backend layout, the Phase 0 tree-scoping of Place (vs. the eventual shared gazetteer), and the temporary X-User-Id auth shim.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Justin Paul <justin@jpaul.me>
This commit is contained in:
2026-06-06 10:40:19 -04:00
parent 64388b75bf
commit e5a8713293
2 changed files with 5 additions and 4 deletions
+2 -2
View File
@@ -38,13 +38,13 @@ Pick libraries consistent with this stack. If you introduce a significant depend
``` ```
/ # docs and project meta (this file, README, LICENSE, COC, CONTRIBUTING) / # docs and project meta (this file, README, LICENSE, COC, CONTRIBUTING)
/docs # PRD.md, ARCHITECTURE.md /docs # PRD.md, ARCHITECTURE.md
/backend # FastAPI service (uv-managed). app/ = api / core (config, db); more layers land with the data model /backend # FastAPI service (uv-managed). app/{api/v1, services (+ privacy engine), repositories, models, schemas, core}; migrations/ = Alembic
/deploy # docker-compose.yml, Caddyfile, .env.example — the self-host stack /deploy # docker-compose.yml, Caddyfile, .env.example — the self-host stack
/.gitea/workflows # Gitea Actions CI (build images → Gitea registry) /.gitea/workflows # Gitea Actions CI (build images → Gitea registry)
/frontend # Next.js app — not yet scaffolded (Phase 0, after the deploy story) /frontend # Next.js app — not yet scaffolded (Phase 0, after the deploy story)
``` ```
Phase 0 is landing **deploy-first**: the compose stack (Postgres + MinIO + Caddy + a minimal FastAPI backend exposing `/health` and `/health/ready`) and CI come before the real data model and the frontend. Backend dependencies are managed with **uv**; migrations will use **Alembic**. Keep this section current as the tree grows. Phase 0 is landing **deploy-first**: the compose stack (Postgres + MinIO + Caddy + a minimal FastAPI backend exposing `/health` and `/health/ready`) and CI come before the real data model and the frontend. Backend dependencies are managed with **uv**; migrations use **Alembic**. The core data model (ARCHITECTURE §5) and its initial migration have landed; local auth and the frontend are next. A temporary `X-User-Id` header shim stands in for auth until that slice. Keep this section current as the tree grows.
## Where to start ## Where to start
+3 -2
View File
@@ -93,7 +93,7 @@ Core entities and the important relationships. (Illustrative, not final DDL.)
- **Person** — belongs to a Tree. Has many **Name** records (with parts: given, surname, prefix/suffix, and a type such as birth/married/alias) to support variants and changes over time. Carries living/deceased status. - **Person** — belongs to a Tree. Has many **Name** records (with parts: given, surname, prefix/suffix, and a type such as birth/married/alias) to support variants and changes over time. Carries living/deceased status.
- **Relationship** — typed edge between two Persons within a Tree: parentchild (with a qualifier: biological, adoptive, step, foster, donor, guardian), partnership/marriage (with its own events), sibling (often derived). Modeling parentage as qualified edges — rather than assuming a two-biological-parent nuclear family — is what makes adoption, donor conception, and blended families first-class rather than awkward. - **Relationship** — typed edge between two Persons within a Tree: parentchild (with a qualifier: biological, adoptive, step, foster, donor, guardian), partnership/marriage (with its own events), sibling (often derived). Modeling parentage as qualified edges — rather than assuming a two-biological-parent nuclear family — is what makes adoption, donor conception, and blended families first-class rather than awkward.
- **Event** — typed (birth, death, marriage, residence, immigration, etc.), with a date (supporting ranges, approximations, and non-Gregorian calendars), an optional Place, and attachable to a Person or a partnership. - **Event** — typed (birth, death, marriage, residence, immigration, etc.), with a date (supporting ranges, approximations, and non-Gregorian calendars), an optional Place, and attachable to a Person or a partnership.
- **Place** — a tenant-shared gazetteer entity: hierarchical (place within place), with **historical name variants and date ranges** so a record entered as "Königsberg, 1900" sorts and displays correctly against "Kaliningrad." Optional coordinates. - **Place** — a tenant-shared gazetteer entity: hierarchical (place within place), with **historical name variants and date ranges** so a record entered as "Königsberg, 1900" sorts and displays correctly against "Kaliningrad." Optional coordinates. *(Phase 0 scopes Place to a Tree via `tree_id` for absolute tenant isolation; a deployment-wide shared gazetteer is a deliberate later refinement. Variants live in a `PlaceName` child table.)*
### Sources (first-class) ### Sources (first-class)
- **Source** — a reusable record of an origin: title, repository, type, optional URL, free citation text, optional quality grade. One Source backs many facts. - **Source** — a reusable record of an origin: title, repository, type, optional URL, free citation text, optional quality grade. One Source backs many facts.
@@ -147,6 +147,7 @@ Three parts, deliberately separated:
- `AuthProvider` interface with implementations for **local** (password + email verification/reset), **OIDC** (validated against Authentik; expected to work with Keycloak, Auth0, etc.), and **social** (Google, Apple, Facebook). - `AuthProvider` interface with implementations for **local** (password + email verification/reset), **OIDC** (validated against Authentik; expected to work with Keycloak, Auth0, etc.), and **social** (Google, Apple, Facebook).
- Operators enable any subset via config. This deployment will use Authentik (`auth.jpaul.io`) plus selected social providers; a bare self-hoster can run local-only. - Operators enable any subset via config. This deployment will use Authentik (`auth.jpaul.io`) plus selected social providers; a bare self-hoster can run local-only.
- Sessions are backend-issued; the assistant principal is minted per-session and scoped to the acting user. - Sessions are backend-issued; the assistant principal is minted per-session and scoped to the acting user.
- *Phase 0 interim:* until the auth slice lands, the API identifies the caller via a temporary `X-User-Id` header shim (replaced by real sessions/tokens), and account creation is an open dev bootstrap. Every write still records an attributable actor in the audit log.
## 10. Search ## 10. Search
@@ -178,7 +179,7 @@ Jobs are idempotent and retryable; an external failure degrades gracefully rathe
**Repository layout (as scaffolded):** **Repository layout (as scaffolded):**
``` ```
/backend # FastAPI service, uv-managed (app/ = api, core; service/repository/domain land with the data model) /backend # FastAPI, uv-managed. app/{api/v1, services (+privacy), repositories, models, schemas, core}; migrations/ = Alembic
/deploy # docker-compose.yml, Caddyfile, .env.example /deploy # docker-compose.yml, Caddyfile, .env.example
/.gitea/workflows # Gitea Actions: build images → Gitea registry /.gitea/workflows # Gitea Actions: build images → Gitea registry
/frontend # Next.js (pending) /frontend # Next.js (pending)