Athena — definition-of-done.md

Definition of Done — Workspace-wide

This document is the single source of truth for what "done" means across every project in this workspace. It applies to mahmoud-consultancy, europeLogin, auditPic, claimio, valideerleeftijd, developer-portal, roomy-mobile, athena — and any new project added later.

Each project's own CLAUDE.md may extend this with project-specific rules but must not weaken any control here. If a rule cannot be met for a specific change, open a GitHub issue with the dod-exception label explaining why, and link it from the PR.

Sister docs: root CLAUDE.md (tech-stack standard, port registry, branching, secrets), docs/ci-cd-reference.md (canonical pipeline).


1. Pre-merge checklist (every PR)

A PR is not ready to merge until all of these are true. The CI pipeline enforces most of them; the PR author is responsible for the rest.

Code quality

  • [ ] Builds cleanly: mvn -B clean verify (backend) and npm run build (frontend) both pass with zero warnings
  • [ ] No TODO/FIXME/XXX introduced in the diff without a linked GitHub issue
  • [ ] No commented-out code, no console.log, no System.out.println, no e.printStackTrace()
  • [ ] No new dependencies without justification in the PR description
  • [ ] No file > 400 lines (split into smaller units) — refactor when crossed
  • [ ] No method > 50 lines and no class > 300 lines unless unavoidable (e.g. generated code)
  • [ ] No any type in TypeScript without an inline // any: <why> comment

Tests

  • [ ] Branch coverage ≥ 90 % on every new/changed class, component, or service (measured by JaCoCo for Java, Karma coverage-istanbul-reporter for Angular)
  • [ ] Line coverage ≥ 95 % on new code (lines that should not be tested — e.g. trivial getters — should be @Generated-excluded with a reason)
  • [ ] No @Ignore, @Disabled, xit(...), it.skip(...) left behind
  • [ ] Cucumber feature for every user-facing webapp flow that crosses HTTP — at minimum the golden path. Features live in backend/src/test/resources/features/ and are run by mvn verify via the cucumber-spring integration.
  • [ ] Maestro flow for every user-facing mobile flow — at minimum login, primary action, logout. Flows live in .maestro/ at the repo root and are runnable via maestro test .maestro/.
  • [ ] Integration tests use real Postgres / Redis via Testcontainers — never in-memory H2 or mock Redis, since prior incidents have shown migration- and locking-related drift between mocked and real backends.

Security

  • [ ] OWASP ZAP baseline scan (make test-security or the zap-baseline workflow job) passes with zero high / medium findings. Low findings need a written justification in the PR.
  • [ ] SpotBugs + FindSecBugs runs in mvn verify and reports zero high-priority findings. Configure via spotbugs-maven-plugin with <effort>Max</effort> and <threshold>Low</threshold>, fail the build on high.
  • [ ] Dependency-Check (org.owasp:dependency-check-maven) fails on CVSS ≥ 7. Run nightly via the security-nightly workflow.
  • [ ] npm audit runs in CI with --audit-level=high and fails the build.
  • [ ] Snyk / Aikido scan (/aikido:scan from the plugin) passes on changed files.
  • [ ] No secrets in the diff — pre-commit gitleaks hook enforces this. New secrets go to 1Password → Sealed Secrets per root CLAUDE.md.
  • [ ] All API endpoints have @PreAuthorize or are explicitly marked @PermitAll with a reason comment.
  • [ ] All user input flows through @Validated DTOs with Bean Validation annotations (@NotBlank, @Size, @Pattern, etc.).
  • [ ] All SQL is parameterized (use @Query with named params or JPA, never string concatenation).

Observability

  • [ ] Every new endpoint has structured logging at INFO (request received), DEBUG (decision points), ERROR (handled exceptions). Use the SLF4J Logger named after the class; no string concatenation in log messages (log.info("user {} did {}", id, action)).
  • [ ] Every long-running operation (>200 ms expected) is wrapped in a Micrometer timer (@Timed or programmatic).
  • [ ] Every external call (HTTP, DB, queue) has a resilience4j @CircuitBreaker and @Retry annotation with sensible defaults (3 retries, 50 % failure threshold).
  • [ ] No printStackTrace. All exceptions go through the @ControllerAdvice global handler which logs and returns a ProblemDetail.

