A finding is one stored row that says something specific is wrong or worth a look at a specific place: a marker that lowers a file's health score, a file nothing imports, a committed credential, a markdown link to a heading that no longer exists. Every finding is computed from the index with no model, and every one carries the evidence that produced it.
The finding families
| Family | Produced by | Keyed by | Carries a triage status |
|---|---|---|---|
| Health findings | Code health markers across defect risk, maintainability and performance, plus governance findings | biomarker_type, with a dimension and severity | Yes |
| Dead-code findings | Dead code | kind: unreachable_file, unused_export, unused_internal, zombie_package | Yes |
| Refactoring plans | Refactoring intelligence | plan id and a content-derived public_id | Yes |
| Security findings | Security signals | kind and a fixed severity | No, see below |
| Documentation drift | Documentation drift | kind: path, link, anchor, command, symbol | No |
| Component ownership | Components and ownership | no active owner, inactive owner, bus factor of 1, empty claim | No |
Health and dead-code findings meet in one place: dead-code percentage folds into module health, and the dashboard's Code Health page lists both. Security and documentation-drift findings sit on their own tabs of the same page and do not deduct from a file's health score.
Which finding types a surface shows
A finding type can be real in the analyzer and still too noisy to put in front
of a reader. The finding-type registry records, per dead-code kind and per
health biomarker_type, one of three states:
| State | What surfaces do |
|---|---|
validated | Shown everywhere. A type the registry does not list is validated. |
provisional | Shown only when a caller names the type or opts into unverified types, and then labelled unverified. |
hidden | Left out of every default surface, even when named. |
The registry changes visibility only. The analyzers still run, the rows are
still stored and scored, and the raw CLI analysis still reports them. What
changes is what the reading surfaces show: the MCP tools
(get_health, get_dead_code,
and the health blocks inside get_context and
get_risk), the REST finding lists, the dashboard's
attention list, file pages and "Do next" actions, the PR change-health review,
the generated CLAUDE.md, and the prompts behind the documentation pages.
Three types are hidden, each with the measurement that put it there:
| Type | Family | Why it is hidden |
|---|---|---|
unused_internal | Dead code | Measured at 0.5% precision on 1,511 hand-labelled findings. TS/JS/Python emit no same-file read edges, so a private symbol used only in its own file looks unused. |
duplicated_assertion_block | Health | Clones are matched on token shape only, so unrelated assertions pair up. |
dry_violation | Health | Flags duplicated import blocks and data literals as design duplication. |
No type is provisional. A type returns to validated only when its
measured precision clears the bar for its family.
Preview by language
The same registry holds one switch per layer per language. Where a layer's
hand-judged precision on a language is below the bar, its rows are served by
default with a Preview label carrying the measurement: a badge with the
measurement in its tooltip on the web, a preview field in REST, one compact
_meta.preview entry in MCP and one line in the CLI.
| Layer | Preview on | Measured as |
|---|---|---|
| Health findings | C++ | 82.5% not wrong, the lower of 2 repositories |
| Refactoring plans | Java, C#, C, C++ | Precision of judged plans |
| Performance opportunities | Java, C#, C, C++ | Precision of judged opportunities |
| Dead code | Java, C#, C, C++, Kotlin, Swift | Pooled precision, with the lowest repository named |
Health findings on Java, C# and C ship without a label, each measured at
92.5% or more correct or debatable on a blind sample. A layer switched to
gated on a language is held back instead and counted in a gated field.
Held back by the file
Some health findings are held back by where they sit, not by type:
- Findings on vendored and generated files: that code is not yours to change.
- Git-history markers on test files: they describe how the test changes.
- Git-history markers on production files leave the default defect lists and
are counted under
history, while scores, KPIs,get_riskand the Fix first order still use them.
include=["unverified"] shows all of them.
get_dead_code says what it held back instead of shrinking silently: when a
withheld type had rows, its summary carries a withheld_types entry with the
count, the state and the reason.
get_dead_code() # hidden kinds left out, counted in summary.withheld_types
get_health(include=["unverified"]) # also show provisional types, labelled "unverified"Triage: setting a status
Health findings, dead-code findings and refactoring plans share one status vocabulary:
| Status | Meaning |
|---|---|
open | Outstanding. The default, and what every list shows unless asked otherwise. |
acknowledged | Real, and the team is not acting on it now. |
resolved | Fixed, or the code moved on. |
false_positive | Wrong for this repository. |
Where you set it:
- Dashboard. The health drawer on the Code Health page and the dead-code list have a status control per finding.
- MCP.
set_finding_statussets the status of one refactoring plan. It is opt-in and the only tool on the MCP surface that writes. - REST.
PATCH /api/repos/{repo_id}/health/findings/{id}for a health finding andPATCH /api/dead-code/{id}for a dead-code finding, on the local server started byrepowise serve.
There is no CLI command for triage.
What persists across re-analysis
A re-run of the analysis replaces only open rows, so a status you set is
never overwritten. What the next run does with the same issue depends on the
family:
- Dead code. A finding you acted on is not re-created. The next run drops any new finding that matches it on file, kind and symbol.
- Refactoring plans. A plan marked
false_positiveis never re-emitted. A plan a person markedresolvedstays resolved even if the detector still fires.acknowledgedrecords the judgment without suppressing anything. - Health findings. A re-detected finding that matches a triaged row
refreshes that row:
acknowledgedandfalse_positivestand, andresolvedreopens. Finding ids are anchored on the symbol and the line offset into it, so edits above a function keep the id.
Security and documentation-drift findings
Neither family has a status in the local index. To silence a security
finding, put a repowise-security-ignore comment on its line
(repowise-security-ignore: kind, kind for specific kinds). The finding leaves
the stored scan, so it does not reach the dashboard, get_risk or REST, and
the CI gate counts it as suppressed and does not fail on it. Other
scanners' markers and a .gitleaks.toml allowlist are honoured too; see
Security signals.
A documentation-drift finding goes away when the document or the tree is
fixed, since the check runs again on every update.
On the hosted platform, the Security page's secret and vulnerability lists carry statuses of their own, and only the repo owner can change them.