Diagram hierarchy and per-level colors
Most detailed diagrams the stig pipeline emits are IR-first: the leaf nodes are LLVM IR instructions from controlFlowGraph.nodes[]. Source-code lines and data-flow segment membership are metadata on those IR nodes. Data-flow also has a segment-first detailed view because the analyzer DAG itself is defined over segments, not over exact instruction-to-instruction data-flow edges.
The hierarchy
ICFG/control-flow edges live between Instructions. Compound-box edges are derived at render time — when a box is collapsed, cytoscape-expand-collapse promotes its outgoing edges to the box itself. The loader does not materialize edges on parent boxes.
Source lines are metadata. Several IR instructions can share one
sourceFile:line, and cloned or branch-specific IR can make source-line grouping misleading. The diagram therefore labels IR nodes with IR identity (instruction-N: opcode @ col C) and shows the source text / source link in the tooltip.Source-index identities are canonical for interactive navigation. Cytoscape and Sigma node clicks resolve the clicked segment or instruction ID through the generated source index before using
sourceFilemetadata embedded in the graph. Embedded paths can retain the compiler's spelling while the source index uses a repository placeholder; the identity mapping selects the authoritative file and line in that case.Single-child collapsing (future): when a parent has exactly one child, we could hide the parent. Today we always render the full chain so the JS rendering stays predictable; level-skip is a future optimization (#16 family).
Segment semantics
Segment is not a structural parent in the detailed per-IR diagram.
Data-flow DAG diagrams.
Segment= PhASAR'spathsDagTosegment — emitted by the analyzer atsinks[].dataFlowDAG.segments[]. Each segment owns aninstructionIds[]membership list that referencescontrolFlowGraph.nodes[]. The list order is preserved for diagnostics and tooltips, but it is not a renderer contract for IR-to-IR data-flow edges.ICFG/control-flow diagrams. Control-flow edges come from
controlFlowGraph.edges[]; no segment layer is used for detailed rendering.
One IR instruction can occur in many data-flow segments, so segment IDs are stored as metadata (segmentIds) on the IR node. Segments can overlap and can contain identical instruction sets. The data-flow summary axis has two detailed projections:
detailed_per_ir_instruction— one node per IR instruction. This is the detailed view for control-flow diagrams. Data-flow diagrams do not render this summary type from segment DAGs or Z3 feasible paths; it would become valid only if a future analyzer schema emitted exact instruction-level data-flow edges.detailed_per_segment— data-flow only. The renderer contracts the analyzer's DAG segments into one synthetic node per segment and keeps the analyzer's inter-segment DAG edges. The segment tooltip lists the IR labels that belong to that segment. If the same IR instruction occurs in several segments, it appears in each segment tooltip.
Never construct data-flow IR arrows from adjacent instructionIds[], from fromSegment.instructionIds.last() to toSegment.instructionIds.first(), or from any other representative instruction pair. Those choices are display heuristics, not analyzer facts. If a future analyzer schema emits exact instruction-level data-flow path edges, model them as a separate explicit edge source and make renderers fail when that requested edge source is absent.
ASIL Color Discipline
Data-flow code diagrams separate two concepts that used to share one color:
nominal ASIL is the configured classification of a diagram-scoped source or sink endpoint. Renderers use it as node fill;
propagated ASIL is the weakest ASIL taint that reaches the IR instruction or segment in this Vista. Renderers use it as node border.
Internal IR instructions and data-flow segments without a scoped endpoint keep neutral fill even when ASIL-tainted data reaches them. Their border still shows the propagated ASIL. A scoped sink reached by a lower propagated ASIL keeps its nominal fill, receives the lower-ASIL border, and is marked with a thick border through cascadingFault = true.
For detailed per-segment diagrams, the segment node aggregates member instructions with the same rule: nominal fill comes only from scoped endpoint members, while the border uses the weakest propagated ASIL among represented members. Endpoint-looking harness calls that are not part of the generated diagram's endpoint scope are tooltip/source metadata only; they must not color or fault-mark the segment.
ASILPalette.colorForLevel(...) remains the renderer-side source of truth for nominal fill colors. Border colors use ASILPalette.edgeHex(...) for the propagated ASIL. edgeHex(...) is the darker stroke color from the same ASIL family as the fill color, so a cascading sink is visible as a nominal fill with a propagated-ASIL frame rather than as a generic warning color.
Report sink/source tables reuse the same palette for ASIL chips. The chip fill is always the endpoint's nominal ASIL. The chip border is normally the same ASIL, but cascading-failure sink rows with unexpected lower-ASIL data flow use the propagated lower ASIL as the border color and increase the border width.
Adding a new level
If you ever need an intermediate (say, a segment-level view between Function and Instruction):
Add it to the
DiagramHierarchyLevelenum with the right index. Existing levels shift only by adjusting theirindex— the color routine is index-driven so no other code changes.Add the corresponding
CodeConstructsubclass.Update the loader to emit that parent for the right children.
The JS render layer picks it up automatically via the
iconImage/ color attributes pulled from node data.
If a level is empty for a particular diagram (e.g. no Activity because the source isn't under an activity directory), the loader just doesn't emit that level — children become direct children of the next level up.
Source-line labels and source links
The analyzer emits only file + line + column per instruction — never the source text. Turning that into a readable diagram is the Kotlin pipeline's job, in two passes that both read the developer's checkout on disk:
SourceLineLabelerreads each instruction'sfile:lineand stores the trimmed source line assourceLineTexttooltip metadata. It does not replace the IR node label. For this to find multi-repo sources, the loader must be givenanalysisJsonDirso eachrepos[].pathin theanalysis.jsonresolves to the real checkout — without it, paths are CWD-relative and only files that happen to live under the Gradle CWD resolve.Enrichment.applySourceUrlsturns a resolved line into a clickable provider-correct source link, using the git remote + commit of the line's repo (resolveGitRepoInfo→ oneCodeBrowserUrl.Contextper configured repo, deepest-prefix match per file). The SSH remote form (git@host:org/repo.git) is rewritten tohttps://host/org/repoautomatically, andoriginis the remote that's read. A repo with no usable remote produces no link — its source lines still render and showfile:line, just not clickable.DSL override. A checkout may have no
origin, multiple remotes, or none at all (CI export, faked local mirror). Declare the base URL explicitly so links don't depend on local git config — the commit still comes fromHEAD:repo("sourceRepo") { root = "../../../sourceRepo" remoteUrl = "git@example.com:source/source-repo.git" // SSH or HTTPS // codeBrowser = CodeBrowser.GITLAB // only for ambiguous self-hosted URLs }
Completeness checks (CRITICAL)
StigAnalysisP7GenerateDiagramsTask is the producer of the diagram cells, so it owns the postcondition. Two diagram-source defects are reported as ProblemSeverity.CRITICAL DiagramCompletenessViolations, gated by failAt exactly like the other completeness checks (see failure-reporting.md):
Source line not found on disk — a tracked-repo instruction (non-empty
sourceRepo, resolved to an absolute path, not compiler-generated) whose file the labeler can't read → stale checkout or wrong repo path.No source-browse URL for a tracked repo — a readable tracked-repo line that produced no link → the repo has no resolvable git remote + commit. One problem per repo.
Both deliberately tolerate the benign cases the labeler tolerates: system headers (empty sourceRepo), unresolved/relative paths, and compiler-generated instructions.
The static SVG is always summarized
Each cell also emits a static .svg, but it is always built from Transform.toSummarized(...), regardless of the summary axis. The SVG is an endpoint overview:
data-flow SVGs contract non-endpoint chains over analyzer
DATA_FLOWDAG reachability, which must be derived from the segment DAG unless an exact instruction-edge data-flow source is available;control-flow and ICFG SVGs contract non-endpoint chains over analyzer
CONTROL_FLOWICFG edges;for data-flow, a directed source-to-sink edge
S -> Kis emitted when a segment containingScan reach a segment containingK;for control-flow and ICFG, endpoint-to-endpoint relations are preserved over the analyzer ICFG;
fan-out from one endpoint/source to several next endpoints/sinks is rendered as one edge per next target.
Therefore detailed_per_ir_instruction/*.svg, detailed_per_segment/*.svg, and summarized/*.svg are overview diagrams and never show instruction-level IR detail. The detailed per-IR or per-segment data lives in the interactive *.cyjson/*.sigma.json payloads that the HTML viewer renders.