Files
familienarchiv/.specify/constitution.md
Marcel b05990fffb
All checks were successful
CI / OCR Service Tests (pull_request) Successful in 23s
CI / Backend Unit Tests (pull_request) Successful in 4m48s
SDD Gate / RTM Check (pull_request) Successful in 15s
SDD Gate / Contract Validate (pull_request) Successful in 24s
CI / fail2ban Regex (pull_request) Successful in 46s
CI / Semgrep Security Scan (pull_request) Successful in 22s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m6s
SDD Gate / Constitution Impact (pull_request) Successful in 18s
CI / Unit & Component Tests (push) Successful in 4m58s
CI / OCR Service Tests (push) Successful in 24s
CI / Backend Unit Tests (push) Successful in 5m51s
CI / fail2ban Regex (push) Successful in 48s
CI / Semgrep Security Scan (push) Successful in 23s
CI / Compose Bucket Idempotency (push) Successful in 1m9s
CI / Unit & Component Tests (pull_request) Successful in 3m36s
docs(adr): renumber SDD adoption ADR 041 -> 042 (collision with renovate ADR)
Two ADR-041 files landed on main in parallel (sdd-adoption and
renovate-runner-setup). Renames the SDD one to 042 and repoints its references
(SPEC_DRIVEN_DEVELOPMENT, constitution, .specify/adrs/README, sdd-gate.yml).
The renovate ADR keeps 041; its references are left untouched. Riding this PR
per request.

Refs #778
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 13:38:12 +02:00

6.7 KiB

Familienarchiv Constitution

Version: v1.0.0 Status: Ratified Date: 2026-06-13 Adoption ADR: docs/adr/042-sdd-adoption.md

The non-negotiable rules of this project. Every spec, every PR, and every AI agent is bound by this document. Rules here are deliberately few and absolute — guidance and rationale live in CLAUDE.md, COLLABORATING.md, CODESTYLE.md, CONTRIBUTING.md, and the ADR archive (docs/adr/). When this file conflicts with any of those, this file wins — open an ADR to change it.

Versioning is semantic: MAJOR = a rule removed or weakened (existing code may now violate the constitution), MINOR = a rule added or tightened, PATCH = wording only. Any change requires the Sync Impact review in the last section.


1. Architecture Principles

  1. The backend is organised package-by-domain under org.raddatz.familienarchiv; a new domain lives in its own package, never spread across layer packages.
  2. Controllers never call repositories directly — a controller calls only services.
  3. A service accesses only its own domain's repository; cross-domain data is fetched through the other domain's service, never its repository.
  4. The frontend mirrors the backend domain split under frontend/src/lib/<domain>/, and cross-domain imports are allowed only where frontend/eslint.config.js (boundaries/dependencies) permits them.
  5. A Person (historical subject) and an AppUser (login account) are distinct domains and never share an identity or an account guard.
  6. Lazy-collection-bearing entities are never serialized across the controller boundary; the owning service assembles an explicit view inside the transaction (see ADR-036).
  7. A new backend domain package is added to ArchitectureTest's package allow-lists in the same change that introduces it.
  8. Synchronous cross-domain side effects use in-transaction domain events, not direct service-to-service write calls (see ADR-006).

2. Security Defaults

  1. Every POST, PUT, PATCH, and DELETE endpoint carries @RequirePermission(Permission.X) — there is no unguarded mutating endpoint.
  2. Authorization uses the typed Permission enum and @RequirePermission, never magic-string @PreAuthorize.
  3. All user input is validated at the system boundary (controller / form action), and validation failures return a typed ErrorCode, never a raw exception.
  4. Audit fields (createdBy/updatedBy) are set from the session principal inside the service and are never bound from a request body.
  5. Untrusted text is rendered through Svelte's default {...} escaping; {@html} is never used on user- or import-derived strings.
  6. Secrets are read only from environment variables (see .env.example); no secret, token, password, or DSN is ever committed to the repository or written to a log.
  7. Logs never contain PII beyond a stable user/entity UUID — no names, email addresses, document contents, or transcription text.
  8. Every state-mutating endpoint is covered by an Unwanted-behavior requirement (EARS If) describing the unauthenticated/unauthorized response.
  9. A dependency security audit runs on every CI run (npm audit --audit-level=high frontend, Semgrep .semgrep/security.yml backend) and nightly; a high finding blocks merge.

3. Code Quality Rules

  1. All new behavior is driven by a failing test written before the implementation (Red → Green → Refactor); a passing-on-first-run test proves nothing and is rejected.
  2. KISS beats DRY — no premature abstraction; an abstraction is introduced only on the third real caller.
  3. Each commit does exactly one logical thing and references its Gitea issue (Closes #n / Refs #n) on the last line of the body.
  4. No backwards-compatibility shims are added for code that has no callers.
  5. Every entity/DTO field the backend always populates carries @Schema(requiredMode = REQUIRED), and npm run generate:api is run after any backend model or endpoint change.
  6. A new ErrorCode is added in all four places at once: ErrorCode.java, frontend/src/lib/shared/errors.ts, getErrorMessage(), and messages/{de,en,es}.json.
  7. Dates built from an ISO date string append T12:00:00 to avoid UTC off-by-one.
  8. npm run lint (Prettier + ESLint, including the domain boundary rule) passes before every commit.

4. Do-Not-Touch List

  1. Do not edit generated artifacts: frontend/src/lib/generated/api.ts, frontend/src/lib/paraglide/, frontend/.svelte-kit/, frontend/build/, backend/target/.
  2. Do not edit an Accepted ADR — supersede it with a new, higher-numbered ADR.
  3. Do not upgrade actions/upload-artifact / download-artifact past @v3 (Gitea act_runner lacks the v4 protocol — ADR-014).
  4. Do not remove or weaken a CI guard step (banned-pattern greps, self-tested regexes) without an ADR recording why.
  5. Do not commit to main directly — all work flows through a branch and a PR.
  6. Do not edit a Flyway migration that has shipped; add a new forward-only migration instead.
  7. Do not commit the worktree copy directories (familienarchiv-*, .worktrees/) or data/.

5. Dependency Policy

  1. A new runtime dependency (backend pom.xml or frontend dependencies) requires an ADR in Accepted status before it is merged.
  2. A new dependency must be version-pinned in the manifest, and any exact pin (no caret) carries a comment stating why it cannot float (see the @vitest/browser-playwright pin).
  3. Renovate manages dependency-update PRs; a major-version bump is treated as a feature requiring its own spec and review, not an auto-merge.
  4. A dependency with an unresolved high+ advisory is not merged; it is pinned to a safe version or replaced.

6. Sync Impact

When this constitution changes, the author MUST, in the same PR:

  1. Bump the Version header per the semantic rule above and record the change in docs/adr/042-sdd-adoption.md's revision log (or a superseding ADR for a MAJOR change).
  2. Re-read and reconcile every file that restates a rule changed here: CLAUDE.md, COLLABORATING.md, CODESTYLE.md, CONTRIBUTING.md, .specify/AGENTS.md, and the affected .specify/personas/*.md checklists.
  3. Update any .specify/templates/* section that quotes a changed rule.
  4. Run the constitution-diff CI job locally (or read its PR comment) and resolve every file it lists.
  5. Announce the version bump in the PR description so reviewers re-read the constitution before approving.