tools/cube-notes-agent: dedupe findings by ISSUE (not just coord)
_canonical_issue() strips FK/FK#/FA/FB/FLAG/FIX prefixes and normalizes separators so duplicate representations of one root cause collapse (e.g. FK1-SELINUX_RESTORECON + SELINUX-RESTORECON -> selinux_restorecon; FK3-WAL_CHECKPOINT + CONCURRENT-STORE-CHECKPOINT -> checkpoint_before_exit via an alias map). Per issue only the most-detailed note is reported. mark_covered now covers ALL uncovered finding coords (including deduped-away duplicates) so nothing re-reports. Corresponding notes re-tagged: issue logs -> finding, status notes -> impl (no uncategorized notes remain).
This commit is contained in:
+31
-12
@@ -160,11 +160,26 @@ def uncovered_findings(project=None):
|
||||
un = "".join(f"{coord} {subj}\n" for coord, cat, subj in _log_findings(project) if coord not in covered)
|
||||
return un.strip() or "(no new findings)"
|
||||
|
||||
# Canonical issue key: collapse duplicate representations of the same root cause
|
||||
# (e.g. FK1-SELINUX_RESTORECON vs SELINUX-RESTORECON) so the report dedupes by ISSUE,
|
||||
# not just by coordinate. Prefixes (FK, FK#, FA, FB, FLAG, FIX) are stripped and
|
||||
# separators normalized; a small alias map unifies differently-worded duplicates.
|
||||
_ISSUE_PREFIX_RE = _re.compile(r'^(FK\d*|FA|FB|FLAG|FIX)[-_]?', _re.I)
|
||||
_ISSUE_ALIAS = {
|
||||
"wal_checkpoint": "checkpoint_before_exit",
|
||||
"concurrent_store_checkpoint": "checkpoint_before_exit",
|
||||
}
|
||||
|
||||
def _canonical_issue(flag):
|
||||
f = _ISSUE_PREFIX_RE.sub('', flag)
|
||||
f = _re.sub(r'[-_/\s]+', '_', f).strip('_').lower()
|
||||
return _ISSUE_ALIAS.get(f, f)
|
||||
|
||||
def _uncovered_meta(project=None):
|
||||
"""[(coord, flag, desc)] for the currently-UNCOVERED findings (grounded).
|
||||
desc is the FULL note body (not the truncated list subject)."""
|
||||
"""[(coord, flag, desc)] for the currently-UNCOVERED findings, DEDUPED BY ISSUE.
|
||||
desc is the FULL note body; the most-detailed note per canonical issue is kept."""
|
||||
covered = _covered(project)
|
||||
out = []
|
||||
best = {} # issue -> (coord, flag, desc, len)
|
||||
for coord, cat, subj in _log_findings(project):
|
||||
if coord in covered:
|
||||
continue
|
||||
@@ -175,8 +190,11 @@ def _uncovered_meta(project=None):
|
||||
flag, sep, rest = body_text.partition(":")
|
||||
flag = flag.strip()
|
||||
desc = rest.strip() if sep else body_text
|
||||
out.append((coord, flag, desc))
|
||||
return out
|
||||
issue = _canonical_issue(flag)
|
||||
cur = best.get(issue)
|
||||
if cur is None or len(desc) > cur[3]:
|
||||
best[issue] = (coord, flag, desc, len(desc))
|
||||
return [(c, f, d) for c, f, d, _ in best.values()]
|
||||
|
||||
# Snapshot of the uncovered findings captured when read_findings ran, so the
|
||||
# report can be composed faithfully even after mark_covered has covered them.
|
||||
@@ -253,10 +271,11 @@ def compose_report(project=None, model_text=""):
|
||||
return "\n".join(lines)
|
||||
|
||||
def mark_covered(project=None, text=""):
|
||||
meta = CURRENT_META or _uncovered_meta(project)
|
||||
if not meta:
|
||||
covered = _covered(project)
|
||||
coords = [c for c, cat, subj in _log_findings(project) if c not in covered]
|
||||
if not coords:
|
||||
return "nothing to cover"
|
||||
body = "covered coords:\n" + "\n".join(f"{coord} {flag}" for coord, flag, _ in meta)
|
||||
body = "covered coords:\n" + "\n".join(coords)
|
||||
args = [CUBE, "note", "add", "--cat", "checkpoint"]
|
||||
if project:
|
||||
args += ["--project", project]
|
||||
@@ -368,10 +387,10 @@ def main():
|
||||
if not report_text:
|
||||
# Run ended without an explicit "done" — still produce a grounded report.
|
||||
report_text = compose_report(a.project, last_assistant)
|
||||
if CURRENT_META:
|
||||
# Deterministic coverage: mark the reported findings covered regardless of
|
||||
# whether the model did it, so the next run is genuinely incremental.
|
||||
mark_covered(a.project)
|
||||
# Deterministic coverage: mark all uncovered findings covered (including any
|
||||
# deduped-away duplicates) regardless of whether the model did it, so the next
|
||||
# run is genuinely incremental. No-op when there are no uncovered findings.
|
||||
mark_covered(a.project)
|
||||
with open(a.report, "w") as f:
|
||||
f.write(report_text)
|
||||
os.makedirs(os.path.dirname(a.report), exist_ok=True) if os.path.dirname(a.report) else None
|
||||
|
||||
Reference in New Issue
Block a user