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).
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.
mvn -B clean verify (backend) and npm run build (frontend) both pass with zero warningsTODO/FIXME/XXX introduced in the diff without a linked GitHub issueconsole.log, no System.out.println, no e.printStackTrace()any type in TypeScript without an inline // any: <why> commentcoverage-istanbul-reporter for Angular)@Generated-excluded with a reason)@Ignore, @Disabled, xit(...), it.skip(...) left behindbackend/src/test/resources/features/ and are run by mvn verify via the cucumber-spring integration..maestro/ at the repo root and are runnable via maestro test .maestro/.make test-security or the zap-baseline workflow job) passes with zero high / medium findings. Low findings need a written justification in the PR.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.org.owasp:dependency-check-maven) fails on CVSS ≥ 7. Run nightly via the security-nightly workflow.--audit-level=high and fails the build./aikido:scan from the plugin) passes on changed files.gitleaks hook enforces this. New secrets go to 1Password → Sealed Secrets per root CLAUDE.md.@PreAuthorize or are explicitly marked @PermitAll with a reason comment.@Validated DTOs with Bean Validation annotations (@NotBlank, @Size, @Pattern, etc.).@Query with named params or JPA, never string concatenation).Logger named after the class; no string concatenation in log messages (log.info("user {} did {}", id, action)).@Timed or programmatic).@CircuitBreaker and @Retry annotation with sensible defaults (3 retries, 50 % failure threshold).printStackTrace. All exceptions go through the @ControllerAdvice global handler which logs and returns a ProblemDetail.docs/{project}/setup.md is updateddocs/{project}/api.md is updateddocs/{project}/decisions/NNNN-<slug>.mdThese rules apply to every backend.
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.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.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.record for DTOs unless you need JPA / Hibernate proxies. Records get equals/hashCode/toString for free.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.Map.of / List.of / Set.of for immutable literals. Never new ArrayList<>(){{ add(...); }}.extends between domain classes; use interfaces + delegation.static mutable fields. static final constants only.@Autowired on fields. Use the implicit Spring 6 single-constructor injection (no annotation needed).dto/ package). Never serialize a JPA entity directly.@Transactional(readOnly = true) is the default on services. Override with @Transactional only on write methods. Never put @Transactional on a controller.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 Formatjacoco-maven-plugin with BUNDLE rule: 90 % branch, 95 % line — fails the build below thresholdinterimplaza-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.NgModule in any new code. Use provideRouter, provideHttpClient, etc.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: true and strictTemplates: true in tsconfig. noImplicitAny, strictNullChecks, noUncheckedIndexedAccess all on.any — if you must, comment // any: <reason> on the same line.@angular-eslint/recommended, @typescript-eslint/strict-type-checked, unicorn/recommended. Build fails on any lint error.port: 0 in karma.conf.js (Karma picks a free port automatically). Never hardcode 9876 — multiple projects can otherwise collide.*.component.html.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.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.flutter test --coverage + lcov.codemagic.yaml must include: flutter analyze, flutter test --coverage, Maestro cloud run, Firebase App Distribution upload, Codecov upload.Same as root CLAUDE.md. PRs require:
feat(scope): …, fix(scope): …, chore(scope): …)docs/ci-cd-reference.md for the full template)Every project's pipeline runs the same canonical stages, in this order:
lint — formatters + linterstest — unit + integration (Testcontainers) + coverage gatesecurity — SpotBugs/FindSecBugs, Dependency-Check, ZAP baselinebuild — mvn package + npm run build + Docker buildx multi-arch (linux/amd64,linux/arm64)publish — push to ghcr.io/mahmoudholding/<project>/<service>:sha-<short> (+ branch tag) — canonical org is mahmoudholding; the older theroomyapp and glorylabs namespaces are legacydeploy-staging (on push to develop) — helm upgrade --install to the staging namespace on the shared k3s VPSdeploy-prod (on push to main) — same to the production namespace, gated by manual approvalAll 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.
1Password → Bitnami Sealed Secrets → k3s, as in root CLAUDE.md. Never commit a plaintext secret. Pre-commit gitleaks enforces this.
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:.
Before opening a PR, the author confirms:
mvn -B clean verify && cd frontend/<portal> && npm run build && npm run test:cimake test-security) reports zero high/medium@Validated on all inputssetup.md, api.md, ADR if architectural)any, no null returns, no printStackTrace, no commented-out codeIf you can tick every box, you're done.
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:
Link the issue from the PR description. Reviewers may merge the PR with the exception linked, but never silently waive a rule.
Reacties