STIG Help

Import and export models

stigImportModel reads configured model inputs and updates Kotlin specification files in the consumer's source directories. stigExportModel exports selected model content or its native JSON schema. Checking and reporting run in subsequent invocations. Neither task initializes Gradle modules.

Configure recurring imports

Keep the source locations, order, viewpoint selections and exceptional name bindings in the consumer's build.gradle.kts:

import com.stigway.metamodels.highway.Viewpoint.* import com.stigway.engine.model.ModelImportMode.* import com.stigway.engine.model.MissingInterfacePointPolicy.DECLARE_INCOMPLETE import com.stigway.metamodels.highway.ElementType stigProject { model { imports { syml { type = ElementType.COMPONENT root(repos["implementation"] / "applications", into = "components/applications") definitionsFrom(repos["implementation"] / "shared/interfaces") context = OVERWRITE interfacesModule = ":interfaces:shared" bindings = emptyMap() } highwayJson { from("imports/safety.highway.json") context = COMPLEMENT_WITH_DESCRIPTION_AND_ASIL missingInterfacePoints = DECLARE_INCOMPLETE requirements = OVERWRITE analysis = OVERWRITE interfacesModule = ":interfaces:shared" } } exports { highwayJson { elements("Controller") viewpoints(CONTEXT, REQUIREMENTS, ANALYSIS) output("build/exchange/controller.highway.json") } } } }

File inputs are processed in declaration order. Here SYML defines Context ports, service interfaces and member uses; the native file adds Requirements and Analysis and supplies selected metadata for those same Context interface points. Descriptions append once. Missing endpoint ASILs are filled; conflicting values on the same endpoint are HIGH and leave its existing classification unchanged. Component, port and member ASILs are separate attributes. The component's ASIL is not a substitute for an absent endpoint ASIL. Other valid updates continue. Newly filled descriptions, appended description contributions and filled endpoint ASILs report LOW when their Context source is written. Missing incoming metadata, equal values and description contributions already present produce no change notice; QM is an explicit ASIL value. Rebuilding an unchanged Context does not repeat LOW notices, while unresolved ASIL conflicts continue to report HIGH. Diagnostics identify the endpoint and include available source provenance. Metadata complements preserve existing directions, service types and member selections. Use Element/port for a port and Element/port/member for its member use, including when both have the same name.

OVERWRITE replaces the selected viewpoint contents, including empty containers and omitted source descriptions/ASIL. Canonical Kotlin element identity and project hierarchy remain in the receiving project. Context complements change only endpoint metadata unless missing-interface declarations are explicitly enabled. SKIP leaves a viewpoint unchanged; it is the default for an omitted selection. viewpoints(...) is a shorthand for selecting OVERWRITE on several viewpoints. enabled = false skips an input. elements(...) narrows element selection. More than one file input replacing the same element/viewpoint reports HIGH and skips the later replacement.

The default missingInterfacePoints = REPORT reports unmatched endpoints without adding them. DECLARE_INCOMPLETE retains declarations supplied by native Context inside an existing element and reports HIGH with their source provenance. It never guesses a missing module, ambiguous match, provider connection, payload or signature. Methods retain supplied functionType text; omitted signature details stay unspecified. An unavailable service member uses canonical UnresolvedInterfaceMember, which can be selected through PortMemberUse and produces a HIGH model finding in its declaring module until its definition is supplied, when checks are enabled there. Consumers do not repeat defects in imported declarations. Native Context is reconciled before Requirements and Analysis references bind, so those viewpoints can refer to the retained endpoints. Native producers can supply partial Context using ordinary canonical Highway constructors; no format-specific endpoint DTO or synthetic module is needed. Interpreting a foreign C signature string as typed parameters and return types belongs in its converter; the native importer preserves supplied canonical properties without inferring them from prose.

interfacesModule selects an existing model library by absolute Gradle path. Initialize it with stigInitModule and configure its module.packageName; new shared declarations are written to ImportedServiceInterfaces.kt in that package. Existing generated declarations already located in the destination library retain their packages and exact generated symbols. This allows several catalogues to move into one library without changing their Kotlin identities. A repeated generated symbol in several packages is ambiguous and must be reconciled explicitly. Move the source files, update Gradle dependencies, and compile/check the model before the next import so the existing declarations can be located. It requires no Element or viewpoint container. For an element-owned catalogue, sharedDefinitionsOwner remains available; configure one destination setting per input. Neither setting assigns Element.parent. Modules using those declarations need ordinary Gradle dependencies on the destination module. Root mappings and engineering-specific exceptions belong in the consumer.

