STIG Help

NEXY internals

This note is for STIG developers. The user guide explains the metric meaning, percentile policy, and report interpretation. This page only records the implementation boundaries that must stay stable.

Source Of Truth

NEXY policy lives in Kotlin:

Concern

Source

Percentile markers, zones, equal-zone axis

NexyModel.kt

Metric descriptors and analyzer availability gates

NexyMetricDescriptors.kt

Default metric anchors and scope scales

NexyDefaultMetricModelFactory.kt

Runtime default metric models

NexyDefaultMetricModelFactory.defaultMetricModelSet()

Gauge/ruler render plans

NexyRenderPlans.kt

Report payload maps

NexyReportPayloads.kt

JavaScript must not carry independent metric policy. It renders the Kotlin-produced payloads only.

Transformation Chain

The engine transforms metrics in this order:

raw metric value -> metric model -> percentile/NEXY -> zone percentile/NEXY -> equal-zone visual axis position

NexyMetricModel.metricToPercentile applies metric direction. Current shipped complexity metrics use HIGHER_IS_MORE_COMPLEX; lower-tail or two-sided risk scores must be modeled explicitly rather than hidden inside zone rendering.

The browser receives schemaVersion = 2 render plans with already-computed axis positions, labels, bands, and tick values. It must not run CDF or inverse-CDF logic.

Model Types

Report-time metric models are intentionally small:

NexyMetricModel( schemaVersion, id, scope, metricKey, direction, distributionModel, rawAxisModel, )

Supported distribution DTOs:

QuantileTableDistributionModel(points) NormalDistributionModel(mean, standardDeviation) LogNormalDistributionModel(logMean, logStandardDeviation) PoissonDistributionModel(lambda)

Supported raw-value metadata DTOs:

LinearRawAxisModel(rawUnit, tickRounding, minMetricValue, maxMetricValue) LogarithmicRawAxisModel(rawUnit, tickRounding, minMetricValue, maxMetricValue, base)

The raw-axis DTOs carry unit, rounding, and reference-range metadata. They do not define the gauge angular coordinate; NexyEqualZoneAxis does.

Default Model Runtime Path

Default NEXY metric models are not generated Kotlin source. Runtime scoring uses NexyMetricModels.defaultMetricModelSet(), which lazily calls NexyDefaultMetricModelFactory.defaultMetricModelSet() in the compiled engine. This keeps the report pipeline tied to the current Kotlin source when a local sibling checkout is rebuilt.

Current default policy:

  • all shipped default models are lognormal;

  • P75 is the function-scope medium-to-high raw anchor;

  • P50 = P75 / DEFAULT_MEDIUM_TO_HIGH_TO_MEDIAN_RATIO;

  • default ratio is 2.0;

  • sink raw boundaries are 20x function boundaries;

  • component raw boundaries are 50x function boundaries.

NexyDefaultMetricModelFactory.writeDefaultMetricModelsJson() remains an explicit interchange helper. JSON is not the runtime default constant store.

Generated Metric Test Artifacts

The checked-in NEXY test fixture artifact is generated explicitly:

./gradlew stigGenerateMetrics

Generated output:

stig-gradle-plugin/js-vendor/test/fixtures/nexy-gauge-fixtures.generated.js

After changing anchors, scales, percentile markers, descriptors, or render-plan shape, run ./gradlew stigGenerateMetrics and review the generated fixture diff. NexyModelTest compares the checked-in fixture with the Kotlin generator output, so stale fixture content fails tests. Normal report and viewer tasks do not implicitly regenerate tracked files.

Generated JavaScript Artifacts

Two JavaScript files are generated from Kotlin source-of-truth code and are intentionally tracked so regeneration produces visible git diffs:

File

Generator

Source of truth

stig-gradle-plugin/js-vendor/src/stig-asil-palette.generated.js

:stig-gradle-plugin:stigGenerateAsilPaletteJs

ASILPalette.kt

stig-gradle-plugin/js-vendor/test/fixtures/nexy-gauge-fixtures.generated.js

:stig-gradle-plugin:stigGenerateNexyGaugeFixtureJs

NEXY render plans

The ASIL file is part of the esbuild import graph. The NEXY fixture file is only for Playwright HTML fixtures; it prevents test HTML from duplicating percentile cuts or equal-zone axis math.

The ASIL palette is not a metric artifact. Regenerate it explicitly with:

./gradlew :stig-gradle-plugin:stigGenerateAsilPaletteJs

Browser Boundary

stig-gauge.js may:

  • parse data-stig-gauge;

  • validate schema/kind and required numeric fields;

  • render ECharts gauges from precomputed bands, ticks, labels, and axis positions;

  • react to theme and resize changes.

stig-gauge.js must not:

  • select metric models;

  • evaluate distribution CDFs or inverse CDFs;

  • choose percentile thresholds;

  • classify NEXY zones;

  • invent fallback thresholds.

If a payload is malformed, the renderer should fail visibly instead of guessing.

Analyzer-Derived Metrics

Analyzer-derived IR metrics are optional. Current shipped descriptors require analyzer graph evidence but do not require Z3 evidence:

dataFlowBranches dataFlowStatements controlFlowStatements sourcesUsed

Unavailable analyzer-gated metrics are omitted from composite scoring. They are not substituted with zero and are not kept in the denominator.

Validation Expectations

Engine tests must cover:

  • distribution forward/inverse transformations;

  • percentile markers and zone classification;

  • equal-zone gauge positioning;

  • default model generation policy and scope ratios;

  • composite weighted-average scoring with unavailable metrics;

  • JSON/render-plan payload shape.

Viewer tests must cover:

  • schema-v2 gauge rendering;

  • generated fixture consumption;

  • absence of duplicated percentile policy in fixture HTML.

Report-generation postconditions should fail on missing metric models, duplicate model keys, and inconsistent availability coverage before rendering misleading output.

23 October 2026