Skip to content

Commit 536ef3b

Browse files
committed
feat(merge): implement implicit conflict detection (Phase 1)
Implements Phase 1 of implicit conflict detection using string matching: - Detect when one task removes a function/import that another task references - Extend detect_implicit_conflicts() with basic pattern matching - Track removed entities and check for references in other tasks This provides basic implicit conflict detection without requiring full AST parsing. More sophisticated detection can be added in Phase 2.
1 parent b720312 commit 536ef3b

File tree

1 file changed

+73
-14
lines changed

1 file changed

+73
-14
lines changed

apps/backend/merge/conflict_analysis.py

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -257,31 +257,90 @@ def detect_implicit_conflicts(
257257
"""
258258
Detect implicit conflicts not caught by location analysis.
259259
260-
This includes conflicts like:
261-
- Function rename + function call changes
262-
- Import removal + usage
263-
- Variable rename + references
260+
Phase 1 Implementation: Basic string matching for common patterns.
261+
262+
Currently detects:
263+
- Removed function/method referenced by another task
264+
- Removed import used by another task
264265
265266
Args:
266267
task_analyses: Map of task_id -> FileAnalysis
267268
268269
Returns:
269270
List of implicit conflict regions
270271
271-
Note:
272-
These advanced checks are currently TODO.
273-
The main location-based detection handles most cases.
272+
TODO (Phase 2): Rename detection
273+
- Detect RENAME_FUNCTION (currently treated as remove+add)
274+
- Use content similarity to identify renames vs replacements
275+
- Track functions_renamed: dict[str, str] in FileAnalysis
276+
277+
TODO (Phase 3): Full reference tracking
278+
- AST-based reference finding (which symbols are used where)
279+
- symbol_references: dict[str, list[int]] in FileAnalysis
280+
- Cross-file conflict detection (rename in utils.ts, call in App.tsx)
274281
"""
275-
conflicts = []
282+
conflicts: list[ConflictRegion] = []
283+
task_ids = list(task_analyses.keys())
276284

277-
# Check for function rename + function call changes
278-
# (If task A renames a function and task B calls the old name)
285+
if len(task_analyses) <= 1:
286+
return conflicts
287+
288+
# Collect what each task removes/modifies
289+
task_removals: dict[str, set[str]] = {} # task_id -> removed symbols
290+
task_additions: dict[str, set[str]] = {} # task_id -> added/modified code content
279291

280-
# Check for import removal + usage
281-
# (If task A removes an import and task B uses it)
292+
for task_id, analysis in task_analyses.items():
293+
removals: set[str] = set()
294+
additions: set[str] = set()
282295

283-
# For now, these advanced checks are TODO
284-
# The main location-based detection handles most cases
296+
# Track removed functions
297+
for change in analysis.changes:
298+
if change.change_type in (
299+
ChangeType.REMOVE_FUNCTION,
300+
ChangeType.REMOVE_METHOD,
301+
):
302+
removals.add(change.target)
303+
# Track content of additions/modifications for reference checking
304+
if change.content_after:
305+
additions.add(change.content_after)
306+
307+
# Track removed imports
308+
removals.update(analysis.imports_removed)
309+
310+
task_removals[task_id] = removals
311+
task_additions[task_id] = additions
312+
313+
# Check for conflicts: Task A removes something that Task B references
314+
for task_a in task_ids:
315+
for task_b in task_ids:
316+
if task_a == task_b:
317+
continue
318+
319+
removals_a = task_removals[task_a]
320+
additions_b = task_additions[task_b]
321+
322+
# Check if any removed symbol appears in other task's new code
323+
for removed_symbol in removals_a:
324+
for added_code in additions_b:
325+
if removed_symbol in added_code:
326+
# Potential conflict: Task A removes what Task B uses
327+
file_path = next(iter(task_analyses.values())).file_path
328+
conflicts.append(
329+
ConflictRegion(
330+
file_path=file_path,
331+
location=f"implicit:{removed_symbol}",
332+
tasks_involved=[task_a, task_b],
333+
change_types=[
334+
ChangeType.REMOVE_FUNCTION,
335+
ChangeType.MODIFY_FUNCTION,
336+
],
337+
severity=ConflictSeverity.HIGH,
338+
can_auto_merge=False,
339+
merge_strategy=MergeStrategy.AI_REQUIRED,
340+
reason=f"Task '{task_a}' removes '{removed_symbol}' but task '{task_b}' references it",
341+
)
342+
)
343+
break # One conflict per symbol pair is enough
285344

286345
return conflicts
287346

0 commit comments

Comments
 (0)