Import, check, then report

./gradlew stigImportModel ./gradlew stigCheckModel ./gradlew stigGenerateHtml

Run any consumer conversion task before the first command. Import does not invoke conversion, compilation, checking or reporting. HIGH findings do not fail import: matched elements and resolvable declarations are written while missing modules, unresolved declarations/links and conflicting metadata remain in the report. A bad reference-list entry does not discard its valid siblings. A file that cannot be rendered keeps its previous contents, with a model-file-skipped finding; independent files can still be written. CRITICAL findings stop the file batch before source writes. A failed filesystem replacement rolls back preceding replacements. The diagnostic report is .stig/model-import/report.json; no intermediate Context JSON or imported Kotlin tree is written under build/. There is no apply task, preview mode or compatibility alias.

File import reads the existing compiled Kotlin declarations to retain their canonical names, packages, members and viewpoint filenames. Generated classes are placed at file scope before root instance properties. Existing nested classes are flattened with their enclosing names joined by underscores; member instance properties retain the containment relationships. Subclasses of InterfacePointAnalysis use an Analysis suffix; legacy names such as ControllerAnalysis_OutputType become Controller_OutputAnalysis. Moving or renaming a class changes its members' declaring-class IDs. Existing flat class names that follow this convention remain stable on later imports. The writer keeps unresolved-member reasons on the shared definition and does not copy that boilerplate onto each Context use. See the Kotlin declaration layout in Elements and CREATE viewpoints.

After initializing modules or changing declarations manually, run stigCheckModel in a separate invocation before importing. On a fresh checkout with no compiled model, SYML discovery still reports the source paths and proposed destinations. Import never compiles the model it is updating.

Imported viewpoint files are overwritten on refresh, including manual edits in those files. Review the Git diff and run the applicable model checks afterward. Reference validation during import does not replace engineering review or the model-rule checks. Select exactly one stigImportModel task per invocation; a combined import and compile/check/report invocation is rejected. The root task processes the whole configured batch.

Map source trees to modules

Each root(source, into) replays component directories below the source root into the target root and omits a trailing technical syml/ directory, regardless of capitalization. For example, applications/Monitor/syml/Monitor.syml maps to components/applications/Monitor. Discovery is per declared atomic component: files containing only interfaces do not create modules, and compatible partial component declarations are merged.

An explicit binding takes precedence over the mirrored path:

bindings = mapOf("example.LegacyMonitor" to "components/applications/Monitor")

The target must already declare the intended canonical Kotlin element. Without a binding, matching tries the mirrored module, then a unique element name inside the configured target roots. Unique case, underscore or hyphen differences use the existing spelling and report LOW. An explicit different-name binding reports MODERATE. Missing or ambiguous identities and incompatible element types report HIGH and skip the affected element.

Missing-module records contain every original component declaration path, the proposed target directory and, when initialization is appropriate, a stigInitElement command with an explicit --elementName. The task prints all initializations together in one copyable shell block. Commands include the source's explicit ASIL and a base package when existing sibling modules agree on it. Separate Kotlin mapping proposals cover existing elements with different names; review these identity choices before adding them to bindings. Import does not initialize modules or assign engineering parents. Initialize the required modules, check them in a fresh invocation, then rerun import.

For occasional use, the same task accepts a single root override:

./gradlew stigImportModel --from syml --root-folder /path/to/applications --into components/applications --type component

--from syml alone filters configured inputs. The CLI accepts filesystem paths; repos[...] / ... is Kotlin DSL syntax. Formats, element types and viewpoints use enums, with validated string forms on the CLI.

Direct SYML import

SYML files

ANTLR4 grammar and resolver

Canonical Highway objects

External safety export

Consumer correction and conversion

Native Highway JSON

Existing Kotlin declarations

Canonical names and module bindings

KotlinPoet

