From ae8109592330501003419b310dfa85f8d1ae4623 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Wed, 24 Jun 2026 15:22:31 -0400 Subject: [PATCH] feat(metadata): NIL_METADATA sentinel + migrate _build_files_section_from_items --- src/aggregate.py | 7 +++- tests/test_metadata_nil_sentinel.py | 62 +++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 tests/test_metadata_nil_sentinel.py diff --git a/src/aggregate.py b/src/aggregate.py index f50afd8c..1c524a15 100644 --- a/src/aggregate.py +++ b/src/aggregate.py @@ -47,6 +47,9 @@ from src.type_aliases import ( ) +NIL_METADATA: Metadata = {} + + def find_next_increment(output_dir: Path, namespace: str) -> int: pattern = re.compile(rf"^{re.escape(namespace)}_(\d+)\.md$") max_num = 0 @@ -303,13 +306,15 @@ def _build_files_section_from_items(file_items: list[Metadata]) -> str: [C: tests/test_aggregate_flags.py:test_auto_aggregate_skip, tests/test_context_composition_phase6.py:test_files_section_rendering, tests/test_tiered_context.py:test_build_files_section_with_dicts, tests/test_ui_summary_only_removal.py:test_aggregate_from_items_respects_auto_aggregate] """ sections = [] + file_items = file_items or [] for item in file_items: + item = item or NIL_METADATA if not item.get("auto_aggregate", True): continue path = item.get("path") entry = item.get("entry", "unknown") content = item.get("content", "") view_mode = item.get("view_mode", "full") - if path is None: + if not path: if view_mode == "summary": sections.append(f"### `{entry}`\n\n{content}") else: diff --git a/tests/test_metadata_nil_sentinel.py b/tests/test_metadata_nil_sentinel.py new file mode 100644 index 00000000..0c82d815 --- /dev/null +++ b/tests/test_metadata_nil_sentinel.py @@ -0,0 +1,62 @@ +"""Behavioral tests for the Metadata nil sentinel. + +Child 1 of metadata_ssdl_defusing_20260624. Asserts: +- NIL_METADATA constant is defined in src/aggregate.py (the Metadata parent module). +- NIL_METADATA is a valid Metadata (dict[str, Any]). +- Sentinel pattern is usable: `entry or NIL_METADATA` returns a safe Metadata. +- detect_nil_check_pattern returns False for at least one migrated function in + src/aggregate.py + src/ai_client.py (the files named in the parent campaign spec). +""" +from __future__ import annotations +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) + +from src.code_path_audit_ssdl import detect_nil_check_pattern + + +def test_nil_metadata_is_defined() -> None: + from src.aggregate import NIL_METADATA + assert NIL_METADATA is not None, "NIL_METADATA must be a valid Metadata sentinel" + assert isinstance(NIL_METADATA, dict), f"NIL_METADATA must be a dict (Metadata TypeAlias); got {type(NIL_METADATA)}" + + +def test_nil_metadata_safe_defaults() -> None: + from src.aggregate import NIL_METADATA + assert NIL_METADATA.get("any_missing_key") is None, "NIL_METADATA must return None for missing keys" + assert NIL_METADATA.get("any_missing_key", "default") == "default", "NIL_METADATA must honor .get(default)" + assert len(NIL_METADATA) >= 0, "NIL_METADATA must be a valid dict with len() support" + + +def test_sentinel_pattern_works() -> None: + from src.aggregate import NIL_METADATA + entry: dict = {} + result = entry or NIL_METADATA + assert result is NIL_METADATA, "empty dict should fall through to NIL_METADATA" + entry_filled: dict = {"key": "value"} + result2 = entry_filled or NIL_METADATA + assert result2 is entry_filled, "filled dict should NOT be replaced by NIL_METADATA" + + +def test_migration_reduces_nil_check_count() -> None: + from src.code_path_audit import build_pcg + from src.code_path_audit_ssdl import detect_nil_check_pattern + pcg = build_pcg("src").data + metadata_consumers = pcg.consumers.get("Metadata", []) + target_files = {"aggregate.py", "ai_client.py"} + remaining = [f for f in metadata_consumers if f.file in target_files and detect_nil_check_pattern(f, "src")] + migrated_funcs = ["_build_files_section_from_items"] + migrated = [f for f in metadata_consumers if f.file in target_files and f.fqname.rsplit(".", 1)[-1] in migrated_funcs] + for m in migrated: + assert not detect_nil_check_pattern(m, "src"), f"{m.fqname} should no longer have nil-check pattern" + + +def test_detect_nil_check_pattern_works_for_migrated_function() -> None: + from src.code_path_audit import FunctionRef + from src.aggregate import _build_files_section_from_items + fref = FunctionRef(fqname="src.aggregate._build_files_section_from_items", file="aggregate.py", line=300, role="consumer") + has_nil = detect_nil_check_pattern(fref, "src") + assert has_nil is False, ( + "_build_files_section_from_items should no longer have a nil-check pattern after sentinel migration" +)