devforgeai 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +120 -0
- package/package.json +9 -1
- package/src/CLAUDE.md +699 -0
- package/src/claude/scripts/README.md +396 -0
- package/src/claude/scripts/audit-command-skill-overlap.sh +67 -0
- package/src/claude/scripts/check-hooks-fast.sh +70 -0
- package/src/claude/scripts/devforgeai-validate +6 -0
- package/src/claude/scripts/devforgeai_cli/README.md +531 -0
- package/src/claude/scripts/devforgeai_cli/__init__.py +12 -0
- package/src/claude/scripts/devforgeai_cli/cli.py +716 -0
- package/src/claude/scripts/devforgeai_cli/commands/__init__.py +1 -0
- package/src/claude/scripts/devforgeai_cli/commands/check_hooks.py +384 -0
- package/src/claude/scripts/devforgeai_cli/commands/invoke_hooks.py +149 -0
- package/src/claude/scripts/devforgeai_cli/commands/phase_commands.py +731 -0
- package/src/claude/scripts/devforgeai_cli/commands/validate_installation.py +412 -0
- package/src/claude/scripts/devforgeai_cli/context_extraction.py +426 -0
- package/src/claude/scripts/devforgeai_cli/feedback/AC_TO_TEST_MAPPING.md +636 -0
- package/src/claude/scripts/devforgeai_cli/feedback/DELIVERY_SUMMARY.txt +329 -0
- package/src/claude/scripts/devforgeai_cli/feedback/README_TEST_SPECS.md +486 -0
- package/src/claude/scripts/devforgeai_cli/feedback/TEST_IMPLEMENTATION_GUIDE.md +529 -0
- package/src/claude/scripts/devforgeai_cli/feedback/TEST_SPECIFICATIONS.md +2652 -0
- package/src/claude/scripts/devforgeai_cli/feedback/TEST_SPECS_INDEX.md +398 -0
- package/src/claude/scripts/devforgeai_cli/feedback/__init__.py +34 -0
- package/src/claude/scripts/devforgeai_cli/feedback/adaptive_questioning_engine.py +581 -0
- package/src/claude/scripts/devforgeai_cli/feedback/aggregation.py +179 -0
- package/src/claude/scripts/devforgeai_cli/feedback/commands.py +535 -0
- package/src/claude/scripts/devforgeai_cli/feedback/config_defaults.py +58 -0
- package/src/claude/scripts/devforgeai_cli/feedback/config_manager.py +423 -0
- package/src/claude/scripts/devforgeai_cli/feedback/config_models.py +192 -0
- package/src/claude/scripts/devforgeai_cli/feedback/config_schema.py +140 -0
- package/src/claude/scripts/devforgeai_cli/feedback/coverage.json +1 -0
- package/src/claude/scripts/devforgeai_cli/feedback/feature_flag.py +152 -0
- package/src/claude/scripts/devforgeai_cli/feedback/feedback_indexer.py +394 -0
- package/src/claude/scripts/devforgeai_cli/feedback/hot_reload.py +226 -0
- package/src/claude/scripts/devforgeai_cli/feedback/longitudinal.py +115 -0
- package/src/claude/scripts/devforgeai_cli/feedback/models.py +67 -0
- package/src/claude/scripts/devforgeai_cli/feedback/question_router.py +236 -0
- package/src/claude/scripts/devforgeai_cli/feedback/retrospective.py +233 -0
- package/src/claude/scripts/devforgeai_cli/feedback/skip_tracker.py +177 -0
- package/src/claude/scripts/devforgeai_cli/feedback/skip_tracking.py +221 -0
- package/src/claude/scripts/devforgeai_cli/feedback/template_engine.py +549 -0
- package/src/claude/scripts/devforgeai_cli/feedback/validation.py +163 -0
- package/src/claude/scripts/devforgeai_cli/headless/__init__.py +30 -0
- package/src/claude/scripts/devforgeai_cli/headless/answer_models.py +206 -0
- package/src/claude/scripts/devforgeai_cli/headless/answer_resolver.py +204 -0
- package/src/claude/scripts/devforgeai_cli/headless/exceptions.py +36 -0
- package/src/claude/scripts/devforgeai_cli/headless/pattern_matcher.py +156 -0
- package/src/claude/scripts/devforgeai_cli/hooks.py +313 -0
- package/src/claude/scripts/devforgeai_cli/metrics/__init__.py +46 -0
- package/src/claude/scripts/devforgeai_cli/metrics/command_metrics.py +142 -0
- package/src/claude/scripts/devforgeai_cli/metrics/failure_modes.py +152 -0
- package/src/claude/scripts/devforgeai_cli/metrics/story_segmentation.py +181 -0
- package/src/claude/scripts/devforgeai_cli/orchestrate_hooks.py +780 -0
- package/src/claude/scripts/devforgeai_cli/phase_state.py +1229 -0
- package/src/claude/scripts/devforgeai_cli/session/__init__.py +30 -0
- package/src/claude/scripts/devforgeai_cli/session/checkpoint.py +268 -0
- package/src/claude/scripts/devforgeai_cli/tests/__init__.py +1 -0
- package/src/claude/scripts/devforgeai_cli/tests/conftest.py +29 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/TEST_EXECUTION_GUIDE.md +298 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/__init__.py +3 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_adaptive_questioning_engine.py +2171 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_aggregation.py +476 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_defaults.py +133 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_manager.py +592 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_models.py +373 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_schema.py +130 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_configuration_management.py +1355 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_edge_cases.py +308 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_feature_flag.py +307 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_feedback_indexer.py +384 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_hot_reload.py +580 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_integration.py +402 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_models.py +105 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_question_routing.py +262 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_retrospective.py +333 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_skip_tracker.py +410 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_skip_tracking.py +159 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_skip_tracking_integration.py +1155 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_template_engine.py +1389 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_validation_comprehensive.py +210 -0
- package/src/claude/scripts/devforgeai_cli/tests/fixtures/autonomous-deferral-story.md +46 -0
- package/src/claude/scripts/devforgeai_cli/tests/fixtures/missing-impl-notes.md +31 -0
- package/src/claude/scripts/devforgeai_cli/tests/fixtures/valid-deferral-story.md +46 -0
- package/src/claude/scripts/devforgeai_cli/tests/fixtures/valid-story-complete.md +48 -0
- package/src/claude/scripts/devforgeai_cli/tests/manual_test_invoke_hooks.sh +200 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/DELIVERABLES.md +518 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/TEST_SUMMARY.md +468 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/__init__.py +6 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/fixtures/corrupted-checkpoint.json +1 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/fixtures/missing-fields-checkpoint.json +4 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/fixtures/valid-checkpoint.json +15 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/test_checkpoint.py +851 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_check_hooks.py +1886 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_depends_on_normalizer.py +171 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_dod_validator.py +97 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_invoke_hooks.py +1902 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_phase_commands.py +320 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_phase_commands_error_handling.py +1021 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_phase_commands_import.py +697 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_phase_state.py +2187 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_skip_tracking.py +2141 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_skip_tracking_coverage_gap.py +195 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_subagent_enforcement.py +539 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_validate_installation.py +361 -0
- package/src/claude/scripts/devforgeai_cli/utils/__init__.py +11 -0
- package/src/claude/scripts/devforgeai_cli/utils/depends_on_normalizer.py +149 -0
- package/src/claude/scripts/devforgeai_cli/utils/markdown_parser.py +219 -0
- package/src/claude/scripts/devforgeai_cli/utils/story_analyzer.py +249 -0
- package/src/claude/scripts/devforgeai_cli/utils/yaml_parser.py +152 -0
- package/src/claude/scripts/devforgeai_cli/validators/__init__.py +27 -0
- package/src/claude/scripts/devforgeai_cli/validators/ast_grep_validator.py +373 -0
- package/src/claude/scripts/devforgeai_cli/validators/context_validator.py +180 -0
- package/src/claude/scripts/devforgeai_cli/validators/dod_validator.py +309 -0
- package/src/claude/scripts/devforgeai_cli/validators/git_validator.py +107 -0
- package/src/claude/scripts/devforgeai_cli/validators/grep_fallback.py +300 -0
- package/src/claude/scripts/install_hooks.sh +186 -0
- package/src/claude/scripts/invoke_feedback_hooks.sh +59 -0
- package/src/claude/scripts/migrate-ac-headers.sh +122 -0
- package/src/claude/scripts/plan_file_kb.sh +704 -0
- package/src/claude/scripts/requirements.txt +8 -0
- package/src/claude/scripts/session_catalog.sh +543 -0
- package/src/claude/scripts/setup.py +55 -0
- package/src/claude/scripts/start-devforgeai.sh +16 -0
- package/src/claude/scripts/statusline.sh +27 -0
- package/src/claude/scripts/validate_deferrals.py +344 -0
- package/src/claude/skills/devforgeai-qa/SKILL.md +1 -1
- package/src/claude/skills/researching-market/SKILL.md +2 -1
- package/src/cli/lib/copier.js +13 -1
- package/src/claude/skills/designing-systems/scripts/__pycache__/detect_anti_patterns.cpython-312.pyc +0 -0
- package/src/claude/skills/designing-systems/scripts/__pycache__/validate_all_context.cpython-312.pyc +0 -0
- package/src/claude/skills/designing-systems/scripts/__pycache__/validate_architecture.cpython-312.pyc +0 -0
- package/src/claude/skills/designing-systems/scripts/__pycache__/validate_dependencies.cpython-312.pyc +0 -0
- package/src/claude/skills/devforgeai-story-creation/scripts/__pycache__/migrate_story_v1_to_v2.cpython-312.pyc +0 -0
- package/src/claude/skills/devforgeai-story-creation/scripts/tests/__pycache__/measure_accuracy.cpython-312.pyc +0 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Failure mode identification and ranking module.
|
|
3
|
+
|
|
4
|
+
AC#2: Identify and rank most common failure modes from error entries.
|
|
5
|
+
|
|
6
|
+
STORY-227: Calculate Workflow Success Metrics
|
|
7
|
+
"""
|
|
8
|
+
from collections import Counter
|
|
9
|
+
from typing import Any, Dict, List
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Category mapping for error types
|
|
13
|
+
CATEGORY_MAP: Dict[str, str] = {
|
|
14
|
+
"test_failure": "testing",
|
|
15
|
+
"coverage_gap": "quality",
|
|
16
|
+
"validation_failure": "validation",
|
|
17
|
+
"timeout": "infrastructure",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def identify_failure_modes(error_entries: List[Dict[str, Any]]) -> List[str]:
|
|
22
|
+
"""
|
|
23
|
+
Return list of unique failure modes (error_type values).
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
error_entries: List of error entries with 'error_type' field.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
List of unique failure mode strings.
|
|
30
|
+
Skips entries without error_type field.
|
|
31
|
+
Returns empty list for empty input.
|
|
32
|
+
"""
|
|
33
|
+
if not error_entries:
|
|
34
|
+
return []
|
|
35
|
+
|
|
36
|
+
# Extract unique error_type values, skipping entries without the field
|
|
37
|
+
failure_modes = set()
|
|
38
|
+
for entry in error_entries:
|
|
39
|
+
error_type = entry.get("error_type")
|
|
40
|
+
if error_type is not None:
|
|
41
|
+
failure_modes.add(error_type)
|
|
42
|
+
|
|
43
|
+
return list(failure_modes)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def rank_failure_modes(error_entries: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
47
|
+
"""
|
|
48
|
+
Return failure modes ranked by frequency (most common first).
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
error_entries: List of error entries with 'error_type' field.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
List of dicts, each containing:
|
|
55
|
+
- error_type: str
|
|
56
|
+
- count: int
|
|
57
|
+
- percentage: float (0-100)
|
|
58
|
+
Returns empty list for empty input.
|
|
59
|
+
"""
|
|
60
|
+
if not error_entries:
|
|
61
|
+
return []
|
|
62
|
+
|
|
63
|
+
# Count occurrences of each error_type (skip entries without error_type)
|
|
64
|
+
error_types = [
|
|
65
|
+
entry.get("error_type")
|
|
66
|
+
for entry in error_entries
|
|
67
|
+
if entry.get("error_type") is not None
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
if not error_types:
|
|
71
|
+
return []
|
|
72
|
+
|
|
73
|
+
# Count frequencies
|
|
74
|
+
counter = Counter(error_types)
|
|
75
|
+
total_errors = len(error_types)
|
|
76
|
+
|
|
77
|
+
# Sort by count (descending) and create result
|
|
78
|
+
ranked = []
|
|
79
|
+
for error_type, count in counter.most_common():
|
|
80
|
+
percentage = (count / total_errors) * 100.0
|
|
81
|
+
ranked.append({
|
|
82
|
+
"error_type": error_type,
|
|
83
|
+
"count": count,
|
|
84
|
+
"percentage": percentage,
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
return ranked
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def categorize_failure_mode(error_type: str) -> str:
|
|
91
|
+
"""
|
|
92
|
+
Categorize error types into categories.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
error_type: The error type string to categorize.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Category string:
|
|
99
|
+
- "test_failure" -> "testing"
|
|
100
|
+
- "coverage_gap" -> "quality"
|
|
101
|
+
- "validation_failure" -> "validation"
|
|
102
|
+
- "timeout" -> "infrastructure"
|
|
103
|
+
- anything else -> "unknown"
|
|
104
|
+
"""
|
|
105
|
+
return CATEGORY_MAP.get(error_type, "unknown")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_failure_mode_summary(error_entries: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
109
|
+
"""
|
|
110
|
+
Return summary of failure modes.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
error_entries: List of error entries with 'error_type' field.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Dictionary containing:
|
|
117
|
+
- total_errors: int
|
|
118
|
+
- unique_failure_modes: int
|
|
119
|
+
- ranked_modes: List[Dict] (from rank_failure_modes)
|
|
120
|
+
- by_category: Dict[str, int] (count per category)
|
|
121
|
+
"""
|
|
122
|
+
if not error_entries:
|
|
123
|
+
return {
|
|
124
|
+
"total_errors": 0,
|
|
125
|
+
"unique_failure_modes": 0,
|
|
126
|
+
"ranked_modes": [],
|
|
127
|
+
"by_category": {},
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
# Get failure modes and ranked list
|
|
131
|
+
failure_modes = identify_failure_modes(error_entries)
|
|
132
|
+
ranked_modes = rank_failure_modes(error_entries)
|
|
133
|
+
|
|
134
|
+
# Count by category
|
|
135
|
+
by_category: Dict[str, int] = {}
|
|
136
|
+
for entry in error_entries:
|
|
137
|
+
error_type = entry.get("error_type")
|
|
138
|
+
if error_type is not None:
|
|
139
|
+
category = categorize_failure_mode(error_type)
|
|
140
|
+
by_category[category] = by_category.get(category, 0) + 1
|
|
141
|
+
|
|
142
|
+
# Count total errors (only entries with error_type)
|
|
143
|
+
total_errors = sum(
|
|
144
|
+
1 for entry in error_entries if entry.get("error_type") is not None
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
"total_errors": total_errors,
|
|
149
|
+
"unique_failure_modes": len(failure_modes),
|
|
150
|
+
"ranked_modes": ranked_modes,
|
|
151
|
+
"by_category": by_category,
|
|
152
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Story point segmentation and averages module.
|
|
3
|
+
|
|
4
|
+
AC#3: Segment metrics by story points (1, 2, 3, 5, 8 - Fibonacci-based).
|
|
5
|
+
|
|
6
|
+
STORY-227: Calculate Workflow Success Metrics
|
|
7
|
+
"""
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Valid Fibonacci-based story points
|
|
12
|
+
VALID_STORY_POINTS: List[int] = [1, 2, 3, 5, 8]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_valid_story_points() -> List[int]:
|
|
16
|
+
"""
|
|
17
|
+
Return the list of valid story point values.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
[1, 2, 3, 5, 8] (Fibonacci-based story points)
|
|
21
|
+
"""
|
|
22
|
+
return VALID_STORY_POINTS.copy()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def is_valid_story_point(point: Any) -> bool:
|
|
26
|
+
"""
|
|
27
|
+
Check if a value is a valid story point.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
point: The value to check.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
True if point is in [1, 2, 3, 5, 8], False otherwise.
|
|
34
|
+
Returns False for None.
|
|
35
|
+
"""
|
|
36
|
+
if point is None:
|
|
37
|
+
return False
|
|
38
|
+
return point in VALID_STORY_POINTS
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def segment_metrics_by_story_points(
|
|
42
|
+
workflow_metrics: List[Dict[str, Any]]
|
|
43
|
+
) -> Dict[int, List[Dict[str, Any]]]:
|
|
44
|
+
"""
|
|
45
|
+
Segment metrics by story points.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
workflow_metrics: List of workflow metric entries with 'story_points' field.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Dictionary with valid story points as keys:
|
|
52
|
+
{
|
|
53
|
+
1: [stories with 1 point...],
|
|
54
|
+
2: [stories with 2 points...],
|
|
55
|
+
3: [], # empty if no stories
|
|
56
|
+
5: [],
|
|
57
|
+
8: []
|
|
58
|
+
}
|
|
59
|
+
- Only includes valid points (1, 2, 3, 5, 8)
|
|
60
|
+
- Excludes stories with missing/invalid story_points
|
|
61
|
+
- Returns structure with empty lists for empty input
|
|
62
|
+
"""
|
|
63
|
+
# Initialize result with empty lists for each valid point
|
|
64
|
+
result: Dict[int, List[Dict[str, Any]]] = {
|
|
65
|
+
point: [] for point in VALID_STORY_POINTS
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if not workflow_metrics:
|
|
69
|
+
return result
|
|
70
|
+
|
|
71
|
+
for metric in workflow_metrics:
|
|
72
|
+
story_points = metric.get("story_points")
|
|
73
|
+
|
|
74
|
+
# Skip if story_points is missing, None, or invalid
|
|
75
|
+
if not is_valid_story_point(story_points):
|
|
76
|
+
continue
|
|
77
|
+
|
|
78
|
+
# Add to appropriate segment
|
|
79
|
+
result[story_points].append(metric)
|
|
80
|
+
|
|
81
|
+
return result
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def calculate_segment_averages(
|
|
85
|
+
segments: Dict[int, List[Dict[str, Any]]]
|
|
86
|
+
) -> Dict[int, Dict[str, Optional[float]]]:
|
|
87
|
+
"""
|
|
88
|
+
Calculate averages per segment.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
segments: Dictionary of story point segments from segment_metrics_by_story_points.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Dictionary with averages per segment:
|
|
95
|
+
{
|
|
96
|
+
1: {"avg_completion_rate": float, "avg_error_rate": float},
|
|
97
|
+
2: {...},
|
|
98
|
+
...
|
|
99
|
+
}
|
|
100
|
+
- Returns 0.0 for empty segments
|
|
101
|
+
"""
|
|
102
|
+
result: Dict[int, Dict[str, Optional[float]]] = {}
|
|
103
|
+
|
|
104
|
+
for point in VALID_STORY_POINTS:
|
|
105
|
+
stories = segments.get(point, [])
|
|
106
|
+
|
|
107
|
+
if not stories:
|
|
108
|
+
result[point] = {
|
|
109
|
+
"avg_completion_rate": 0.0,
|
|
110
|
+
"avg_error_rate": 0.0,
|
|
111
|
+
}
|
|
112
|
+
else:
|
|
113
|
+
# Calculate averages
|
|
114
|
+
total_completion = sum(
|
|
115
|
+
story.get("completion_rate", 0.0) for story in stories
|
|
116
|
+
)
|
|
117
|
+
total_error = sum(
|
|
118
|
+
story.get("error_rate", 0.0) for story in stories
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
avg_completion = total_completion / len(stories)
|
|
122
|
+
avg_error = total_error / len(stories)
|
|
123
|
+
|
|
124
|
+
# Round to 2 decimal places
|
|
125
|
+
result[point] = {
|
|
126
|
+
"avg_completion_rate": round(avg_completion, 2),
|
|
127
|
+
"avg_error_rate": round(avg_error, 2),
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return result
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def get_segmentation_summary(
|
|
134
|
+
workflow_metrics: List[Dict[str, Any]]
|
|
135
|
+
) -> Dict[str, Any]:
|
|
136
|
+
"""
|
|
137
|
+
Return summary of story point segmentation.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
workflow_metrics: List of workflow metric entries.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Dictionary containing:
|
|
144
|
+
- total_stories: int (all stories including invalid)
|
|
145
|
+
- segmented_stories: int (stories with valid points)
|
|
146
|
+
- excluded_stories: int (stories without valid points)
|
|
147
|
+
- segments: Dict[int, List[Dict]] (from segment_metrics_by_story_points)
|
|
148
|
+
- averages_by_segment: Dict[int, Dict] (from calculate_segment_averages)
|
|
149
|
+
"""
|
|
150
|
+
if not workflow_metrics:
|
|
151
|
+
return {
|
|
152
|
+
"total_stories": 0,
|
|
153
|
+
"segmented_stories": 0,
|
|
154
|
+
"excluded_stories": 0,
|
|
155
|
+
"segments": {point: [] for point in VALID_STORY_POINTS},
|
|
156
|
+
"averages_by_segment": {
|
|
157
|
+
point: {"avg_completion_rate": 0.0, "avg_error_rate": 0.0}
|
|
158
|
+
for point in VALID_STORY_POINTS
|
|
159
|
+
},
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
# Segment the metrics
|
|
163
|
+
segments = segment_metrics_by_story_points(workflow_metrics)
|
|
164
|
+
|
|
165
|
+
# Count segmented stories
|
|
166
|
+
segmented_count = sum(len(stories) for stories in segments.values())
|
|
167
|
+
|
|
168
|
+
# Calculate total and excluded
|
|
169
|
+
total_stories = len(workflow_metrics)
|
|
170
|
+
excluded_stories = total_stories - segmented_count
|
|
171
|
+
|
|
172
|
+
# Calculate averages
|
|
173
|
+
averages = calculate_segment_averages(segments)
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
"total_stories": total_stories,
|
|
177
|
+
"segmented_stories": segmented_count,
|
|
178
|
+
"excluded_stories": excluded_stories,
|
|
179
|
+
"segments": segments,
|
|
180
|
+
"averages_by_segment": averages,
|
|
181
|
+
}
|