feat: compare accountability update deltas

This commit is contained in:
2026-05-24 10:05:39 +02:00
parent 071a4c49e3
commit 355b7be66a
7 changed files with 410 additions and 2 deletions

View File

@@ -19,6 +19,7 @@ from .accountability_roots import (
AccountabilityEvidenceStore,
build_identity_projection,
build_ownership_review,
build_update_delta,
collect_accountability_root_evidence,
load_accountability_root_manifest,
)
@@ -125,6 +126,9 @@ def build_parser() -> argparse.ArgumentParser:
discover_roots.add_argument("--max-items-per-root", type=int, default=200)
discover_roots.add_argument("--identity-projection", action="store_true", help="Print normalized identity candidates instead of raw evidence.")
discover_roots.add_argument("--ownership-review", action="store_true", help="Print ownership resolution and review blockers.")
discover_roots.add_argument("--delta", action="store_true", help="Print a delta against previous identity/ownership review files.")
discover_roots.add_argument("--previous-identity-projection", type=Path, default=None)
discover_roots.add_argument("--previous-ownership-review", type=Path, default=None)
discover_roots.add_argument("--store-db", type=Path, default=None, help="Persist evidence and identity candidates in a SQLite store.")
review_identity = sub.add_parser(
@@ -363,9 +367,27 @@ def main(argv: list[str] | None = None) -> int:
store = AccountabilityEvidenceStore(args.store_db) if args.store_db else None
decisions = store.latest_review_decisions() if store else {}
ownership_review = build_ownership_review(projection, manifest, review_decisions=decisions)
update_delta = build_update_delta(
projection,
ownership_review,
previous_identity_projection=_load_json_file(args.previous_identity_projection)
if args.previous_identity_projection
else None,
previous_ownership_review=_load_json_file(args.previous_ownership_review)
if args.previous_ownership_review
else None,
)
if args.store_db:
store.add_evidence_run(payload, projection)
output = ownership_review if args.ownership_review else projection if args.identity_projection else payload
output = (
update_delta
if args.delta
else ownership_review
if args.ownership_review
else projection
if args.identity_projection
else payload
)
print(json.dumps(output, indent=2, sort_keys=True))
return 0
@@ -1648,6 +1670,13 @@ def _load_graph_or_exit(paths: list[Path]) -> FabricGraph:
return graph
def _load_json_file(path: Path) -> dict[str, Any]:
payload = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(payload, dict):
raise ValueError(f"JSON file must contain an object: {path}")
return payload
def _print_providers(graph: FabricGraph, capability: str) -> None:
providers = graph.providers(capability)
if not providers: