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

70 lines
3.3 KiB
Python

# SPDX-FileCopyrightText: 2026 Art Sea LLC
#
# SPDX-License-Identifier: AGPL-3.0-only
"""Query carried file context from existing backlog and provenance records."""
from __future__ import annotations
from collections import defaultdict
from copy import deepcopy
import json
from typing import TYPE_CHECKING
from band_aider.context import provenance
from band_aider.context.class_meta import ClassMeta
from band_aider.judgement.file_manager import FileManager
if TYPE_CHECKING:
from band_aider.context.working_backlog import InspectionBacklog
def current_sources(file_manager: FileManager, backlog: InspectionBacklog | None = None) -> list[ClassMeta]:
"""Resolve carried backlog identities to current managed files without changing history."""
from band_aider.context import phase_start_record
from band_aider.context.working_backlog import TestingBacklog
items: list[InspectionBacklog] = []
# Include context discovered after the immutable phase-start snapshot.
if backlog is not None:
items.append(deepcopy(backlog))
active = provenance.current()
# Standalone consumers can query their backlog without a running provenance graph.
if active is not None:
graph, _phase, message_id = active
parents: dict[str, set[str]] = defaultdict(set)
# Reused phase starts can have more than one recorded incoming handoff.
for edge in graph.edges:
parents[edge.to_id].add(edge.from_id)
pending = [message_id]
seen: set[str] = set()
# Traverse only this item's causal history, including interrupted revival attempts.
while pending:
current_id = pending.pop()
message = graph.messages[current_id]
item = phase_start_record.phase_start_from_record(json.loads(message.payload))
items.append(item)
seen.add(current_id)
origins = set(map(lambda guidance: guidance.origin_message_id, filter(
lambda guidance: guidance.addressed_message_id == current_id,
item.future_guidance,
)))
predecessors = (parents[current_id] | origins | set(filter(None, (message.parent_id,)))) & graph.messages.keys()
# Stable provenance order prevents unchanged context from churning between retries.
pending.extend(sorted(predecessors - seen - set(pending), key=lambda key: graph.messages[key].sequence))
sources: dict[str, ClassMeta] = {}
# Backlog dependencies and current source context retain identities, not historical bytes.
for item in items:
context = [*item.upstream_classes, *item.downstream_classes]
# QA and remediation also carry already-admitted failure context.
if isinstance(item, TestingBacklog):
context.extend(item.failure_context_sources)
# Resolve ownership and metadata mechanically; deleted files cannot become live candidates.
for recorded in context:
source = file_manager.current_metadata(recorded.location, content_type=recorded.content_type)
# Only existing files in the current managed inventory supply readable evidence.
if source is not None and file_manager.repo_exec.is_file(source.resolve_path(file_manager)):
sources.setdefault(source.location, source)
return list(sources.values())