Specification .kt files

Separate stigCheckModel invocation

The grammar covers the Context subset: atomic components, component interfaces, provided/consumed ports, shared service interfaces, fields/events, operations, parameters and per-port use selections. It supports partial declarations, namespaces, exact/wildcard imports, annotations, comments and balanced implementation blocks. The parser retains #when [ ... ] conditions on declarations and attributes, including alternative values for the same attribute and annotations inside grouped implementation bodies. This is syntax support; the importer does not yet select or evaluate a variant configuration. Conditions affecting the selected Context produce HIGH syml-condition-unresolved findings. Conditional declarations and attribute values are not assumed active. Unconditional ports referencing a conditional service keep their service reference and unresolved member uses; ports or uses whose own presence/type is conditional remain source evidence until the configuration is resolved. Unrelated conditional declarations do not prevent importing the selected unconditional Context. Operation return payloads use canonical Method.returnType. Missing referenced definitions are HIGH. An absent service definition does not erase a declared port or its used_element/used_operation selections. Its name, direction, service reference and selected member names remain available. Missing members are represented by UnresolvedInterfaceMember, with the original reference and reason; their unknown field/event behavior, payloads and operation signatures are not invented. Generated comments and HIGH findings identify the missing definitions. The same component.port.member access works for resolved and unresolved members. Supplying the authoritative definitions replaces unresolved members during the next overwrite. A conflicting definition still requires reconciliation. definitionsFrom(...) supplies reference definitions without selecting their components for import. Default discovery excludes test/ and tests/ trees.

For imported declarations, nonempty long_name_en and description_en populate the owning entity's description. The long name comes first; a distinct English description follows as a separate paragraph. Equal contributions occur once and blank values are omitted. Names and ASIL handling are unchanged. Ordinary comments and descriptions inside skipped declarations, such as CDC signal mappings, are not projected into a parent's description.

Deployment, scheduling, data-type internals and implementation behavior remain outside this projection. Unsupported Context constructs produce findings; implementation-only constructs do not become invented endpoints.

Member selections are declared inside typed port subclasses using canonical PortMemberUse. For example:

class Output : ProvidedPort<Status>(serviceInterface = status) { val reading = use(serviceInterface.reading) }

Consumers refer to component.output.reading. SYML used_element and used_operation names remain the property names, including aliases; unused service members do not acquire a use. A use is constructed once as a property, so reading it preserves its endpoint identity. Underscores in the source name remain, but the importer does not join port and member names with an extra underscore. Generic ports do not delegate Kotlin properties automatically, and Kotlin cannot hide inherited public members with private overrides.

KotlinPoet controls indentation and multiline constructors. The writer reads defaults from the canonical Kotlin constructors and omits equal optional values; empty optional text with a null default is also omitted. Required arguments and explicit overrides of a different default remain. Formatting does not invoke project cleanup or an IDE.

Direct C header import

For components whose only interface evidence is a plain C API (typically MCAL wrappers, shared libraries and low-level services with no SYML), cHeader scans public header directories for function prototypes instead:

stigProject { model { imports { cHeader { type = ElementType.COMPONENT root(repos["platform-deployment"] / "ecu_instances/dcdc/shared/components", into = "components/dcdc") context = OVERWRITE } } } }

This is deliberately not a C parser: no preprocessor/macro expansion, no compile database, no type resolution. It scans declarative embedded-C headers (comments stripped) for top-level statements shaped like [extern] <type> name(params); after stripping comments, and does not invoke a compiler or read a native build target. Components requiring real compiled evidence (accurate types, macro-expanded signatures) belong in code import instead. It is scoped to C: idiomatic C++ (namespaces, templates, overloads, member declarations inside a class, constructors/destructors) is not recognized and, for overloaded names specifically, would be silently collapsed to one entry -- do not point this at C++ headers.

A root(source, into) is either a single component's own directory — when source directly holds a conventional public header directory (includes or inc) — or a container whose immediate subdirectories are each one component, mirroring how SYML roots are usually pointed at a directory of many components. Header discovery is recursive under each component directory (so includes/app/Foo.h-style nesting is found), and Default discovery excludes test/ and tests/ trees, same as SYML. Explicitly private and internal header names are also excluded from the public Context import. elements(...) restricts which discovered directories import; anything selected but not found is a HIGH c-header-component-unresolved finding, and anything found but not already a canonical element is the usual HIGH element-not-found finding (initialize it with stigInitElement first).

