STIG Help

Gradle command inputs and validation

STIG build configuration must be explicit, typed, validated, and visible in Gradle's task model. Persistent consumer configuration belongs in the Gradle Kotlin DSL or gradle.properties. Do not use environment variables or other global process state for build or analyzer configuration.

This page distinguishes Gradle project properties from task command-line options and defines when STIG may use each mechanism.

Core comparison

Topic

Gradle project property

Custom task option

Typical command

-Pstig.mode=quick

:stigGenerateHtml --quick

Gradle mechanism

Built-in project-property transport

Task-specific @Option declaration

Name registration

No schema; the key is an ordinary string

Registered by the selected task type

Initial value type

String

Supported scalar, enum, flag, or list type

Scope

Build-wide; every project can read it

Only the task to which the option is applied

Dependent tasks

Can read the same property provider

Do not inherit or receive the option

Project-path meaning

None unless STIG parses and resolves it

None unless STIG parses and resolves it

Task-graph effect

Possible when dependencies consume the provider lazily

Possible when the selected task exposes a lazy, typed mode used by dependency wiring

Persistent default

Can also come from gradle.properties

Must come from the task convention, DSL, or another explicitly wired provider

Discoverability

The generic -P mechanism is visible, but individual keys are not

Option name and description appear in help --task

Main advantage

One value is readily available to several projects and task families

Strong task-local interface, typo detection, typing, and generated help

Main risk

Misspelled or unused keys are normally accepted silently

The option is not global and does not propagate through task dependencies

-Pstig.analysis.projects=:activities:fusion_activity therefore transports one string. Gradle does not interpret the value as a project selector. STIG would have to parse it, resolve the exact included project, verify that it is an analyzable STIG element, and use the result when constructing task dependencies.

Validation and failure timing

Check

Project property

Task option

Command syntax

Gradle recognizes -Pname=value before project configuration

Gradle resolves the requested task and then recognizes its declared options

Unknown name

Accepted unless STIG explicitly audits its property namespace

Rejected as an unknown option for the selected task

Missing value

May be present as an empty string; STIG must reject blank values where invalid

Rejected automatically when the declared option requires an argument

Primitive conversion

STIG must parse the string

Gradle converts supported declared types

Enum conversion

STIG must map the string to an enum and reject unknown values

Prefer an enum option; Gradle can reject unsupported values

List syntax

STIG defines splitting, trimming, repetition, and duplicate rules

Prefer a list/repeatable option; STIG still validates every item

Project existence

STIG must resolve the exact Gradle project path

STIG must resolve the exact Gradle project path

Rule-based validity

Checked by STIG when the provider is read

Checked in the option setter or by the lazy graph/input provider

Earliest useful failure

Configuration or task-graph calculation, before expensive work

Option parsing for local values; task-graph calculation for dependency selection

Unused input

Usually no failure

The option cannot be supplied to a task that does not declare it

Configuration cache

Use providers.gradleProperty; changing a consumed value becomes a tracked configuration input

Keep the option in a managed task property and cover graph-changing options with configuration-cache tests

Task output cache

Wire every behavior-changing value into an @Input

Declare the option-backed managed property as an @Input

Do not postpone selection validation to a task action when the value changes dependencies. At that point Gradle has already constructed the task graph. Parse and validate graph selectors while Gradle calculates the graph, and fail before native compilation or analysis begins.

For a project-selection value, validation must cover at least:

  • canonical absolute Gradle path syntax, such as :activities:fusion_activity;

  • existence in the current included-project set;

  • application of the STIG plugin and the expected element type;

  • whether the selected operation is supported by that element;

  • duplicates, mutually exclusive selectors, and empty selections.

Help and automated verification

./gradlew --help lists the generic -P, --project-prop mechanism. It cannot list application-specific property names because Gradle does not register them. ./gradlew help --task :path:taskName lists a task's declared @Option names and descriptions. ./gradlew tasks --all normally lists task descriptions, not their complete option schemas.

