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)
|
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)"
|
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):
|
def _uncovered_meta(project=None):
|
||||||
"""[(coord, flag, desc)] for the currently-UNCOVERED findings (grounded).
|
"""[(coord, flag, desc)] for the currently-UNCOVERED findings, DEDUPED BY ISSUE.
|
||||||
desc is the FULL note body (not the truncated list subject)."""
|
desc is the FULL note body; the most-detailed note per canonical issue is kept."""
|
||||||
covered = _covered(project)
|
covered = _covered(project)
|
||||||
out = []
|
best = {} # issue -> (coord, flag, desc, len)
|
||||||
for coord, cat, subj in _log_findings(project):
|
for coord, cat, subj in _log_findings(project):
|
||||||
if coord in covered:
|
if coord in covered:
|
||||||
continue
|
continue
|
||||||
@@ -175,8 +190,11 @@ def _uncovered_meta(project=None):
|
|||||||
flag, sep, rest = body_text.partition(":")
|
flag, sep, rest = body_text.partition(":")
|
||||||
flag = flag.strip()
|
flag = flag.strip()
|
||||||
desc = rest.strip() if sep else body_text
|
desc = rest.strip() if sep else body_text
|
||||||
out.append((coord, flag, desc))
|
issue = _canonical_issue(flag)
|
||||||
return out
|
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
|
# Snapshot of the uncovered findings captured when read_findings ran, so the
|
||||||
# report can be composed faithfully even after mark_covered has covered them.
|
# 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)
|
return "\n".join(lines)
|
||||||
|
|
||||||
def mark_covered(project=None, text=""):
|
def mark_covered(project=None, text=""):
|
||||||
meta = CURRENT_META or _uncovered_meta(project)
|
covered = _covered(project)
|
||||||
if not meta:
|
coords = [c for c, cat, subj in _log_findings(project) if c not in covered]
|
||||||
|
if not coords:
|
||||||
return "nothing to cover"
|
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"]
|
args = [CUBE, "note", "add", "--cat", "checkpoint"]
|
||||||
if project:
|
if project:
|
||||||
args += ["--project", project]
|
args += ["--project", project]
|
||||||
@@ -368,10 +387,10 @@ def main():
|
|||||||
if not report_text:
|
if not report_text:
|
||||||
# Run ended without an explicit "done" — still produce a grounded report.
|
# Run ended without an explicit "done" — still produce a grounded report.
|
||||||
report_text = compose_report(a.project, last_assistant)
|
report_text = compose_report(a.project, last_assistant)
|
||||||
if CURRENT_META:
|
# Deterministic coverage: mark all uncovered findings covered (including any
|
||||||
# Deterministic coverage: mark the reported findings covered regardless of
|
# deduped-away duplicates) regardless of whether the model did it, so the next
|
||||||
# whether the model did it, so the next run is genuinely incremental.
|
# run is genuinely incremental. No-op when there are no uncovered findings.
|
||||||
mark_covered(a.project)
|
mark_covered(a.project)
|
||||||
with open(a.report, "w") as f:
|
with open(a.report, "w") as f:
|
||||||
f.write(report_text)
|
f.write(report_text)
|
||||||
os.makedirs(os.path.dirname(a.report), exist_ok=True) if os.path.dirname(a.report) else None
|
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