Files
markitect-main/markitect/prompts/incremental/metrics.py
tegwick bd1d05ba79 feat(prompts): implement Phase 6 - Incremental Execution (FR-7, FR-8)
Add change detection, structural diff-based impact analysis,
configurable-depth incremental recomputation with circular suppression,
and impact debt tracking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 13:18:27 +01:00

96 lines
2.8 KiB
Python

"""
Pure diff functions for impact analysis.
Implements FR-8.2: Structural diff-based impact measurement.
Uses difflib.SequenceMatcher for computing change magnitude between
old and new content versions.
"""
import difflib
from typing import Optional
def structural_diff_ratio(old_content: str, new_content: str) -> float:
"""
Calculate structural similarity ratio between old and new content.
Uses SequenceMatcher to compute a ratio of matching blocks.
Returns the *change* ratio (1.0 - similarity), so higher values
mean more change.
Args:
old_content: Previous content
new_content: Current content
Returns:
Change ratio from 0.0 (identical) to 1.0 (completely different)
"""
if old_content == new_content:
return 0.0
if not old_content and not new_content:
return 0.0
if not old_content or not new_content:
return 1.0
matcher = difflib.SequenceMatcher(None, old_content, new_content)
similarity = matcher.ratio()
return 1.0 - similarity
def line_diff_ratio(old_content: str, new_content: str) -> float:
"""
Calculate line-level change ratio between old and new content.
Splits content into lines and computes SequenceMatcher ratio
at the line level. Returns the change ratio.
Args:
old_content: Previous content
new_content: Current content
Returns:
Change ratio from 0.0 (identical) to 1.0 (completely different)
"""
if old_content == new_content:
return 0.0
if not old_content and not new_content:
return 0.0
if not old_content or not new_content:
return 1.0
old_lines = old_content.splitlines()
new_lines = new_content.splitlines()
matcher = difflib.SequenceMatcher(None, old_lines, new_lines)
similarity = matcher.ratio()
return 1.0 - similarity
def calculate_change_magnitude(
old_content: Optional[str],
new_content: Optional[str],
method: str = "structural",
) -> float:
"""
Calculate the magnitude of a change between two content versions.
This is the primary entry point for impact measurement (FR-8.2).
Args:
old_content: Previous content (None for created artifacts)
new_content: Current content (None for deleted artifacts)
method: Diff method to use ("structural" or "line")
Returns:
Change magnitude from 0.0 (no change) to 1.0 (complete change)
"""
# Handle None cases (creation/deletion)
if old_content is None and new_content is None:
return 0.0
if old_content is None or new_content is None:
return 1.0
if method == "line":
return line_diff_ratio(old_content, new_content)
return structural_diff_ratio(old_content, new_content)