STIG Help

Configure an element

Every STIG element module has its own build.gradle.kts that applies the STIG convention plugin and declares the module's element settings via the stig { element { ... } } DSL block. This page describes what goes inside that block.

A freshly initialized module records its explicit element type and import policy. For example:

import com.stigway.metamodels.highway.ASIL import com.stigway.metamodels.highway.ElementType plugins { id("ai.exida.stig") } stig { element { group = groups["activities"] id = "example.activities.braking.context.braking" elementType = ElementType.ACTIVITY asil = ASIL.B importEnabled = false } }

Pass --asil QM|A|B|C|D to stigInitElement to emit the ASIL import and assignment shown above. The option is explicit rather than defaulted because the initializer must not guess a safety classification. This assignment is element build configuration; it does not currently add asil = ... to the generated Kotlin model object.

Source analysis defaults to DISABLED during initialization. The generated file contains safe insertion points for P1/P3/P4/P5/P7 plus explicit analyzerExecution, complexityMetrics, and functionDescriptions overrides that keep every executable source-code-analysis feature off. Configure and enable one phase at a time after its required inputs are ready. Pass --sourceCodeAnalysis INHERIT only when the new module should immediately use its group/default/project source-code-analysis policy.

In most cases that's all you need. The remaining defaults come from the root project's stigProject { ... } block in build.gradle.kts (defaults, groups, source profiles), and the convention plugin provides the rest.

What the convention plugin gives you

Applying id("ai.exida.stig") to a module is enough to get:

  • Kotlin/JVM compilation (org.jetbrains.kotlin.jvm, java-library),

  • Dokka documentation generation (org.jetbrains.dokka),

  • Coverage reporting (org.jetbrains.kotlinx.kover),

  • The matching ai.exida:stig-core dependency on implementation (version pinned to the plugin version, so stig-core and the plugin always stay in lockstep),

  • Per-module STIG tasks: stigCheckModel, stigGenerateMarkdown, stigGenerateHtml, stigImportModel, stigAnalysisP1PatchNativeSources, stigAnalysisP1BuildNativeTarget, stigAnalysisP1ExtractNativeCompileCommands.

Model import is an explicit source edit. Configure recurring file inputs in the root stigProject.model.imports block; element.importEnabled opts a module into code-derived Context import. See Import and export models.

You do not need a dependencies { implementation("ai.exida:stig-core:...") } line — adding one is redundant. Add dependencies { ... } entries only for project-local dependencies you actually need (e.g. another module in the same build).

Element settings (the element { ... } block)

Each property is optional. When unset, it falls back to the matching value from the group, source profile, or project default declared in stigProject { ... }.

Property

Type

Purpose

group

String

Logical group of elements that share common defaults (e.g. "components", "activities", "clusters"). Not a folder. The named entry must exist in stigProject { group("...") }.

id

String

Canonical ID of the context-model element, normally <context package>.<top-level instance name>. stigInitElement generates it; model checking verifies that it still matches the Kotlin context declaration.

sourceProfile

String

Names a source profile declared at the project level via stigProject { sourceProfile("...") { ... } }. Each group has a default profile; set this only when this element needs a different one (e.g. a different upstream repository root).

placement

StigElementPlacement

DIRECT or STIG_SUBDIR — where the module's STIG viewpoint sources live (module root vs. a stig/ subfolder).

elementType

StigElementType

Metamodel type for this element. One of VEHICLE, ITEM, SYSTEM, SUBSYSTEM, CLUSTER, COMPONENT, ACTIVITY, UNIT.

importEnabled

Boolean

Enables this module's explicit stigImportModel code reader. It requires a configured C++ interface inventory. Root SYML and Highway file inputs use stigProject.model.imports independently of this flag.

continueOnCodeAnalysisErrors

Boolean

Soft-fail switch for analysis: when true, aggregation tasks treat an analysis failure on this element as non-fatal and keep going.

implementationClassName

String

Upstream implementation class name (e.g. the C++ class the element wraps, like "FusionActivity"). Distinct from the Kotlin module name. Used by source-code analysis tasks to match LLVM symbols. Defaults to the module's name converted to UpperCamelCase (e.g. fusion_activity → FusionActivity).

