Scoring & Calculation Reference

This document describes the exact formulas behind every computed score in Calyntro. All metrics are derived exclusively from Git commit history and, optionally, ticket metadata from a configured issue tracker. No source code parsing, test runners, or external quality tools are required.


Hotspot Scores

Calyntro provides two complementary hotspot analyses. Both answer the question “which files are most likely to cause problems?” but from different structural angles.

Age/Churn Hotspot

Captures files that have been changing for a long time (structural instability) or are changing very rapidly right now (current incident signal).

Activity Score — measures current intensity:

activity_score = churn / max(age_days, 1)

High value: many lines changed in a short window. Penalises old files that suddenly reappear.

Structural Score — measures long-term instability:

structural_score = log₁₊(churn) × age_days

High value: a file that has been continuously modified over a long period. log₁₊ dampens the effect of extreme outliers.

Both scores are min-max normalised within the current result set before concentration classification is applied:

normalised = (score − min_score) / (max_score − min_score)

Concentration thresholds (consistent with the critical_hotspot warning):

Complexity Hotspot (Git-only)

Combines weighted churn with Cognitive Complexity — the structural dimension that most directly predicts maintainability cost.

Step 1 — weighted churn per file:

weighted_churn = Σ (added_lines + deleted_lines) × w

where w is determined per commit by keyword matching on the commit message:

Commit message pattern (case-insens.)

Weight

contains fix, bug, or issue

1.5

contains refactor or format

0.5

all other commits

1.0

Step 2 — hotspot score:

hotspot_score = weighted_churn × cognitive_complexity

cognitive_complexity is the value from the latest commit within the selected time window (arg_max by author_date), ensuring that complexity reflects the current state of the file, not an average over history.

Endpoint: POST /v1/analyze/hotspots/complexity


Tracker-Enriched Complexity Hotspot

The tracker-enriched variant replaces keyword heuristics with resolved ticket types from the configured issue tracker. The formula structure is identical; only the weight derivation changes.

Prerequisite: a ticket_provider block in config.yaml and consistent ticket ID references in commit messages.

Three provider types are supported:

type

Use case

Commit reference pattern

jira

Jira Cloud / Server with auth

PROJ-1234 (configured project_prefix)

jira_public

Public Jira Server (no auth, e.g. jira.mongodb.org)

SERVER-1234 (configured project_prefix)

github_issues

GitHub repositories (public or private with token)

#123 (project_prefix is fixed to "#")

Example configurations:

# Jira Cloud (authenticated)
ticket_provider:
  type: jira
  project_prefix: PESWA
  base_url: https://company.atlassian.net
  email: user@company.com
  api_token: xxxxx

# Public Jira Server (no credentials required)
ticket_provider:
  type: jira_public
  project_prefix: SERVER
  base_url: https://jira.mongodb.org

# GitHub Issues (token optional — raises rate limit from 60 to 5,000 req/hr)
ticket_provider:
  type: github_issues
  owner: astral-sh
  repo: uv
  token: ghp_xxxxx   # optional

For github_issues, issue labels are mapped to canonical ticket types: bug / defectBug; enhancement / improvementImprovement; feature / new featureNew Feature; any other label → raw label name; no labels → unresolvable (ticket_type = NULL).

Step 1 — ticket type resolution (at import time):

  1. The importer scans every commit message for ticket references matching the configured provider pattern (e.g. PESWA-\d+ for Jira, #\d+ for GitHub).

  2. Extracted ticket IDs are resolved via the tracker API — batched JQL for Jira, paginated issue list for GitHub Issues (cached in memory per import run).

  3. The resolved type (Bug, Story, Task, Epic, …) is stored in commit_tickets.ticket_type.

  4. Commits without a matching ticket reference have ticket_type = NULL.

This enrichment runs once per import — there is no runtime dependency on the tracker.

Step 2 — ticket-type weights (at query time):

w = CASE
      WHEN ticket_type IN ('bug', 'defect', 'error')  → 2.0
      WHEN ticket_type IS NOT NULL                     → 1.0
      ELSE (no ticket reference)                       → 1.2
    END

Normalisation is case-insensitive and applied inline in SQL at query time. Raw ticket types are preserved in storage; no information is lost.

Step 3 — hotspot score:

hotspot_score = ticket_weighted_churn × cognitive_complexity

Identical formula to the Git-only variant — the only difference is the churn weights. This makes the two results directly comparable.

Step 4 — coverage (per response):

coverage_pct = commits_with_type / commits_total × 100

commits_with_type counts distinct commits where at least one ticket’s type was successfully resolved. A low coverage_pct does not break the analysis — uncovered commits receive weight 1.2 (untracked) — but it limits the signal quality of the bug/feature distinction.

Interpretation guide:

Coverage

Interpretation

≥ 80 %

High confidence — ticket-type ranking is reliable

40 – 79 %

Directional signal — compare with base hotspot

< 40 %

Low coverage — base hotspot is the safer reference

Endpoint: POST /v1/analyze/hotspots/complexity/tracker

Diagnostic endpoint: GET /v1/analysis/tracker/coverage

Before presenting tracker-enriched results, call the coverage diagnostic to evaluate data quality. It returns three layers:

Level 1 — Reach
    commits_total       all commits in the window
    commits_no_ref      commits with no ticket ID pattern (true untracked work,
                        or indicator that project_prefix is misconfigured)

Level 2 — Regexp quality
    commits_with_ref    commits where a ticket ID was extracted
    tickets_extracted   distinct ticket IDs found across all commits

Level 3 — API resolution
    commits_with_resolved_type   commits where the type was confirmed
    tickets_resolved             ticket IDs confirmed by the tracker
    resolution_rate_pct          tickets_resolved / tickets_extracted × 100

resolution_rate_pct is the decisive quality signal for the regexp:

Rate

Interpretation

≥ 90 %

Regexp matches well — enrichment is reliable

70–89 %

False positives or deleted tickets — check prefix

< 70 %

Regexp misconfigured — use base hotspot as reference

The confidence field (high / medium / low / none) combines coverage and resolution into a single signal for UI display.

Comparing the two methodologies: Files that rank significantly higher in the tracker view than in the Git-only view are structurally risky and disproportionately driven by confirmed defects — the highest-priority refactoring targets.


Knowledge Risk (Silo Ratio)

A file is classified as a silo when one developer’s share of total commits meets or exceeds the configured threshold (default: 80 %).

ownership_share = developer_commits / total_file_commits

The silo ratio for a module is the fraction of its files that are silos:

silo_ratio = silo_files / total_files_in_module

Risk levels:

Level

Ownership share

Low

80 – 89 %

Medium

90 – 94 %

High

95 – 99 %

Critical

100 %

The threshold is configurable per deployment. Lowering it to 70 % surfaces earlier-stage concentration risk; raising it to 90 % focuses attention on near-complete silos only.


Code Map Risk Score

The Code Map treemap colours each module by a composite risk score:

risk_score = 0.6 × normalised(cognitive_complexity)
           + 0.4 × normalised(absolute_churn)

Both inputs are min-max normalised across the current module set. The score is therefore always relative — it shows which modules are riskiest compared to each other within the selected period, not against an absolute threshold.

Colour scale:

Colour

Range

Signal

Green

0–25

Low risk

Yellow

25–50

Moderate — monitor

Orange

50–75

Elevated — schedule review

Red

75–100

Critical — prioritise refactoring


Dashboard Warnings

Warnings are pre-computed signals surfacing the most actionable risks. All scores used for threshold evaluation are normalised within the current dataset.

defect_attractor requires a configured issue tracker. If no ticket_type data is present in the database, the warning type is silently skipped — no error, no empty entries.


General Notes

  • Time windows: All analyses accept start_date / end_date parameters. When omitted, the full import history is used.

  • Component filter: Passing a component_name restricts the analysis to files whose path prefix matches the component. Normalisation (min-max, averages) is always relative to the filtered set.

  • Complexity source: Cyclomatic and Cognitive Complexity are computed by rust-code-analysis-cli (most languages), csharpmetrics (C#), or qmlmetrics (QML) at import time. The latest value per file within the requested window is used (arg_max by commit date).

  • Authorship resolution: Commit authors are resolved to canonical developer names via the aliases table before ownership calculations. Team membership uses time-bounded records (valid_from / valid_to) so historical attribution remains accurate after team restructuring.