Skip to content

strict score penalizes auto_resolved like wontfix (undocumented) — fix-then-rescan tanks strict #627

Description

@afiler-sp

Summary

The strict score counts work-items with status auto_resolved as failures — the same as wontfix. The status legend and docs describe strict as only penalizing wontfix (e.g. "strict = like overall, but wontfix counts against you"), so this is surprising and undocumented.

In desloppify/engine/_scoring/policy/core.py:

FAILURE_STATUSES_BY_MODE = {
    "lenient": frozenset({"open", "deferred", "triaged_out"}),
    "strict":  frozenset({"open", "wontfix", "auto_resolved", "deferred", "triaged_out"}),
    "verified_strict": frozenset({"open", "wontfix", "fixed", "false_positive", "deferred", "triaged_out"}),
}

So strict additionally penalizes auto_resolved (and deferred/triaged_out), none of which the docs mention.

Why it's a trap

The documented loop is fix → resolve → rescan. But if you fix code and rescan before plan resolve-ing each finding, the scanner re-runs, sees the finding is gone, and marks it auto_resolved (not fixed). auto_resolved passes lenient/health and verified, but fails strict.

Net effect: genuine, scanner-confirmed fixes become invisible to the north-star strict score. In a real campaign this left ~530 genuinely-fixed items at auto_resolved, holding strict ~3.6 points below where the work warranted. A vivid symptom: Test health showed 100% health but ~48% strict with zero test wontfix items — purely from auto_resolved coverage findings.

Arguably auto_resolved is stronger evidence of a real fix than a human-attested fixed (the detector re-ran and confirmed the issue is gone), yet strict trusts fixed and distrusts auto_resolved.

Recovery is non-obvious

plan resolve only targets open items, so you can't directly promote auto_resolved → fixed. You must:

desloppify plan reopen <detector>     # auto_resolved -> open
desloppify plan resolve <detector> ... # open -> fixed

…per detector, which is tedious and easy to miss.

Repro

  1. Fix code that clears mechanical findings (e.g. add tests for untested modules, split a large file).
  2. Run desloppify scan (without plan resolve first).
  3. Observe: health ~100% for that dimension, but strict far lower, with no wontfix items — the gap is auto_resolved.

Suggestions (any one helps)

  1. Docs/UX: update the status legend + guide to state strict penalizes auto_resolved + deferred + triaged_out, not just wontfix.
  2. Scoring: don't penalize scanner-confirmed auto_resolved in strict (or weight it lighter than wontfix) — it's confirmed-gone, not accepted debt.
  3. Workflow: add a command to confirm/promote auto_resolved → fixed in bulk so legitimate fix-then-rescan work isn't stranded.

Ref: desloppify/engine/_scoring/policy/core.pyFAILURE_STATUSES_BY_MODE.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions