# Quality gates, profiles and issues

## The idea: judge new code

Legacy code is what it is. A quality gate that fails on old problems gets switched off within a week.
Qualor's default gate therefore looks only at **new code**, meaning the lines a change adds or
modifies. It asks three questions:

- Did the change add any issue?
- Is the new code covered by tests?
- Is the new code duplicated?

The overall numbers stay visible in the UI, and you can add conditions on them too.

## New code

New code is always "the lines changed between a **baseline** and the analysed revision". The
baseline depends on what is analysed:

| Analysis of | Baseline |
|---|---|
| a merge request / pull request | `merge-base(revision, target branch)` |
| another branch | `merge-base(revision, reference branch)`, by default the main branch |
| the main branch | the project's **new-code definition**, below |

New-code definitions for the main branch (`newCodeDefinition` of the project, set through the API):

| Definition | Baseline |
|---|---|
| `{ "type": "days", "value": 30 }` (default) | the oldest analysis of the last 30 days |
| `{ "type": "previous_version" }` | the latest analysis with another `project.version`, for example `${CI_COMMIT_TAG}` |
| `{ "type": "analysis", "analysisId": "…" }` | a fixed analysis |

An issue is **new** when its line lies in new code. So a newly enabled rule that flags old lines does
not suddenly fail merge requests.

The baseline comes from git, so CI jobs need the **full history** (`GIT_DEPTH: 0`, `fetch-depth: 0`).
If the baseline cannot be found, new-code conditions end in `error`, and Qualor fails the gate rather
than guess.

## The built-in gate: "Qualor way"

| Condition | Fails when |
|---|---|
| `new_issues` | > 0 |
| `new_coverage` | < 80 % |
| `new_duplicated_lines_density` | > 3 % |

It is the default for every organisation, and it is read-only. To change it, **Quality gates → copy**,
edit the copy, and make it the default, or assign it to single projects (`qualityGateId`).

Rules of evaluation:

- On merge requests and branches, only `new_*` conditions count.
- **Small changes:** below 20 new lines, the coverage and duplication conditions on new code are
  ignored. So a 5-line fix is not blocked by a coverage percentage.
- A value that cannot be computed, such as coverage when no coverage report was imported, is shown as
  **no value**. It does not fail the gate.
- Changing a gate does not re-judge past analyses. Changing an issue's status does re-judge the
  branch's latest analysis at once.

### Metrics you can use in conditions

| Group | Metrics (add `new_` for the new-code version) |
|---|---|
| Issues | `issues`, `blocker_issues`, `high_issues`, `medium_issues`, `low_issues`, `info_issues`, `security_issues`, `reliability_issues`, `maintainability_issues` |
| Ratings (1 = A … 5 = E) | `security_rating`, `reliability_rating` (from the worst severity among open issues of that quality) |
| Coverage | `coverage`, `line_coverage`, `branch_coverage`, `lines_to_cover`, `uncovered_lines`, `conditions_to_cover`, `uncovered_conditions` |
| Duplication | `duplicated_lines`, `duplicated_blocks`, `duplicated_lines_density` |
| Size and complexity (overall only) | `files`, `lines`, `ncloc`, `comment_lines`, `functions`, `classes`, `statements`, `complexity`, `cognitive_complexity`, `accepted_issues`, `false_positive_issues` |
| New-code size | `new_lines` |

A condition is `metric`, `gt` or `lt`, and a threshold, for example `new_security_rating gt 1`. A gate
has at most one condition per metric.

Common additions:

- `new_security_issues gt 0`: no new vulnerabilities, secrets or vulnerable dependencies, even when
  you relax `new_issues`.
- `new_blocker_issues gt 0` and `new_high_issues gt 0` instead of `new_issues gt 0`, for a softer
  start on a noisy codebase.
- `security_rating gt 3` on the main branch: an overall floor.

## Quality profiles: which rules count

A **quality profile** decides which analyzer rules become issues, and at which severity. There is one
profile per language (`typescript`, `javascript`, `java`, `csharp`) plus `*` for everything else:
secrets, dependencies, OpenGrep, external SARIF, and files without a language. The built-in profiles
are called "Qualor way".

- **Quality profiles → copy** a built-in profile, then turn rules on or off, override a severity, and
  make the copy the default.
- A profile may **inherit** from a parent of the same language (up to 3 levels), and changes only the
  rules it names.
- **Unknown rules**, meaning rules a profile does not mention, follow the profile's `unknownRules`
  setting: `activate` (the default) or `ignore`. With `activate`, a new ESLint plugin rule shows up
  without anyone editing the profile. Findings the profile drops are counted in the analysis warnings.
- A profile change applies **from the next analysis**.

The analyzers' own configuration still decides what runs: your ESLint config, PMD ruleset,
`.editorconfig`, `.gitleaks.toml`. The profile filters and re-grades what the analyzers report.
Qualor keeps the team's configuration in the repository, where reviewers see it.

**Rules** lists every rule the organisation has met, with its engine, language, quality and default
severity.

## Severities and qualities

| Severity | Meaning |
|---|---|
| blocker | must fix before merging: a real vulnerability, a leaked secret, a certain crash |
| high | very likely a bug or a security problem |
| medium | a probable problem, or a significant maintainability issue |
| low | a minor issue |
| info | information only |

Each rule also has a **quality**: security, reliability or maintainability.

## Issue statuses

| Status | Set by | Meaning |
|---|---|---|
| open | system / user | an active problem |
| resolved | user | "fixed". If the next analysis still finds it, it reopens |
| won't fix | user, **comment required** | accepted as is. It stays so across analyses |
| false positive | user, **comment required** | the analyzer is wrong. It stays so across analyses |
| closed | system | no longer detected. If it comes back within 30 days, its earlier won't fix or false positive is restored |

Won't fix and false positive issues do not count in the gate. Changing a status re-evaluates the
branch's latest gate result and updates the merge request comment, the commit status or the check
run, **without a new scan**. Every change is recorded in the issue's changelog.

Issues keep their identity when code moves. The fingerprint is the rule, the path and the surrounding
code, and it tolerates line shifts. When two analyzers report the same finding, only one issue is
shown.

Merge requests and branches inherit the statuses of the target branch. A false positive marked on
`main` stays a false positive in every merge request.