From 0141459517e5be87f1a2fbca437285176d997392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Engebr=C3=A5ten?= Date: Mon, 16 Feb 2026 10:51:18 +0100 Subject: [PATCH] feat(agents): add investigation context loader module Provides load_investigation_context() and load_investigation_for_qa() to load investigation data from spec directories for GitHub-sourced tasks. Co-Authored-By: Claude Sonnet 4.5 --- apps/backend/agents/investigation_context.py | 88 ++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 apps/backend/agents/investigation_context.py diff --git a/apps/backend/agents/investigation_context.py b/apps/backend/agents/investigation_context.py new file mode 100644 index 00000000..1e9d6bbe --- /dev/null +++ b/apps/backend/agents/investigation_context.py @@ -0,0 +1,88 @@ +""" +Investigation context loading for agents. + +Provides utilities to load investigation data from spec directories +for GitHub-sourced tasks. +""" + +import json +from pathlib import Path +from typing import Any + + +def load_investigation_context(spec_dir: Path) -> dict[str, Any] | None: + """ + Load investigation context if this spec was created from a GitHub issue. + + Args: + spec_dir: Path to the spec directory + + Returns: + Structured investigation context with root_cause, fix_approaches, + reproducer, gotchas, and patterns_to_follow, or None if no + investigation data exists. + """ + investigation_report_path = spec_dir / "investigation_report.json" + + if not investigation_report_path.exists(): + return None + + try: + with open(investigation_report_path) as f: + report = json.load(f) + + # Structure the context for agents + return { + "root_cause": { + "summary": report.get("root_cause", {}).get("summary"), + "evidence": report.get("root_cause", {}).get("evidence", []), + "code_paths": report.get("root_cause", {}).get("code_paths", []) + }, + "fix_approaches": report.get("fix_approaches", []), + "reproducer": report.get("reproducer"), + "gotchas": report.get("gotchas", []), + "patterns_to_follow": report.get("patterns_to_follow", []), + "impact": report.get("impact", {}) + } + except (json.JSONDecodeError, OSError): + return None + + +def load_investigation_for_qa(spec_dir: Path, base_branch: str) -> dict[str, Any] | None: + """ + Load investigation context for QA validation. + + Similar to load_investigation_context but includes base_branch + for QA comparison. + + Args: + spec_dir: Path to the spec directory + base_branch: Base branch to compare against (e.g., 'main', 'develop') + + Returns: + Structured investigation context with root_cause, reproducer, + impact, expected_outcome, and base_branch, or None if no + investigation data exists. + """ + investigation_report_path = spec_dir / "investigation_report.json" + + if not investigation_report_path.exists(): + return None + + try: + with open(investigation_report_path) as f: + report = json.load(f) + + return { + "root_cause": { + "summary": report.get("root_cause", {}).get("summary"), + "evidence": report.get("root_cause", {}).get("evidence", []), + "code_paths": report.get("root_cause", {}).get("code_paths", []) + }, + "reproducer": report.get("reproducer"), + "impact": report.get("impact", {}), + "expected_outcome": report.get("expected_outcome"), + "base_branch": base_branch + } + except (json.JSONDecodeError, OSError): + return None