Every new STIG command input must have automated coverage for the behavior it promises:

  • accepted and rejected syntax;

  • every enum value and an unknown value;

  • default behavior when the input is absent;

  • exact task-graph inclusion and exclusion;

  • repeated configuration-cache use with the same value and invalidation after the value changes;

  • task up-to-date/build-cache separation when the value changes task behavior;

  • help --task output for public task options;

  • unknown, inactive, and unsuitable project paths for project selectors.

Prefer enums such as FULL and QUICK over unrelated Boolean flags. Parse a project property once into the enum, fail with the complete allowed-value list, and expose the typed provider to consumers. Do not let each task parse the same raw string differently.

Practical choice

stigSource=published|local is a persistent, build-wide bootstrap selection in gradle.properties, paired with the existing stigVersion spelling. Users switch by editing that file. Settings must select the plugin source before loading STIG, so this cannot be a task option or a project DSL setting evaluated afterward. The bootstrap validates the two text values; the loaded plugin resolves them once into StigSource.PUBLISHED or StigSource.LOCAL and shares that typed selection with its project/task registrations. See source selection.

Use the following order of preference:

  1. Put durable consumer and analyzer configuration in the Gradle Kotlin DSL. Put standard Gradle build settings and suitable persistent scalar values in gradle.properties.

  2. Use a typed task @Option for behavior owned by one explicitly requested task. Prefer enums, managed properties, generated help --task documentation, and automatic invalid-value tests.

  3. Avoid command-line -P properties by default. Use a namespaced property such as -Pstig.mode=quick only when one invocation value is genuinely shared by several projects or task families and therefore acts as build-wide configuration. Parse it once into a strongly typed enum and propagate that provider to typed task inputs.

  4. Do not use bare keys such as -Pquick, environment variables, ORG_GRADLE_PROJECT_*, or JVM system properties as STIG configuration channels.

A command-line property is justified for a shared mode, not merely because it requires fewer lines of plugin code. A public task-local control should remain a task option even if several dependent tasks need properties derived from it; the plugin must propagate the resulting typed mode explicitly.

Native analyzer concurrency

One source solve uses one PhASAR solver thread. Eight workers take eight different source jobs from a shared queue; as a worker finishes, it takes another job. They do not cooperate on one source solve. Each worker owns its LLVM context, module, helper analyses, solver and mutable caches. A coordinator merges completed evidence in source-plan order. Workers do not mutate the shared result while solving. The process time and memory limits cover all its workers.

Both limits are configurable in the existing analyzerExecution block in build.gradle.kts:

analyzerExecution { maxParallelAnalyzerExecutions = 0 solverWorkers = 0 }

Zero is the default and means automatic: the available logical CPU count minus one, with a minimum of one. The process limit applies across activity projects in one Gradle build. Source workers share a CPU budget of that same size. A run requests workers according to its configured limit and source/sink jobs, receives currently free capacity, and returns it after the run. A single run can use the whole budget; concurrent runs share it without multiplying the two limits. Running processes keep their assigned worker counts until they finish; newly free capacity goes to subsequent runs. Positive values impose smaller per-process or process-count limits. The --solver-workers P6 task option overrides its DSL value for one invocation, and also accepts zero. Separate Gradle invocations are independent.

On a 32-vCPU host, automatic mode offers 31 worker slots. If one run needs 20, another can use the remaining 11. Gradle's --max-workers separately limits Gradle task concurrency. Non-IDE-XTaint solvers remain serial. Direct native CLI execution uses the same automatic CPU-count rule, but has no Gradle build coordinator.

The configured limits and available CPU budget are task inputs; each granted count is recorded in the native command and execution log. Existing memory ceilings still apply. CPU admission does not reserve RAM or guarantee that a memory-intensive analysis will fit. Reserve-one-CPU scheduling reduces CPU contention; memory exhaustion requires its own diagnosis, suitable limits and, when appropriate, fewer source workers.

A processor package contains physical cores. A core may expose more than one hardware thread, each presented as a logical CPU. A virtual machine exposes vCPUs scheduled by its host; its reported core topology does not prove that it owns that many dedicated physical cores. htop displays a gauge per visible logical CPU. Automatic STIG scheduling uses the CPUs available to its runtime, rather than the physical processor's advertised core count.

More workers are not necessarily faster. An October 12, 2026 measurement of one 136-source plan on a 32-vCPU host took 847 seconds / 39.0 GiB peak RSS with eight workers, 737 seconds / 58.6 GiB with sixteen, and 766 seconds / 82.0 GiB with thirty-one. Sixteen was about 13% faster than eight; serial work, unequal source durations and memory pressure limit the benefit. The sixteen-worker native JSON and monitor completed, but its interrupted supervisor did not retain an exit status. Compare source/sink results as well as time and memory; a worker-count change is not a new bug-fix round.

Dokka concurrency

STIG's Dokka module and publication tasks reuse the native analyzer's automatic CPU-budget policy: the logical CPUs available to the Gradle runtime minus one, with a minimum of one. One build-scoped coordinator applies that limit to active Dokka generation tasks across all projects. On a 32-vCPU host, up to 31 Dokka generators can run together. Gradle's --max-workers remains a separate upper bound; a smaller explicit value still limits generation, and STIG does not change the build's global worker setting.

This counts active Dokka invocations, not JVM threads or fixed CPU reservations. A Dokka worker JVM can use several compiler and garbage-collection threads, and Gradle can reuse worker processes between invocations. Native analyzer workers and Dokka generators use separate coordinators with the same automatic policy; other tasks and independent Gradle builds also consume host resources. This limit does not guarantee that one logical CPU remains idle.

Stop after analyzer validation

Run the activity's stigAnalysisP6RunAnalyzer task when checking a native fix. It executes and validates the analyzer JSON without scheduling P7 diagrams or HTML:

./gradlew :activities:example_activity:stigAnalysisP6RunAnalyzer --console=plain --info

For unchanged user source, use the captured transformed-input mode below to skip P1–P5. For changed user source, use the normal P6 task so its declared producers rebuild the affected input. Completed propagation alone is not a completed analysis: value computation, source/sink results and evidence validation can still fail. No partial-output flag is needed to avoid HTML, and a propagation-only diagnostic must not be recorded as an accepted restoration.

Archived analysis-evidence replay

stigAnalysisP6RunAnalyzer has a task-local --start-with=transformed mode. Its optional enum has one supported value; omission retains the local pipeline. A lazy dependency provider validates the required --from and --output-directory selectors before scheduling producers, and both explicit dependencies and producer-backed file properties are conditional. An explicit --analyzer-executable disconnects native analyzer construction as well. Transformed runs always execute and have separate outputs, so neither an up-to-date result nor a build-cache hit can replace a timing measurement. The pipeline guide owns the workflow and checkpoint contract. Graph, help, option-validation and configuration-cache behavior have Gradle functional coverage.

stigReplayArchivedAnalyzerRun is a root task with typed task-local options rather than a build-wide project property:

./gradlew :stigReplayArchivedAnalyzerRun \ --evidence-directory=/absolute/path/to/extracted-evidence \ --run=cascading-faults-A \ --output-directory=/tmp/stig-replay-output

The three values select one immutable evidence directory, one exact archived run, and one task-owned output directory. Repeatable --debug-facts-at-function, optional --debug-fact-filter, and opt-in --debug-trace affect only this execution. The task has no dependency on consumer native compilation or P3/P4/P5/P6 tasks. This graph separation is the supported substitute for attempting to omit coupled pipeline predecessors with -x.

The evidence directory is validated from its manifest at execution because the selected module/configuration paths are archive data, not consumer build outputs. Every option remains a managed Gradle property and is exposed by help --task stigReplayArchivedAnalyzerRun; functional coverage asserts both the public option schema and the excluded product-pipeline graph.

Analysis evidence module selection

The root :stigCheckAnalysis task owns the repeatable --modules option:

./gradlew :stigCheckAnalysis \ --modules=:components:one,:components:two \ --configuration-only --zip --zip-directory=../exchange

Values are absolute Gradle project paths from the consumer's actual layout. Each occurrence may contain a comma-separated list. Omit --modules to select every eligible source-analysis module. STIG rejects blank, duplicate, unknown, and ineligible paths while calculating dependencies; selection controls both the producer tasks and membership of the combined ZIP. Use the leading colon: an unqualified task name also selects same-named subproject tasks, which do not own --modules.