A header can contain both a component's API declarations and declarations of external dependencies. Its location or a function-name prefix alone does not establish the provider, so this reader retains neutral Method declarations. Use ProvidedMethod or RequiredMethod when reviewed source metadata establishes the component's role, as described in Context and interfaces. The reader does not invent ServiceInterface/Port relationships (no interfacesModule needed). Each header file becomes one ServicePort — a purely structural grouping declared directly on the component, the same pattern already used e.g. in PecuswContext.kt (class X : ServicePort(...) { val field = Field(...) }) — named after the header file (capitalized). Each function prototype becomes one Method directly inside that group, named after the function, with the complete original signature retained verbatim in Method.functionType and the return type (when not void) in Method.returnType. Parameters are not modeled individually; the raw signature already carries them. Both ServicePort and Method are already InterfacePoints (ServiceInterfaceMember : InterfacePoint), so a Method is directly referenceable from an InterfacePointAnalysis with no wrapping. cHeader imports the Context viewpoint only, the same as SYML, and only ever replaces Context (COMPLEMENT_WITH_DESCRIPTION_AND_ASIL is not supported).

./gradlew stigImportModel --from c-header

Keeping hand-authored description/asil across a structural overwrite

A structural Context overwrite (syml, cHeader, or highwayJson with context = OVERWRITE) fully replaces the interface tree, but a source that never carries a description or ASIL (every C header import, and any SYML entity missing long_name_en/description_en/asil) would otherwise clear a value an engineer already typed directly into the Kotlin declaration. Set description/asil independently of context to opt out, per field:

cHeader { context = OVERWRITE description = FieldImportMode.PRESERVE_IF_SOURCE_EMPTY asil = FieldImportMode.PRESERVE_IF_SOURCE_EMPTY }

FieldImportMode.PRESERVE_IF_SOURCE_EMPTY keeps the existing canonical declaration's value, at any depth (the element itself and every nested Context declaration -- ports, service interfaces, methods, fields, parameters), whenever the freshly imported entity has none. It never overwrites a present value with an empty one, and never invents a value neither side has. Ambiguous name matches (more than one same-named child) are skipped, not guessed. description/asil default to FieldImportMode.OVERWRITE (today's behavior, unchanged) except when context is COMPLEMENT_WITH_DESCRIPTION_AND_ASIL, where they default to PRESERVE_IF_SOURCE_EMPTY to match that mode's existing semantics -- so no configured import changes behavior unless you set these explicitly.

Context from code

Select code imports explicitly in the root build.gradle.kts, alongside any SYML or native safety-model inputs:

stigProject { model { imports { code { fromModules(":activities:monitor") } } } }

Code import reads the selected module's compiled C++ interface inventory and updates its existing ordinary Context source. For example, MonitorContext.kt contains class Monitor : Activity(...) and val monitor. There is no extra base class or filename prefix. Kotlin bindings preserve the existing class and instance names, parent, internal units and aliases referenced by other viewpoints. The importer refreshes topic ports and their topic declarations; unrelated declarations in the Context remain. Commit these sources like hand-authored models.

Topic display names retain the qualified C++ name, such as app::LaneGuardTopic; Kotlin property names retain their existing spelling or use the unqualified topic name for a new declaration. Code import preserves authored topic, subscriber and publisher descriptions. It supplies no description when none exists, and the KotlinPoet writer omits absent description arguments. stigCheckModel still reports missing descriptions through descriptionDefinedInContextPackage; removing boilerplate leaves those items for engineering authors to describe.

An endpoint's ASIL is the highest ASIL among its inventory harnessFields; a topic uses the highest across its endpoints. These are reconciled field annotations from interfacePointsAnnotationFile: Required_ASIL(QM,A,B,C,D) for subscribers and Actual_ASIL(QM,A,B,C,D) for publishers. Inventory extraction reports missing valid leaf annotations and uses the configured activity element ASIL when available. If an inventory endpoint has no classified leaves, Context import uses the existing activity's ASIL. This is aggregation of supplied metadata.

