Quality Gates & CI Checks
Every gate defined in the repository: what it checks, which script or workflow implements it, and how it is triggered. Strategy and layers are on Test Strategy; the workflows themselves are described on CI/CD Pipelines.
CI OK (the summary job of ci.yml) and CodeQL OK (codeql.yml) are the
required checks (CONTRIBUTING.md). CI OK covers the static gates (static-gates.yml, called
from ci.yml), the secret scan, DCO sign-off, dependency review and the docs link check. The same static gates run locally through
make gates and the .githooks/pre-push hook (enable with make hooks).
Static gates (static-gates.yml)
Called from ci.yml on pull requests and pushes to main, or by manual dispatch. Each script also runs from a shell:
| Gate | Script | What it enforces | Mode |
|---|---|---|---|
| Flyway lint | scripts/ci/flyway-lint.sh | (a) no edit or renumber of a migration that already exists at the merge-base; (b) new migrations containing DROP TABLE, DROP COLUMN or ALTER COLUMN ... SET NOT NULL need a -- gated:contract marker; (c) no version number twice across controlplane/ and tenant/ (one shared namespace). Writes scripts/ci/flyway.lock, uploaded as the flyway-lock artifact. Escape hatches: flyway-lint-allowed-renames.txt, flyway-lint-allowed-edits.txt. | Blocking |
| Duplicate bean / entity names | UniqueBeanNamesTest (core/app, run by mvn verify; make spring-wiring) | Duplicate Spring bean names (scanned components, Spring Data repositories, unconditional @Bean methods, named with Spring's own generator) and JPA entity names across core modules (a class of failure that passes compile and unit tests but fails at Spring context start). | Blocking |
| Injectable constructor | InjectableConstructorTest (core/app, run by mvn verify; make spring-wiring) | A Spring component with several constructors and none marked @Autowired ("No default constructor found" at startup), a @ConfigurationProperties class Boot cannot bind, and a no-arg constructor that silently shadows a @Value one. | Blocking |
| Namespace lint | scripts/ci/entity-name-lint.sh | Entity, table, bean and controller base-path collisions; allowlist in entity-name-lint-allowlist.txt. | Blocking |
| Expired test-skip check | scripts/ci/skip-expiry.sh | Any test.fixme, @Disabled or .skip that carries an expires:YYYY-MM-DD token fails once the date has passed; environment-gated skips are exempt. | Blocking |
| File-size ratchet | scripts/ci/check-file-size.sh | Hard cap of 700 lines: no new tracked source file over 665 lines, i.e. over the cap or within 5% of it (override FILE_SIZE_CAP, FILE_SIZE_NEAR_CAP_PCT); no file in scripts/ci/file-size-baseline.txt may grow, and a change may not add a path to the baseline (compared with the merge-base). Never raise a baseline to go green; --update regenerates it after a split. | Ratchet |
| Module-size ratchet | scripts/ci/check-module-size.sh | ADR-0023: at most 300 main .java files per core/* module (override MODULE_SIZE_MAX) and no classes directly in com.agenticai.core.agents; modules and the root package already over are listed in scripts/ci/module-size-baseline.txt, may not grow, and a change may not add an entry (compared with the merge-base). --update lowers the baseline after a split. | Ratchet |
| Secret scan | scripts/ci/secret-scan.sh | gitleaks over commits added since the base ref (a separate Secret scan job), not full history. | Blocking |
The pre-push hook runs the full static-gate suite when pushing to main, Flyway lint alone for other pushes that touch core/app/src/main/resources/db/migration, and gitleaks (when installed) on every push. git push --no-verify bypasses it.
Build and test jobs (ci.yml)
Path-filtered; the CI OK job aggregates them. Details are on CI/CD Pipelines.
| Job | Local equivalent | Mode |
|---|---|---|
core (Maven verify, JDK 21, Postgres with pgvector + AGE) | make test-core | Blocking |
web, web-admin (lint, typecheck, test, build) | make test-web, make test-web-admin, make lint | Blocking |
python (ruff, sidecar pytest, code-intel harness) | make test-python, make lint | Blocking |
python-extra (app-builder, tool-sandbox, voice-agent) | — | Blocking |
node-services (mcp-stdio-runner, example MCP servers) | — | Blocking (http-gateway example advisory) |
shellcheck, hadolint | make shellcheck | Blocking |
compose (docker compose config) | make compose-validate | Blocking |
helm (lint, template, kubeconform, render guards) | make helm-lint | Blocking |
Other workflows
| Workflow | What it runs | Trigger |
|---|---|---|
codeql.yml | CodeQL SAST for java-kotlin, javascript-typescript and python | Pull request, push, weekly, dispatch |
scorecard.yml | OpenSSF Scorecard | Weekly, push, dispatch |
e2e.yml | Playwright suite in tests/e2e against the compose stack built from the commit; see E2E Suites | Nightly, dispatch |
Running the gates yourself
make gates # all static gates
make hooks # install the pre-push hook
make lint # eslint + tsc, ruff, shellcheck
make test # core, web, web-admin, python
make compose-validate
make helm-lint
Required-status-check configuration on GitHub, current pass/fail status of any workflow, and coverage figures are not recorded in the repository.
Evidence & references
.github/workflows/static-gates.yml,ci.yml,codeql.yml,scorecard.yml,e2e.yml.scripts/ci/flyway-lint.sh,entity-name-lint.sh,skip-expiry.sh,check-file-size.sh,secret-scan.sh.Makefile,CONTRIBUTING.md,.githooks/pre-push.
Related: Test Strategy · E2E Suites · ADR-0002