1"""Dependency mapper data models.

2

3 Dataclasses for cross-issue dependency discovery and validation.

4"""

5

6 from __future__ import annotations

7

8 from dataclasses import dataclass, field

9

10

11@dataclass

12 class DependencyProposal:

13 """A proposed dependency relationship between two issues.

14

15 Attributes:

16 source_id: Issue that would be blocked

17 target_id: Issue that would block (the blocker)

18 reason: Category of discovery method

19 confidence: Score from 0.0 to 1.0

20 rationale: Human-readable explanation

21 overlapping_files: Files referenced by both issues

22 conflict_score: Semantic conflict score from 0.0 to 1.0

23 """

24

25 source_id: str

26 target_id: str

27 reason: str

28 confidence: float

29 rationale: str

30 overlapping_files: list[str] = field(default_factory=list)

31 conflict_score: float = 0.5

32

33

34@dataclass

35 class ParallelSafePair:

36 """A pair of issues that share files but can safely run in parallel.

37

38 Attributes:

39 issue_a: First issue ID

40 issue_b: Second issue ID

41 shared_files: Files referenced by both issues

42 conflict_score: Semantic conflict score (< 0.4)

43 reason: Why these are parallel-safe

44 """

45

46 issue_a: str

47 issue_b: str

48 shared_files: list[str] = field(default_factory=list)

49 conflict_score: float = 0.0

50 reason: str = ""

51

52

53@dataclass

54 class ValidationResult:

55 """Result of validating existing dependency references.

56

57 Attributes:

58 broken_refs: (issue_id, missing_ref_id) pairs for broken blocked_by refs

59 missing_backlinks: (issue_id, should_have_backlink_from) pairs

60 cycles: Cycle paths from DependencyGraph.detect_cycles()

61 stale_completed_refs: (issue_id, completed_ref_id) pairs

62 broken_depends_on_refs: (issue_id, missing_ref_id) pairs for broken depends_on refs

63 broken_relates_to_refs: (issue_id, missing_ref_id) pairs for broken relates_to refs

64 """

65

66 broken_refs: list[tuple[str, str]] = field(default_factory=list)

67 missing_backlinks: list[tuple[str, str]] = field(default_factory=list)

68 cycles: list[list[str]] = field(default_factory=list)

69 stale_completed_refs: list[tuple[str, str]] = field(default_factory=list)

70 broken_depends_on_refs: list[tuple[str, str]] = field(default_factory=list)

71 broken_relates_to_refs: list[tuple[str, str]] = field(default_factory=list)

72

73 @property

74 def has_issues(self) -> bool:

75 """Return True if any validation problems were found."""

76 return bool(

77 self.broken_refs

78 or self.missing_backlinks

79 or self.cycles

80 or self.stale_completed_refs

81 or self.broken_depends_on_refs

82 or self.broken_relates_to_refs

83 )

84

85

86@dataclass

87 class DependencyReport:

88 """Complete dependency analysis report.

89

90 Attributes:

91 proposals: Proposed new dependency relationships

92 parallel_safe: Pairs of issues that share files but can run in parallel

93 validation: Validation results for existing dependencies

94 issue_count: Total issues analyzed

95 existing_dep_count: Number of existing dependency edges

96 """

97

98 proposals: list[DependencyProposal] = field(default_factory=list)

99 parallel_safe: list[ParallelSafePair] = field(default_factory=list)

100 validation: ValidationResult = field(default_factory=ValidationResult)

101 issue_count: int = 0

102 existing_dep_count: int = 0

103

104

105@dataclass

106 class FixResult:

107 """Result of auto-fixing dependency validation issues.

108

109 Attributes:

110 changes: Human-readable descriptions of each fix applied

111 modified_files: Set of file paths that were modified

112 skipped_cycles: Number of cycles skipped (out of scope for auto-fix)

113 """

114

115 changes: list[str] = field(default_factory=list)

116 modified_files: set[str] = field(default_factory=set)

117 skipped_cycles: int = 0