implementationNamespace

String

Upstream namespace containing implementationClassName (e.g. "app::aeb::fusion_activity"). Used to build the LLVM symbol prefix for class-scoped member matching. Multiple namespaces can be passed as a ;-separated list. Falls back to the task's convention default when unset.

topicsFile

String

Topics JSON file for this element (lives in the upstream user-sources tree). Absolute or relative to the user-repo root (e.g. "activities/aeb/fusion_activity/aeb_fusion_activity.json"). Consumed by stigAnalysisP5DeriveInterfacePoints.

interfacePointsFile

String

Path to the ASIL interface-points JSON for this element (lives in the STIG configuration tree, alongside stig-project, not in the user repo). Absolute or relative to the stig-project root.

Analyzer input and interface points

Per-element analyzer-input construction settings live under analyzerInput { ... }. The interfacePoints { ... } block tells P5 how entry points, sources, and sinks are obtained before analyzer run plans are generated:

Property

Type

Default

Purpose

mode

InterfacePointMode

LLVM_HARNESS_DISCOVERY

LLVM_HARNESS_DISCOVERY derives source/sink symbols from compiled LLVM IR harness markers. DSL_DECLARATION uses explicit source(...) and sink(...) declarations.

entryPoint/entryPoints

String/List<String>

emptyList()

Entry-point function names used by interface-point symbol derivation and PhASAR run-plan generation.

topicsFile

StigRepoFile

unset

Upstream topics JSON file used for interface-point import and annotation.

interfacePointsAnnotationFile

StigRepoFile

unset

ASIL/interface annotation JSON file. In LLVM_HARNESS_DISCOVERY mode this file annotates discovered symbols; it does not create source/sink symbols by itself.

source(...)

function call

none

Explicit source symbol declaration for DSL_DECLARATION mode.

sink(...)

function call

none

Explicit sink symbol declaration for DSL_DECLARATION mode.

In LLVM_HARNESS_DISCOVERY mode the P5 derivation task scans the final analysis module.ll, demangles LLVM symbols, and keeps harness marker functions. It does not scan C++ harness source files. The matching prefixes are hard-coded to:

  • harness::SourceGroup<

  • harness::SourceLeaf<

  • harness::SinkGroup<

  • harness::SinkLeaf<

Field-level analysis uses harness::SourceLeaf<...> and harness::SinkLeaf<...> symbols. Group-level analysis uses harness::SourceGroup<...> and harness::SinkGroup<...> symbols. For leaf markers, template argument 1 is interpreted as the topic type and template argument 2 as the harness::tags::... field path. The annotation JSON is then used to attach ASIL to those discovered symbols.

This means a field from the annotation JSON is analyzed only if the harness or stub code materializes the corresponding marker call in the compiled analysis bitcode. Accessing message.data.some_field directly is not enough for LLVM_HARNESS_DISCOVERY unless it also flows through a marker function.

