TramAI - governed AI workflows for Java and Kotlin

Adding an Event

Every tramai.* runtime event is declared exactly once in RuntimeEventCatalogue, referenced through the compile-time RuntimeEvents registry, and emitted through the engine's emission helper. The architecture guard scans both bytecode and source: any tramai.* string literal that does not go through RuntimeEvents.X fails the build — even if the literal already exists in the catalogue.

The Extension Points

All in tramai-core/src/main/kotlin/dev/tramai/core/observation/event/:

FileOwns
RuntimeEventCatalogue.ktThe object catalogue holding allEvents: List<RuntimeEventDefinition>; init fails fast on duplicate names, conflicting attribute types, required attributes outside the allowed set, and undeclared or duplicate metric mappings. event(name) errors on an unknown id. Also holds the dynamic DynamicAttributeNamespaces.WORKFLOW_CONTEXT namespace.
RuntimeEventModel.ktRuntimeAttributeKey<T>(name, valueType), RuntimeMetricDefinition, and RuntimeEventDefinition(name, domain, allowedAttributes, requiredAttributes, sensitivity, auditEligible, evidenceEligible, metricMapping, spanEligible, failurePolicy)
RuntimeAttributes.ktOne RuntimeAttributeKey<T>("tramai…", T::class) per canonical key
RuntimeMetrics.ktMetric descriptors and the all aggregate
RuntimeEvents.ktThe compile-time registry — for example val WORKER_HEARTBEAT = RuntimeEventCatalogue.event("tramai.worker.heartbeat")
RuntimeEvent.ktThe typed builder: set rejects non-allowed attributes and wrong types, build enforces required attributes; plus `RuntimeEvent.of(definition

Real names you can pattern-match against: tramai.worker.heartbeat, tramai.route.selected, tramai.circuit.opened, tramai.retry.scheduled, tramai.dlp.inspection_failed, tramai.token_budget.soft_limit_exceeded, tramai.parse.failure.

Files That Change

Work top to bottom — a new event must be consistent with every registry at once:

  1. RuntimeAttributes.kt — add a RuntimeAttributeKey if a new attribute is needed.
  2. RuntimeMetrics.kt — add a RuntimeMetricDefinition if a new metric is needed.
  3. RuntimeEventCatalogue.kt — add the RuntimeEventDefinition(...) entry to allEvents.
  4. RuntimeEvents.kt — add val X = RuntimeEventCatalogue.event("tramai…").
  5. docs/reference/runtime-event-catalogue.md — regenerate, do not hand-edit (see below).
  6. The emission call site in the owning module, through the module's emission helper.

The Generated Reference Document

docs/reference/runtime-event-catalogue.md is produced by RuntimeEventCatalogueRenderer and carries a "do not edit by hand" header. RuntimeEventCatalogueDocumentationTest fails with a "Regenerate it from RuntimeEventCatalogueRenderer" message when the committed document drifts from the catalogue. Regenerate it in the same change that adds the event.

Mandatory Contract Tests

TestWherePins
RuntimeEventCatalogueTesttramai-core/src/test/kotlin/dev/tramai/core/observation/event/Uniqueness, metadata, canonical types, determinism
RuntimeEventCatalogueDocumentationTestsame packageGenerated document matches the renderer
RuntimeEventCatalogueArchitectureTesttramai-observability/src/test/kotlin/dev/tramai/observability/Two fail-closed layers (below)
RuntimeEventCatalogueVerifierRulesTestsame packageMutation semantics of the verifier

RuntimeEventCatalogueArchitectureTest enforces two independent scans:

  1. Bytecode scan — ASM9 visitLdcInsn over the classes of tramai-core, tramai-engine, tramai-orchestration, and tramai-observability. It skips only dev/tramai/core/observation/event/, fails on any "tramai." LDC constant, and fails closed on zero classes scanned so a broken classpath can never silently pass.
  2. Repo-wide source scan — every module's src/main/**/*.kt, matched against "tramai\.[a-zA-Z0-9_.-]*". Only an exact allowlist of configuration-property literals passes.

Compatibility

  • Event, attribute, and metric names are the long-term observability contract. External dashboards, alerts, and audit tooling depend on them; renaming is a breaking change.
  • The catalogue init fails fast on duplicate names, conflicting attribute types, required attributes outside the allowed set, undeclared metric mappings, and duplicate metric names. A new event has to be consistent with all registries simultaneously.
  • Emission honours the event's declared failurePolicy: an emit failure must never break the invocation flow.

Verification

./gradlew :tramai-observability:test \
  --tests '*RuntimeEventCatalogueArchitectureTest*' \
  --tests '*RuntimeEventCatalogueVerifierRulesTest*'
./gradlew :tramai-core:test --tests '*RuntimeEventCatalogue*'
./gradlew verifyPr

The guard re-runs whenever a literal is added anywhere: tramai-observability/build.gradle.kts declares every module's production Kotlin sources as test inputs, so Gradle cannot skip it as up to date.

What Not To Change In This Pull Request

  • The renderer and its documentation test. The reference document is generated.
  • The architecture guard test and RuntimeEventCatalogueVerifierRulesTest. They pin mutation semantics; never weaken them.
  • Existing event names or attribute types.
  • config/quality/0.6.0-baseline.json.

Common Mistakes

SymptomCause
Documentation test failsdocs/reference/runtime-event-catalogue.md was hand-edited instead of regenerated
RuntimeEventCatalogueArchitectureTest fails on a literalA "tramai.…" string was emitted directly instead of through RuntimeEvents.X — the guard flags even already-catalogued literals
Catalogue init throwsA required attribute was not added to allowedAttributes, or a metric mapping was left undeclared
Guard passes locally but fails in CIA module was added to the build without adding its production sources as guard inputs