--zip is Boolean; its optional destination is a separate --zip-directory=<path> option, which requires --zip. Relative destinations resolve from the root project directory. The default is ../<root-project-name>-check-analysis-evidence/. The archive mode and selected module set determine its filename, so exporting a subset does not replace an all-module collection. See analysis evidence for the collection format, provisional-analysis findings, and partial-failure behavior.

Root aggregate module exclusion

stigGenerateHtml implements invocation-scoped aggregate filtering as a repeatable typed task option:

./gradlew :stigGenerateHtml \ --exclude :activities:fusion_activity \ --exclude :activities:other_activity

The managed list property is owned by the root aggregate task. STIG resolves each value as one canonical absolute Gradle project path, rejects duplicates, missing projects, root/relative paths, and projects without the STIG plugin, and then uses the validated provider while calculating the graph. The provider filters all aggregate entry points: root Markdown dependencies and complexity rows, Dokka project dependencies and preparation tasks, and per-module generated assets. Consequently an excluded module cannot reappear merely because retained Markdown or diagram files exist under its build directory.

Use one --exclude <path> occurrence per module. Shell brace expansion and comma-separated parsing are deliberately not part of the Gradle task contract. The option is not an alias for Gradle's -x/--exclude-task: -x accepts task paths and cannot remove a project from a multi-module Dokka publication.

stigPublishHtml owns the same repeatable typed option and propagates it to the stigGenerateHtml dependency, so the published bundle and retained-version preparation use the same invocation-scoped module selection.

Dependency maintenance in ordinary Gradle applications

Ordinary applications and STIG model projects use the same standalone stigUpdate runtime. The application profile selects one STIG build-toolchain release for Gradle, exact Java and Kotlin, without applying STIG model conventions or selecting native analyzer sources. Application libraries remain in the application's version catalog. A library's STIG version is independent of the tooling release. Compose compiler follows the shared Kotlin version; Compose and Spring library versions remain application choices.

An application declares its profile and application-owned task lists in gradle.properties:

stigUpdateProfile=application stigApplicationUpdateTasks=dependencyUpdates,versionCatalogUpdate stigApplicationCheckTasks=:check

Installation records stigToolchainVersion and stigToolchainSha256, the exact version and SHA-256 of the checked-in gradle/stig-update.jar. The selected artifact supplies the wrapper, Java bootstrap and policy, and gradle/stig-toolchain.properties; its Kotlin value is generated from STIG's authoritative version catalog. The application catalog renders this Kotlin pin under kotlin and must exclude it from independent version-catalog updates. Builds using only Gradle's embedded Kotlin DSL compiler omit that catalog entry; their compiler already follows the selected Gradle distribution, and the updater does not introduce an external Kotlin plugin. Apply the installed gradle/stig-dependency-maintenance.gradle.kts once in the root build to register resolveVerificationDependencies and make it a prerequisite of root check.

./gradlew stigUpdate --yes ./gradlew stigUpdate patch --yes ./gradlew stigUpdate minor --yes ./gradlew stigUpdate major --yes ./gradlew stigUpdate --dependencies --yes ./gradlew stigUpdate --verification --yes ./gradlew :resolveVerificationDependencies

The ordinary update, with no mode specified, uses major. It selects the latest stable tooling and application dependency versions allowed by their explicit pins, including major upgrades. It runs the declared application update tasks, refreshes dependency locks and missing checksums, resolves IDE sources and Javadoc, then runs the declared checks in a fresh strict process. Invoke ./gradlew stigUpdate directly; applications do not need an update shell wrapper. No preliminary repair command is required when adding a dependency or module, or when an IDE attachment lacks a checksum. Choose patch or minor explicitly when upgrades must stay within that compatibility level. Application catalogs can use StigDependencyUpdatePolicy.accepts(current, candidate, mode) from the installed updater JAR as their shared version selector. It accepts stable numeric releases, rejects downgrades, and defaults to the same major policy. Add gradle/stig-update.jar to the root build-script classpath to use it. Application project arguments such as -PlibrarySource=published are forwarded to every update and check process. STIG-owned policy, internal worker properties and updateMode cannot be overridden this way; choose the update mode on the command line and maintain the STIG policy in gradle.properties.

