Hotspots v1.17.0: Model Risk Map and Monorepo Subsystems

Stephen CollinsMay 20, 2026
5 mins

Key Points

What is the model risk map and how is it different from a snapshot?

A snapshot ranks individual functions by LRS. The model risk map groups those functions by the data models they're associated with — either because they live in the same file as a struct/class or because they import it directly. Each model gets a weighted risk score (critical 1.5×, high 1.25×, moderate 1.0×, low 0.5×) so you can see which models are the center of gravity for your riskiest code.

How does --include-models relate to --mode models?

--mode models is a standalone output mode that prints a ranked text or JSON model risk map. --include-models is a flag for --mode snapshot that embeds the model risk map inside your HTML or JSON snapshot report. They use the same underlying extraction and ranking logic.

What languages are supported for model extraction?

TypeScript and JavaScript (classes, interfaces, types), Rust (structs, enums), Go (structs), Python (classes), and Java (classes, interfaces, records). The extraction uses regex-based pattern matching against file content, so it works without a full build step.

What is the subsystem field and how is it populated?

Each FunctionSnapshot now includes a subsystem string — the relative path to the nearest manifest-containing ancestor directory (package.json, Cargo.toml, go.mod, pyproject.toml, etc.). It's populated by a single directory walk at analysis time. Functions at the repo root get an empty string; functions with no manifest ancestor get null (field omitted).

Will the subsystem field break existing snapshot consumers?

No. The field uses serde skip_serializing_if so it's absent from JSON when null. Existing consumers that don't read subsystem are unaffected.

This post was originally published on hotspots.dev.


Hotspots v1.17.0 ships two features: a model risk map and subsystem tagging.

Model Risk Map

Risky functions cluster around the data models at the center of your system. --mode models surfaces that relationship directly.

hotspots analyze . --mode models

Each model declaration (struct, class, interface, record) is associated with functions by two mechanisms: same-file co-location and direct imports. Models are then ranked by a weighted risk score — critical functions count 1.5×, high 1.25×, moderate 1.0×, low 0.5×.

#1  Snapshot     hotspots-core/src/snapshot.rs    critical×4 high×15
    emit_snapshot_output          LRS 14.08  [fire]
    build_snapshot_via_db         LRS 11.60  [fire]

#2  Delta        hotspots-core/src/delta.rs       critical×2 high×3
    emit_delta_output             LRS 10.91  [fire]
    compute_pr_delta              LRS  8.64  [fire]

Add --include-models to any snapshot run to embed the map as an interactive force-directed graph in your HTML report. Nodes are sized and color-coded by weighted risk — large red nodes are models you should worry about.

hotspots analyze . --mode snapshot --format html --include-models

Subsystem Tagging

In a monorepo, global LRS percentiles are misleading. v1.17.0 adds a subsystem field to every FunctionSnapshot — the relative path to the nearest manifest root (package.json, Cargo.toml, go.mod, etc.).

{
  "function": "compileTemplate",
  "file": "packages/compiler-core/src/compile.ts",
  "subsystem": "packages/compiler-core",
  "lrs": 9.4
}

Downstream pipelines can now filter by subsystem before computing percentiles, producing per-package risk rankings. On next.js this yields 428 distinct subsystems; on vuejs/core, 22.

Upgrading

brew upgrade hotspots
# or
cargo install hotspots-cli

Full release notes and source at github.com/Stephen-Collins-tech/hotspots.