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

2843 lines
95 KiB
Python

from __future__ import annotations
from collections.abc import Mapping
from collections.abc import Callable
from band_aider.judgement.source_discovery_strategy import CallTraceNode
from dataclasses import dataclass, field, fields, replace
from datetime import datetime
import inspect
import logging
from pathlib import Path
import re
from typing import Any, Iterator, Sequence, get_type_hints
import pytest
from band_aider.ai_client.completion_client import CompletionClient, OpenAIConfig
from band_aider.ai_client.token_budget_estimator import TokenBudgetEstimator
from band_aider.context.class_meta import ClassMeta, ContentType
from band_aider.context.token_budget_state import (
DEFAULT_BATCH_TARGET_RATIO,
DEFAULT_MAX_OUTPUT_TOKENS,
DEFAULT_MAX_PROMPT_TOKENS,
DEFAULT_TOKENS_PER_WORD,
configure_token_budget_settings,
reset_token_budget_state_for_tests,
)
from band_aider.judgement.contextual_file_render_history import (
ContextualFileRenderHistory,
)
from band_aider.judgement.contextual_file_renderer import (
AUTO_WHOLE_START_COUNT,
MAX_CONTEXT_REFINEMENTS,
MIN_KEEP_LINES,
SELECTOR_CATALOGUE_TOKEN_RATIO,
ContextualFileRenderer,
_CallableBucket,
_CallableChunk,
_FileAnalysis,
_RangedBeat,
_SelectedChunk,
_assign_class_owners,
_group_callable_behaviors_for_selection,
_merge_overlapping_callable_behaviors,
_build_class_containers,
_classify_ranged_beats,
_reduce_state_ranges,
_mask_omitted_callable_ranges,
_overlaps,
)
from band_aider.judgement.fidelity import Fidelity
from band_aider.judgement.prompt_formatter import MarkdownPromptFormatterBuilder
from band_aider.judgement.file_manager import FileManager
from band_aider.judgement.source_discovery_strategy import (
SearchableBeat,
SearchableBeatValue,
SourceDiscoveryStrategy,
)
from band_aider.strategies.language_strategy_set import LanguageStrategySet
from band_aider.strategies.oop_signature_extract import (
Declaration,
DeclarationType,
OOpSignature,
OOpSignatureExtract,
OOpSignatureRender,
)
from band_aider.strategies.python.python_source_discovery import (
PythonSourceDiscovery,
)
from tests.support.memory_path import MemoryPath
class _Client(CompletionClient):
"""Provide explicit context-selection answers and retain correction evidence."""
def __init__(
self,
response: str | Exception | list[str] = "NO_KEEP\nREASON: none\n",
) -> None:
"""Bind a fixed result, provider failure or finite response script."""
self.response = response
self.prompts: list[str] = []
@property
def inner(self) -> CompletionClient | None:
return None
@property
def openai_configs(self) -> tuple[OpenAIConfig, ...]:
return (OpenAIConfig(model="test", api_base="https://example.invalid"),)
def complete(self, prompt: str, logger=None) -> str:
"""Record context evidence and return the next scripted selector response."""
self.prompts.append(prompt)
if isinstance(self.response, Exception):
raise self.response
# Script correction cases explicitly; exhaustion is a test failure.
if isinstance(self.response, list):
return self.response.pop(0)
return self.response
class _Discovery(SourceDiscoveryStrategy):
"""Provide nominal stationary source-discovery evidence to the renderer."""
def visit_call_sites(self, target: CallTraceNode, already_seen: set[str] | None = None, predicate: Callable[[CallTraceNode], bool] | None = None, *, target_args: dict[str, CallTraceNode] | None = None, pristine: Mapping[str, str] | None = None) -> None:
"""Supply no imported calls in this stationary discovery double."""
del target, already_seen, predicate
def get_call_tree(self, location: str, name: str | None = None, *, line: int | None = None, depth: int = 1, already_seen: set[str] | None = None, predicate: Callable[[CallTraceNode], bool] | None = None, pristine: Mapping[str, str] | None = None) -> CallTraceNode:
"""Reject origins because this double contains no callable definitions."""
raise LookupError(location)
def __init__(self, beats: Sequence[SearchableBeat]) -> None:
"""Store explicit language-neutral beats for every renderer read."""
self.beats = tuple(beats)
self.calls = 0
def get_sources(
self,
recursive: bool = True,
type: Any | None = None,
) -> list[Path]:
"""Return no filesystem-discovered sources in this evidence-only double."""
del recursive, type
return []
def get_tests(
self,
recursive: bool = True,
type: Any | None = None,
) -> list[Path]:
"""Return no filesystem-discovered tests in this evidence-only double."""
del recursive, type
return []
def searchable_beats(self, file_name: str, source: str) -> list[SearchableBeatValue]:
"""Return the explicit beat population without inspecting host source."""
del file_name, source
self.calls += 1
return list(self.beats)
class _SignatureExtract(OOpSignatureExtract):
"""Keep renderer selection tests independent from declaration extraction."""
def extract(self, module: str, name: str):
del module, name
return None
def extract_references(self, sources_by_location):
del sources_by_location
return ()
def extract_source(self, location, source):
del location, source
return ()
class _CurrentSourceCards(OOpSignatureExtract):
"""Make renderer card refresh behavior explicit without parsing fixture source."""
def extract(self, module: str, name: str):
del module, name
return None
def extract_references(self, sources_by_location):
source = "\n".join(sources_by_location.values())
name = "NewDependency" if "new_dependency" in source else "OldDependency"
return (
OOpSignature(
"dependency.module",
name,
Declaration(DeclarationType.CLASS, name),
),
)
def extract_source(self, location, source):
del location
# Expose source refresh through a stable fake-card identity.
name = "CurrentNew" if "new_callable" in source else "CurrentOld"
return (
OOpSignature(
"current.module",
name,
Declaration(DeclarationType.FUNCTION, name, pydoc="Current card."),
),
)
class _CurrentFileCards(OOpSignatureExtract):
"""Provide only the compact current-file cards used by economical rendering."""
def __init__(self) -> None:
"""Record source extraction so cache behavior remains publicly observable."""
self.sources: list[str] = []
def extract(self, module: str, name: str):
"""Return no dependency declaration for this source-card-only fixture."""
del module, name
return None
def extract_references(self, sources_by_location):
"""Keep dependency-card selection outside this focused shortcut test."""
del sources_by_location
return ()
def extract_source(self, location, source):
"""Return one stable generic card without invoking a language parser."""
del location
self.sources.append(source)
return (
OOpSignature(
"current.module",
"Current",
Declaration(DeclarationType.FUNCTION, "Current"),
),
)
class _StableIdentityCards(OOpSignatureExtract):
"""Change a card surface while retaining the same dependency identity."""
def extract(self, module: str, name: str):
"""Keep the fixture focused on source-set reference discovery."""
del module, name
return None
def extract_references(self, sources_by_location):
"""Expose current declaration text independently from stable card identity."""
source = "\n".join(sources_by_location.values())
# Model an imported dependency whose public declaration changed in source.
pydoc = "second declaration" if "second" in source else "first declaration"
return (
OOpSignature(
"dependency.module",
"StableDependency",
Declaration(DeclarationType.CLASS, "StableDependency", pydoc=pydoc),
),
)
def extract_source(self, location, source):
del location, source
return ()
class _ManyReferenceCards(OOpSignatureExtract):
"""Provide multiple candidates so renderer-owned admission remains observable."""
def extract(self, module: str, name: str):
"""Keep the fixture focused on source-set reference discovery."""
del module, name
return None
def extract_references(self, sources_by_location):
"""Return a stable card catalogue independent of fixture source text."""
del sources_by_location
return (
OOpSignature(
"dependency.module",
"Keep",
Declaration(DeclarationType.CLASS, "Keep"),
),
OOpSignature(
"dependency.module",
"Discard",
Declaration(DeclarationType.CLASS, "Discard"),
),
)
def extract_source(self, location, source):
del location, source
return ()
class _ReferenceClient(_Client):
"""Select the sole dependency card currently offered by the renderer fixture."""
def complete(self, prompt: str, logger=None) -> str:
"""Return the current fixture candidate as a strict module/name selection."""
self.prompts.append(prompt)
match = re.search(r"REFERENCE: dependency\.module\.(\w+)", prompt)
assert match is not None
return (
'{"references": [{"module": "dependency.module", "name": "'
+ match.group(1)
+ '"}]}'
)
class _ReferenceQueueClient(_Client):
"""Return queued strict reference-card selections while recording each prompt."""
def __init__(self, responses: Sequence[str]) -> None:
"""Store one selection response for each changed renderer evidence state."""
super().__init__()
self.responses = list(responses)
def complete(self, prompt: str, logger=None) -> str:
"""Record and return the next exact reference selection response."""
del logger
self.prompts.append(prompt)
return self.responses.pop(0)
class _SignatureRender(OOpSignatureRender):
"""Keep renderer tests independent from language-specific card spelling."""
def render(self, signature: OOpSignature, output) -> None:
"""Write a stable test declaration without Python source syntax."""
output.write(f"{signature.declaration.decl_type.value} {signature.name}\n")
class _PydocSignatureRender(OOpSignatureRender):
"""Expose fixture declaration content so live-card rendering is observable."""
def render(self, signature: OOpSignature, output) -> None:
"""Write the declaration documentation supplied by the current extractor result."""
output.write(signature.declaration.pydoc)
class _FileManager(FileManager):
"""Nominal FileManager surface backed by the fixture's memory repository."""
def __init__(
self,
repo_root: MemoryPath,
diff: str = "",
*,
metadata_by_location: dict[str, ClassMeta] | None = None,
refreshed_by_location: dict[str, ClassMeta] | None = None,
) -> None:
"""Bind only the renderer-owned FileManager state to memory."""
self.repo_root = Path(str(repo_root))
self.repo_exec = repo_root.repository
self.diff = diff
self.metadata_by_location = dict(metadata_by_location or {})
self.refreshed_by_location = dict(refreshed_by_location or {})
self.refresh_calls: list[tuple[ClassMeta, str, str]] = []
def repo_path(self, location: str | Path) -> Path:
path = Path(location)
return path if path.is_absolute() else self.repo_root / path
def repo_location(self, location: str | Path) -> str:
return self.repo_path(location).relative_to(self.repo_root).as_posix()
def git_diff_for(self, location: str | Path) -> str:
return self.diff
def current_src(self, location: str) -> ClassMeta | None:
metadata = self.metadata_by_location.get(location)
if metadata is not None and metadata.content_type is ContentType.Source:
return metadata
return None
def current_test(self, location: str) -> ClassMeta | None:
metadata = self.metadata_by_location.get(location)
if metadata is not None and metadata.content_type is ContentType.Test:
return metadata
return None
def refresh_class_meta(
self,
class_meta: ClassMeta,
prompt: str,
tailing_logs: str = "",
) -> ClassMeta:
self.refresh_calls.append((class_meta, prompt, tailing_logs))
refreshed = self.refreshed_by_location[class_meta.location]
self.metadata_by_location[refreshed.location] = refreshed
return refreshed
@pytest.fixture(autouse=True)
def _isolate_state() -> Iterator[None]:
"""Reset renderer-owned history without changing process functions."""
previous = LanguageStrategySet.instance
reset_token_budget_state_for_tests()
configure_token_budget_settings(
max_prompt_tokens=DEFAULT_MAX_PROMPT_TOKENS,
max_output_tokens=DEFAULT_MAX_OUTPUT_TOKENS,
tokens_per_word=DEFAULT_TOKENS_PER_WORD,
batch_target_ratio=DEFAULT_BATCH_TARGET_RATIO,
)
ContextualFileRenderHistory.get().reset()
yield
ContextualFileRenderHistory.get().reset()
reset_token_budget_state_for_tests()
LanguageStrategySet.instance = previous
def _beat(
kind: str,
start: int,
end: int,
*,
text: str = "",
attributes: tuple[tuple[str, str], ...] = (),
) -> SearchableBeat:
return SearchableBeat(
kind=kind,
text=text,
attributes=attributes,
start_line=start,
end_line=end,
)
def _ranged(
kind: str,
start: int,
end: int,
order: int = 0,
*,
text: str = "",
attributes: tuple[tuple[str, str], ...] = (),
) -> _RangedBeat:
return _RangedBeat(
beat=_beat(
kind,
start + 1,
end,
text=text,
attributes=attributes,
),
original_order=order,
start=start,
end=end,
)
def _renderer(
tmp_path: MemoryPath,
source: str,
beats: Sequence[SearchableBeat],
response: str | Exception = "NO_KEEP\nREASON: none\n",
*,
readonly: bool = False,
diff: str = "",
pinned_context: Sequence[str] = (),
backlog_guidance: Sequence[str] = (),
failure_error_message: str = "",
failure_stack_trace: Sequence[str] = (),
failure_raw_output: str = "",
call_trace: str = "",
repair_episode: bool = False,
class_meta: ClassMeta | None = None,
signature_extract: OOpSignatureExtract | None = None,
) -> tuple[ContextualFileRenderer, _Client, _Discovery]:
location = "src/example.py"
path = tmp_path / location
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(source)
discovery = _Discovery(beats)
LanguageStrategySet.set_instance(
lambda file_manager, **overrides: LanguageStrategySet(
{SourceDiscoveryStrategy: discovery}
)
)
client = _Client(response)
renderer = ContextualFileRenderer(
file_manager=_FileManager(
tmp_path,
diff,
metadata_by_location=(
{location: class_meta} if class_meta is not None else None
),
),
client=client,
editable_files=[] if readonly else [class_meta or location],
readonly_files=[class_meta or location] if readonly else [],
prompt="Change the relevant behavior.",
pinned_context=pinned_context,
backlog_guidance=backlog_guidance,
failure_error_message=failure_error_message,
failure_stack_trace=failure_stack_trace,
failure_raw_output=failure_raw_output,
call_trace=call_trace,
logger=logging.getLogger("contextual-renderer-test"),
repair_episode=repair_episode,
prompt_formatter_builder=MarkdownPromptFormatterBuilder(),
oop_signature_extract=signature_extract or _SignatureExtract(),
oop_signature_render=_SignatureRender(),
)
return renderer, client, discovery
def _multi_file_renderer(
tmp_path: MemoryPath,
declared: Sequence[str] = ("src/a.py", "src/b.py"),
*,
repair_episode: bool = False,
readonly: bool = False,
signature_extract: OOpSignatureExtract | None = None,
) -> tuple[ContextualFileRenderer, _Client, dict[str, str]]:
sources: dict[str, str] = {}
for location in declared:
marker = Path(location).stem
source = (
f"def callable_{marker}():\n"
+ "".join(
f" {marker}_value_{index} = {index}\n"
for index in range(24)
)
+ f" return {marker}_value_23\n"
)
path = tmp_path / location
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(source)
sources[location] = source
discovery = _Discovery((_beat("function", 1, 26),))
LanguageStrategySet.set_instance(
lambda file_manager, **overrides: LanguageStrategySet(
{SourceDiscoveryStrategy: discovery}
)
)
client = _Client()
renderer = ContextualFileRenderer(
file_manager=_FileManager(tmp_path),
client=client,
editable_files=[] if readonly else declared,
readonly_files=declared if readonly else [],
prompt="Change the relevant callable.",
logger=logging.getLogger("contextual-renderer-file-index-test"),
repair_episode=repair_episode,
prompt_formatter_builder=MarkdownPromptFormatterBuilder(),
oop_signature_extract=signature_extract or _SignatureExtract(),
oop_signature_render=_SignatureRender(),
)
return renderer, client, sources
def _method_source() -> tuple[str, list[SearchableBeat]]:
ordinary_lines = "".join(
f" value_{index} = {index}\n"
for index in range(21)
)
source = (
"import os\n"
"CONSTANT = 1\n"
"\n"
"class Example:\n"
" class_value = 2\n"
"\n"
" def first(self):\n"
f"{ordinary_lines}"
" return value_20\n"
"\n"
" def second(self):\n"
" return 2\n"
)
beats = [
_beat("import", 1, 1),
_beat("assignment", 2, 2),
_beat(
"class",
4,
32,
text="Example",
attributes=(("name", "Example"),),
),
_beat("class_header", 4, 4),
_beat("class_variable", 5, 5),
_beat("method", 7, 29, text="Example.first"),
_beat("assignment", 8, 8),
_beat("return_site", 29, 29),
_beat("method", 31, 32, text="Example.second"),
_beat("return_site", 32, 32),
]
return source, beats
def test_searchable_beat_has_no_presentation_policy_field() -> None:
names = {field.name for field in fields(SearchableBeat)}
forbidden = {
"render" + "able",
"visible",
"selectable",
"displayable",
"presentable",
}
assert names.isdisjoint(forbidden)
@pytest.mark.parametrize("repair_episode,readonly", ((False, False), (False, True), (True, False)))
@pytest.mark.parametrize("response", ("NO_KEEP\nREASON: none\n", "invalid selector output"))
@pytest.mark.parametrize("trace", ("Widget.execute", "execute"))
def test_trace_chunk_stays_in_selector_and_exact_source(
tmp_path: MemoryPath, repair_episode: bool, readonly: bool, response: str, trace: str,
) -> None:
"""A traced callable remains complete even when selection rejects every chunk."""
traced = "callable body\n" + "\tvalue = 1 \n" * 22 + "\n\treturn value\n"
unrelated = "other body\n" + "\tother = 2\n" * 24 + "\treturn other\n"
renderer, client, _discovery = _renderer(
tmp_path, traced + unrelated,
(
SearchableBeat(kind="method", text="Widget.execute", tokens=("execute",), start_line=1, end_line=25),
_beat("method", 26, 51, text="Widget.unused"),
),
response, readonly=readonly, repair_episode=repair_episode,
failure_stack_trace=(f"frame: {trace}",),
)
client.response = [response] * 5 + ["NO_KEEP\nREASON: none\n"]
rendered = renderer.read("src/example.py", Fidelity.SEGMENT)
assert traced in rendered
assert unrelated not in rendered
assert "⋮" in rendered
assert client.prompts
# Mandatory code remains evidence for finding helpers, not a hidden preselection.
for prompt in client.prompts:
assert traced.rstrip("\n") in prompt
assert "other body" in prompt
@pytest.mark.parametrize("source_kind", ("runtime", "static", "both"))
@pytest.mark.parametrize("indent", ("", " "))
@pytest.mark.parametrize("response", ("NO_KEEP\nREASON: none\n", "invalid selector output"))
def test_call_trace_uses_the_same_chunk_whitelist_and_deduplication(
tmp_path: MemoryPath, source_kind: str, indent: str, response: str,
) -> None:
"""Only the evidence source changes; repeated matches cannot duplicate a source chunk."""
source, beats = _method_source()
unrelated = "def unused():\n" + " value = 3\n" * 24 + " return value\n"
trace = indent + 'File "src/example.py", in Example.second\n'
renderer, client, _ = _renderer(
tmp_path, source + "\n" + unrelated,
(*beats, _beat("function", 34, 59, text="unused")), response,
failure_stack_trace=(trace,) if source_kind != "static" else (),
call_trace=trace * 3 if source_kind != "runtime" else "",
)
client.response = [response] * 5 + ["NO_KEEP\nREASON: none\n"]
rendered = renderer.read("src/example.py", Fidelity.SEGMENT)
assert source in rendered
assert unrelated not in rendered
assert rendered.count("def first(self):") == 1
assert rendered.count("def second(self):") == 1
assert "def first(self):" in client.prompts[0]
assert "def second(self):" in client.prompts[0]
def test_static_trace_keeps_a_moved_method_after_source_refresh(tmp_path: MemoryPath) -> None:
"""The existing source refresh updates mandatory chunks without a new call traversal."""
source, beats = _method_source()
renderer, _client, discovery = _renderer(tmp_path, source, beats,
call_trace='File "src/example.py", in Example.second', repair_episode=True)
assert source in renderer.read("src/example.py", Fidelity.SEGMENT)
moved = "# moved\n" + source
(tmp_path / "src/example.py").write_text(moved)
moved_beats: list[SearchableBeat] = []
# This fixture moves complete known ranges, not optional discovery coordinates.
for beat in beats:
assert beat.start_line is not None and beat.end_line is not None
moved_beats.append(replace(beat, start_line=beat.start_line + 1, end_line=beat.end_line + 1))
discovery.beats = tuple(moved_beats)
renderer.set_failure("unchanged runtime failure")
assert source in renderer.read("src/example.py", Fidelity.SEGMENT)
def test_trace_match_keeps_neighboring_callables_in_the_same_chunk(tmp_path: MemoryPath) -> None:
"""One matching method retains the whole existing multi-callable chunk."""
source, beats = _method_source()
unrelated = "def unused():\n" + " value = 3\n" * 24 + " return value\n"
renderer, client, _discovery = _renderer(
tmp_path, source + "\n" + unrelated,
(*beats, _beat("function", 34, 59, text="unused")),
failure_stack_trace=("Example.second failed",),
)
rendered = renderer.read("src/example.py", Fidelity.SEGMENT)
assert source in rendered
assert unrelated not in rendered
assert "def first(self):" in client.prompts[0]
assert "def second(self):" in client.prompts[0]
@pytest.mark.parametrize("repair_episode", (False, True))
def test_new_trace_replaces_cached_omission_without_consuming_refinement(
tmp_path: MemoryPath, repair_episode: bool,
) -> None:
"""A new focused failure exposes source while preserving prior episode context."""
first = "def first():\n" + " value = 1\n" * 24 + " return value\n"
second = "def second():\n" + " value = 2\n" * 24 + " return value\n"
renderer, client, _discovery = _renderer(
tmp_path, first + second,
(_beat("function", 1, 26, text="first"), _beat("function", 27, 52, text="second")),
repair_episode=repair_episode,
)
assert first not in renderer.read("src/example.py", Fidelity.SEGMENT)
renderer.set_failure("first failed")
assert first in renderer.read("src/example.py", Fidelity.SEGMENT)
renderer.set_failure("second failed")
rendered = renderer.read("src/example.py", Fidelity.SEGMENT)
assert second in rendered
assert renderer._refinement_count == 0
# Progressive episodes retain earlier admitted context even after the focus moves.
if repair_episode:
assert first in rendered
assert client.prompts
def test_trace_matching_ignores_arguments_metadata_and_unrelated_guidance(tmp_path: MemoryPath) -> None:
"""Incidental strings cannot turn argument metadata or work prose into trace matches."""
source = "def unrelated(value):\n" + " value += 1\n" * 24 + " return value\n"
beat = SearchableBeat(
kind="function", text="unrelated", tokens=("", " ", "failure", "value"),
attributes=(("documentation", "failure"),),
children=(SearchableBeat(kind="argument", text="value"),),
start_line=1, end_line=26,
)
renderer, _client, _discovery = _renderer(
tmp_path, source, (beat,),
failure_stack_trace=("failure value",), pinned_context=("Inspect unrelated",),
)
assert source not in renderer.read("src/example.py", Fidelity.SEGMENT)
@pytest.mark.parametrize("evidence,visible", (
("Widget.execute failed", True),
("in execute", True),
("Widget.execute_more failed", False),
("shared fault words", False),
("value str", False),
("return value", False),
("Widget failed", False),
))
def test_factory_ast_callable_names_alone_force_visibility(
tmp_path: MemoryPath, evidence: str, visible: bool,
) -> None:
"""AST names admit source; docstrings, arguments, body text and prefixes do not."""
method = (
" async def execute(self, value: str):\n"
' """shared fault words"""\n'
+ " value = value\n" * 23
+ " return value\n"
)
renderer, client, _discovery = _renderer(
tmp_path, "class Widget:\n" + method, (), failure_stack_trace=(evidence,),
)
discovery = PythonSourceDiscovery(
tmp_path / "src", tmp_path / "tests", repo_exec=tmp_path.repository,
)
LanguageStrategySet.set_instance(
lambda file_manager, **overrides: LanguageStrategySet({SourceDiscoveryStrategy: discovery})
)
rendered = renderer.read("src/example.py", Fidelity.SEGMENT)
assert (method in rendered) is visible
assert method.rstrip("\n") in client.prompts[0]
def test_callable_name_index_reuses_catalogue_and_refreshes_after_source_change(
tmp_path: MemoryPath,
) -> None:
"""Evidence changes reuse the name index; source changes cannot reuse stale names."""
traced = "def traced():\n" + " value = 1\n" * 24 + " return value\n"
unrelated = "def unrelated():\n" + " value = 2\n" * 24 + " return value\n"
renderer, _client, _discovery = _renderer(tmp_path, traced + unrelated, ())
discovery = PythonSourceDiscovery(
tmp_path / "src", tmp_path / "tests", repo_exec=tmp_path.repository,
)
LanguageStrategySet.set_instance(
lambda file_manager, **overrides: LanguageStrategySet({SourceDiscoveryStrategy: discovery})
)
renderer.set_failure("traced failed")
assert traced in renderer.read("src/example.py", Fidelity.SEGMENT)
[index] = renderer._chunk_ids_by_name.values()
assert index == {"traced": {"F01.C0001"}, "unrelated": {"F01.C0002"}}
renderer.set_failure("unrelated failed")
assert unrelated in renderer.read("src/example.py", Fidelity.SEGMENT)
assert tuple(renderer._chunk_ids_by_name.values()) == (index,)
assert next(iter(renderer._chunk_ids_by_name.values())) is index
renamed = traced.replace("def traced", "def renamed")
(tmp_path / "src/example.py").write_text(renamed + unrelated)
renderer.refresh_current_source()
assert not renderer._chunk_ids_by_name
renderer.set_failure("traced failed")
assert renamed not in renderer.read("src/example.py", Fidelity.SEGMENT)
[refreshed] = renderer._chunk_ids_by_name.values()
assert refreshed == {"renamed": {"F01.C0001"}, "unrelated": {"F01.C0002"}}
assert refreshed is not index
renderer.set_failure("renamed failed")
assert renamed in renderer.read("src/example.py", Fidelity.SEGMENT)
def test_trace_retains_all_matching_chunks_and_llm_can_add_support(tmp_path: MemoryPath) -> None:
"""Mandatory matches are unioned with—not substituted for—LLM-selected support."""
first = "first body\n" + " first = 1\n" * 25
second = "second body\n" + " second = 2\n" * 25
support = "helper body\n" + " help = 3\n" * 25
renderer, client, _discovery = _renderer(
tmp_path, first + second + support,
(
_beat("method", 1, 26, text="shared"),
_beat("method", 27, 52, text="shared"),
_beat("function", 53, 78, text="helper"),
),
"KEEP: F01.C0003\nREASON: traced code needs its helper\n",
failure_stack_trace=("shared failed",),
)
assert renderer.read("src/example.py", Fidelity.SEGMENT) == f"FILE: src/example.py\n{first}{second}{support}"
assert first in client.prompts[0]
assert second in client.prompts[0]
assert support in client.prompts[0]
def test_mandatory_source_informs_dependency_selection_and_later_helper_pages(tmp_path: MemoryPath) -> None:
"""Retained chunks remain evidence even when only new helper gaps are selectable."""
traced = "def traced():\n" + " new_dependency()\n" * 24 + " return helper()\n"
helper = "def helper():\n" + " value = 1\n" * 24 + " return value\n"
renderer, client, _discovery = _renderer(
tmp_path, traced + helper,
(_beat("function", 1, 26, text="traced"), _beat("function", 27, 52, text="helper")),
repair_episode=True, signature_extract=_CurrentSourceCards(),
)
renderer.set_failure("traced failed")
client.response = '{"references": []}'
renderer.get_reference_cards()
assert traced in client.prompts[-1]
assert helper not in client.prompts[-1]
client.response = "NO_KEEP\nREASON: none\n"
assert traced in renderer.read("src/example.py", Fidelity.SEGMENT)
renderer.refine("traced failed", stale_files=[])
assert traced in client.prompts[-1]
assert helper in client.prompts[-1]
assert traced in renderer.read("src/example.py", Fidelity.SEGMENT)
def test_discovery_metadata_stays_out_of_selector_presentation(tmp_path: MemoryPath) -> None:
"""Selector presentation is derived from source and chunks, not discovery flags."""
source = "def target():\n" + " value = 1\n" * 25
renderer, client, _discovery = _renderer(
tmp_path,
source,
(
_beat(
"file",
1,
1,
text="src/example.py",
attributes=(("renderable", "true"),),
),
_beat("function", 1, 26, text="target"),
),
)
rendered = renderer.read("src/example.py", Fidelity.SEGMENT)
assert rendered.startswith("FILE: src/example.py\n")
assert client.prompts
assert "renderable" not in client.prompts[0]
def test_nested_callable_ranges_produce_one_outer_bucket() -> None:
buckets = _merge_overlapping_callable_behaviors((
_ranged("method", 20, 100),
_ranged("function", 35, 55, 1),
))
assert [(bucket.start, bucket.end) for bucket in buckets] == [(20, 100)]
def test_overlapping_callable_expands_current_bucket() -> None:
buckets = _merge_overlapping_callable_behaviors((
_ranged("function", 20, 80),
_ranged("function", 60, 110, 1),
))
assert [(bucket.start, bucket.end) for bucket in buckets] == [(20, 110)]
def test_adjacent_callable_ranges_stay_separate() -> None:
buckets = _merge_overlapping_callable_behaviors((
_ranged("function", 10, 20),
_ranged("function", 20, 30, 1),
))
assert [(bucket.start, bucket.end) for bucket in buckets] == [
(10, 20),
(20, 30),
]
def test_callable_bucket_builder_sorts_and_merges_a_mixed_input_population() -> None:
"""Callable buckets preserve source order even when discovery evidence is unordered."""
buckets = _merge_overlapping_callable_behaviors((
_ranged("function", 60, 70, 3),
_ranged("function", 20, 35, 1),
_ranged("method", 30, 45, 2),
_ranged("function", 0, 10),
))
assert [(bucket.start, bucket.end) for bucket in buckets] == [
(0, 10),
(20, 45),
(60, 70),
]
class _CountingBuckets(Sequence[_CallableBucket]):
def __init__(self, values: Sequence[_CallableBucket]) -> None:
self.values = tuple(values)
self.lookups = 0
def __len__(self) -> int:
return len(self.values)
def __getitem__(self, index):
self.lookups += 1
return self.values[index]
def test_classification_uses_one_monotonic_bucket_pointer() -> None:
buckets = _CountingBuckets([
_CallableBucket(index * 4, index * 4 + 2)
for index in range(100)
])
beats = tuple(
_ranged("assignment", index, index + 1, index)
for index in range(400)
)
_classify_ranged_beats(beats, buckets)
assert buckets.lookups < len(beats) * 3
def test_callable_internal_evidence_is_not_state() -> None:
buckets = (_CallableBucket(10, 30),)
classified = _classify_ranged_beats((
_ranged("method", 10, 30),
_ranged("assignment", 12, 13, 1),
_ranged("binding", 14, 15, 2),
_ranged("return_site", 20, 21, 3),
_ranged("import", 25, 26, 4),
_ranged("assignment", 35, 36, 5),
), buckets)
assert [(item.start, item.end) for item in classified.state] == [(35, 36)]
def test_class_ranges_are_ownership_only() -> None:
class_beat = _ranged(
"class",
0,
40,
text="Example",
attributes=(("name", "Example"),),
)
buckets = (_CallableBucket(10, 20),)
classified = _classify_ranged_beats((class_beat,), buckets)
classes = _build_class_containers(classified.classes)
owners = _assign_class_owners(((10, 20),), classes)
assert classified.state == ()
assert owners == (0,)
def test_file_imports_and_constants_appear_once_in_prompt(tmp_path: MemoryPath) -> None:
source, beats = _method_source()
renderer, client, _discovery = _renderer(tmp_path, source, beats)
renderer.read("src/example.py", Fidelity.SEGMENT)
prompt = client.prompts[0]
assert prompt.count("import os") == 1
assert prompt.count("CONSTANT = 1") == 1
def test_class_declaration_and_variable_appear_once_in_prompt(tmp_path: MemoryPath) -> None:
source, beats = _method_source()
renderer, client, _discovery = _renderer(tmp_path, source, beats)
renderer.read("src/example.py", Fidelity.SEGMENT)
prompt = client.prompts[0]
assert prompt.count("class Example:") == 1
assert prompt.count("class_value = 2") == 1
def test_nested_class_uses_smallest_containing_owner() -> None:
classes = _build_class_containers((
_ranged(
"class",
0,
100,
text="Outer",
attributes=(("name", "Outer"),),
),
_ranged(
"class",
20,
60,
1,
text="Outer.Inner",
attributes=(("name", "Outer.Inner"),),
),
))
assert classes[1].parent_index == 0
assert _assign_class_owners(((25, 30),), classes) == (1,)
def test_state_containment_keeps_smallest_ranges() -> None:
reduced = _reduce_state_ranges((
_ranged("declaration", 0, 20),
_ranged("assignment", 2, 3, 1),
_ranged("binding", 2, 3, 2),
_ranged("assignment", 10, 11, 3),
))
assert [(item.start, item.end, item.beat.kind) for item in reduced] == [
(2, 3, "assignment"),
(10, 11, "assignment"),
]
def test_interval_helpers_keep_outer_class_ownership_and_external_state() -> None:
"""A mixed range population keeps callable ownership separate from external state."""
ranged = (
_ranged("class", 0, 100, text="Outer", attributes=(("name", "Outer"),)),
_ranged("method", 10, 40, 1),
_ranged("class", 20, 60, 2, text="Outer.Inner", attributes=(("name", "Outer.Inner"),)),
_ranged("function", 25, 50, 3),
_ranged("assignment", 12, 13, 4),
_ranged("assignment", 70, 71, 5),
)
buckets = _merge_overlapping_callable_behaviors(ranged)
classified = _classify_ranged_beats(ranged, buckets)
classes = _build_class_containers(classified.classes)
owners = _assign_class_owners(tuple((bucket.start, bucket.end) for bucket in buckets), classes)
chunks = _group_callable_behaviors_for_selection(
"src/example.py",
1,
buckets,
owners,
)
assert [(item.start, item.end) for item in classified.state] == [(70, 71)]
assert [container.name for container in classes] == ["Outer", "Outer.Inner"]
assert owners == (0,)
assert chunks == (
_CallableChunk("F01.C0001", "src/example.py", ((10, 50),), (0,)),
)
def test_short_neighboring_buckets_form_one_chunk() -> None:
chunks = _group_callable_behaviors_for_selection(
"src/example.py",
1,
(
_CallableBucket(0, 10),
_CallableBucket(20, 30),
_CallableBucket(40, 50),
),
(None, None, None),
)
assert len(chunks) == 1
assert chunks[0].callable_ranges == ((0, 10), (20, 30), (40, 50))
def test_short_trailing_chunk_merges_backward() -> None:
chunks = _group_callable_behaviors_for_selection(
"src/example.py",
1,
(
_CallableBucket(0, 25),
_CallableBucket(30, 55),
_CallableBucket(60, 61),
),
(None, None, None),
)
assert len(chunks) == 2
assert chunks[-1].callable_ranges == ((30, 55), (60, 61))
def test_every_chunk_meets_minimum_without_widening_or_overlap() -> None:
buckets = (
_CallableBucket(2, 12),
_CallableBucket(20, 35),
_CallableBucket(40, 65),
_CallableBucket(80, 84),
)
chunks = _group_callable_behaviors_for_selection(
"src/example.py",
1,
buckets,
(0, 0, 1, 1),
)
assert all(
sum(end - start for start, end in chunk.callable_ranges)
>= MIN_KEEP_LINES
for chunk in chunks
)
assert [chunk.class_indexes for chunk in chunks] == [(0,), (1,)]
emitted_ranges = [
callable_range
for chunk in chunks
for callable_range in chunk.callable_ranges
]
assert emitted_ranges == [
(bucket.start, bucket.end)
for bucket in buckets
]
assert all(
left[1] <= right[0]
for left, right in zip(emitted_ranges, emitted_ranges[1:])
)
def test_chunk_ownership_is_precomputed_and_legacy_lookup_is_absent() -> None:
assert not hasattr(ContextualFileRenderer, "_required_class_indexes")
chunks = _group_callable_behaviors_for_selection(
"src/example.py",
1,
(
_CallableBucket(0, 15),
_CallableBucket(20, 30),
_CallableBucket(40, 65),
),
(3, 3, 7),
)
assert [chunk.class_indexes for chunk in chunks] == [(3,), (7,)]
def test_total_callable_source_below_minimum_produces_no_chunks() -> None:
assert _group_callable_behaviors_for_selection(
"src/example.py",
1,
(
_CallableBucket(0, 10),
_CallableBucket(20, 30),
),
(None, None),
) == ()
def test_short_callable_file_is_unchanged_without_selector(tmp_path: MemoryPath) -> None:
source = "VALUE = 1\n\ndef short():\n return VALUE\n"
renderer, client, discovery = _renderer(
tmp_path,
source,
(
_beat("assignment", 1, 1),
_beat("function", 3, 4),
_beat("return_site", 4, 4),
),
)
rendered = renderer.read("src/example.py", Fidelity.SEGMENT)
assert rendered == f"FILE: src/example.py\n{source}"
assert client.prompts == []
class _CountingEstimator(TokenBudgetEstimator):
"""Return a stationary cost while retaining the estimator's nominal contract."""
def __init__(self, cost: int) -> None:
super().__init__()
self.cost = cost
self.calls: list[str] = []
def estimate(self, text: str) -> int:
"""Record one exhibit and return its stationary token cost."""
self.calls.append(text)
return self.cost
def test_chunk_paging_estimates_each_exhibit_once(tmp_path: MemoryPath) -> None:
source = "".join(f"line {index}\n" for index in range(100))
renderer, _client, _discovery = _renderer(tmp_path, source, ())
chunks = tuple(
_CallableChunk(
identifier=f"F01.C{index + 1:04d}",
location="src/example.py",
callable_ranges=((index * 25, (index + 1) * 25),),
class_indexes=(),
)
for index in range(3)
)
estimator = _CountingEstimator(6)
renderer._estimator = estimator
configure_token_budget_settings(max_prompt_tokens=20)
pages = renderer._chunk_pages(chunks)
assert len(estimator.calls) == len(chunks)
assert pages == tuple((chunk,) for chunk in chunks)
assert [chunk for page in pages for chunk in page] == list(chunks)
assert len({chunk.identifier for page in pages for chunk in page}) == len(chunks)
assert SELECTOR_CATALOGUE_TOKEN_RATIO == 1 / 2
def test_source_lines_and_chunk_exhibits_are_cached(tmp_path: MemoryPath) -> None:
source, beats = _method_source()
renderer, _client, _discovery = _renderer(tmp_path, source, beats)
analysis = renderer._analyze_current_file("src/example.py")
chunks = _group_callable_behaviors_for_selection(
"src/example.py",
1,
analysis.buckets,
analysis.bucket_owners,
)
renderer._chunk_pages(chunks)
first_prompt = renderer._selector_prompt(
chunks,
{"src/example.py": analysis},
use_cdc=False,
)
second_prompt = renderer._selector_prompt(
chunks,
{"src/example.py": renderer._analyze_current_file("src/example.py")},
use_cdc=False,
)
assert first_prompt == second_prompt
assert renderer._analyze_current_file("src/example.py") is analysis
assert renderer._source_lines_by_location["src/example.py"] == tuple(
source.splitlines(keepends=True)
)
assert set(renderer._chunk_exhibits_by_id) == {
chunk.identifier for chunk in chunks
}
def test_class_context_is_emitted_once_per_page(tmp_path: MemoryPath) -> None:
source, beats = _method_source()
renderer, _client, _discovery = _renderer(tmp_path, source, beats)
analysis = renderer._analyze_current_file("src/example.py")
chunks = (
_CallableChunk(
identifier="F01.C0001",
location="src/example.py",
callable_ranges=((6, 19),),
class_indexes=(0,),
),
_CallableChunk(
identifier="F01.C0002",
location="src/example.py",
callable_ranges=((19, 32),),
class_indexes=(0,),
),
)
prompt = renderer._selector_prompt(
chunks,
{"src/example.py": analysis},
use_cdc=False,
)
assert prompt.count("CLASS CONTEXT: Example") == 1
assert prompt.count("class_value = 2") == 1
def test_oversized_chunk_occupies_one_page(tmp_path: MemoryPath) -> None:
source = "".join(f"line {index}\n" for index in range(30))
renderer, _client, _discovery = _renderer(tmp_path, source, ())
chunk = _CallableChunk(
identifier="F01.C0001",
location="src/example.py",
callable_ranges=((0, 25),),
class_indexes=(),
)
renderer._estimator = _CountingEstimator(100_000)
assert renderer._chunk_pages((chunk,)) == ((chunk,),)
def test_chunk_paging_preserves_every_chunk_in_source_order(tmp_path: MemoryPath) -> None:
"""Paging never drops or reorders a stationary catalogue of callable chunks."""
renderer, _client, _discovery = _renderer(tmp_path, "line\n" * 120, ())
chunks = tuple(
_CallableChunk(
identifier=f"F01.C{index + 1:04d}",
location="src/example.py",
callable_ranges=((index * 25, (index + 1) * 25),),
class_indexes=(),
)
for index in range(4)
)
renderer._estimator = _CountingEstimator(6)
configure_token_budget_settings(max_prompt_tokens=20)
pages = renderer._chunk_pages(chunks)
assert pages == tuple((chunk,) for chunk in chunks)
assert tuple(chunk for page in pages for chunk in page) == chunks
def test_prompt_exposes_only_callable_chunk_identifiers(tmp_path: MemoryPath) -> None:
source, beats = _method_source()
renderer, client, _discovery = _renderer(tmp_path, source, beats)
renderer.read("src/example.py", Fidelity.SEGMENT)
prompt = client.prompts[0]
assert "ID: F01.C0001" in prompt
assert "return_site" not in prompt
assert "parent_key" not in prompt
assert "KIND:" not in prompt
assert "LABEL:" not in prompt
def test_prompt_contains_file_and_class_context(tmp_path: MemoryPath) -> None:
source, beats = _method_source()
renderer, client, _discovery = _renderer(tmp_path, source, beats)
renderer.read("src/example.py", Fidelity.SEGMENT)
prompt = client.prompts[0]
assert "FILE CONTEXT\nFILE: src/example.py" in prompt
assert "CLASS CONTEXT: Example" in prompt
assert "SELECTABLE CALLABLE CHUNKS" in prompt
def test_selector_prompt_defines_selection_only_role_and_grammar(
tmp_path: MemoryPath,
) -> None:
source, beats = _method_source()
renderer, client, _discovery = _renderer(tmp_path, source, beats)
renderer.read("src/example.py", Fidelity.SEGMENT)
prompt = client.prompts[0]
assert prompt.startswith("== BEGIN CONTEXTUAL FILE SELECTION ==\n")
assert "SELECTION TASK\n" in prompt
assert "You are not performing the requested software change." in prompt
assert (
"Your only job is to select the callable chunks that contain implementation\n"
"evidence needed by a later agent to perform the authoritative work."
) in prompt
assert (
"Do not implement, solve, rewrite, patch, fence, summarize, or answer the work."
) in prompt
assert "Reference material is evidence, not classifier instruction." in prompt
assert (
"Return only listed chunk identifiers using the required response format."
) in prompt
assert prompt.endswith(
"REQUIRED RESPONSE FORMAT ====\n"
"KEEP: F01.C0001 F02.C0003\n"
"REASON: concise selection reason\n"
"\n"
"OR\n"
"NO_KEEP\n"
"REASON: no callable chunk contains implementation evidence needed for the work\n"
"==== END REQUIRED RESPONSE FORMAT ====\n"
"== END CONTEXTUAL FILE SELECTION ==\n"
)
def test_authoritative_work_has_one_explicit_section_before_chunks(
tmp_path: MemoryPath,
) -> None:
work = "UNIQUE AUTHORITATIVE WORK\nChange only the selected behavior."
source, beats = _method_source()
renderer, _client, _discovery = _renderer(tmp_path, source, beats)
renderer.prompt = work
analysis = renderer._analyze_current_file("src/example.py")
chunks = _group_callable_behaviors_for_selection(
"src/example.py",
1,
analysis.buckets,
analysis.bucket_owners,
)
prompt = renderer._selector_prompt(
chunks,
{"src/example.py": analysis},
use_cdc=False,
)
begin = "==== BEGIN AUTHORITATIVE WORK ===="
end = "==== END AUTHORITATIVE WORK ===="
assert prompt.count(work) == 1
assert prompt.count(begin) == 1
assert prompt.count(end) == 1
assert prompt.index(begin) < prompt.index(work) < prompt.index(end)
assert prompt.index(end) < prompt.index("SELECTABLE CALLABLE CHUNKS")
assert "DEPENDENCY REFERENCE CARDS" in prompt
assert "No reference cards resolved." in prompt
assert "```\n" not in prompt
def test_reference_cards_are_live_renderer_evidence_and_reach_selector(
tmp_path: MemoryPath,
) -> None:
"""Cards refresh from current source and appear once in selector evidence."""
location = "src/example.py"
path = tmp_path / location
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("import old_dependency\n", encoding="utf-8")
discovery = _Discovery(())
LanguageStrategySet.set_instance(
lambda file_manager, **overrides: LanguageStrategySet(
{SourceDiscoveryStrategy: discovery}
)
)
client = _ReferenceClient()
renderer = ContextualFileRenderer(
file_manager=_FileManager(tmp_path),
client=client,
editable_files=[location],
readonly_files=[],
prompt="Choose the relevant callable.",
logger=logging.getLogger("contextual-reference-card-test"),
prompt_formatter_builder=MarkdownPromptFormatterBuilder(),
oop_signature_extract=_CurrentSourceCards(),
oop_signature_render=_SignatureRender(),
)
assert "REFERENCE: dependency.module.OldDependency" in renderer.get_reference_card_text()
path.write_text("import new_dependency\n", encoding="utf-8")
renderer.refresh_current_source()
assert "REFERENCE: dependency.module.NewDependency" in renderer.get_reference_card_text()
assert len(client.prompts) == 2
evidence = renderer.get_reference_card_evidence()
assert "direct dependencies of supplied editable and readonly files" in evidence
assert "do not expand or narrow the permitted reference set" in evidence
prompt = renderer._selector_prompt((), {location: _FileAnalysis((), (), (), (), ())}, use_cdc=False)
assert prompt.count("DEPENDENCY REFERENCE CARDS") == 1
assert "REFERENCE: dependency.module.NewDependency" in prompt
assert "direct dependencies of editable or readonly files" in prompt
assert "cannot expand or narrow the permitted reference set" in prompt
def test_reference_selection_reuses_admission_for_an_updated_card_surface(
tmp_path: MemoryPath,
) -> None:
"""Refresh renders an admitted identity's latest declaration without a new selection."""
location = "src/example.py"
path = tmp_path / location
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("import dependency\n", encoding="utf-8")
client = _ReferenceQueueClient((
'{"references": [{"module": "dependency.module", "name": "StableDependency"}]}',
))
renderer = ContextualFileRenderer(
file_manager=_FileManager(tmp_path),
client=client,
editable_files=[location],
readonly_files=[],
prompt="Use the stable dependency.",
logger=logging.getLogger("contextual-reference-selection-refresh-test"),
prompt_formatter_builder=MarkdownPromptFormatterBuilder(),
oop_signature_extract=_StableIdentityCards(),
oop_signature_render=_PydocSignatureRender(),
)
assert "first declaration" in renderer.get_reference_card_text()
path.write_text("import dependency\n# second\n", encoding="utf-8")
renderer.refresh_current_source()
assert "second declaration" in renderer.get_reference_card_text()
assert len(client.prompts) == 1
def test_reference_cards_are_pruned_by_llm_selection_and_reselected_for_new_evidence(
tmp_path: MemoryPath,
) -> None:
"""The renderer admits only returned identities and reselects after fault evidence changes."""
location = "src/example.py"
path = tmp_path / location
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("import dependency\n", encoding="utf-8")
client = _ReferenceQueueClient((
'{"references": [{"module": "dependency.module", "name": "Keep"}]}',
'{"references": [{"module": "dependency.module", "name": "Discard"}]}',
))
renderer = ContextualFileRenderer(
file_manager=_FileManager(tmp_path),
client=client,
editable_files=[location],
readonly_files=[],
prompt="Use the needed dependency only.",
guidance=["Preserve the public contract."],
pinned_context=["The requested repair is narrow."],
failure_error_message="Attribute access failed.",
failure_stack_trace=["src/example.py:12"],
fault="Initial repair evidence.",
logger=logging.getLogger("contextual-reference-selection-test"),
prompt_formatter_builder=MarkdownPromptFormatterBuilder(),
oop_signature_extract=_ManyReferenceCards(),
oop_signature_render=_SignatureRender(),
)
first = renderer.get_reference_card_text()
assert "REFERENCE: dependency.module.Keep" in first
assert "REFERENCE: dependency.module.Discard" not in first
assert renderer.get_reference_card_text() == first
assert len(client.prompts) == 1
selector_prompt = client.prompts[0]
assert "Use the needed dependency only." in selector_prompt
assert "Preserve the public contract." in selector_prompt
assert "The requested repair is narrow." in selector_prompt
assert "Attribute access failed." in selector_prompt
assert "src/example.py:12" in selector_prompt
assert "Initial repair evidence." in selector_prompt
assert "REFERENCE: dependency.module.Keep" in selector_prompt
assert "REFERENCE: dependency.module.Discard" in selector_prompt
renderer.fault = "Changed repair evidence."
second = renderer.get_reference_card_text()
assert "REFERENCE: dependency.module.Keep" not in second
assert "REFERENCE: dependency.module.Discard" in second
assert len(client.prompts) == 2
def test_reference_selection_corrects_unknown_identity_before_caching(tmp_path: MemoryPath) -> None:
"""A partially valid page cannot hide an invented identity or cache partial admission."""
location = "src/example.py"
path = tmp_path / location
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("import dependency\n", encoding="utf-8")
wrong = '{"references": [{"module": "dependency.module", "name": "Keep"}, {"module": "invented", "name": "Missing"}]}'
client = _ReferenceQueueClient((*([wrong] * 15), '{"references": []}'))
renderer = ContextualFileRenderer(
file_manager=_FileManager(tmp_path), client=client,
editable_files=[location], readonly_files=[], prompt="Inspect the dependency.",
logger=logging.getLogger(__name__), prompt_formatter_builder=MarkdownPromptFormatterBuilder(),
oop_signature_extract=_ManyReferenceCards(), oop_signature_render=_SignatureRender(),
)
assert renderer.get_reference_cards() == ()
assert renderer.get_reference_cards() == ()
assert len(client.prompts) == 16
assert "Select only supplied module/name identities" in client.prompts[-1]
assert client.prompts[-1].count(wrong) == 1
def test_each_callable_chunk_has_an_independent_explicit_section(
tmp_path: MemoryPath,
) -> None:
source = "".join(f"source line {index}\n" for index in range(60))
renderer, _client, _discovery = _renderer(tmp_path, source, ())
analysis = _FileAnalysis(
buckets=(),
classes=(),
retained_state=(),
bucket_owners=(),
state_owners=(),
)
chunks = (
_CallableChunk(
identifier="F01.C0001",
location="src/example.py",
callable_ranges=((0, 25),),
class_indexes=(),
),
_CallableChunk(
identifier="F01.C0002",
location="src/example.py",
callable_ranges=((30, 55),),
class_indexes=(),
),
)
prompt = renderer._selector_prompt(
chunks,
{"src/example.py": analysis},
use_cdc=False,
)
for chunk in chunks:
begin = f"====== BEGIN CALLABLE CHUNK {chunk.identifier} ======"
end = f"====== END CALLABLE CHUNK {chunk.identifier} ======"
begin_index = prompt.index(begin)
end_index = prompt.index(end, begin_index)
exhibit = prompt[begin_index:end_index + len(end)]
assert exhibit.count(begin) == 1
assert exhibit.count(end) == 1
assert f"ID: {chunk.identifier}\n" in exhibit
assert f"FILE: {chunk.location}\n" in exhibit
assert (
f"CALLABLE SOURCE:\n{renderer._chunk_source(chunk)}\n{end}"
in exhibit
)
assert "```" not in prompt
def test_selector_prompt_sections_follow_required_order(tmp_path: MemoryPath) -> None:
source, beats = _method_source()
metadata = ClassMeta(
location="src/example.py",
summary="renderer source",
relevance=[],
relevant_symbols={},
cdc=[(datetime(2026, 1, 2), "tester", "cdc evidence")],
)
renderer, _client, _discovery = _renderer(
tmp_path,
source,
beats,
diff="diff evidence",
class_meta=metadata,
)
renderer.guidance = ("guidance evidence",)
renderer.fault = "fault evidence"
analysis = renderer._analyze_current_file("src/example.py")
chunks = _group_callable_behaviors_for_selection(
"src/example.py",
1,
analysis.buckets,
analysis.bucket_owners,
)
prompt = renderer._selector_prompt(
chunks,
{"src/example.py": analysis},
use_cdc=True,
)
ordered_sections = (
"SELECTION TASK",
"==== BEGIN AUTHORITATIVE WORK ====",
"IMPORTANT GUIDANCE",
"FAULT EVIDENCE",
"CURRENT GIT DIFF",
"CURRENT CDC SELECTION HINT",
"==== BEGIN SOURCE CONTEXT ====",
"CLASS CONTEXT: Example",
"==== BEGIN SELECTABLE CALLABLE CHUNKS ====",
"==== BEGIN REQUIRED RESPONSE FORMAT ====",
)
positions = [prompt.index(section) for section in ordered_sections]
assert positions == sorted(positions)
assert prompt.count("==== BEGIN SOURCE CONTEXT ====") == 2
assert all(
prompt.count(section) == 1
for section in ordered_sections
if section != "==== BEGIN SOURCE CONTEXT ===="
)
def test_selector_prompt_renders_structured_edit_request_context(
tmp_path: MemoryPath,
) -> None:
source, beats = _method_source()
renderer, client, discovery = _renderer(
tmp_path,
source,
beats,
pinned_context=("Pinned dependency contract.",),
backlog_guidance=("Backlog says to preserve compatibility.",),
failure_error_message="AssertionError: wrong value",
failure_stack_trace=("tests/test_example.py:7: AssertionError",),
failure_raw_output="FAILED tests/test_example.py::test_value\n",
)
renderer.read("src/example.py", Fidelity.SEGMENT)
prompt = client.prompts[0]
structured_context = (
"IMPORTANT GUIDANCE\n"
"\n"
"PINNED CONTEXT\n"
"Pinned dependency contract.\n"
"\n"
"BACKLOG GUIDANCE\n"
"Backlog says to preserve compatibility.\n"
"\n"
"FAULT EVIDENCE\n"
"\n"
"ERROR MESSAGE\n"
"AssertionError: wrong value\n"
"\n"
"FAILURE EVIDENCE\n"
"tests/test_example.py:7: AssertionError\n"
)
assert "RAW OUTPUT" not in prompt
assert "FAILURE EVIDENCE" in prompt
assert prompt.index("==== BEGIN AUTHORITATIVE WORK ====") < prompt.index(structured_context)
assert prompt.index(structured_context) < prompt.index("FILE CONTEXT")
for section in (
"IMPORTANT GUIDANCE",
"PINNED CONTEXT",
"BACKLOG GUIDANCE",
"FAULT EVIDENCE",
"ERROR MESSAGE",
"FAILURE EVIDENCE",
):
assert prompt.count(section) == 1
def test_no_keep_removes_callables_and_retains_state(tmp_path: MemoryPath) -> None:
source, beats = _method_source()
renderer, _client, _discovery = _renderer(tmp_path, source, beats)
rendered = renderer.read("src/example.py", Fidelity.SEGMENT)
assert "import os\n" in rendered
assert "CONSTANT = 1\n" in rendered
assert "class Example:\n" in rendered
assert "class_value = 2\n" in rendered
assert "def first" not in rendered
assert "def second" not in rendered
assert rendered.count("⋮\n") == 2
def test_debug_logs_expose_interval_and_selector_boundaries(
tmp_path: MemoryPath,
caplog,
) -> None:
source, beats = _method_source()
renderer, _client, _discovery = _renderer(tmp_path, source, beats)
with caplog.at_level(
logging.DEBUG,
logger="contextual-renderer-test",
):
renderer.read("src/example.py", Fidelity.SEGMENT)
messages = "\n".join(record.getMessage() for record in caplog.records)
for field in (
"location=src/example.py",
"ranged_beat_count=",
"callable_bucket_count=",
"class_count=",
"retained_state_count=",
"chunk_count=",
"page_count=",
"chunk_ids_per_page=",
"selected_chunk_ids=",
"unselected_callable_range_count=",
"unselected_callable_ranges=",
):
assert field in messages
def test_selecting_multi_callable_chunk_preserves_every_callable(tmp_path: MemoryPath) -> None:
source, beats = _method_source()
renderer, _client, _discovery = _renderer(
tmp_path,
source,
beats,
"KEEP: F01.C0001\nREASON: both methods are needed\n",
)
rendered = renderer.read("src/example.py", Fidelity.SEGMENT)
assert rendered == f"FILE: src/example.py\n{source}"
def test_unselected_outer_callable_removes_nested_callable(tmp_path: MemoryPath) -> None:
ordinary_lines = "".join(
f" value_{index} = {index}\n"
for index in range(21)
)
source = (
"VALUE = 1\n"
"\n"
"def outer():\n"
f"{ordinary_lines}"
" def nested():\n"
" return 1\n"
" return nested()\n"
)
beats = [
_beat("assignment", 1, 1),
_beat("function", 3, 27),
_beat("assignment", 4, 4),
_beat("function", 25, 26),
_beat("return_site", 26, 26),
_beat("return_site", 27, 27),
]
renderer, _client, _discovery = _renderer(tmp_path, source, beats)
rendered = renderer.read("src/example.py", Fidelity.SEGMENT)
assert "VALUE = 1\n" in rendered
assert "outer" not in rendered
assert "nested" not in rendered
assert rendered.count("⋮\n") == 1
def test_adjacent_negative_ranges_collapse_to_one_ellipsis() -> None:
source = "one\ntwo\nthree\nfour\nfive\n"
assert _mask_omitted_callable_ranges(source, ((0, 2), (2, 4))) == "⋮\nfive\n"
def test_file_without_callables_is_unchanged_without_selector(tmp_path: MemoryPath) -> None:
source = "import os\nVALUE = 1\n"
renderer, client, discovery = _renderer(
tmp_path,
source,
(_beat("import", 1, 1), _beat("assignment", 2, 2)),
)
rendered = renderer.read("src/example.py", Fidelity.SEGMENT)
assert rendered == f"FILE: src/example.py\n{source}"
assert client.prompts == []
assert discovery.calls == 1
def test_selected_range_identity_includes_file_location(tmp_path: MemoryPath) -> None:
renderer, client, _sources = _multi_file_renderer(tmp_path)
client.response = "KEEP: F01.C0001\nREASON: only file A\n"
omitted = renderer._select_omitted_callable_ranges(
("src/a.py", "src/b.py"),
use_cdc=False,
)
assert omitted["src/a.py"] == ()
assert omitted["src/b.py"] == ((0, 26),)
def test_separate_reads_use_stable_declared_file_indexes(tmp_path: MemoryPath) -> None:
renderer, client, sources = _multi_file_renderer(
tmp_path,
("src/b.py", "src/a.py"),
)
renderer.read("src/a.py", Fidelity.SEGMENT)
renderer.read("src/b.py", Fidelity.SEGMENT)
assert "ID: F01.C0001" in client.prompts[0]
assert "ID: F02.C0001" in client.prompts[1]
assert sources["src/b.py"].rstrip() in client.prompts[1]
assert sources["src/a.py"].rstrip() not in client.prompts[1]
def test_reverse_read_order_does_not_change_file_indexes(tmp_path: MemoryPath) -> None:
renderer, client, _sources = _multi_file_renderer(tmp_path)
renderer.read("src/b.py", Fidelity.SEGMENT)
renderer.read("src/a.py", Fidelity.SEGMENT)
assert "ID: F02.C0001" in client.prompts[0]
assert "ID: F01.C0001" in client.prompts[1]
def test_multi_file_helper_uses_declared_file_indexes(tmp_path: MemoryPath) -> None:
renderer, client, _sources = _multi_file_renderer(tmp_path)
renderer._select_omitted_callable_ranges(
("src/b.py", "src/a.py"),
use_cdc=False,
)
prompt = client.prompts[0]
assert "ID: F01.C0001\nFILE: src/a.py" in prompt
assert "ID: F02.C0001\nFILE: src/b.py" in prompt
def test_repeated_read_keeps_the_same_file_index(tmp_path: MemoryPath) -> None:
renderer, client, _sources = _multi_file_renderer(tmp_path)
renderer.read("src/b.py", Fidelity.SEGMENT)
renderer.read("src/b.py", Fidelity.SEGMENT | Fidelity.CDC)
assert len(client.prompts) == 2
assert all("ID: F02.C0001" in prompt for prompt in client.prompts)
def test_declared_file_index_mapping_is_the_only_file_index_authority(
tmp_path: MemoryPath,
) -> None:
renderer, client, _sources = _multi_file_renderer(
tmp_path,
("src/b.py", "src/a.py"),
)
assert renderer._file_index_by_location == {
"src/a.py": 1,
"src/b.py": 2,
}
renderer.read("src/a.py", Fidelity.SEGMENT)
renderer.read("src/b.py", Fidelity.SEGMENT)
assert "ID: F01.C0001\nFILE: src/a.py" in client.prompts[0]
assert "ID: F02.C0001\nFILE: src/b.py" in client.prompts[1]
def test_callable_masker_accepts_only_source_and_omitted_ranges() -> None:
assert _mask_omitted_callable_ranges(
"first\nsecond\nthird\n",
((1, 2),),
) == "first\n⋮\nthird\n"
def test_real_python_discovery_integrates_with_callable_chunks(
tmp_path: MemoryPath,
) -> None:
ordinary_lines = "".join(
f" value_{index} = {index}\n"
for index in range(20)
)
source = (
"import os\n"
"\n"
"CONSTANT = 1\n"
"\n"
"class Example:\n"
" class_value = 2\n"
"\n"
" def first(self):\n"
" local = 1\n"
f"{ordinary_lines}"
"\n"
" def nested():\n"
" return local\n"
"\n"
" return nested()\n"
"\n"
" def second(self):\n"
" return self.class_value\n"
)
location = "src/example.py"
path = tmp_path / location
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(source)
discovery = PythonSourceDiscovery(
src_root=tmp_path / "src",
test_root=tmp_path / "tests",
repo_exec=tmp_path.repository,
)
LanguageStrategySet.set_instance(
lambda file_manager, **overrides: LanguageStrategySet(
{SourceDiscoveryStrategy: discovery}
)
)
client = _Client()
renderer = ContextualFileRenderer(
file_manager=_FileManager(tmp_path),
client=client,
editable_files=[location],
readonly_files=[],
prompt="Change the relevant behavior.",
logger=logging.getLogger("real-python-discovery-renderer-test"),
prompt_formatter_builder=MarkdownPromptFormatterBuilder(),
oop_signature_extract=_SignatureExtract(),
oop_signature_render=_SignatureRender(),
)
analysis = renderer._analyze_current_file(location)
chunks = _group_callable_behaviors_for_selection(
location,
1,
analysis.buckets,
analysis.bucket_owners,
)
assert chunks
assert all(
sum(end - start for start, end in chunk.callable_ranges)
>= MIN_KEEP_LINES
for chunk in chunks
)
assert len(analysis.buckets) == 2
assert len(chunks[0].callable_ranges) == 2
assert all(
not _overlaps(
state.start,
state.end,
bucket.start,
bucket.end,
)
for state in analysis.retained_state
for bucket in analysis.buckets
)
rendered = renderer.read(location, Fidelity.SEGMENT)
prompt = client.prompts[0]
assert prompt.count("import os") == 1
assert prompt.count("CONSTANT = 1") == 1
assert prompt.count("class Example:") == 1
assert prompt.count("class_value = 2") == 1
assert prompt.count("def nested") == 1
assert prompt.count("ID: F01.C") == 1
assert "def first" not in rendered
assert "def nested" not in rendered
assert "def second" not in rendered
assert "import os\n" in rendered
assert "CONSTANT = 1\n" in rendered
assert "class Example:\n" in rendered
assert "class_value = 2\n" in rendered
def test_fidelity_aliases_and_invalid_combinations(tmp_path: MemoryPath) -> None:
renderer, _client, _discovery = _renderer(tmp_path, "one\ntwo\n", ())
assert Fidelity.DEFAULT is Fidelity.AUTO
with pytest.raises(ValueError):
renderer.read(
"src/example.py",
Fidelity.WHOLE | Fidelity.SEGMENT,
)
with pytest.raises(ValueError):
renderer.read("src/example.py", Fidelity.CDC)
def test_explicit_whole_preserves_exact_source(tmp_path: MemoryPath) -> None:
source = " first \n\n# comment\nlast"
renderer, client, _discovery = _renderer(tmp_path, source, ())
rendered = renderer.read(
"src/example.py",
Fidelity.WHOLE | Fidelity.CDC,
)
assert rendered == f"FILE: src/example.py\n{source}"
assert client.prompts == []
def test_read_without_llm_leaves_expensive_selection_unstarted(
tmp_path: MemoryPath,
) -> None:
"""An unavailable cheap render has no model or render-history side effect."""
source, beats = _method_source()
renderer, client, _discovery = _renderer(
tmp_path,
source,
beats,
readonly=True,
)
rendered = renderer.read_without_llm("src/example.py", Fidelity.SEGMENT)
assert rendered is None
assert client.prompts == []
assert ContextualFileRenderHistory.get().count("src/example.py") == 0
def test_read_without_llm_renders_cards_for_expensive_selection(
tmp_path: MemoryPath,
) -> None:
"""Current-file cards are the no-model branch for an expensive segment."""
source, beats = _method_source()
renderer, client, _discovery = _renderer(
tmp_path,
source,
beats,
readonly=True,
signature_extract=_CurrentFileCards(),
)
rendered = renderer.read_without_llm("src/example.py", Fidelity.SEGMENT)
assert rendered == "FILE: src/example.py\nREFERENCE: current.module.Current\nfunction Current\n"
assert client.prompts == []
assert ContextualFileRenderHistory.get().count("src/example.py") == 1
def test_cheap_read_uses_current_file_cards_before_ordinary_retry(
tmp_path: MemoryPath,
) -> None:
"""A first irrelevant readonly segment avoids selection; a retry restores it."""
source, beats = _method_source()
renderer, client, _discovery = _renderer(
tmp_path,
source,
beats,
readonly=True,
signature_extract=_CurrentFileCards(),
)
first = renderer.cheap_read("src/example.py", Fidelity.SEGMENT)
retry = renderer.cheap_read("src/example.py", Fidelity.SEGMENT)
assert first == "FILE: src/example.py\nREFERENCE: current.module.Current\nfunction Current\n"
assert "def first" not in first
assert len(client.prompts) == 1
assert "def first" not in retry
def test_cheap_attempts_controls_the_first_ordinary_read(
tmp_path: MemoryPath,
) -> None:
"""A caller may retain cards for two attempts before ordinary selection."""
source, beats = _method_source()
cards = _CurrentFileCards()
renderer, client, _discovery = _renderer(
tmp_path,
source,
beats,
readonly=True,
signature_extract=cards,
)
first = renderer.cheap_read(
"src/example.py",
Fidelity.SEGMENT,
cheap_attempts=2,
)
second = renderer.cheap_read(
"src/example.py",
Fidelity.SEGMENT,
cheap_attempts=2,
)
third = renderer.cheap_read(
"src/example.py",
Fidelity.SEGMENT,
cheap_attempts=2,
)
assert first == second
assert "REFERENCE: current.module.Current" in first
assert "REFERENCE: current.module.Current" not in third
assert cards.sources == [source]
assert len(client.prompts) == 1
def test_cheap_attempts_advance_independently_for_each_file(
tmp_path: MemoryPath,
) -> None:
"""Reading one file twice does not consume another file's first cheap attempt."""
cards = _CurrentFileCards()
renderer, client, _sources = _multi_file_renderer(
tmp_path,
readonly=True,
signature_extract=cards,
)
first_a = renderer.cheap_read("src/a.py", Fidelity.SEGMENT)
first_b = renderer.cheap_read("src/b.py", Fidelity.SEGMENT)
retry_a = renderer.cheap_read("src/a.py", Fidelity.SEGMENT)
assert "REFERENCE: current.module.Current" in first_a
assert "REFERENCE: current.module.Current" in first_b
assert "REFERENCE: current.module.Current" not in retry_a
assert len(client.prompts) == 1
def test_zero_cheap_attempts_uses_ordinary_rendering_immediately(
tmp_path: MemoryPath,
) -> None:
"""A zero threshold disables current-file cards for this call."""
source, beats = _method_source()
renderer, client, _discovery = _renderer(
tmp_path,
source,
beats,
readonly=True,
signature_extract=_CurrentFileCards(),
)
rendered = renderer.cheap_read(
"src/example.py",
Fidelity.SEGMENT,
cheap_attempts=0,
)
assert "REFERENCE: current.module.Current" not in rendered
assert len(client.prompts) == 1
def test_each_cheap_read_uses_its_supplied_attempt_limit(
tmp_path: MemoryPath,
) -> None:
"""A later call may lower the threshold against accumulated read attempts."""
source, beats = _method_source()
renderer, client, _discovery = _renderer(
tmp_path,
source,
beats,
readonly=True,
signature_extract=_CurrentFileCards(),
)
first = renderer.cheap_read(
"src/example.py",
Fidelity.SEGMENT,
cheap_attempts=2,
)
second = renderer.cheap_read(
"src/example.py",
Fidelity.SEGMENT,
cheap_attempts=1,
)
assert "REFERENCE: current.module.Current" in first
assert "REFERENCE: current.module.Current" not in second
assert len(client.prompts) == 1
def test_cheap_read_rejects_negative_attempt_counts(
tmp_path: MemoryPath,
) -> None:
"""A cheap-read threshold cannot admit a negative number of attempts."""
renderer, _client, _discovery = _renderer(
tmp_path,
"VALUE = 1\n",
(_beat("assignment", 1, 1),),
readonly=True,
)
with pytest.raises(ValueError, match="cheap_attempts"):
renderer.cheap_read(
"src/example.py",
Fidelity.SEGMENT,
cheap_attempts=-1,
)
def test_cheap_read_keeps_segment_without_callable_candidates_ordinary(
tmp_path: MemoryPath,
) -> None:
"""A segmented read that cannot select a callable is already inexpensive."""
renderer, client, _discovery = _renderer(
tmp_path,
"VALUE = 1\n",
(_beat("assignment", 1, 1),),
readonly=True,
signature_extract=_CurrentFileCards(),
)
rendered = renderer.cheap_read(
"src/example.py",
Fidelity.SEGMENT,
)
assert rendered == "FILE: src/example.py\nVALUE = 1\n"
assert client.prompts == []
def test_cheap_read_keeps_cached_segmented_output_ordinary(
tmp_path: MemoryPath,
) -> None:
"""A prior selected render is already inexpensive and remains authoritative."""
source, beats = _method_source()
cards = _CurrentFileCards()
renderer, client, _discovery = _renderer(
tmp_path,
source,
beats,
readonly=True,
signature_extract=cards,
)
expected = renderer.read("src/example.py", Fidelity.SEGMENT)
rendered = renderer.cheap_read(
"src/example.py",
Fidelity.SEGMENT,
)
assert rendered == expected
assert cards.sources == []
assert len(client.prompts) == 1
def test_cheap_read_caches_cards_for_each_exact_source(
tmp_path: MemoryPath,
) -> None:
"""Repeated source reuses cards while changed source receives fresh evidence."""
source, beats = _method_source()
cards = _CurrentFileCards()
renderer, client, _discovery = _renderer(
tmp_path,
source,
beats,
readonly=True,
signature_extract=cards,
)
first = renderer.cheap_read(
"src/example.py",
Fidelity.SEGMENT,
cheap_attempts=3,
)
repeated = renderer.cheap_read(
"src/example.py",
Fidelity.SEGMENT,
cheap_attempts=3,
)
changed_source = source.replace("CONSTANT = 1", "CONSTANT = 2")
(tmp_path / "src/example.py").write_text(changed_source)
changed = renderer.cheap_read(
"src/example.py",
Fidelity.SEGMENT,
cheap_attempts=3,
)
assert first == repeated == changed
assert cards.sources == [source, changed_source]
assert client.prompts == []
def test_cheap_read_keeps_an_explicit_empty_diff_with_cards(
tmp_path: MemoryPath,
) -> None:
"""Card output preserves the renderer's explicit empty-diff evidence."""
source = "def selected():\n" + " value = 1\n" * 24 + " return value\n"
renderer, client, _discovery = _renderer(
tmp_path,
source,
(_beat("function", 1, 26),),
readonly=True,
signature_extract=_CurrentFileCards(),
)
rendered = renderer.cheap_read(
"src/example.py",
Fidelity.SEGMENT | Fidelity.DIFF,
)
assert rendered.endswith("GIT DIFF\n[no git diff]")
assert "REFERENCE: current.module.Current" in rendered
assert client.prompts == []
@pytest.mark.parametrize(
("fidelity", "readonly", "diff", "relevance"),
(
(Fidelity.AUTO, True, "", ()),
(Fidelity.WHOLE, True, "", ()),
(Fidelity.DIFF, True, "", ()),
(Fidelity.SEGMENT, False, "", ()),
(Fidelity.SEGMENT, True, "", ("failure matches this file",)),
(Fidelity.SEGMENT, True, "changed\n", ()),
),
)
def test_cheap_read_honors_ordinary_read_exceptions(
tmp_path: MemoryPath,
fidelity: Fidelity,
readonly: bool,
diff: str,
relevance: tuple[str, ...],
) -> None:
"""Cheap fidelity and conservative evidence exceptions retain the renderer."""
source = "def selected():\n" + " value = 1\n" * 24 + " return value\n"
metadata = ClassMeta(
"src/example.py",
"",
list(relevance),
{},
content_type=ContentType.Source,
)
renderer, client, _discovery = _renderer(
tmp_path,
source,
(_beat("function", 1, 26),),
readonly=readonly,
diff=diff,
class_meta=metadata,
signature_extract=_CurrentFileCards(),
)
rendered = renderer.cheap_read(
metadata,
fidelity,
)
assert "REFERENCE: current.module" not in rendered
assert (len(client.prompts) == 1) is (fidelity is Fidelity.SEGMENT)
def test_explicit_diff_remains_after_source(tmp_path: MemoryPath) -> None:
source = "VALUE = 1\n"
diff = "diff --git a/src/example.py b/src/example.py\n@@ -1 +1 @@\n+VALUE = 1\n"
renderer, _client, _discovery = _renderer(
tmp_path,
source,
(_beat("assignment", 1, 1),),
diff=diff,
)
automatic = renderer.read("src/example.py", Fidelity.AUTO)
explicit = renderer.read(
"src/example.py",
Fidelity.AUTO | Fidelity.DIFF,
)
assert "GIT DIFF" not in automatic
assert explicit.endswith(diff)
assert explicit.index("GIT DIFF") > explicit.index("VALUE = 1")
def test_auto_history_eventually_promotes_to_whole(tmp_path: MemoryPath) -> None:
configure_token_budget_settings(max_prompt_tokens=30)
source = "def selected():\n" + " pass\n" * 30
beats = (_beat("function", 1, 31),)
rendered = ""
for _ in range(AUTO_WHOLE_START_COUNT):
renderer, _client, _discovery = _renderer(
tmp_path,
source,
beats,
"KEEP: F01.C0001\nREASON: relevant\n",
)
rendered = renderer.read("src/example.py", Fidelity.AUTO)
assert rendered == f"FILE: src/example.py\n{source}"
def test_selector_failure_does_not_break_explicit_whole(
tmp_path: MemoryPath,
) -> None:
source = "def selected():\n" + " value = 1\n" * 23 + " return value\n"
renderer, _client, _discovery = _renderer(
tmp_path,
source,
(_beat("function", 1, 25),),
RuntimeError("selector unavailable"),
)
with pytest.raises(RuntimeError):
renderer.read("src/example.py", Fidelity.SEGMENT)
assert renderer.read("src/example.py", Fidelity.WHOLE) == (
f"FILE: src/example.py\n{source}"
)
def test_repair_refinement_falls_back_to_whole_editable_source(
tmp_path: MemoryPath,
) -> None:
"""A direct-edit presenter stops growing selections after its bounded refinements."""
source = (
"def first():\n" + " value = 1\n" * 24 + " return value\n\n"
"def second():\n" + " value = 2\n" * 24 + " return value\n"
)
renderer, _client, _discovery = _renderer(
tmp_path,
source,
(_beat("function", 1, 26), _beat("function", 28, 53)),
"KEEP: F01.C0001\nREASON: relevant\n",
repair_episode=True,
)
segmented = renderer.cheap_read("src/example.py", Fidelity.SEGMENT)
assert "⋮" in segmented
_client.response = "NO_KEEP\nREASON: no additional chunks\n"
for _ in range(MAX_CONTEXT_REFINEMENTS):
renderer.refine("compiler evidence", stale_files=[])
assert renderer.cheap_read("src/example.py", Fidelity.SEGMENT) == (
f"FILE: src/example.py\n{source}"
)
def test_refine_requires_typed_keyword_only_stale_files() -> None:
"""The stale-source boundary cannot accept an anonymous location by omission."""
parameter = inspect.signature(ContextualFileRenderer.refine).parameters["stale_files"]
assert parameter.kind is inspect.Parameter.KEYWORD_ONLY
assert parameter.default is inspect.Parameter.empty
assert get_type_hints(ContextualFileRenderer.refine)["stale_files"] == Sequence[ClassMeta]
def test_stale_refresh_replaces_metadata_and_whole_source_after_limit(
tmp_path: MemoryPath,
) -> None:
"""Whole-file repair mode still renders the canonical source after each edit."""
location = "src/example.py"
path = tmp_path / location
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("S4-only\n", encoding="utf-8")
old = ClassMeta(
location=location,
summary="old source",
relevance=[],
relevant_symbols={},
content_type=ContentType.Source,
)
first_refresh = ClassMeta(
location=location,
summary="S5 source",
relevance=[],
relevant_symbols={},
content_type=ContentType.Source,
)
second_refresh = ClassMeta(
location=location,
summary="S6 source",
relevance=[],
relevant_symbols={},
content_type=ContentType.Source,
)
file_manager = _FileManager(
tmp_path,
metadata_by_location={location: old},
refreshed_by_location={location: first_refresh},
)
renderer = ContextualFileRenderer(
file_manager=file_manager,
client=_Client(),
editable_files=[old],
readonly_files=[],
prompt="Repair the current source.",
logger=logging.getLogger("contextual-renderer-stale-test"),
repair_episode=True,
prompt_formatter_builder=MarkdownPromptFormatterBuilder(),
oop_signature_extract=_SignatureExtract(),
oop_signature_render=_SignatureRender(),
)
renderer._refinement_count = MAX_CONTEXT_REFINEMENTS
path.write_text("S5-only\n", encoding="utf-8")
assert not renderer.refine("compiler evidence", stale_files=[old])
assert file_manager.current_src(location) is first_refresh
assert renderer._class_meta_by_location[location] is first_refresh
assert renderer.read(location, Fidelity.SEGMENT) == f"FILE: {location}\nS5-only\n"
assert "S4-only" not in renderer.read(location, Fidelity.SEGMENT)
file_manager.refreshed_by_location[location] = second_refresh
path.write_text("S6-only\n", encoding="utf-8")
assert not renderer.refine("next compiler evidence", stale_files=[first_refresh])
assert file_manager.current_src(location) is second_refresh
assert renderer._class_meta_by_location[location] is second_refresh
assert renderer.read(location, Fidelity.SEGMENT) == f"FILE: {location}\nS6-only\n"
assert file_manager.refresh_calls == [
(old, "Repair the current source.", "compiler evidence"),
(first_refresh, "Repair the current source.", "next compiler evidence"),
]
def test_repair_refinement_reconciles_moved_selection_without_reselecting_it(
tmp_path: MemoryPath,
) -> None:
"""Exact selected callable source survives a line shift without another LLM call."""
source = "def selected():\n" + " value = 1\n" * 24 + " return value\n"
renderer, client, discovery = _renderer(
tmp_path,
source,
(_beat("function", 1, 26),),
"KEEP: F01.C0001\nREASON: relevant\n",
repair_episode=True,
)
renderer.read("src/example.py", Fidelity.SEGMENT)
path = tmp_path / "src/example.py"
path.write_text("# shifted\n" + source)
discovery.beats = ()
grew = renderer.refine("compiler evidence", stale_files=[])
assert not grew
assert len(client.prompts) == 1
assert renderer._episode_selection_by_location["src/example.py"].chunks[0].ranges == ((1, 27),)
def test_repair_refinement_selects_only_the_gap_left_by_deleted_context(
tmp_path: MemoryPath,
) -> None:
"""Deleted selected source leaves the current gap as the only selector input."""
first = "def first():\n" + " value = 1\n" * 24 + " return value\n\n"
second = "def second():\n" + " value = 2\n" * 24 + " return value\n"
renderer, client, discovery = _renderer(
tmp_path,
first + second,
(_beat("function", 1, 26), _beat("function", 28, 53)),
"KEEP: F01.C0001\nREASON: first\n",
repair_episode=True,
)
renderer.read("src/example.py", Fidelity.SEGMENT)
(tmp_path / "src/example.py").write_text(second)
discovery.beats = (_beat("function", 1, 26),)
client.response = "KEEP: F01.G0001\nREASON: compiler gap\n"
assert renderer.refine("missing first", stale_files=[])
assert len(client.prompts) == 2
assert renderer._episode_selection_by_location["src/example.py"].chunks[0].fragments == (second,)
def test_gap_chunks_preserve_oversized_atomic_behavior(
tmp_path: MemoryPath,
) -> None:
"""One large callable remains intact when no behavior-boundary split exists."""
source = "def unselected():\n" + " value = 1\n" * 99 + " return value\n"
renderer, _client, _discovery = _renderer(
tmp_path,
source,
(_beat("function", 1, 101),),
repair_episode=True,
)
gap_chunks = renderer._build_unselected_gap_chunks(
"src/example.py",
(),
identifier_kind="G",
)
assert len(gap_chunks.selectable_chunks) == 1
assert gap_chunks.selectable_chunks[0].callable_ranges == ((0, 101),)
def test_gap_chunks_recursively_split_only_between_complete_behaviors(
tmp_path: MemoryPath,
) -> None:
"""A large gap becomes balanced minimum-sized whole-behavior candidates."""
parts: list[str] = []
beats: list[SearchableBeat] = []
line = 1
for index in range(6):
behavior = (
f"def behavior_{index}():\n"
+ " value = 1\n" * 11
+ " return value\n"
)
parts.append(behavior)
beats.append(_beat("function", line, line + 12))
line += 13
renderer, _client, _discovery = _renderer(
tmp_path,
"".join(parts),
tuple(beats),
repair_episode=True,
)
gap_chunks = renderer._build_unselected_gap_chunks(
"src/example.py",
(),
identifier_kind="G",
)
assert [chunk.callable_ranges for chunk in gap_chunks.selectable_chunks] == [
((0, 13), (13, 26)),
((26, 39), (39, 52)),
((52, 65), (65, 78)),
]
assert all(
sum(end - start for start, end in chunk.callable_ranges) >= MIN_KEEP_LINES
for chunk in gap_chunks.selectable_chunks
)
def test_short_gap_absorbs_preceding_selected_behavior(
tmp_path: MemoryPath,
) -> None:
"""A short gap is retained automatically instead of disappearing from context."""
selected = "def selected():\n" + " value = 1\n" * 24 + " return value\n"
missing = "def missing():\n" + " value = 2\n" * 8 + " return value\n"
renderer, _client, _discovery = _renderer(
tmp_path,
selected + missing,
(_beat("function", 1, 26), _beat("function", 27, 36)),
repair_episode=True,
)
retained = (
_SelectedChunk("F01.C0001", "src/example.py", ((0, 26),), (selected,)),
)
gap_chunks = renderer._build_unselected_gap_chunks(
"src/example.py",
retained,
identifier_kind="G",
)
assert not gap_chunks.selectable_chunks
assert not gap_chunks.whole_file
assert gap_chunks.selected_chunks[0].ranges == ((0, 26), (26, 36))
def test_short_leading_gap_absorbs_following_selected_behavior(
tmp_path: MemoryPath,
) -> None:
"""The first short gap remains visible by joining the next retained behavior."""
missing = "def missing():\n" + " value = 2\n" * 8 + " return value\n"
selected = "def selected():\n" + " value = 1\n" * 24 + " return value\n"
renderer, _client, _discovery = _renderer(
tmp_path,
missing + selected,
(_beat("function", 1, 10), _beat("function", 11, 36)),
repair_episode=True,
)
retained = (
_SelectedChunk("F01.C0001", "src/example.py", ((10, 36),), (selected,)),
)
gap_chunks = renderer._build_unselected_gap_chunks(
"src/example.py",
retained,
identifier_kind="G",
)
assert not gap_chunks.selectable_chunks
assert not gap_chunks.whole_file
assert gap_chunks.selected_chunks[0].ranges == ((0, 10), (10, 36))
def test_gap_chunks_sort_multiple_selected_chunks_before_scanning(
tmp_path: MemoryPath,
) -> None:
"""Selected chunks may arrive out of source order without breaking gap scans."""
first = "def first():\n" + " value = 1\n" * 24 + " return value\n"
middle = "def middle():\n" + " value = 2\n" * 24 + " return value\n"
last = "def last():\n" + " value = 3\n" * 24 + " return value\n"
renderer, _client, _discovery = _renderer(
tmp_path,
first + middle + last,
(_beat("function", 1, 26), _beat("function", 27, 52), _beat("function", 53, 78)),
repair_episode=True,
)
selected_chunks = (
_SelectedChunk("F01.C0002", "src/example.py", ((52, 78),), (last,)),
_SelectedChunk("F01.C0001", "src/example.py", ((0, 26),), (first,)),
)
gap_chunks = renderer._build_unselected_gap_chunks(
"src/example.py",
selected_chunks,
identifier_kind="G",
)
assert [chunk.callable_ranges for chunk in gap_chunks.selectable_chunks] == [
((26, 52),),
]
def test_gap_chunks_sort_multiple_ranges_in_one_selected_chunk(
tmp_path: MemoryPath,
) -> None:
"""Every selected range participates in the source-order gap scan."""
first = "def first():\n" + " value = 1\n" * 24 + " return value\n"
middle = "def middle():\n" + " value = 2\n" * 24 + " return value\n"
last = "def last():\n" + " value = 3\n" * 24 + " return value\n"
renderer, _client, _discovery = _renderer(
tmp_path,
first + middle + last,
(_beat("function", 1, 26), _beat("function", 27, 52), _beat("function", 53, 78)),
repair_episode=True,
)
selected_chunks = (
_SelectedChunk(
"F01.C0001",
"src/example.py",
((52, 78), (0, 26)),
(last, first),
),
)
gap_chunks = renderer._build_unselected_gap_chunks(
"src/example.py",
selected_chunks,
identifier_kind="G",
)
assert [chunk.callable_ranges for chunk in gap_chunks.selectable_chunks] == [
((26, 52),),
]
def test_gap_candidates_group_behaviors_without_whitespace_ranges(
tmp_path: MemoryPath,
) -> None:
"""Whitespace between complete behaviors does not become selector evidence."""
first = "def first():\n" + " value = 1\n" * 11 + " return value\n"
second = "def second():\n" + " value = 2\n" * 11 + " return value\n"
renderer, _client, _discovery = _renderer(
tmp_path,
first + "\n" * 30 + second,
(_beat("function", 1, 13), _beat("function", 44, 56)),
repair_episode=True,
)
gap_chunks = renderer._build_unselected_gap_chunks(
"src/example.py",
(),
identifier_kind="G",
)
assert gap_chunks.selectable_chunks[0].callable_ranges == ((0, 13), (43, 56))
def test_isolated_short_behavior_forces_whole_editable_file(
tmp_path: MemoryPath,
) -> None:
"""A short callable without retained context stays visible as whole-file source."""
source = "def small():\n" + " value = 1\n" * 8 + " return value\n"
renderer, client, _discovery = _renderer(
tmp_path,
source,
(_beat("function", 1, 10),),
repair_episode=True,
)
assert renderer.read("src/example.py", Fidelity.SEGMENT) == (
f"FILE: src/example.py\n{source}"
)
assert not client.prompts
assert not renderer.used_segmentation
def test_empty_episode_selection_is_not_reselected_after_refresh(
tmp_path: MemoryPath,
) -> None:
"""No-keep is a stable initial selection rather than an uninitialized state."""
source = "def ignored():\n" + " value = 1\n" * 24 + " return value\n"
renderer, client, _discovery = _renderer(
tmp_path,
source,
(_beat("function", 1, 26),),
repair_episode=True,
)
renderer.read("src/example.py", Fidelity.SEGMENT)
renderer.refresh_current_source()
renderer.read("src/example.py", Fidelity.SEGMENT)
assert len(client.prompts) == 1
def test_repair_episode_initializes_selection_state_for_every_editable_file(
tmp_path: MemoryPath,
) -> None:
"""Declared editable files always own selection state before a renderer read."""
renderer, _client, _sources = _multi_file_renderer(
tmp_path,
repair_episode=True,
)
assert tuple(renderer._episode_selection_by_location) == (
"src/a.py",
"src/b.py",
)
assert all(
not selection.chunks and not selection.initialized
for selection in renderer._episode_selection_by_location.values()
)