band-aider-pub/band_aider/judgement/source_needed_to_understand_failure_check.py
2026-10-17 11:57:37 -07:00

72 lines
2.7 KiB
Python

# SPDX-FileCopyrightText: 2026 Art Sea LLC
#
# SPDX-License-Identifier: AGPL-3.0-only
"""Select managed files whose paths occur in the supplied failure evidence."""
from __future__ import annotations
from itertools import chain
from band_aider.context.class_meta import ClassMeta
from band_aider.judgement import failure_source_path
from band_aider.judgement.failure_source_candidate_selection_strategy import FailureCandidateScope
from band_aider.judgement.file_manager import FileManager
from band_aider.judgement.mechanical_check import MechanicalCheck
class SourceNeededToUnderstandFailureCheck(MechanicalCheck):
"""Match current source and test inventories against one failure snapshot.
Callers supply complete failure evidence and retain authority over how matched
files are used. For example, ``check(); files = check.matching_files`` exposes
all matches while ``bool(check.matching_files)`` is the call's boolean result.
"""
def __init__(
self,
*,
file_manager: FileManager,
scope: FailureCandidateScope,
failure_error_message: str,
failure_stack_trace: list[str],
failure_raw_output: str,
) -> None:
"""Bind failure evidence without reading source contents or consulting a model."""
super().__init__(changes=[])
self._file_manager = file_manager
self._scope = scope
self._failure_evidence = "\n".join((
failure_error_message,
*failure_stack_trace,
failure_raw_output,
))
self._matching_files: tuple[ClassMeta, ...] = ()
@property
def matching_files(self) -> tuple[ClassMeta, ...]:
"""Expose the latest matches in source-then-test inventory order."""
return self._matching_files
def __call__(self) -> bool:
"""Refresh all managed path matches and record whether any were found."""
matched: dict[str, ClassMeta] = {}
# Scan both current inventories without admitting paths outside managed scope.
for candidate in filter(
lambda candidate: candidate.content_type in self._scope.value,
chain(self._file_manager.src_classes, self._file_manager.test_classes),
):
# Keep the first inventory entry for each complete path named by the failure.
if failure_source_path.is_referenced(
file_manager=self._file_manager,
source=candidate,
evidence=self._failure_evidence,
):
matched.setdefault(candidate.location, candidate)
self._matching_files = tuple(matched.values())
result = bool(self._matching_files)
self.changes.append((self, result))
return result