Introduces the SDD root: a v1.0.0 constitution and machine-readable AGENTS.md grounded in the project's real conventions; six EARS-aware persona spec-review checklists that cross-reference .claude/personas/; feature-spec/ADR/threat-model/ api-contract templates; a fully worked _example feature; a living RTM; and an adrs/ pointer that reuses the existing docs/adr/ archive. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6.7 KiB
Familienarchiv Constitution
Version: v1.0.0 Status: Ratified Date: 2026-06-13 Adoption ADR: docs/adr/041-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
- The backend is organised package-by-domain under
org.raddatz.familienarchiv; a new domain lives in its own package, never spread across layer packages. - Controllers never call repositories directly — a controller calls only services.
- A service accesses only its own domain's repository; cross-domain data is fetched through the other domain's service, never its repository.
- The frontend mirrors the backend domain split under
frontend/src/lib/<domain>/, and cross-domain imports are allowed only wherefrontend/eslint.config.js(boundaries/dependencies) permits them. - A
Person(historical subject) and anAppUser(login account) are distinct domains and never share an identity or an account guard. - Lazy-collection-bearing entities are never serialized across the controller boundary; the owning service assembles an explicit view inside the transaction (see ADR-036).
- A new backend domain package is added to
ArchitectureTest's package allow-lists in the same change that introduces it. - Synchronous cross-domain side effects use in-transaction domain events, not direct service-to-service write calls (see ADR-006).
2. Security Defaults
- Every
POST,PUT,PATCH, andDELETEendpoint carries@RequirePermission(Permission.X)— there is no unguarded mutating endpoint. - Authorization uses the typed
Permissionenum and@RequirePermission, never magic-string@PreAuthorize. - All user input is validated at the system boundary (controller / form action), and validation failures return a typed
ErrorCode, never a raw exception. - Audit fields (
createdBy/updatedBy) are set from the session principal inside the service and are never bound from a request body. - Untrusted text is rendered through Svelte's default
{...}escaping;{@html}is never used on user- or import-derived strings. - 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. - Logs never contain PII beyond a stable user/entity UUID — no names, email addresses, document contents, or transcription text.
- Every state-mutating endpoint is covered by an Unwanted-behavior requirement (EARS
If) describing the unauthenticated/unauthorized response. - A dependency security audit runs on every CI run (
npm audit --audit-level=highfrontend, Semgrep.semgrep/security.ymlbackend) and nightly; ahighfinding blocks merge.
3. Code Quality Rules
- 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.
- KISS beats DRY — no premature abstraction; an abstraction is introduced only on the third real caller.
- Each commit does exactly one logical thing and references its Gitea issue (
Closes #n/Refs #n) on the last line of the body. - No backwards-compatibility shims are added for code that has no callers.
- Every entity/DTO field the backend always populates carries
@Schema(requiredMode = REQUIRED), andnpm run generate:apiis run after any backend model or endpoint change. - A new
ErrorCodeis added in all four places at once:ErrorCode.java,frontend/src/lib/shared/errors.ts,getErrorMessage(), andmessages/{de,en,es}.json. - Dates built from an ISO date string append
T12:00:00to avoid UTC off-by-one. npm run lint(Prettier + ESLint, including the domain boundary rule) passes before every commit.
4. Do-Not-Touch List
- Do not edit generated artifacts:
frontend/src/lib/generated/api.ts,frontend/src/lib/paraglide/,frontend/.svelte-kit/,frontend/build/,backend/target/. - Do not edit an
AcceptedADR — supersede it with a new, higher-numbered ADR. - Do not upgrade
actions/upload-artifact/download-artifactpast@v3(Gitea act_runner lacks the v4 protocol — ADR-014). - Do not remove or weaken a CI guard step (banned-pattern greps, self-tested regexes) without an ADR recording why.
- Do not commit to
maindirectly — all work flows through a branch and a PR. - Do not edit a Flyway migration that has shipped; add a new forward-only migration instead.
- Do not commit the worktree copy directories (
familienarchiv-*,.worktrees/) ordata/.
5. Dependency Policy
- A new runtime dependency (backend
pom.xmlor frontenddependencies) requires an ADR inAcceptedstatus before it is merged. - 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-playwrightpin). - Renovate manages dependency-update PRs; a major-version bump is treated as a feature requiring its own spec and review, not an auto-merge.
- 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:
- Bump the Version header per the semantic rule above and record the change in docs/adr/041-sdd-adoption.md's revision log (or a superseding ADR for a MAJOR change).
- 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/*.mdchecklists. - Update any
.specify/templates/*section that quotes a changed rule. - Run the
constitution-diffCI job locally (or read its PR comment) and resolve every file it lists. - Announce the version bump in the PR description so reviewers re-read the constitution before approving.