72 lines
2.7 KiB
Python
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
|