Release modes select a permitted newer STIG tooling release when one exists, then run the declared application update tasks with -PupdateMode=patch|minor|major. A successful release lookup with no eligible newer release retains the installed, SHA-verified tooling artifact and still updates application dependencies. It does not fetch or reinstall the current tooling artifact. Release lookup, network and signature failures stop the update. none (also spelled --dependencies) is an optional mode that uses the installed tooling artifact and refreshes dependency locks and missing checksums without choosing versions or fetching a tooling release. --verification optionally uses the installed tooling artifact to repair missing checksums without selecting versions or writing locks, and works offline when the required artifacts are cached. Normal builds require no sibling checkout or latest release lookup. Both profiles reuse the verified Java cache under Gradle's user home.

The resolver visits the actual project tree and included builds, including settings and build-script dependencies, and resolves available sources and Javadoc as well as binary artifacts. It also checks Gradle distribution and bundled Groovy sources for IDE import. Newly included modules need no manually maintained resolver task list. The application still owns wiring its test and other validation tasks into the declared check tasks.

The standalone launcher snapshots policy, locks, catalogs and managed runtime files before loading Gradle. Its first write process can therefore repair a missing checksum required to compile settings or a build script. Each write result must preserve accepted checksums and trust entries, and a fresh strict process must pass before completion. --yes accepts validated additions; otherwise the launcher requests acceptance. Failure restores the snapshot and retains the rejected candidate under .gradle/stig/application-update-*. Write-mode Gradle may load a configuration artifact before the subsequent audit rejects changed bytes; rollback prevents accepting those bytes as the new policy, but does not undo code already executed by that process.

For explicit local validation, adopt a reviewed updater artifact without publishing it:

java -jar /path/to/stig-update.jar /path/to/application --install-application

This installs the artifact's embedded runtime and records its exact version and SHA-256. A qualified development version such as 3.7.1-SNAPSHOT supports the ordinary update immediately. It keeps that reviewed tooling artifact while updating application dependencies until an eligible stable release is available. Release-selection modes can then adopt its published 3.7.1 final release or a newer stable release permitted by the selected mode, using the same signed release bundle verification. Local adoption never infers a release from sibling directory names.

The lower-level root-only ai.exida.stig-maintenance plugin exposes updateDependencyVerification and the same resolver for builds embedding the maintenance API. It does not install the shared toolchain or register stigUpdate. Its Gradle task can only start after build plugins load, so it does not replace the standalone launcher for repairing configuration-time checksums.

Impact of adding --quick to stigGenerateHtml

This is a medium-sized pipeline change, not just an @Option annotation.

Today the typed StigGenerateHtmlTask is registered on the root project and every STIG element. Its local --serve and --serve-port options manage report preview without changing the dependency graph. The root flow is:

:stigGenerateHtml -> :dokkaGenerate -> root and per-element stigGenerateMarkdown -> stigCheckModel -> per-element analysis and diagram tasks when the cascaded DSL enables code analysis

The analysis decision is made in each element's stigGenerateMarkdown dependency provider from its effective DSL configuration. A root task option is not inherited by those tasks. A correct root --quick implementation would therefore require:

  1. a new managed enum or Boolean quick-mode property on the typed HTML task;

  2. an explicit root invocation contract, preferably ./gradlew :stigGenerateHtml --quick, because the unqualified task name also matches per-element stigGenerateHtml tasks;

  3. a shared, typed invocation-mode provider that does not create a dependency cycle from element tasks back to their root lifecycle task;

  4. lazy propagation of that mode into per-element markdown and analysis dependency selection;

  5. @Input declarations on every task whose behavior or outputs differ in quick mode;

  6. a defined policy for existing full-analysis artifacts: deliberately reuse them, deliberately omit them, or isolate quick output so stale evidence cannot appear as freshly generated evidence;

  7. functional tests for the full and quick graphs, root versus module invocation, generated help, invalid options, output transitions, and configuration-cache reuse.

The root aggregate must still compile and render every included module if quick mode promises a complete project-level site. Quick mode can reduce selected analysis work, but it cannot silently remove modules from the aggregate or treat retained analysis files as results from the current invocation.

23 October 2026