|
| 1 | +#!/usr/bin/env python3 |
| 2 | +"""Audit actual bounded Direct context for every feature in one frozen KB tree. |
| 3 | +
|
| 4 | +Facet status here describes intact served metadata. Source verification and |
| 5 | +native approval are separate audits; these probes do not measure PR bug recall. |
| 6 | +""" |
| 7 | + |
| 8 | +from __future__ import annotations |
| 9 | + |
| 10 | +import argparse |
| 11 | +import hashlib |
| 12 | +import json |
| 13 | +import re |
| 14 | +from pathlib import Path |
| 15 | + |
| 16 | +import yaml |
| 17 | + |
| 18 | +from infermatrix_copilot.direct_routing import direct_review_plan |
| 19 | +from infermatrix_copilot.kb_service.knowledge_coverage import load_policy, policy_path |
| 20 | +from infermatrix_copilot.kb_service.knowledge_depth import FACETS, depth_page |
| 21 | +from infermatrix_copilot.knowledge_service.lifecycle import depth_sections |
| 22 | +from infermatrix_copilot.knowledge_view import KnowledgeView, build_manifest |
| 23 | + |
| 24 | +PROBES = ("description_and_paths", "query_only", "paths_only") |
| 25 | + |
| 26 | + |
| 27 | +def _hash(text: str) -> str: |
| 28 | + return hashlib.sha256(text.encode("utf-8")).hexdigest() |
| 29 | + |
| 30 | + |
| 31 | +def _document(document: dict, view: KnowledgeView, pin: str, problems: list[str], label: str) -> dict: |
| 32 | + raw = view.read_text(document["path"]) |
| 33 | + sections = {row["facet"]: row for row in depth_sections(raw)} |
| 34 | + content = document["content"] |
| 35 | + included = document["included_facets"] |
| 36 | + available = document["available_facets"] |
| 37 | + expected = set(sections) |
| 38 | + if set(available) != expected or len(available) != len(expected) or len(set(included)) != len(included): |
| 39 | + problems.append(f"{label}: delivered facet metadata differs from the intact page") |
| 40 | + if not set(included) <= expected: |
| 41 | + problems.append(f"{label}: included facets are not available") |
| 42 | + if document.get("facet_basis") != {facet: row["basis"] for facet, row in sections.items()}: |
| 43 | + problems.append(f"{label}: delivered basis differs from the intact page") |
| 44 | + if document.get("included_facet_basis") != {facet: sections[facet]["basis"] for facet in included if facet in sections}: |
| 45 | + problems.append(f"{label}: delivered injected basis differs from the actual included facets") |
| 46 | + if document.get("verified_gaps") != {facet: row["gap_label"] for facet, row in sections.items() if row["gap_label"]}: |
| 47 | + problems.append(f"{label}: delivered gap labels differ from the intact page") |
| 48 | + if document.get("not_injected_facets") != [facet for facet in available if facet not in included]: |
| 49 | + problems.append(f"{label}: uninjected facet metadata differs from selection") |
| 50 | + missing = [facet for facet in FACETS if facet not in expected] if document["feature"] else [] |
| 51 | + if document["missing_facets"] != missing: |
| 52 | + problems.append(f"{label}: missing facet metadata differs from the intact page") |
| 53 | + if len(content) > 3000: |
| 54 | + problems.append(f"{label}: document exceeds 3000 characters") |
| 55 | + if any(row["pin"] != pin for row in sections.values()): |
| 56 | + problems.append(f"{label}: served depth has another source pin") |
| 57 | + facet_states = {} |
| 58 | + for facet in FACETS: |
| 59 | + section = sections.get(facet) |
| 60 | + injection = "not_injected" |
| 61 | + if facet in included and section: |
| 62 | + if section["content"].strip() in content: |
| 63 | + injection = "full" |
| 64 | + elif content.strip() and len(included) == 1 and section["content"].strip().startswith(content.rstrip()): |
| 65 | + injection = "partial" |
| 66 | + else: |
| 67 | + problems.append(f"{label}: declared injected facet {facet} is absent from actual content") |
| 68 | + facet_states[facet] = {"status": section["basis"] if section else "unknown", "injection": injection, |
| 69 | + "gap_label": section["gap_label"] if section else None} |
| 70 | + return {**document, "content_sha256": _hash(content), "page_sha256": _hash(raw), |
| 71 | + "content_chars": len(content), "facet_states": facet_states} |
| 72 | + |
| 73 | + |
| 74 | +def audit_retrieval(root: Path, repo: str, pin: str, *, require_depth: bool = False) -> dict: |
| 75 | + if not re.fullmatch(r"[0-9a-f]{40}", pin): |
| 76 | + raise ValueError("retrieval audit requires a full pinned source SHA") |
| 77 | + root = root.resolve() |
| 78 | + knowledge = root / "knowledge" |
| 79 | + manifest = yaml.safe_load((root / f"adapters/{repo}/manifest.yaml").read_text()) |
| 80 | + repo_dir = manifest["knowledge"]["repo_subdir"] |
| 81 | + policy_file = root / policy_path(repo) |
| 82 | + policy_text = policy_file.read_text() |
| 83 | + policy = load_policy(policy_text, repo_dir) |
| 84 | + frozen = build_manifest(knowledge, "depth-retrieval-audit") |
| 85 | + view = KnowledgeView(root=knowledge, snapshot=f"depth-retrieval-audit-{frozen['tree_sha256']}", files=frozen["files"]) |
| 86 | + cases, problems, feature_status = [], [], {} |
| 87 | + for feature in policy.features: |
| 88 | + page = depth_page(feature) |
| 89 | + rows = depth_sections(view.read_text(page)) if page in view.files else [] |
| 90 | + rows = [row for row in rows if row["feature"] == feature.id] |
| 91 | + available = {row["facet"]: row["basis"] for row in rows} |
| 92 | + feature_status[feature.id] = {"page": page, "entry_points": list(feature.entry_points), |
| 93 | + "facets": {facet: available.get(facet, "unknown") for facet in FACETS}} |
| 94 | + if require_depth and not available: |
| 95 | + problems.append(f"{feature.id}: no intact depth available") |
| 96 | + for probe in PROBES: |
| 97 | + changed = list(feature.entry_points) if probe != "query_only" else [] |
| 98 | + title = f"{feature.title} {feature.id}" if probe != "paths_only" else "" |
| 99 | + plan = direct_review_plan(repo_dir.removeprefix("repos/"), title=title, changed_files=changed, view=view) |
| 100 | + related = plan["related_knowledge"] |
| 101 | + label = f"{feature.id}/{probe}" |
| 102 | + documents = [_document(d, view, pin, problems, label) for d in related["documents"]] |
| 103 | + if any(not d["path"].startswith(repo_dir + "/") for d in documents): |
| 104 | + problems.append(f"{label}: automatic context escaped the selected repository") |
| 105 | + actual_chars = sum(len(d["content"]) for d in documents) |
| 106 | + if len(documents) > 2 or actual_chars > 6000 or actual_chars != related["content_chars"]: |
| 107 | + problems.append(f"{label}: actual context violates the two-page/6000-character contract") |
| 108 | + if len({d["feature"] or d["path"] for d in documents}) != len(documents): |
| 109 | + problems.append(f"{label}: duplicate feature selected") |
| 110 | + hit = next((d for d in documents if d["feature"] == feature.id), None) |
| 111 | + delivered_depth = bool(hit and any(s["injection"] != "not_injected" for s in hit["facet_states"].values())) |
| 112 | + if probe != "paths_only" and (not hit or (available and (hit["path"] != page or not delivered_depth))): |
| 113 | + problems.append(f"{label}: feature depth absent from actual Direct context") |
| 114 | + followups = plan["navigation_policy"]["related_document_read_paths"] |
| 115 | + if followups != [d["path"] for d in documents if d["more_available"]]: |
| 116 | + problems.append(f"{label}: followup paths differ from the selected incomplete documents") |
| 117 | + cases.append({"feature": feature.id, "probe": probe, "changed_files": changed, "query": title, |
| 118 | + "feature_hit": bool(hit), "depth_hit": delivered_depth, |
| 119 | + "content_chars": actual_chars, "documents": documents, |
| 120 | + "followup_read_paths": followups, "execution_budget": plan["execution_budget"], |
| 121 | + "facet_injection": {facet: hit["facet_states"][facet]["injection"] if hit else "not_injected" |
| 122 | + for facet in FACETS}}) |
| 123 | + if build_manifest(knowledge, frozen["snapshot"])["files"] != frozen["files"] or policy_file.read_text() != policy_text: |
| 124 | + problems.append("knowledge or policy changed during the frozen retrieval audit") |
| 125 | + statuses = ("supported", "verified_absent", "unknown") |
| 126 | + summary = {"total_features": len(policy.features), "total_facets": len(policy.features) * len(FACETS), |
| 127 | + "features_with_intact_depth": sum(any(s != "unknown" for s in f["facets"].values()) for f in feature_status.values()), |
| 128 | + "available_facet_status": {s: sum(status == s for f in feature_status.values() for status in f["facets"].values()) |
| 129 | + for s in statuses}, "probes": {}} |
| 130 | + for probe in PROBES: |
| 131 | + selected = [case for case in cases if case["probe"] == probe] |
| 132 | + summary["probes"][probe] = {"cases": len(selected), "feature_hits": sum(c["feature_hit"] for c in selected), |
| 133 | + "depth_hits": sum(c["depth_hit"] for c in selected), |
| 134 | + "max_documents": max((len(c["documents"]) for c in selected), default=0), |
| 135 | + "max_content_chars": max((c["content_chars"] for c in selected), default=0), |
| 136 | + "target_feature_injection": {s: sum(state == s for c in selected for state in c["facet_injection"].values()) |
| 137 | + for s in ("full", "partial", "not_injected")}} |
| 138 | + return {"summary": summary, "problems": problems, "features": feature_status, "cases": cases, |
| 139 | + "provenance": {"source_pin": pin, "policy_sha256": _hash(policy_text), |
| 140 | + "knowledge_tree_sha256": view.tree_sha256, "snapshot_files": len(view.files)}, |
| 141 | + "limits": "Statuses describe intact served metadata, not independent source/native approval. " |
| 142 | + "Full and partial injection are counted separately from available knowledge. " |
| 143 | + "Paths-only misses are diagnostic when entry points overlap. Catalog delivery does not " |
| 144 | + "establish PR bug recall, review quality, verified behavior or passing tests."} |
| 145 | + |
| 146 | + |
| 147 | +def main() -> int: |
| 148 | + parser = argparse.ArgumentParser(description=__doc__) |
| 149 | + parser.add_argument("--root", type=Path, default=Path.cwd()) |
| 150 | + parser.add_argument("--repo", required=True) |
| 151 | + parser.add_argument("--pin", required=True) |
| 152 | + parser.add_argument("--snapshot-commit", help="Full commit SHA identifying the caller's read-only Git export") |
| 153 | + parser.add_argument("--require-depth", action="store_true") |
| 154 | + parser.add_argument("--report", type=Path) |
| 155 | + args = parser.parse_args() |
| 156 | + if args.snapshot_commit and not re.fullmatch(r"[0-9a-f]{40}", args.snapshot_commit): |
| 157 | + parser.error("snapshot commit must be a full SHA") |
| 158 | + if args.report and args.report.resolve().is_relative_to((args.root / "knowledge").resolve()): |
| 159 | + parser.error("retrieval evidence belongs outside product knowledge") |
| 160 | + report = audit_retrieval(args.root, args.repo, args.pin, require_depth=args.require_depth) |
| 161 | + if args.snapshot_commit: |
| 162 | + report["provenance"]["declared_snapshot_commit"] = args.snapshot_commit |
| 163 | + report["notes"] = ["The caller identifies this read-only Git export with declared_snapshot_commit; " |
| 164 | + "the audit independently binds its actual files with knowledge_tree_sha256."] |
| 165 | + if args.report: |
| 166 | + args.report.parent.mkdir(parents=True, exist_ok=True) |
| 167 | + args.report.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n") |
| 168 | + print(json.dumps({"summary": report["summary"], "problems": report["problems"]}, ensure_ascii=False, indent=2)) |
| 169 | + return int(bool(report["problems"])) |
| 170 | + |
| 171 | + |
| 172 | +if __name__ == "__main__": |
| 173 | + raise SystemExit(main()) |
0 commit comments