#!/usr/bin/env python3 """Aggregate per-file deep-read quality (V2.1 three-part gate). A-stage implementation run by the main agent after each sub-agent wave. Reads sub-agent output JSON files that were written to disk (not returned into the main context), computes the three-part gate per file: ① unit completeness : sub-agent semantic_units must cover every graph unit (Function/Class/Test node) of the file. Hard, 100%. Giant files (units<3 or max unit >80% of lines) are exempt from this gate (unit_exempt_files). ② line coverage : |union(read_ranges)| / real line count >= 95%. Denominator is the REAL file line count read once (cached), NOT graph node line_end (verified +-1 skew). ③ anti-fake budget : reported to the main agent; only a sampled re-read can detect fake reads (engine cannot). Usage: python aggregate_deep_read.py repository root (for graph + real file reads) directory containing sub-agent result JSON files (each: {"path": rel, "total_lines": int, "read_ranges": [[s,e],...], "semantic_units": [{"range":[s,e],"kind","name"}], "findings": [...]}) Outputs to stdout: {status, line_gap_files[], unit_gap_files[], unit_exempt_files[], verified_files[], summary} """ import json import sys from pathlib import Path def _abs(root: Path, rel: str) -> Path: p = Path(rel) return p if p.is_absolute() else root / p def _real_line_count(root: Path, rel: str, cache: dict) -> int: """Real file line count, cached. Independent of graph node line_end.""" if rel in cache: return cache[rel] p = _abs(root, rel) try: n = len(p.read_text(encoding="utf-8", errors="replace").splitlines()) except OSError: n = 0 cache[rel] = n return n def _union_len(ranges: list) -> int: """Covered line count of a list of inclusive [s,e] ranges.""" if not ranges: return 0 merged: list[list[int]] = [] for s, e in sorted((int(a), int(b)) for a, b in ranges): if s < 1: s = 1 if e < s: continue if merged and s <= merged[-1][1] + 1: merged[-1][1] = max(merged[-1][1], e) else: merged.append([s, e]) return sum(e - s + 1 for s, e in merged) def _graph_units(store, root: Path, rel: str) -> list[dict]: """Graph semantic-unit nodes (Function/Class/Test) with line ranges.""" abs_path = _abs(root, rel).as_posix().replace("/", "\\") q = abs_path.replace("\\", "/") try: rows = store._conn.execute( "SELECT kind, name, line_start, line_end FROM nodes " "WHERE file_path = ? AND kind IN ('Function','Class','Test') " "ORDER BY line_start", (q,), ).fetchall() except Exception: return [] out = [] for r in rows: try: out.append( { "kind": r["kind"], "name": r["name"], "line_start": int(r["line_start"]), "line_end": int(r["line_end"]), } ) except (KeyError, TypeError): continue return out def _overlap(a: list, b: list) -> int: lo, hi = max(a[0], b[0]), min(a[1], b[1]) return max(0, hi - lo + 1) def _unit_covered( graph_unit: dict, read_ranges: list, unit_ranges: list, matched_uids: set, ) -> bool: """A graph unit is covered iff one reported unit range (a) overlaps its range by >=80% of the graph unit's span, AND (b) is not already claimed by a higher-overlap graph unit (one-to-one matching), AND (c) >=80% of the graph unit's lines fall inside union(read_ranges). The one-to-one rule stops a single wide range (e.g. the whole file) from covering every unit by pretending to be all of them.""" gs, ge = graph_unit["line_start"], graph_unit["line_end"] gspan = max(1, ge - gs + 1) # exact-range match wins (handles nested/overlapping graph units) exact = [i for i, (s, e) in enumerate(unit_ranges) if s == gs and e == ge] if exact: best_idx = exact[0] if best_idx in matched_uids: return False matched_uids.add(best_idx) else: best_idx, best_overlap = None, 0 for i, (s, e) in enumerate(unit_ranges): ov = _overlap([gs, ge], [s, e]) if ov > best_overlap: best_overlap, best_idx = ov, i if best_idx is None or best_idx in matched_uids: return False if best_overlap / gspan < 0.8: return False matched_uids.add(best_idx) in_union = 0 for s, e in _merge_ranges(read_ranges): lo, hi = max(gs, s), min(ge, e) if lo <= hi: in_union += hi - lo + 1 if (in_union / gspan) < 0.8: return False matched_uids.add(best_idx) return True def _merge_ranges(ranges: list) -> list[list[int]]: merged: list[list[int]] = [] for s, e in sorted((int(a), int(b)) for a, b in ranges or []): if s < 1: s = 1 if e < s: continue if merged and s <= merged[-1][1] + 1: merged[-1][1] = max(merged[-1][1], e) else: merged.append([s, e]) return merged def _is_giant_file(graph_units: list, real_lines: int) -> bool: """unit-exempt when the largest unit spans >80% of the file's lines. Aligned with the engine (scoring.py _is_giant_file): small files with a few ordinary units are NOT exempt - they must cover every unit. A single huge function (e.g. migrations.rs run_migrations = 98%) makes unit completeness meaningless, so such files are checked on line coverage only. """ if not graph_units or real_lines <= 0: return False largest = max(u["line_end"] - u["line_start"] + 1 for u in graph_units) return (largest / real_lines) > 0.8 def _load_subagent_files(out_dir: Path) -> list[dict]: payloads = [] for f in sorted(out_dir.glob("*.json")): try: payloads.append(json.loads(f.read_text(encoding="utf-8"))) except (OSError, ValueError) as exc: print(json.dumps({"status": "error", "file": str(f), "error": str(exc)})) return payloads def aggregate(repo_root: str, out_dir: str): root = Path(repo_root) out = Path(out_dir) # engine import path: prefer CRG_ENGINE env, else known local checkout engine = ( __import__("os").environ.get("CRG_ENGINE") or r"D:\code-review-graph\code-review-graph-main" ) sys.path.insert(0, engine) from code_review_graph.tools._common import _get_store store, root2 = _get_store(repo_root) cache: dict = {} verified: list[str] = [] line_gap: list[dict] = [] unit_gap: list[dict] = [] exempt: list[dict] = [] seen: set = set() for payload in _load_subagent_files(out): rel = (payload.get("path") or "").replace("\\", "/") if not rel or rel in seen: continue seen.add(rel) ranges = payload.get("read_ranges") or [] units = payload.get("semantic_units") or [] reported_total = payload.get("total_lines") real = _real_line_count(root, rel, cache) if real == 0: continue line_cov = _union_len(ranges) / real g_units = _graph_units(store, root, rel) giant = _is_giant_file(g_units, real) unit_ranges = [tuple(map(int, (u.get("range") or [0, 0])[:2])) for u in units] # ① unit completeness if g_units and not giant: matched_uids: set = set() uncovered = [ {"name": u["name"], "range": [u["line_start"], u["line_end"]]} for u in g_units if not _unit_covered(u, ranges, unit_ranges, matched_uids) ] if uncovered: unit_gap.append( { "path": rel, "total_units": len(g_units), "covered_units": len(g_units) - len(uncovered), "uncovered": uncovered, } ) # a unit gap also fails the file continue elif g_units and giant: exempt.append( { "path": rel, "reason": "giant-file: units=%d largest_span=%.0f%%" % (len(g_units), 100 * max(u["line_end"] - u["line_start"] + 1 for u in g_units) / real), } ) # ② line coverage (applies to every file) if line_cov < 0.95: line_gap.append( { "path": rel, "coverage_pct": round(line_cov * 100, 1), "total_lines": real, "covered_lines": _union_len(ranges), "reported_total": reported_total, } ) continue verified.append(rel) store.close() result = { "status": "ok", "verified_files": sorted(verified), "line_gap_files": sorted(line_gap, key=lambda x: x["coverage_pct"]), "unit_gap_files": sorted(unit_gap, key=lambda x: x["total_units"]), "unit_exempt_files": sorted(exempt, key=lambda x: x["path"]), "summary": ( f"verified={len(verified)} line_gap={len(line_gap)} " f"unit_gap={len(unit_gap)} unit_exempt={len(exempt)} " f"(unit_exempt are checked on line coverage only)" ), } print(json.dumps(result, ensure_ascii=False, indent=1)) if __name__ == "__main__": if len(sys.argv) != 3: print(__doc__) sys.exit(2) aggregate(sys.argv[1], sys.argv[2])