The writer orders eager values by constructor dependencies. Referenced topic values must initialize before an activity instance constructs its subscribers and publishers. Kotlin allows forward references to declarations, but moving an eager instance above its dependencies can still fail at initialization. Formatting whitespace does not change that order.

./gradlew :activities:monitor:stigExtractAnalysisInterfaceInventory ./gradlew :activities:monitor:stigImportModel --from code ./gradlew :activities:monitor:stigCheckModel

Root stigImportModel processes the configured inputs in declaration order using the same Kotlin writer for code, SYML and Highway JSON. An element task selects only that module's configured code inputs. An omitted or disabled code entry imports no code; element.importEnabled alone does not select an import. Code import reads an existing inventory and schedules no native scan. The inventory and harness retain their own normal native-analysis tasks. Compilation and HTML never refresh Context implicitly. Use one structural Context source for each element. The batch rejects a second structural Context replacement for the same element. Subsequent native-file metadata can enrich the imported Context through the same conflict checks as SYML imports.

Native JSON and schema

The domain contract is the canonical Highway metamodel in stig-core. HighwaySchema reads those Kotlin constructors, required/default values, nullability and enums. HighwayModel holds actual model roots plus generic source information, diagnostics and property updates; it introduces no parallel port, requirement or failure types. ModelScope is a temporary in-memory registry for materializing references, unrelated to directory mapping.

The Jackson Kotlin codec com.stigway.engine.model.HighwayJson writes a versioned graph of roots and entities. Each entity has id, a canonical type, constructor properties and named children. References are {"ref":"identity"}; their expected types come from Kotlin properties. Shared objects occur once and cyclic reference lists are reconstructed without duplicating domain objects. Unselected viewpoints are not materialized merely to discard them; their referenced declarations can bind to the existing model.

Generic updates select existing identities and common constructor properties. The importer applies only the configured interface description and asil fields. Absent fields do not clear data. External references must resolve uniquely in the destination model. Duplicate keys, unknown fields, incompatible versions and malformed constructor values are rejected or reported as HIGH. Custom subclass state is rejected rather than discarded.

./gradlew stigExportModel --what schema --format highway-json --output build/exchange/highway.schema.json ./gradlew stigExportModel --what model --element Controller --viewpoints context,requirements,analysis --output build/exchange/controller.highway.json

Schema export needs no compiled consumer model. Model export reads an existing compilation and supports element, module and viewpoint selection. Schema selectors exclude element/module/viewpoint filters. JSON Schema checks file shape; identity resolution and model rules establish additional constraints.

Markdown exports use the modules and viewpoints selected in the consumer's stig.model.exports configuration. Include every intended component family in fromModules; selecting components/pecu/**, for example, does not refresh sibling component families. Existing Markdown outside the selection remains unchanged. Include shared interface-library modules as well when their declarations are referenced by the selected model. Their top-level declarations are grouped by Kotlin source file into their own viewpoint pages. Navigation labels for these additional pages use the source filename. Repeated filenames in different packages are package-qualified to preserve both pages. Inline port contracts appear beneath their owning ports, with links to their members; shared declarations remain on their own source pages. HTML Context navigation follows the same ownership: inline contract implementation classes belong to the model entry point rather than separate navigation entries.

MkDocs pages render the compiled model as nested collapsible admonitions. The page title is stored in YAML metadata and rendered once as a level-one heading; root objects use level-two headings inside their boxes. Each structural child uses the next heading level, up to Markdown's level-six limit. For example, an Analysis root, its interface-point analyses, and their failures use levels two, three, and four. Every entity has an explicit heading anchor, an Attribute / Value table, and its structural child boxes. Forward and reverse references stay links in the table. The first two model levels start expanded; deeper levels start collapsed. Attribute selection, display labels, metamodel type resolution and reverse relations are shared with Dokka. Comments and other multiline values retain their line breaks, and relation lists put each link on its own line.