Docs

  • [ ] If the change adds/removes an env var, docs/{project}/setup.md is updated
  • [ ] If the change adds/removes an API endpoint, docs/{project}/api.md is updated
  • [ ] If the change is a non-trivial architectural decision, an ADR is added in docs/{project}/decisions/NNNN-<slug>.md

2. Java / Spring Boot specifics

These rules apply to every backend.

Idioms

  • Always use Builder for value objects with 3+ fields. Prefer Lombok @Builder on records (record Foo(...) { @Builder public Foo ... }) or hand-written builders. Constructor-with-positional-args is acceptable only for ≤ 2 fields.
  • Always use Stream for any in-memory collection transformation. No for (X x : xs) { results.add(...) } patterns when a .map/.filter/.collect would do. Exception: when readability genuinely suffers (loop with multiple side effects, early-return logic) — comment why.
  • Always use Optional<T> at API boundaries that may return absent values. Never return null from a public method. Optional is not for fields or collection elements.
  • Always use record for DTOs unless you need JPA / Hibernate proxies. Records get equals/hashCode/toString for free.
  • Always use var for local variables when the RHS is a constructor call or a method that obviously returns the inferred type. Don't use var for primitives or lambdas where the type adds clarity.
  • Always use Map.of / List.of / Set.of for immutable literals. Never new ArrayList<>(){{ add(...); }}.
  • Prefer composition over inheritance. No extends between domain classes; use interfaces + delegation.
  • No static state. No static mutable fields. static final constants only.
  • Constructor injection only. No @Autowired on fields. Use the implicit Spring 6 single-constructor injection (no annotation needed).

Architecture

  • Standard layering: Controller → Service → Repository. No layer skipping.
  • Controllers are thin: validate input, delegate, map output. No business logic.
  • Services are stateless. State lives in the DB / Redis.
  • Repositories return entities or projections — never DTOs. The Service maps to DTO.
  • DTOs at every API boundary (in dto/ package). Never serialize a JPA entity directly.
  • Mappers: hand-written (preferred for small projects) or MapStruct. No reflection-based copy (BeanUtils.copyProperties).
  • Transactions: @Transactional(readOnly = true) is the default on services. Override with @Transactional only on write methods. Never put @Transactional on a controller.

