feat(audit): implement Phase 3 MemoryDim + Phase 4 APD (11 tasks)
Phase 3: MemoryDim classifier with canonical mappings (23 entries, includes ToolSpec/ChatMessage/ProviderHistory now that they're real), file-of-origin heuristic (5 buckets), TOML override loader, classify_memory_dim() with 3-tier precedence. Phase 4: APD with 4 threshold constants, 5 pattern detectors (whole_struct, field_by_field, hot_cold_split, bulk_batched, dominant_pattern), detect_access_pattern() main entry. 30 new unit tests passing (Phase 3: 11, Phase 4: 19). 63 total tests passing. Phase 5 (CFE - Call Frequency Estimator) next.
This commit is contained in:
@@ -4,6 +4,7 @@ import ast
|
||||
import textwrap
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from collections import Counter
|
||||
import pytest
|
||||
from src.code_path_audit import (
|
||||
AggregateKind,
|
||||
@@ -26,6 +27,21 @@ from src.code_path_audit import (
|
||||
P2_pass,
|
||||
P3_pass,
|
||||
build_pcg,
|
||||
CANONICAL_MEMORY_DIM,
|
||||
MEMORY_DIM_FILE_HEURISTIC,
|
||||
load_memory_dim_overrides,
|
||||
file_origin_memory_dim,
|
||||
classify_memory_dim,
|
||||
WHOLE_STRUCT_KEY_THRESHOLD,
|
||||
FIELD_BY_FIELD_KEY_THRESHOLD,
|
||||
MIXED_DOMINANCE_THRESHOLD,
|
||||
AGGREGATE_LEVEL_DOMINANCE_THRESHOLD,
|
||||
is_whole_struct_access,
|
||||
is_field_by_field_access,
|
||||
is_hot_cold_split,
|
||||
is_bulk_batched_access,
|
||||
dominant_pattern,
|
||||
detect_access_pattern,
|
||||
)
|
||||
from src.result_types import Result, ErrorInfo, ErrorKind
|
||||
|
||||
@@ -426,4 +442,170 @@ def test_build_pcg_tolerates_syntax_errors() -> None:
|
||||
assert not result.ok
|
||||
assert len(result.errors) >= 1
|
||||
assert isinstance(result.errors[0], ErrorInfo)
|
||||
assert result.data is not None
|
||||
assert result.data is not None
|
||||
|
||||
def test_canonical_memory_dim_has_aggregates() -> None:
|
||||
"""CANONICAL_MEMORY_DIM has >=13 known aggregate -> dim mappings (10 in-scope + 3 candidate)."""
|
||||
assert len(CANONICAL_MEMORY_DIM) >= 13
|
||||
assert CANONICAL_MEMORY_DIM["Metadata"] == "discussion"
|
||||
assert CANONICAL_MEMORY_DIM["CommsLogEntry"] == "discussion"
|
||||
assert CANONICAL_MEMORY_DIM["FileItem"] == "curation"
|
||||
assert CANONICAL_MEMORY_DIM["FileItems"] == "curation"
|
||||
assert CANONICAL_MEMORY_DIM["Result"] == "control"
|
||||
assert CANONICAL_MEMORY_DIM["ErrorInfo"] == "control"
|
||||
|
||||
def test_memory_dim_file_heuristic_has_5_buckets() -> None:
|
||||
"""MEMORY_DIM_FILE_HEURISTIC has the 5 file-of-origin buckets."""
|
||||
assert len(MEMORY_DIM_FILE_HEURISTIC) == 5
|
||||
assert "curation" in MEMORY_DIM_FILE_HEURISTIC
|
||||
assert "discussion" in MEMORY_DIM_FILE_HEURISTIC
|
||||
assert "rag" in MEMORY_DIM_FILE_HEURISTIC
|
||||
assert "config" in MEMORY_DIM_FILE_HEURISTIC
|
||||
|
||||
def test_load_memory_dim_overrides_empty() -> None:
|
||||
"""load_memory_dim_overrides returns {} for a missing file."""
|
||||
result = load_memory_dim_overrides("/nonexistent/overrides.toml")
|
||||
assert result == {}
|
||||
|
||||
def test_load_memory_dim_overrides_parses_toml() -> None:
|
||||
"""load_memory_dim_overrides parses [memory_dim.<aggregate>] = '<dim>' lines."""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
overrides_path = Path(tmp) / "overrides.toml"
|
||||
overrides_path.write_text('[memory_dim]\nMetadata = "curation"\n')
|
||||
result = load_memory_dim_overrides(str(overrides_path))
|
||||
assert result.get("Metadata") == "curation"
|
||||
|
||||
def test_file_origin_memory_dim_curation() -> None:
|
||||
"""file_origin_memory_dim returns 'curation' for files in the curation bucket."""
|
||||
dim = file_origin_memory_dim("src/aggregate.py")
|
||||
assert dim == "curation"
|
||||
|
||||
def test_file_origin_memory_dim_discussion() -> None:
|
||||
"""file_origin_memory_dim returns 'discussion' for files in the discussion bucket."""
|
||||
dim = file_origin_memory_dim("src/ai_client.py")
|
||||
assert dim == "discussion"
|
||||
|
||||
def test_file_origin_memory_dim_unknown() -> None:
|
||||
"""file_origin_memory_dim returns 'unknown' for files not in any bucket."""
|
||||
dim = file_origin_memory_dim("src/random.py")
|
||||
assert dim == "unknown"
|
||||
|
||||
def test_classify_memory_dim_canonical() -> None:
|
||||
"""classify_memory_dim returns the canonical dim for known aggregates, regardless of producer file."""
|
||||
dim = classify_memory_dim("Metadata", "src/aggregate.py", overrides={})
|
||||
assert dim == "discussion"
|
||||
|
||||
def test_classify_memory_dim_override() -> None:
|
||||
"""classify_memory_dim respects the override file's mapping."""
|
||||
dim = classify_memory_dim("Metadata", "src/aggregate.py", overrides={"Metadata": "curation"})
|
||||
assert dim == "curation"
|
||||
|
||||
def test_classify_memory_dim_file_heuristic() -> None:
|
||||
"""classify_memory_dim falls back to file-of-origin for unknown aggregates."""
|
||||
dim = classify_memory_dim("SomeUnknownAggregate", "src/aggregate.py", overrides={})
|
||||
assert dim == "curation"
|
||||
|
||||
def test_classify_memory_dim_unknown_when_no_evidence() -> None:
|
||||
"""classify_memory_dim returns 'unknown' when no canonical, override, or file evidence."""
|
||||
dim = classify_memory_dim("SomeUnknownAggregate", "src/random.py", overrides={})
|
||||
assert dim == "unknown"
|
||||
|
||||
def test_threshold_constants() -> None:
|
||||
"""The 4 APD threshold constants are defined."""
|
||||
assert WHOLE_STRUCT_KEY_THRESHOLD == 1
|
||||
assert FIELD_BY_FIELD_KEY_THRESHOLD == 3
|
||||
assert MIXED_DOMINANCE_THRESHOLD == 0.6
|
||||
assert AGGREGATE_LEVEL_DOMINANCE_THRESHOLD == 0.25
|
||||
|
||||
def test_is_whole_struct_access_true() -> None:
|
||||
"""is_whole_struct_access returns True for a function that reads the aggregate without accessing fields."""
|
||||
counts: Counter[str] = Counter()
|
||||
assert is_whole_struct_access(counts, has_direct_access=True) is True
|
||||
|
||||
def test_is_whole_struct_access_one_key() -> None:
|
||||
"""is_whole_struct_access returns True for <=1 distinct key."""
|
||||
counts: Counter[str] = Counter({"path": 3})
|
||||
assert is_whole_struct_access(counts, has_direct_access=False) is True
|
||||
|
||||
def test_is_whole_struct_access_two_keys_false() -> None:
|
||||
"""is_whole_struct_access returns False for 2+ distinct keys."""
|
||||
counts: Counter[str] = Counter({"path": 3, "view_mode": 2})
|
||||
assert is_whole_struct_access(counts, has_direct_access=False) is False
|
||||
|
||||
def test_is_field_by_field_access_true() -> None:
|
||||
"""is_field_by_field_access returns True for >=3 distinct keys AND no whole_struct access."""
|
||||
counts: Counter[str] = Counter({"a": 1, "b": 1, "c": 1})
|
||||
assert is_field_by_field_access(counts) is True
|
||||
|
||||
def test_is_field_by_field_access_few_keys() -> None:
|
||||
"""is_field_by_field_access returns False for <3 distinct keys."""
|
||||
counts: Counter[str] = Counter({"a": 1, "b": 1})
|
||||
assert is_field_by_field_access(counts) is False
|
||||
|
||||
def test_is_hot_cold_split_true() -> None:
|
||||
"""is_hot_cold_split returns True for 1-2 hot keys + 2+ cold keys."""
|
||||
hot = {"role", "content"}
|
||||
cold = {"tool_calls", "reasoning_content"}
|
||||
assert is_hot_cold_split(hot, cold) is True
|
||||
|
||||
def test_is_hot_cold_split_too_many_hot() -> None:
|
||||
"""is_hot_cold_split returns False for 3+ hot keys."""
|
||||
hot = {"a", "b", "c"}
|
||||
cold = {"d", "e"}
|
||||
assert is_hot_cold_split(hot, cold) is False
|
||||
|
||||
def test_is_hot_cold_split_too_few_cold() -> None:
|
||||
"""is_hot_cold_split returns False for <2 cold keys."""
|
||||
hot = {"a"}
|
||||
cold = {"b"}
|
||||
assert is_hot_cold_split(hot, cold) is False
|
||||
|
||||
def test_is_bulk_batched_access_true() -> None:
|
||||
"""is_bulk_batched_access returns True for a function iterating over a list of aggregates."""
|
||||
assert is_bulk_batched_access(iterates_over_list=True, body_accesses_uniform=True) is True
|
||||
|
||||
def test_is_bulk_batched_access_no_iteration() -> None:
|
||||
"""is_bulk_batched_access returns False when the function doesn't iterate over a list."""
|
||||
assert is_bulk_batched_access(iterates_over_list=False, body_accesses_uniform=True) is False
|
||||
|
||||
def test_is_bulk_batched_access_non_uniform() -> None:
|
||||
"""is_bulk_batched_access returns False when the body has non-uniform access."""
|
||||
assert is_bulk_batched_access(iterates_over_list=True, body_accesses_uniform=False) is False
|
||||
|
||||
def test_dominant_pattern_clear_winner() -> None:
|
||||
"""dominant_pattern returns the pattern with the highest share if >=25%."""
|
||||
counts = {"field_by_field": 3, "whole_struct": 1}
|
||||
assert dominant_pattern(counts) == "field_by_field"
|
||||
|
||||
def test_dominant_pattern_below_threshold() -> None:
|
||||
"""dominant_pattern returns 'mixed' when no pattern has >=25% share."""
|
||||
counts = {"field_by_field": 1, "whole_struct": 1, "hot_cold_split": 1, "bulk_batched": 1}
|
||||
assert dominant_pattern(counts) == "mixed"
|
||||
|
||||
def test_dominant_pattern_empty() -> None:
|
||||
"""dominant_pattern returns 'mixed' for an empty counts dict."""
|
||||
assert dominant_pattern({}) == "mixed"
|
||||
|
||||
def test_detect_access_pattern_whole_struct() -> None:
|
||||
"""detect_access_pattern returns 'whole_struct' for a function that reads 0-1 keys."""
|
||||
counts: Counter[str] = Counter()
|
||||
pattern = detect_access_pattern(counts, has_direct_access=True)
|
||||
assert pattern == "whole_struct"
|
||||
|
||||
def test_detect_access_pattern_field_by_field() -> None:
|
||||
"""detect_access_pattern returns 'field_by_field' for a function that reads 3+ keys."""
|
||||
counts: Counter[str] = Counter({"a": 1, "b": 1, "c": 1})
|
||||
pattern = detect_access_pattern(counts, has_direct_access=False)
|
||||
assert pattern == "field_by_field"
|
||||
|
||||
def test_detect_access_pattern_hot_cold_split() -> None:
|
||||
"""detect_access_pattern returns 'hot_cold_split' for 1-2 hot + 2+ cold keys."""
|
||||
counts: Counter[str] = Counter({"a": 1, "b": 1, "c": 1, "d": 1})
|
||||
pattern = detect_access_pattern(counts, has_direct_access=False, hot_keys={"a", "b"}, cold_keys={"c", "d"})
|
||||
assert pattern == "hot_cold_split"
|
||||
|
||||
def test_detect_access_pattern_mixed() -> None:
|
||||
"""detect_access_pattern returns 'mixed' when no pattern dominates (2+ distinct keys but <3)."""
|
||||
counts: Counter[str] = Counter({"a": 1, "b": 1})
|
||||
pattern = detect_access_pattern(counts, has_direct_access=False)
|
||||
assert pattern == "mixed"
|
||||
Reference in New Issue
Block a user