stig { element { // ... analyzerInput { interfacePoints { mode = InterfacePointMode.LLVM_HARNESS_DISCOVERY entryPoint = "app::lifecycle::Activity::Step()" topicsFile = repos["sourceRepo"] / "activities/aeb/aeb_topics.json" interfacePointsAnnotationFile = repos["model_repository"] / "models/interface-definitions/Foo.json" } dataAndControlFlowAnalysis { enabled = true } cascadingFailuresAnalysis { enabled = true } } } }

Compilation-unit selection

Compilation-unit selection is split into two explicit pipeline phases:

  • P3 provenanceBuild { } selects softwareUnderAnalysis native compilation units and transform-owner provenance compilation units from the P2 LLVM-compatible compilation commands and builds their P3 bitcode.

  • P4 softwareUnderAnalysis { } chooses the compilation units P5 will use for the final analysis module.

provenanceBuild { exclude(".*logging.*", ".*tracing.*") } softwareUnderAnalysis { from(provenanceBuild.compilationUnits) include( repos["sourceRepo"] / "activities/foo/foo_activity.cpp", repos["stig-project"] / "activities/foo/src/main/resources/harness/foo_harness.cpp", ) }

P3 provenanceBuild starts from the configured upstream compile-command artifact (nativeBuild.compileCommands by default). include(...) narrows the selected entries when present; exclude(...) removes entries from that selected set.

P3 include(...) and exclude(...) are path rules over compile-command entries. P4 include(...) is a concrete source-file list; every listed unit must already be present in the P3 output.

Metrics scope

Per-function source-code metrics are enabled under complexityMetrics { ... }. The source scope is selected separately under sourceCodeSelection { ... }, so function descriptions and metrics can consume the same selected compile database.

sourceCodeSelection { from(analyzerInput.compileCommands) include("activities/foo") exclude("activities/foo/test") } complexityMetrics { enabled = true }

Property

Type

Default

Purpose

sourceCodeSelection.from(...)

pipeline artifact

analyzerInput.compileCommands

Compile-command database consumed by source parsers and complexity metrics.

sourceCodeSelection.include(...)/includes

path rule list

empty

Include source paths. Empty means all C/C++ files from the selected compile commands.

sourceCodeSelection.exclude(...)/excludes

path rule list

empty

Exclude source paths after includes.

complexityMetrics.enabled

Boolean

true

Enables or disables complexity metric collection.

The same include/exclude rule is applied to the compilation units passed to clang-tidy and to the function-definition diagnostics accepted into function_metrics.json, so the per-function table and component total use the same scope.

The aggregator tasks referred to above (stigCheckModel, stigGenerateMarkdown, and stigGenerateHtml) are root-level umbrella tasks registered by the convention plugin: each one depends on the same-named per-module task in every subproject that applies id("ai.exida.stig"). They do not filter by importEnabled.

For the canonical types and their possible values, see the Dokka reference for com.stigway.engine.config linked at the top of each module's build.gradle.kts.

Elements built by Bazel: the nativeBuild { bazel { ... } } block

Elements whose implementation code is built by Bazel declare the target in nativeBuild { bazel { ... } }. Compilation-unit selection is configured separately by provenanceBuild { } and softwareUnderAnalysis { }:

stig { element { group = "components" nativeBuild { bazel { workspaceDir = "." target = "//components/ranging_sensor:ranging_sensor" trackActionInputs = true } } } }

The labels above are placeholders — replace them with the Bazel target and source paths that exist in your repository. The workspaceDir is interpreted relative to the user repository root configured in your stigProject { ... } source profile.

Available fields inside bazel { ... }:

  • workspaceDir — Bazel workspace directory (relative to the user repo root, or absolute).

  • target — single Bazel label to build.

  • targetPattern — alternative pattern when target isn't a single label.

  • buildOption("...")/startupOption("...") — extra Bazel flags (each call appends one).

  • compdbTarget("...")/interfaceTarget("...") — labels consulted when generating the compilation database and interface artifacts.

  • trackActionInputs — when true (default), STIG runs the real Bazel target, fingerprints the C++ compile-action inputs reported by bazel aquery, and wires that fingerprint into source-code-analysis tasks. Header changes, generated source changes, command-line changes, and BUILD-graph changes then invalidate stale STIG analysis outputs without hand-maintained Gradle source lists. Set it to false only for fast/offline/degraded runs where native changes should not invalidate analysis.

When the nativeBuild { bazel { ... } } block is omitted, the element is treated as non-Bazel and Bazel-only pipeline tasks become no-ops for it.

Patching upstream sources: the patch(...) calls

Some elements need small one-time edits to the upstream C/C++ source tree before Bazel builds (workarounds for missing includes, build-system quirks, example-mode injections, etc.). Declare them with patch(StigPatchSpec(...)) inside the element block.

The example below uses an element from the activities group rather than components — it just illustrates that any element, regardless of its group, can declare patches:

import com.stigway.gradle.StigPatchSpec stig { element { group = "activities" nativeBuild { bazel { /* ... */ } } patch( StigPatchSpec( file = "platform/threading/work_queue.cpp", find = "^\\s*#include\\s*\"platform/threading/threading_logging\\.h\"\\s*$", replace = "#include \"platform/threading/threading_logging.h\"\n#include <thread>", regex = true, skipIfContains = "^\\s*#include\\s*<thread>\\s*$", required = false, description = "Add <thread> include after threading_logging.h", ), ) } }

The per-module stigAnalysisP1PatchNativeSources task applies every declared patch before Bazel runs and is a no-op when no patches are declared. skipIfContains makes patches idempotent — they're skipped when the target text is already present, so re-running the task is safe.

Full reference example

The following build.gradle.kts sets every per-element option explicitly and uses inline comments to list the alternative values for enum-typed fields. It is meant as a reference, not a starting point — real modules should set only what they need to override.

import com.stigway.engine.config.StigElementPlacement import com.stigway.engine.config.StigElementType import com.stigway.engine.config.StigImportSource import com.stigway.gradle.StigPatchSpec plugins { id("ai.exida.stig") } stig { element { // Logical group; must match an entry declared in // `stigProject { group("...") { ... } }` in settings.gradle.kts. group = "components" // Override the group's default source profile. Only set when this // element needs a different one (for example, a different upstream // repository root than the group's default). sourceProfile = "external_sources" // Where this module's STIG viewpoint sources live. placement = StigElementPlacement.DIRECT // Alternative: StigElementPlacement.STIG_SUBDIR // Metamodel type for this element. elementType = StigElementType.COMPONENT // Alternatives: VEHICLE, ITEM, SYSTEM, SUBSYSTEM, CLUSTER, ACTIVITY, UNIT // When true, external content (implementation, requirements, tests, …) // is pulled into this element's STIG model by the import pipeline. importEnabled = true // Data source format consulted during import. importFrom = StigImportSource.SOURCES // Alternatives: SYML, YAML, JSON, XML // Directory holding the import-side interface-definition files. importFromLocation = "../external/interfaces" // Soft-fail switch: when true, root aggregator tasks treat an analysis // failure on this element as non-fatal and keep going. continueOnCodeAnalysisErrors = true // Native Bazel build configuration. Omit the whole block for non-Bazel // elements; Bazel-only pipeline tasks then no-op for this element. nativeBuild { bazel { workspaceDir = "." target = "//components/ranging_sensor:ranging_sensor" // Alternative to `target` when you want to build a pattern (e.g. // everything under a directory) rather than a single label: // targetPattern = "//components/ranging_sensor/..." // Extra Bazel flags. buildOptions = listOf("--cxxopt=-std=gnu++17") startupOptions = listOf("--batch_cpu_scheduling") // Labels consulted when generating the compilation database and // interface artifacts. Default to `target` when omitted. compdbTargets = listOf("//components/ranging_sensor:ranging_sensor") interfaceTargets = listOf("//components/ranging_sensor:interfaces") // Keep source-code-analysis outputs tied to the native build graph. // Default is true; set false only for explicit fast/offline runs. trackActionInputs = true } } // Narrow the LLVM-based analysis outside the Bazel block. provenanceBuild { exclude(".*logging.*") } softwareUnderAnalysis { from(provenanceBuild.compilationUnits) include( repos["sourceRepo"] / "components/ranging_sensor/ranging_sensor.cpp", repos["sourceRepo"] / "platform/runtime/component_base.cpp", ) } // Source-code patches applied before Bazel runs. Repeat `patch(...)` // once per patch; `skipIfContains` keeps re-runs idempotent. patch( StigPatchSpec( file = "platform/threading/work_queue.cpp", find = "^\\s*#include\\s*\"platform/threading/threading_logging\\.h\"\\s*$", replace = "#include \"platform/threading/threading_logging.h\"\n#include <thread>", regex = true, skipIfContains = "^\\s*#include\\s*<thread>\\s*$", required = false, description = "Add <thread> include after threading_logging.h", ), ) } } dependencies { // Optional: project-local dependencies to elements that you use. The `ai.exida:stig-core` dependency // is wired automatically by the convention plugin; do not add it manually. implementation(project(":clusters:my_cluster")) }

See also

  • API reference for com.stigway.engine.config (enums and config types used here): see the Dokka API reference.

  • Project-wide configuration: the stigProject { ... } block in settings.gradle.kts (defaults, groups, source profiles, source-code-analysis toggles).

  • Creating a new element module: Add a source-backed element.

23 October 2026