Conventions & Standards
- Scope
- Rules written in the repository (
CONTRIBUTING.md, README.md, CI scripts) plus the deployment invariants this documentation relies on. Formatting tooling is not documented in a contributor guide (core/pom.xml carries a google-java-format.version property).
- Authority
CONTRIBUTING.md. Where it cites a deeper doc it is linked below.
1. Environment invariants
| Rule | Detail |
| Names | Keep production and staging on separate domains (this site uses example.com as a placeholder). Never call a staging or local deploy "production" or "verified live". |
No localhost | No browser-facing or token-issuer URL may point at localhost/127.0.0.1. Public hostnames: app. / admin. / api. / auth. (plus codeintel., builder., livekit. when used) on the environment's domain. |
| Baked variables | NEXT_PUBLIC_* are read at runtime; a value set at build time is baked in as a fallback. Never bake a localhost value into an image you deploy (ADR-0007). |
| Issuer | Core must accept exactly the issuer Keycloak stamps (PUBLIC_AUTH_URL/realms/agentic-ai); extra issuers go in KEYCLOAK_ADDITIONAL_ISSUERS. See ADR-0005. |
| Internal exception | Container healthchecks, compose DNS (http://keycloak:8080, http://core:8080), JWK fetch stay as-is; do not "fix" them to the domain. |
| Encryption key | APP_ENCRYPTION_KEY must be identical everywhere core boots; otherwise stored provider keys and channel secrets become undecryptable. |
| Deploys | Follow the Deployment Guide. Flyway versions share one namespace across controlplane and tenant directories. |
Never power-cycle, stop, delete or tear down a live deployment casually
No operator, CI job or automated agent should reset, reboot or stop a host, or run
docker compose down on a production or shared staging stack, without an explicit human decision for that exact command. A hung deployment is evidence to collect (logs, container states, memory), not a thing to reboot: a reset is a power-yank of the tenant database.
2. Verification vocabulary
- Be exact about state: compiled ≠ tested ≠ deployed-to-staging ≠ deployed-to-prod. "Merged" is not "deployed" — diff the running image against the repo before assuming.
- Verify before you relay: a local negative result must not be promoted into a system-wide fact.
- PR descriptions must say exactly how the change was verified — compiled, unit-tested, run against Postgres, tried in a browser (
CONTRIBUTING.md, PR template).
- Dated design documents under
docs/ are point-in-time; the date is when the document was true.
3. Database migrations (Flyway)
- Location:
core/app/src/main/resources/db/migration/{controlplane,tenant}/. The two directories are one version namespace: controlplane/V227 and tenant/V227 collide and every core instance fails at boot. Pick the next number free across both (README §6).
- A shipped migration is never edited, renamed or renumbered; add a new one. A genuine pre-release renumber needs a justified line in
scripts/ci/flyway-lint-allowed-renames.txt (human sign-off, not verified by the script).
- Destructive DDL (
DROP TABLE, DROP COLUMN, tightening to NOT NULL) requires a marker comment on its own line: -- gated:contract.
- After any Flyway rename use
mvn clean; stale target/classes shadow real migrations.
- Before deploying an image that adds/changes migrations, check migration state against the target database.
CODEOWNERS requires maintainer review for core/app/src/main/resources/db/migration/, core/crypto/, core/security/, .github/ and scripts/ci/.
4. Repository hygiene gates
| Script | Enforces (CONTRIBUTING.md unless noted) |
flyway-lint.sh | Applied-migration edit guard, destructive-DDL marker, version-collision check. |
skip-expiry.sh | Test skips tracking a known gap carry an expiry, e.g. expires:2026-08-15, or CI fails. |
InjectableConstructorTest, UniqueBeanNamesTest (core/app; make spring-wiring) | Beans with several constructors and no @Autowired, duplicate Spring bean / JPA entity names (compile fine, crash core at startup). |
entity-name-lint.sh | Entity, table, bean and controller base-path collisions. |
secret-scan.sh | gitleaks over new commits. Never commit real keys, tokens or passwords, including in .env*.example, fixtures or docs; fake credentials go under src/test/ or *.test.*/*.spec.* (.gitleaks.toml). |
check-file-size.sh | File-size ratchet (hard cap 700 lines; a file over 665, within 5% of the cap, fails unless listed in the shrink-only baseline scripts/ci/file-size-baseline.txt); wired in static-gates.yml. |
5. Build and deploy conventions
- Release images are published to GHCR with version tags and signed with cosign; pin a version in Compose or Helm rather than tracking
latest.
- Deploy
core first (it owns migrations), verify healthy, then the frontends.
- Build images from a clean checkout, never a tree with local edits (
make images for source builds).
- PgBouncer must stay
POOL_MODE=session (README §1).
- No production core image without the boot check described on Build, Test & Verify.
6. Tooling hygiene
- One Maven build at a time; do not run
npm install while other processes run npx in the same project.
- Never route around a real build error with
--force / --legacy-peer-deps (README §2).
- External-system integrations go through the MCP dynamic connector registry (
core/mcp), not bespoke Java modules (README §7).
- Sign off every commit (DCO) and use Conventional Commits (
CONTRIBUTING.md).
Evidence & references
CONTRIBUTING.md — "Checks" and per-gate sections; README.md §1, §2, §4, §6, §7.
.github/CODEOWNERS, .gitleaks.toml, scripts/ci/*.sh, core/pom.xml.
- Deployment Guide, ADR Index.