An authored companion can give its exported report a level-one title after the # Package header and place the generated content explicitly. Put <!-- STIG: model --> under the chapter that owns the model and <!-- STIG: child analyses --> under the chapter that owns its component assessments. Generated model headings start one level below the containing authored heading. With includeChildAnalyses = true, the component index groups assessments by their actual module folders and includes each element's classification and recorded Analysis.argument, including QM assessments. These same insertion points reuse the model boxes and component index in Dokka. Companions without insertion points retain the normal appended model presentation.

Keep distinct work products in distinct source documents. A DFA can be an ordinary Markdown file beside the FMEA companion, linked from that companion. Export follows those authored links and publishes the DFA separately; Dokka publishes the same document independently in aggregate and standalone reports. A document link selects that file, while a directory link continues to select its Module or Package documentation. In Dokka navigation, the nearest enclosing # Package companion in the same module assigns the document to its CREATE viewpoint. For example, PecuswDfa.md beside the Analysis companion appears as pecuswDfa beneath Analysis, alongside pecuswAnalysis. The label uses the source filename with its first letter lowercased; the document URL retains its source identity. MkDocs keeps the site's authored navigation placement.

Mark an authored heading with {.stig-box .note .open} to present its section as an expanded box in both Dokka and MkDocs; omit .open to start collapsed. General box types are note, info, warning, and element. Static DFA documents also support cascading_output, cascading_failure, root_cause_group, root_cause, independent_function_group, and coupling_factor. Each type has a shared semantic color and icon in both formats. The section ends at the next heading of the same or a shallower level, and marked subheadings form nested boxes. The authored text, links, images and headings remain one source; Kotlin supplies the shared box structure and each publishing format renders it. Code examples containing these annotations remain examples.

## Document history {.stig-box .note} | Version | Change | |---------|--------| | 1.0 | Initial report | ## Cascading faults analysis ### Controller {.stig-box .element .open} #### Safety-related output {.stig-box .cascading_output .open} ##### Failure due to interference {.stig-box .cascading_failure .open} Recorded analysis and its linked evidence.

For a bounded paragraph callout, mark the title of a blockquote instead:

> **Release note** {.stig-box .warning .open} > > This paragraph belongs to the callout. This paragraph stays outside it.

The quote boundary supplies the box boundary without adding another document chapter. Unmarked blockquotes retain their normal Markdown presentation. Nested marked blockquotes support DFA hierarchies deeper than Markdown's six heading levels. Keep the quote markers on every line belonging to a box, including blank lines; text outside the quote remains outside the box. Separate adjacent sibling boxes with <!-- --> at their parent's quote depth so CommonMark does not merge them into one blockquote. Use these STIG annotations in the authored source, including when migrating MkDocs ???/!!! admonitions, so both publishing formats retain the intended grouping.

For generated model boxes, the admonition name is exactly the lowercase STIG metamodel class name: safetymechanism, safetymeasure, interfacepointanalysis, requiredport, failure, and so on. Consumer subclasses inherit their metamodel type; a consumer's ControllerDesign produces a design admonition. There is no translation between semantic types. Configure each type once in the MkDocs site's theme.icon.admonition and CSS, using the icons defined by StigModelIconRegistry and StigIcon. Each export writes stylesheets/stig-model-palette.css from StigModelVisualCategory, the same palette used by Dokka. Include that file in the site's extra_css and use its --stig-model-*-icon variables for model borders, title backgrounds and icons. The same generated stylesheet includes the authored stig-document box rules and icons from StigDocumentBoxKind; these presentation types do not define a DFA metamodel. The site needs the admonition, pymdownx.details, tables, and attr_list Markdown extensions. Generated boxes carry the stig-model class; their headings are indented with the box content and carry stig-model-heading and data-stig-model-depth, allowing a theme to mirror the containment in its table of contents and reveal collapsed ancestors when following a link.

Model table-of-contents labels default to declaration identifiers. Set modelTocLabels = ModelLabelMode.NAME in stigProject.model.exports.mkdocs for MkDocs and in stigProject.generateHtml for Dokka. Import com.stigway.engine.docs.ModelLabelMode in build.gradle.kts; ModelLabelMode.ID restores the default. NAME uses the model object's name, including the derived interface-point-analysis name, and falls back to the declaration identifier when no nonblank name exists. Line breaks in labels become spaces. This option changes only TOC text; anchors, relation links and model identities are unchanged. MkDocs headings carry data-toc-label, which the Markdown toc extension consumes when building the TOC.