Build

  • Java 25, maven-compiler-plugin <release>25</release> (bumped from 21 on 2026-07-29 to match interimplaza's backend/pom.xml, which already ran on 25 in practice; other projects follow separately, not blocking)
  • maven-enforcer-plugin with <requireUpperBoundDeps/> and <dependencyConvergence/>
  • spotless-maven-plugin formatting on mvn verify — Google Java Format
  • jacoco-maven-plugin with BUNDLE rule: 90 % branch, 95 % line — fails the build below threshold

3. Frontend stack selection & Angular / TypeScript specifics

Frontend stack — pick by surface type

  • Consumer-facing / marketing / content sites must be built in Astro, not Angular. No new Angular consumer/marketing surface may be created.
  • Grandfathered exceptions (existing Angular marketing surfaces kept as-is; DoD scans must not re-flag these as violations):
    • interimplaza-web (frontend/interimplaza-web/) — owner decision 2026-07-30: keep Angular for now, no rewrite happening currently. The Astro migration stays on the backlog as a future initiative — tracked in issue #821 (intentionally left open, not closed/dismissed, not an active gap needing action).
    • glorylabs-web — pre-existing tracked Astro-migration exception.
  • Internal admin/back-office tools, authenticated app shells, and dashboards are not covered by this rule — Angular/React/etc. is fine there. This rule is scoped to the public marketing/content surface only.

Angular / TypeScript specifics

  • Standalone components only. No NgModule in any new code. Use provideRouter, provideHttpClient, etc.
  • Signals for component state. RxJS only at the HTTP boundary and where we genuinely need stream semantics.
  • ChangeDetectionStrategy.OnPush on every component. No exceptions.
  • inject() instead of constructor parameter DI inside standalone components / services.
  • Strict mode: strict: true and strictTemplates: true in tsconfig. noImplicitAny, strictNullChecks, noUncheckedIndexedAccess all on.
  • No any — if you must, comment // any: <reason> on the same line.
  • ESLint with @angular-eslint/recommended, @typescript-eslint/strict-type-checked, unicorn/recommended. Build fails on any lint error.
  • Karma: port: 0 in karma.conf.js (Karma picks a free port automatically). Never hardcode 9876 — multiple projects can otherwise collide.
  • No inline templates > 20 lines. Move to *.component.html.
  • Feature flags: config/feature-flags.ts. Code behind a flag must have a removal date in the file header — flags older than 90 days require an issue.

4. Flutter / Mobile specifics

  • State management: riverpod. No provider (old), no bloc, no getX.
  • flutter analyze zero warnings. analysis_options.yaml extends package:flutter_lints/flutter.yaml plus the project's stricter rules.
  • Coverage ≥ 90 % via flutter test --coverage + lcov.
  • Maestro flows for every user-visible screen — see Pre-merge above.
  • Codemagic is the CI for mobile. The codemagic.yaml must include: flutter analyze, flutter test --coverage, Maestro cloud run, Firebase App Distribution upload, Codecov upload.
  • Firebase: all writes go through Cloud Functions with App Check. Direct client writes are forbidden on production rules.

5. Cross-cutting infrastructure

Branching & commits

Same as root CLAUDE.md. PRs require:

  • Conventional Commit title (feat(scope): …, fix(scope): …, chore(scope): …)
  • Squash-merge only; merge commit message = PR title
  • At least 1 approving review from a non-author (Claude GitHub App counts when configured)
  • All CI checks green

CI/CD (see docs/ci-cd-reference.md for the full template)

Every project's pipeline runs the same canonical stages, in this order:

  1. lint — formatters + linters
  2. test — unit + integration (Testcontainers) + coverage gate
  3. security — SpotBugs/FindSecBugs, Dependency-Check, ZAP baseline
  4. buildmvn package + npm run build + Docker buildx multi-arch (linux/amd64,linux/arm64)
  5. publish — push to ghcr.io/mahmoudholding/<project>/<service>:sha-<short> (+ branch tag) — canonical org is mahmoudholding; the older theroomyapp and glorylabs namespaces are legacy
  6. deploy-staging (on push to develop) — helm upgrade --install to the staging namespace on the shared k3s VPS
  7. deploy-prod (on push to main) — same to the production namespace, gated by manual approval

All projects share the same VPS (136.144.174.219) and the same ingress-nginx. Hostnames per project are listed in the root CLAUDE.md ingress registry.

Secrets

1Password → Bitnami Sealed Secrets → k3s, as in root CLAUDE.md. Never commit a plaintext secret. Pre-commit gitleaks enforces this.

Ports

See root CLAUDE.md port registry. Every project owns a fixed band — local dev binds to the band, Helm config.serverPort matches, no hostPort: / NodePort:.


6. Definition of Done — quick checklist

Before opening a PR, the author confirms:

  • [ ] CI is green locally: mvn -B clean verify && cd frontend/<portal> && npm run build && npm run test:ci
  • [ ] 90 %+ branch coverage on new/changed units
  • [ ] Cucumber feature (webapp) or Maestro flow (mobile) for the new user-visible behavior
  • [ ] ZAP baseline run (make test-security) reports zero high/medium
  • [ ] SpotBugs + FindSecBugs zero high-priority findings
  • [ ] Used builders for DTO/value object construction (≥ 3 fields)
  • [ ] Used streams for collection transformations
  • [ ] DTOs at API boundary, @Validated on all inputs
  • [ ] Docs updated (setup.md, api.md, ADR if architectural)
  • [ ] No any, no null returns, no printStackTrace, no commented-out code
  • [ ] Sealed secrets updated if a new secret was added
  • [ ] PR title follows Conventional Commits

If you can tick every box, you're done.


7. Exceptions

If a rule genuinely cannot be met (e.g. legacy code coverage gate, a third-party CVE without an upstream fix), open a dod-exception GitHub issue documenting:

  1. Which rule
  2. Why it can't be met
  3. What compensating control exists
  4. When the exception will be revisited

Link the issue from the PR description. Reviewers may merge the PR with the exception linked, but never silently waive a rule.

Reacties

Nog geen reacties