Quality Gates
TramAI 0.6.0 is the release where the quality story stopped being a set of good intentions and became an executable contract. This page is for contributors: it lists what the gates are, what each one refuses to let through, and how to run them.
Every gate name on this page is a real Gradle task or a real config file in the repository. If a name is not here, it does not exist.
The Authoritative Command
./gradlew verify060MaintainabilityRelease --warning-mode=fail --no-daemon
This is the release verification entry point. It is a fail-closed aggregator: it depends on
the full authority list below, and its doLast refuses to pass if any required authority task
was not actually scheduled in the graph, or if the publishable module set comes out empty. It is
deliberately not part of the normal check lifecycle — everyday development uses check and
verifyPr; release verification is invoked explicitly.
| Domain | Authorities |
|---|---|
| Lifecycle and tests | check, all subproject test tasks |
| Formatting and analysis | spotlessCheck, verifyStaticAnalysis, verifyStaticSafetyGuards, verifyCompilerWarnings, verifyDependencyHygiene, verifyCancellationSafety |
| Architecture and contracts | verify060Architecture, verifyMaintainabilityBaseline, verifyModuleManifest, verifyModuleMatrixDrift, apiCheck |
| Coverage and mutation | verifyCriticalCoverage, verifyReleaseMutation |
| Test integrity | verifyJUnitTestSignatures, verifyChangePolicy |
| Consumers | Kotlin and Java consumer smoke compile tasks, :examples:spring-sovereign-starter:e2eTest |
| Publication | verifyPublicationMetadata, verifyPublishedLocalArtifacts, verifyVersionAlignment |
| Sovereign runtime | verifySovereignRuntimeReleaseCandidate, verifySovereignRuntimeVerificationRepoClosure, verifySovereignRuntimeConsumerSmoke, verifySovereignDocumentIntelligenceEvidenceRun, verifySovereignRuntimeApiBoundary, verifySovereignRuntimeClosureDocs, verifySovereignOpsObservabilityDocs, verifySovereignEvidencePackContainsReleaseBundle, prepareSovereignReleaseArtifacts, verifySovereignReleaseManifest |
| Documentation and audit | verifyReleaseDocumentationIntegrity, verifyReleaseRequiredFiles, verifyAuditClosure |
| Offline proof | verify060ZeroEgress |
| Build logic | the build-logic included-build test suite |
The Day-To-Day Gates
./gradlew verifyPr is the primary local gate. It aggregates the maintainability baseline,
change policy, module manifest, module matrix drift, critical coverage, the mutation ratchet,
the module documentation contract, and the build-logic test suite. The root build script wires
the six code-quality authorities into both check and verifyPr.
| Gate | Authority file | Invariant |
|---|---|---|
spotlessCheck | root .editorconfig + a pinned KtLint engine | Every Kotlin file changed relative to the formatting base satisfies one deterministic formatting policy. The gate is ratcheted (-PtramaiFormattingBaseRef=<sha> in CI), so untouched legacy source is never mass-reformatted. |
verifyStaticAnalysis | config/detekt/detekt.yml, config/detekt/baseline.xml | One repository-wide Detekt run (pinned 1.23.8). The baseline is a ceiling, not an allowance: removals are allowed, additions fail unless the change is an explicit baseline migration. |
verifyCompilerWarnings | config/warnings/baseline.json | Zero unbaselined compiler warnings. Warning identity is path plus Kotlin internal diagnostic name plus a normalized message fingerprint — line and column are excluded so moving code does not manufacture a new finding. |
verifyDependencyHygiene | config/dependency-hygiene/exemptions.yml | No unused direct main-scope dependency. Exemptions are module + configuration + coordinate + rationale; a stale exemption fails. |
verifyStaticSafetyGuards | config/quality/static-safety-guards.yml | Four production-code bans: raw lifecycle creation (GlobalScope, Thread, Executors.new*, CoroutineScope, SupervisorJob, addShutdownHook), unbounded HTTP body reads, sensitive-payload logging, and System.out/System.err. Exemptions are per rule + path + symbol + occurrence count, and the count is ratcheted: a new same-symbol occurrence in an approved file fails, and a removal below the declared count is stale and also fails. |
verifyCancellationSafety | base-aware comparison | CancellationException is preserved and propagated on every path; no new broad catch that swallows or wraps it. |
verifyJUnitTestSignatures | — | Guards against JUnit Jupiter silently discarding a test whose JVM return type is not void. |
verifyChangePolicy | change classification | Rejects change mixes that should not ship together (for example production code plus its governing analyzer, or an unbounded deviation increase). |
verifyModuleManifest | config/quality/module-catalog.yml | Settings ↔ catalog exact-set equality, valid classification combinations, BOM and publishing derivation consistency. |
verifyModuleMatrixDrift | docs/reference/module-matrix.md | The generated module matrix matches a fresh generation from the catalog. |
verifyModuleDocContract | docs/modules/ | Every module in the catalog has a card. |
verifyMaintainabilityBaseline | config/quality/0.6.0-baseline.json, config/quality/maintainability-deviations.yml | Source metrics, resolved dependencies, cancellation catches, global state, nondeterminism sources, and the runtime protocol catalog stay inside the committed baseline and its declared deviation ceilings. verifyRuntimeNondeterminism is part of this authority. |
Baselines are the mechanism that lets a large, evolving repository be honest instead of aspirational. The rule that makes them meaningful is that debt may only shrink: a green gate means zero new findings, not zero findings.
A deliberate, documented exception
verifyCompilerWarnings verifies modules only. build-logic's own warnings are frozen in the
inventory but not gated, because kotlin-dsl compilation cannot be reproduced by a standalone
kotlinc and root listeners cannot capture included-build compiler output. That gap is
documented in the epic rather than papered over.
Architecture and API Gates
./gradlew verify060Architecture
The architecture gate runs ten checks — module-manifest, publishing-topology,
dependency-boundaries, dependency-cycles, global-state, api-architecture,
protocol-catalog, cancellation-safety, provider-contracts, store-contracts — and writes
build/reports/tramai/architecture/architecture-report.json before it throws, so a failing run
still leaves typed evidence. Two consecutive runs must produce a byte-identical report. See
Module Architecture for the layer rules it enforces.
apiCheck is the binary-compatibility authority: the committed api/<module>.api dumps must
match both the current source and the stability policy for that module. The Kotlin and Java
consumer smoke compile tasks feed the same api-architecture check. See
API and Binary Compatibility.
Critical Coverage and Mutation Ratchets
Coverage and mutation policy are declared in config/quality/test-quality.yml and measured into
config/quality/coverage-baseline.json.
Coverage. Nine critical modules participate — :tramai-core, :tramai-engine,
:tramai-security, :tramai-sovereign, :tramai-standalone, :tramai-structured,
:tramai-orchestration, :tramai-persistence-file, :tramai-persistence-jdbc — with a 1.0
percentage-point regression tolerance and one declared exclusion (**/model/**, generated model
classes). There is deliberately no percentage floor: the ratchet is the policy, and the
measurement is base-authoritative — the policy and baseline that judge a pull request are read
from the pull request's base, never from the candidate. The 0.6.0 release notes summarise this
as a "100% critical branch/instruction coverage ratchet on core runtime packages"; the numbers
that actually exist live in the coverage baseline, so read those rather than the summary.
Mutation. The committed mutation baseline records the measured population for seven
families — policy, approval, routing, retry, tools, evidence, structuredOutput —
with a v2 identity schema (module, class, method descriptor, index, mutator, description,
block; line numbers excluded so moving code does not churn the ratchet). Mutants are reduced to
a canonical KILLED / NON_KILLED outcome in the ratchet, while the raw PIT status is kept for
diagnosis. MutationRatchetVerifier then enforces an exact-set comparison against the base
authority:
- a previously killed identity that becomes non-killed fails;
- a new non-killed identity fails;
- a survivor is only tolerated when
config/quality/mutation-classifications.ymlcarries an exact-identity classification (equivalent-mutantortool-limitation) with a reason; - narrowing the configured families, modules, target classes, or target tests fails;
- drift in the PIT plugin version, engine version, mutator set, or timeout configuration fails.
Two distinct modes matter operationally. On a pull request, verifyMutationRatchet runs no
PITest campaign — it is a pure in-memory comparison of committed files against the base, fully
deterministic and cheap. The real PITest campaign is generateCriticalMutationBaseline, a
human-supervised measurement ceremony run during enrollment or audit, never in pull request CI.
Documentation Gates
Documentation is gated like code. verifyReleaseDocumentationIntegrity checks relative links and
rejects hardcoded developer paths in release docs; verifyReleaseRequiredFiles requires the
release artifacts (changelog, release notes, migration guide, readiness document) to exist;
verifyAuditClosure requires every P0/P1 audit finding to be closed and every deferred finding
to retain an owner and rationale. Contributor change guides live under
docs/architecture/change-guides/ and are expected to be updated in the same change that moves
the contract they describe.
When a Gate Is Red
The repository's protocol is diagnosis before editing:
- Read the failing step and its full log; inspect the artifact if there is one.
- Classify the failure — production defect, test defect, analyzer defect, baseline mismatch, environment mismatch, workflow defect, or transient external failure.
- Do not modify a gate until the evidence shows the gate is wrong, and do not raise a deviation ceiling to make a red build green.
- Stop and report after two unsuccessful fixes instead of applying a third speculative patch.
apiCheckandverify060Architectureare not wired intocheck; run them explicitly (or run the release command) when your change touches the public surface or the module graph.
Related Pages
- Module Architecture — the ten layers and the architecture gate
- API and Binary Compatibility — the stability classes and
apiCheck - Testing — writing tests the gates recognize
- CI Integration — running the gates in a pipeline