To group existing interface-point analyses by provided/required direction and data/operations, import com.stigway.engine.docs.InterfaceGrouping and set analysisInterfaceGrouping = InterfaceGrouping.PROVIDED_REQUIRED in stigProject.model.exports.mkdocs. The same setting in stigProject.generateHtml enables the groups in Dokka. The default is InterfaceGrouping.NONE. The ordered MkDocs labels are Provided Elements, Provided Operations, Required Elements, and Required Operations; cluster data groups retain the labels Provided elements and Consumed elements. Dokka uses the metamodel-oriented labels Provided ports, Provided methods, Required ports, and Required methods. All four boxes appear even when empty. An empty box explains that no interface-point analyses are recorded in that group, and that analysis may be unnecessary or corresponding interface points may not exist. A generated link opens the element's full Context page, including interfaces without analyses. This explanatory text does not constitute an assessment that analysis is unnecessary. Both formats use the interface-point color and the same icons: outgoing and incoming data ports on a component boundary, UML provided-interface (lollipop) and required-interface (socket) symbols for methods, and a neutral box for other interface analyses. Each existing port/member/failure subtree appears once, under its group, and keeps its model anchors and relations. This setting does not create analyses for Context interface points or add failures. An explicitly provided or required port retains that direction when its contract or selected members are unresolved; it appears in the corresponding elements/ports group. Fully resolved operation-only ports retain their operations group. Mixed or ambiguous interfaces and standalone methods without a modeled direction appear in an additional Other interface analyses box, emitted only when needed. Every direct interface-point analysis appears in one box; grouping does not infer direction from names or comments. Operations are callable methods; a functional capability is not classified as an operation.

MkDocs group boxes carry stig-analysis-interface-group, and their matching headings carry stig-analysis-interface-group-heading, data-stig-model-depth, and data-toc-label. Their presentation anchors are separate from model identities. Themes should include these boxes in TOC nesting and collapsed-ancestor reveal behavior alongside model boxes.

Keep this site export under stigExportModel. stigGenerateMarkdown prepares the consumer's in-place documentation, including report and diagram sections used by the Dokka pipeline. The export task assembles the separate site's pages, links, assets and navigation from an existing compilation. When rebuilding both outputs, run ./gradlew stigGenerateHtml stigExportModel --console=plain --info so export observes the new compilation.

Set includeReverseRelations = false when a documentation site computes reverse links during its own build. Forward relations remain in the exported pages. The default is true: Kotlin resolves both directions from the compiled model and writes Markdown links. Context pages include interface points with anchors. The interfacePoint, usedIn, and interfacesUnderTest properties link to these points, even when they have no analysis; their labels are interface point, used in, and interfaces under test. With reverse relations enabled, Context points link back through interface point analyses, uses, and interface test cases. Incoming uses references also appear on each analysis of the target point. Dokka and Markdown share the typed relation definitions and labels. Disable downstream reverse-link generators when using this mode. Authored viewpoint companion Markdown is preserved before the generated section; keep unresolved source evidence there until its model identities can be established.

Set includeChildAnalyses = true to end each exported Analysis page with a Child analyses table that links the analyses of the elements it contains, for example the component analyses on a cluster page, so one analysis page leads to every analysis below it. An analysis is listed under the analysis of its nearest ancestor element (Element.parent) that also has an exported, enabled analysis; an element without an analysis, or whose analysis has enabled = false, does not break the chain. Rows are ordered by page path, which keeps the components of one module directory together. Only analyses exported in the same run are listed, so every row links to a written page. The table belongs to the generated block and leaves authored companion text untouched. The default is false.

To refresh an existing MkDocs site's navigation with stigExportModel, select its configuration instead of an output directory. Both mkdocs.yml and mkdocs.yaml are supported when named explicitly:

model { exports { mkdocs { fromModules("items/**", "systems/**", "subsystems/**", "clusters/**", "components/**", "interfaces/**") viewpoints(Viewpoint.CONTEXT, Viewpoint.REQUIREMENTS, Viewpoint.DESIGN, Viewpoint.ANALYSIS, Viewpoint.TESTS) configFile("../documentation/mkdocs.yml") updateNavigation = true failOnUnresolvedLinks = true navParent("Model") navBranch("components", "SW components") publishing { currentPath = "" versionsSubdir = "versions" } } } }

