Module Architecture
TramAI 0.6.0 replaced informal module lists and hand-maintained dependency trees with two machine-readable authorities that are verified on every build. This page is for contributors and architects: it explains the layer model, the catalog fields, the boundary rules, and how a violation is caught.
If you only need to know which module to add to your application, read Module Design instead.
The Two Authorities
| Authority | File | What it owns |
|---|---|---|
| Module catalog | config/quality/module-catalog.yml (schema v3) | Every Gradle project exactly once, with maturity, visibility, owner, dependency policy, layer, publishability, API stability, release inclusion, and rationale |
| Module boundaries | config/quality/module-boundaries.yml (schema v1) | Forbidden dependency edges, plus the exact knownAllowedEdges exceptions to them |
Project-setting ↔ catalog equality is verified by verifyModuleManifest; the generated module
matrix (docs/reference/module-matrix.md) is checked for drift by verifyModuleMatrixDrift.
Neither file is a document you edit to describe reality after the fact — both are inputs the
build reads.
Not an authority:
docs/architecture/module-dependency-graph.mdis a v0.5.0-era snapshot (48 modules). Its generator stamps every output with thev0.5.0baseline identity, so a regeneration cannot produce a current document. Treat the catalog and the boundaries file as the current policy, andverify060Architectureas the current verified topology.
The Ten Layers
The catalog assigns every module to exactly one layer. The module count in 0.6.0 is 60 Gradle projects:
| Layer | Responsibility | Modules |
|---|---|---|
core-contracts | Annotations, shared contracts, request/response models, exceptions. Near zero-dependency. | tramai-core, tramai-bom |
runtime-execution | Engine, structured output, workflow orchestration, standalone runtime. | tramai-engine, tramai-structured, tramai-orchestration, tramai-standalone |
governance-security | Policy, approval, audit, evidence, sovereign runtime profile. | tramai-security, tramai-sovereign |
persistence | Storage implementations for sovereign state. | tramai-persistence-file, tramai-persistence-jdbc |
provider-adapters | Vendor providers behind the provider SPI. | tramai-openai, tramai-anthropic, tramai-ollama, tramai-azure-openai, tramai-bedrock, tramai-gemini, tramai-deepseek |
framework-integrations | Spring core, starters, provider starters, secrets. | 18 tramai-spring* modules |
operations-observability | OTel, platform wiring, and the server/MCP/dashboard surfaces. | tramai-observability, tramai-platform, tramai-server, tramai-mcp, tramai-dashboard* |
higher-capabilities | Memory, RAG, embeddings, scheduling, vector stores. | tramai-memory, tramai-memory-store*, tramai-rag, tramai-embedding, tramai-scheduler, tramai-vectorstore-spi, tramai-vectorstore-chroma, tramai-vectorstore-pgvector |
applications-examples | Executable examples. Excluded from the release. | 9 examples:* projects |
testing-support | TCKs, fakes, consumer boundary tests. | tramai-testing, tramai-spring-consumer-boundary, tramai-spring-consumer-selective |
* internal / not published in 0.6.0. Publishability per module comes from the catalog — see
the Modules Catalog for the consumer-facing view.
Conceptual Dependency Direction
The layer order is orientation, not a strict enforced hierarchy. Exact policy lives in each
module's dependencyPolicy and in the boundaries file:
core-contracts
↓
runtime-execution
↓
governance-security / persistence / provider-adapters
↓
framework-integrations
↓
operations-observability / higher-capabilities
↓
applications-examples
testing-support → supports contracts without entering the runtime dependency flow
Dependency Policies
Each catalog entry names a dependencyPolicy, and each policy declares the layers a module may
depend on:
| Policy | Allowed layers |
|---|---|
core | core-contracts, testing-support |
runtime | core-contracts, runtime-execution, governance-security, testing-support |
provider-adapter | core-contracts, runtime-execution, provider-adapters, testing-support |
framework | core-contracts, runtime-execution, governance-security, persistence, provider-adapters, framework-integrations, operations-observability, higher-capabilities, testing-support |
testing | core-contracts, framework-integrations, testing-support |
example, bom | All layers |
runtime allows governance-security because tramai-engine depends on tramai-security;
framework allows operations-observability and higher-capabilities because the
platform/server/RAG/vectorstore modules live there. The policy check runs in addition to the
forbidden-edge check.
Key Boundary Rules
These rules are enforced by module-boundaries.yml and verified against the resolved
dependency graph. New cycles and forbidden edges fail the gate:
tramai-coremust stay as close to zero-dependency as practical.tramai-enginedepends ontramai-coreand owns orchestration and retry policy.tramai-structuredowns schema generation, extraction, deserialization, and structured failure analysis.tramai-observabilityis optional and must remain decoupled from the core happy path.- Provider modules plug into the engine through the provider SPI; they must not implement retry, fallback, or circuit-breaker logic.
tramai-standalonecomposes the minimal runtime for non-framework users.- Framework adapters such as
tramai-spring-coreare thin integration layers, not alternate runtimes. tramai-orchestrationowns workflow execution semantics and persistence boundaries.tramai-schedulersits above orchestration and must not backflow scheduling concerns into the engine.tramai-serversits above orchestration and the scheduler, exposing operational APIs rather than redefining workflow semantics.tramai-mcpis an adapter, not a second orchestration engine.tramai-platformowns tenancy, governance, and plugin/runtime policy above the server layer.tramai-dashboardis a UI packaging module and must remain optional at runtime.
On top of those rules, the boundaries file hard-codes these classes of forbidden edge:
# core-contracts may not depend on any implementation layer
- fromLayer: core-contracts
toLayer: provider-adapters
reason: "Core contracts must not depend on specific provider implementations"
# published modules may not depend on internal or excluded modules
- fromPublished: true
toPublishability: internal
reason: "Published modules must not depend on internal modules (consumers can't resolve them)"
Exceptions are exact (fromModule, toModule) pairs in knownAllowedEdges with a written
reason — for example :tramai-platform → :tramai-server, and the BOM's inclusion references.
There is no second exception mechanism: if you believe you need a new edge, add it there with a
rationale or change the design.
How It Is Enforced
./gradlew verify060Architecture
The gate aggregates existing verifiers rather than reimplementing them, and writes a deterministic machine-readable report:
| Check id | What it proves |
|---|---|
module-manifest | Catalog ↔ Gradle project equality, catalog validity |
publishing-topology | BOM membership and publishability derivation |
dependency-boundaries | Forbidden layer edges and dependency-policy violations |
dependency-cycles | No new dependency cycle |
global-state | No new process-global mutable state finding |
api-architecture | Public API dumps, compatibility policy, stability inversion |
protocol-catalog | No stable runtime protocol contract removed |
cancellation-safety | No new cancellation finding, no worsened risk |
provider-contracts | Provider TCK enrollment guards |
store-contracts | Store TCK enrollment guards |
The report lands at build/reports/tramai/architecture/architecture-report.json and is written
before the task throws, so a failing run still leaves evidence. Two consecutive runs must
produce a byte-identical report. The gate also fails closed when its evidence sources are
unavailable: an unreadable baseline or a dependency-resolution failure fails every
baseline-backed check rather than silently passing.
Structural metrics — file size, constructor parameter count, global mutable state,
nondeterminism sources, broad cancellation catches — are held to a committed baseline
(config/quality/0.6.0-baseline.json) with any accepted deviation recorded in
config/quality/maintainability-deviations.yml as an MQ-#### entry carrying a measured
baseline, a tolerated ceiling, a reason, an owner, and a target phase. A deviation ceiling may
only be raised deliberately, with the measurement to justify it.
Enrolling a New Module
Adding a module is a catalog change first, code second:
- Add the project to the handwritten
include(...)list insettings.gradle.kts. The settings list is deliberately not generated;verifyModuleManifestenforces exact-set equality between settings and the catalog in both directions. - Add one
- path: ":tramai-x"entry toconfig/quality/module-catalog.ymlwithlayer,maturity,visibility,owner,dependencyPolicy,publishability,apiStability,releaseInclusion, a non-blankrationale, and adescription. The catalog parser rejects invalid combinations outright (internalpublishability withpublicvisibility,stableAPI stability withexperimentalmaturity,includedrelease withexcludedorinternalpublishability, blank owner). - Keep every edge you add inside your policy's allowed layers, and inside the forbidden-edge
rules. If an edge is genuinely required, add the exact pair to
config/quality/module-boundaries.yml#knownAllowedEdgeswith a reason. - Regenerate the module matrix (the generator task derives it from the catalog) so
verifyModuleMatrixDriftstays green. - Add a module card under
docs/modules/— coverage against the catalog inventory is gated byverifyModuleDocContract. - If the module is published, capture its public API dump so
apiCheckhas a signature to freeze. See API and Binary Compatibility. - Run
./gradlew verify060Architectureand./gradlew verifyPr.
BOM membership and the publishable project set are both derived from the catalog
(publishability == published, and published && releaseInclusion == included for the BOM), so
you do not hand-maintain a second list of coordinates — but verifyModuleManifest does compare
the derived sets against the real BOM constraints and fails on drift.
Related Pages
- Module Design — the consumer-facing module split and reading order
- API and Binary Compatibility — what the freeze covers
- Quality Gates — the full gate set and the release command
- Architecture Overview — runtime execution flow