configFile reads the literal docs_dir path relative to the configuration file, defaulting to docs. Use either configFile or output; directory-only exports retain their existing behavior. Navigation updates default to off. Each successful export also writes <docs_dir>/stig-publishing.json from the publishing configuration above. Its defaults serve the latest successful publication directly at the site root and retain new named releases at versions/<tag>/; the optional block documents these defaults without changing them. Stiggy reads this generated configuration as authoritative, so publication paths belong in the consumer's build.gradle.kts rather than a second site configuration. Standalone sites without this artifact can configure their publisher directly. Both paths must use safe relative directory segments. Empty currentPath serves the current publication at the root; empty versionsSubdir publishes new releases directly at <tag>/. Existing named releases keep their published paths even when the configuration for new releases changes. Nonempty current and release directories must not overlap. The export rejects symbolic links in its publication configuration destination. The configuration file is required only when running stigExportModel; stigUpdate and stigCheckModel can run without the destination site's checkout. Export fails if its configured file is missing. navParent selects an existing sequence of navigation titles; omit it for top-level nav. navBranch overrides a module-root title, which otherwise starts with a capital letter (for example items becomes Items). Module patterns must begin with a literal root when updating navigation.

Navigation merges exact exported page paths. Existing entries, including manual overviews, safety concepts, DFA pages and external links inside selected branches, remain intact. Already listed pages keep their titles and location; missing pages join the existing element group or a new group mirroring every module-directory segment, including the element directory name. New groups use Context, Requirements, Design, Analysis, and Tests for viewpoints. An existing <module path>/index.md joins a new group as an untitled first entry; its content remains site-owned. Narrower module/viewpoint selections never remove earlier entries. Duplicate local source-page paths in nav fail before writing. Targets containing fragments or queries are navigation links rather than source-page declarations. A curated tab can link to several sections of the same rendered page with page.html#section while the source page stays in the element tree.

Every MkDocs export writes build/stig/model-export/markdown-links.json, including when a strict link check fails. It records each model relation rendered as plain text with its page, source identity, relation name, and target identity. failOnUnresolvedLinks = true requires all relation targets to have exported pages and anchors; use it for complete site exports. Partial exports default to false and retain the diagnostics without failing. The report covers generated model relations; MkDocs validates ordinary hyperlinks in authored Markdown and generated pages.

The exporter overwrites only its exact generated Markdown paths and copied assets; it never clears directories or prunes other Markdown files. Output is <docs_dir>/<module path>/<viewpoint>/<Kotlin source filename>.md, for example components/controller/analysis/ControllerAnalysis.md. fromModules selects Gradle modules, not Markdown filename patterns. Files no longer produced remain on disk and in navigation until deliberately removed by the site owner.

The updater preserves unrelated navigation, comments, custom YAML tags, anchors, and configuration text. Navigation parents and managed branches must use block YAML lists/mappings without anchors or aliases; ambiguous titles fail before writing pages. Generated names and paths are YAML-escaped, and repeated exports produce stable navigation.

A consumer converter can depend on ai.exida:stig-engine, construct canonical objects, and call HighwayJson.write(HighwayModel(roots), file). When its references target a model unavailable during conversion, HighwayJson.Builder.define(Requirement::class, ...) encodes canonical constructor properties and HighwayJson.Ref values. Keep source diagnostics in the native input so unresolved conversion findings remain visible to import. Converters for SA Tool, spreadsheets and other external dialects belong in consumer tooling.

Current source generation covers service-based and code-derived topic Context, Requirements, Analysis and Design safety measures. Deferred capabilities include preview, automatic initialization, SYML export, YAML/XML/CSV bindings, Tests viewpoint generation, Context wrappers, connectors and other unsupported Context families, and custom consumer scalar/data-type extensions. Unsupported constructs must be reported, not silently projected away.

Use $stig:specification to configure recurring model imports, inspect the source mappings and findings, then check the imported Kotlin specifications.
23 October 2026