htmlgraph 0.26.25__py3-none-any.whl → 0.27.1__py3-none-any.whl

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.
Files changed (175) hide show
  1. htmlgraph/__init__.py +23 -1
  2. htmlgraph/__init__.pyi +123 -0
  3. htmlgraph/agent_registry.py +2 -1
  4. htmlgraph/analytics/cli.py +3 -3
  5. htmlgraph/analytics/cost_analyzer.py +5 -1
  6. htmlgraph/analytics/cost_monitor.py +664 -0
  7. htmlgraph/analytics/cross_session.py +13 -9
  8. htmlgraph/analytics/dependency.py +10 -6
  9. htmlgraph/analytics/strategic/__init__.py +80 -0
  10. htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
  11. htmlgraph/analytics/strategic/pattern_detector.py +876 -0
  12. htmlgraph/analytics/strategic/preference_manager.py +709 -0
  13. htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
  14. htmlgraph/analytics/work_type.py +15 -11
  15. htmlgraph/analytics_index.py +2 -1
  16. htmlgraph/api/cost_alerts_websocket.py +416 -0
  17. htmlgraph/api/main.py +167 -62
  18. htmlgraph/api/websocket.py +538 -0
  19. htmlgraph/attribute_index.py +2 -1
  20. htmlgraph/builders/base.py +2 -1
  21. htmlgraph/builders/bug.py +2 -1
  22. htmlgraph/builders/chore.py +2 -1
  23. htmlgraph/builders/epic.py +2 -1
  24. htmlgraph/builders/feature.py +2 -1
  25. htmlgraph/builders/insight.py +2 -1
  26. htmlgraph/builders/metric.py +2 -1
  27. htmlgraph/builders/pattern.py +2 -1
  28. htmlgraph/builders/phase.py +2 -1
  29. htmlgraph/builders/spike.py +2 -1
  30. htmlgraph/builders/track.py +2 -1
  31. htmlgraph/cli/analytics.py +2 -1
  32. htmlgraph/cli/base.py +2 -1
  33. htmlgraph/cli/core.py +2 -1
  34. htmlgraph/cli/main.py +2 -1
  35. htmlgraph/cli/models.py +2 -1
  36. htmlgraph/cli/templates/cost_dashboard.py +2 -1
  37. htmlgraph/cli/work/__init__.py +2 -1
  38. htmlgraph/cli/work/browse.py +2 -1
  39. htmlgraph/cli/work/features.py +2 -1
  40. htmlgraph/cli/work/orchestration.py +2 -1
  41. htmlgraph/cli/work/report.py +2 -1
  42. htmlgraph/cli/work/sessions.py +2 -1
  43. htmlgraph/cli/work/snapshot.py +2 -1
  44. htmlgraph/cli/work/tracks.py +2 -1
  45. htmlgraph/collections/base.py +10 -5
  46. htmlgraph/collections/bug.py +2 -1
  47. htmlgraph/collections/chore.py +2 -1
  48. htmlgraph/collections/epic.py +2 -1
  49. htmlgraph/collections/feature.py +2 -1
  50. htmlgraph/collections/insight.py +2 -1
  51. htmlgraph/collections/metric.py +2 -1
  52. htmlgraph/collections/pattern.py +2 -1
  53. htmlgraph/collections/phase.py +2 -1
  54. htmlgraph/collections/session.py +12 -7
  55. htmlgraph/collections/spike.py +6 -1
  56. htmlgraph/collections/task_delegation.py +7 -2
  57. htmlgraph/collections/todo.py +2 -1
  58. htmlgraph/collections/traces.py +15 -10
  59. htmlgraph/config/cost_models.json +56 -0
  60. htmlgraph/context_analytics.py +2 -1
  61. htmlgraph/db/schema.py +67 -6
  62. htmlgraph/dependency_models.py +2 -1
  63. htmlgraph/edge_index.py +2 -1
  64. htmlgraph/event_log.py +83 -64
  65. htmlgraph/event_migration.py +2 -1
  66. htmlgraph/file_watcher.py +12 -8
  67. htmlgraph/find_api.py +2 -1
  68. htmlgraph/git_events.py +6 -2
  69. htmlgraph/hooks/cigs_pretool_enforcer.py +5 -1
  70. htmlgraph/hooks/drift_handler.py +3 -3
  71. htmlgraph/hooks/event_tracker.py +40 -61
  72. htmlgraph/hooks/installer.py +5 -1
  73. htmlgraph/hooks/orchestrator.py +4 -0
  74. htmlgraph/hooks/orchestrator_reflector.py +4 -0
  75. htmlgraph/hooks/post_tool_use_failure.py +7 -3
  76. htmlgraph/hooks/posttooluse.py +4 -0
  77. htmlgraph/hooks/prompt_analyzer.py +5 -5
  78. htmlgraph/hooks/session_handler.py +2 -1
  79. htmlgraph/hooks/session_summary.py +6 -2
  80. htmlgraph/hooks/validator.py +8 -4
  81. htmlgraph/ids.py +2 -1
  82. htmlgraph/learning.py +2 -1
  83. htmlgraph/mcp_server.py +2 -1
  84. htmlgraph/operations/analytics.py +2 -1
  85. htmlgraph/operations/bootstrap.py +2 -1
  86. htmlgraph/operations/events.py +2 -1
  87. htmlgraph/operations/fastapi_server.py +2 -1
  88. htmlgraph/operations/hooks.py +2 -1
  89. htmlgraph/operations/initialization.py +2 -1
  90. htmlgraph/operations/server.py +2 -1
  91. htmlgraph/orchestration/claude_launcher.py +23 -20
  92. htmlgraph/orchestration/command_builder.py +2 -1
  93. htmlgraph/orchestration/headless_spawner.py +6 -2
  94. htmlgraph/orchestration/model_selection.py +7 -3
  95. htmlgraph/orchestration/plugin_manager.py +24 -19
  96. htmlgraph/orchestration/spawners/claude.py +5 -2
  97. htmlgraph/orchestration/spawners/codex.py +12 -19
  98. htmlgraph/orchestration/spawners/copilot.py +13 -18
  99. htmlgraph/orchestration/spawners/gemini.py +12 -19
  100. htmlgraph/orchestration/subprocess_runner.py +6 -3
  101. htmlgraph/orchestration/task_coordination.py +16 -8
  102. htmlgraph/orchestrator.py +2 -1
  103. htmlgraph/parallel.py +2 -1
  104. htmlgraph/query_builder.py +2 -1
  105. htmlgraph/reflection.py +2 -1
  106. htmlgraph/refs.py +2 -1
  107. htmlgraph/repo_hash.py +2 -1
  108. htmlgraph/repositories/__init__.py +292 -0
  109. htmlgraph/repositories/analytics_repository.py +455 -0
  110. htmlgraph/repositories/analytics_repository_standard.py +628 -0
  111. htmlgraph/repositories/feature_repository.py +581 -0
  112. htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
  113. htmlgraph/repositories/feature_repository_memory.py +607 -0
  114. htmlgraph/repositories/feature_repository_sqlite.py +858 -0
  115. htmlgraph/repositories/filter_service.py +620 -0
  116. htmlgraph/repositories/filter_service_standard.py +445 -0
  117. htmlgraph/repositories/shared_cache.py +621 -0
  118. htmlgraph/repositories/shared_cache_memory.py +395 -0
  119. htmlgraph/repositories/track_repository.py +552 -0
  120. htmlgraph/repositories/track_repository_htmlfile.py +619 -0
  121. htmlgraph/repositories/track_repository_memory.py +508 -0
  122. htmlgraph/repositories/track_repository_sqlite.py +711 -0
  123. htmlgraph/sdk/__init__.py +398 -0
  124. htmlgraph/sdk/__init__.pyi +14 -0
  125. htmlgraph/sdk/analytics/__init__.py +19 -0
  126. htmlgraph/sdk/analytics/engine.py +155 -0
  127. htmlgraph/sdk/analytics/helpers.py +178 -0
  128. htmlgraph/sdk/analytics/registry.py +109 -0
  129. htmlgraph/sdk/base.py +484 -0
  130. htmlgraph/sdk/constants.py +216 -0
  131. htmlgraph/sdk/core.pyi +308 -0
  132. htmlgraph/sdk/discovery.py +120 -0
  133. htmlgraph/sdk/help/__init__.py +12 -0
  134. htmlgraph/sdk/help/mixin.py +699 -0
  135. htmlgraph/sdk/mixins/__init__.py +15 -0
  136. htmlgraph/sdk/mixins/attribution.py +113 -0
  137. htmlgraph/sdk/mixins/mixin.py +410 -0
  138. htmlgraph/sdk/operations/__init__.py +12 -0
  139. htmlgraph/sdk/operations/mixin.py +427 -0
  140. htmlgraph/sdk/orchestration/__init__.py +17 -0
  141. htmlgraph/sdk/orchestration/coordinator.py +203 -0
  142. htmlgraph/sdk/orchestration/spawner.py +204 -0
  143. htmlgraph/sdk/planning/__init__.py +19 -0
  144. htmlgraph/sdk/planning/bottlenecks.py +93 -0
  145. htmlgraph/sdk/planning/mixin.py +211 -0
  146. htmlgraph/sdk/planning/parallel.py +186 -0
  147. htmlgraph/sdk/planning/queue.py +210 -0
  148. htmlgraph/sdk/planning/recommendations.py +87 -0
  149. htmlgraph/sdk/planning/smart_planning.py +319 -0
  150. htmlgraph/sdk/session/__init__.py +19 -0
  151. htmlgraph/sdk/session/continuity.py +57 -0
  152. htmlgraph/sdk/session/handoff.py +110 -0
  153. htmlgraph/sdk/session/info.py +309 -0
  154. htmlgraph/sdk/session/manager.py +103 -0
  155. htmlgraph/sdk/strategic/__init__.py +26 -0
  156. htmlgraph/sdk/strategic/mixin.py +563 -0
  157. htmlgraph/server.py +21 -17
  158. htmlgraph/session_warning.py +2 -1
  159. htmlgraph/sessions/handoff.py +4 -3
  160. htmlgraph/system_prompts.py +2 -1
  161. htmlgraph/track_builder.py +2 -1
  162. htmlgraph/transcript.py +2 -1
  163. htmlgraph/watch.py +2 -1
  164. htmlgraph/work_type_utils.py +2 -1
  165. {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.1.dist-info}/METADATA +1 -1
  166. htmlgraph-0.27.1.dist-info/RECORD +332 -0
  167. htmlgraph/sdk.py +0 -3500
  168. htmlgraph-0.26.25.dist-info/RECORD +0 -274
  169. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/dashboard.html +0 -0
  170. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/styles.css +0 -0
  171. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
  172. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
  173. {htmlgraph-0.26.25.data → htmlgraph-0.27.1.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
  174. {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.1.dist-info}/WHEEL +0 -0
  175. {htmlgraph-0.26.25.dist-info → htmlgraph-0.27.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,455 @@
1
+ """
2
+ AnalyticsRepository - Abstract interface for analytics and work recommendations.
3
+
4
+ Unifies all analytics patterns across HtmlGraph:
5
+ - Work item recommendations and prioritization
6
+ - Dependency analysis and critical path detection
7
+ - Feature/track health metrics
8
+ - Blocked item detection and resolution tracking
9
+
10
+ Implementations handle:
11
+ - Feature and Track repository integration (no direct data access)
12
+ - Multi-criteria recommendation scoring
13
+ - Transitive dependency analysis
14
+ - Centralized metrics caching
15
+ - Concurrent query safety
16
+
17
+ All implementations MUST pass AnalyticsRepositoryComplianceTests.
18
+ """
19
+
20
+ from abc import ABC, abstractmethod
21
+ from dataclasses import dataclass
22
+ from typing import Any
23
+
24
+
25
+ @dataclass
26
+ class DependencyAnalysis:
27
+ """
28
+ Complete dependency analysis for a single work item.
29
+
30
+ Contains transitive closure of dependencies, blocking items,
31
+ and critical path information.
32
+ """
33
+
34
+ item_id: str
35
+ dependencies: list[str] # All transitive dependencies
36
+ blocking: list[str] # Items blocked by this one
37
+ blocked_by: list[str] # Items blocking this one
38
+ critical_path: bool # Whether on critical path
39
+ blocked_count: int # Count of items blocked by this
40
+ dependency_count: int # Count of dependencies
41
+
42
+ @property
43
+ def is_blocked(self) -> bool:
44
+ """True if any dependencies are incomplete."""
45
+ return len(self.blocked_by) > 0
46
+
47
+ @property
48
+ def is_blocking_others(self) -> bool:
49
+ """True if any items depend on this one."""
50
+ return len(self.blocking) > 0
51
+
52
+
53
+ @dataclass
54
+ class WorkRecommendation:
55
+ """
56
+ Recommended work item with priority and reasoning.
57
+ """
58
+
59
+ item_id: str
60
+ title: str
61
+ priority_score: float # 0-1 normalized score
62
+ rationale: str # Human-readable explanation
63
+ estimated_impact: float # Expected impact on project (0-1)
64
+ blocking_count: int # How many items it unblocks
65
+ dependency_count: int # How many deps must complete first
66
+
67
+
68
+ class AnalyticsRepositoryError(Exception):
69
+ """Base exception for analytics operations."""
70
+
71
+ pass
72
+
73
+
74
+ class AnalysisError(AnalyticsRepositoryError):
75
+ """Raised when analysis cannot be computed."""
76
+
77
+ pass
78
+
79
+
80
+ class InvalidItemError(AnalyticsRepositoryError):
81
+ """Raised when item is not found or invalid."""
82
+
83
+ def __init__(self, item_id: str):
84
+ self.item_id = item_id
85
+ super().__init__(f"Invalid or not found: {item_id}")
86
+
87
+
88
+ class AnalyticsRepository(ABC):
89
+ """
90
+ Abstract interface for work item analytics and recommendations.
91
+
92
+ Uses Feature and Track repositories internally to build unified
93
+ analytics view across all work items. No direct data access.
94
+
95
+ CONTRACT:
96
+ 1. **Data Consistency**: All recommendations based on current repo state
97
+ 2. **Accuracy**: Dependency analysis correctly computes transitive closure
98
+ 3. **Performance**: Heavy computations cached, invalidated on data changes
99
+ 4. **Isolation**: Multiple concurrent recommendations don't interfere
100
+ 5. **Error Handling**: Analysis failures include full context
101
+
102
+ CACHING BEHAVIOR:
103
+ - Recommendations cached by item_id
104
+ - Dependencies cached and invalidated on feature/track changes
105
+ - Metrics cached with optional TTL
106
+ - Cache statistics available for monitoring
107
+
108
+ PERFORMANCE:
109
+ - recommend_next_work(filters): O(n) with caching
110
+ - analyze_dependencies(item_id): O(n) graph traversal, cached
111
+ - calculate_priority(item_id): O(1) if cached, O(n) if not
112
+ - get_work_items(status): O(n) with early termination
113
+ - get_critical_path(): O(n) computed once per session
114
+ - find_blocked_items(): O(n) with caching
115
+ - find_blocking_items(item_id): O(1) lookup
116
+
117
+ THREAD SAFETY:
118
+ - Implementations should be thread-safe
119
+ - Concurrent reads allowed
120
+ - Concurrent analytics queries serialized if needed
121
+ """
122
+
123
+ # ===== RECOMMENDATION OPERATIONS =====
124
+
125
+ @abstractmethod
126
+ def recommend_next_work(
127
+ self,
128
+ filters: dict[str, Any] | None = None,
129
+ limit: int = 10,
130
+ min_priority: float = 0.0,
131
+ ) -> list[WorkRecommendation]:
132
+ """
133
+ Get prioritized recommendations for next work items.
134
+
135
+ Uses multi-criteria scoring:
136
+ - Item dependencies (prefer unblocking others)
137
+ - Priority level
138
+ - Impact on project timeline
139
+ - Team capacity
140
+ - Business value
141
+
142
+ Args:
143
+ filters: Optional filters (status, priority, assigned_to, etc.)
144
+ limit: Max recommendations to return (default 10)
145
+ min_priority: Minimum priority score threshold (0-1)
146
+
147
+ Returns:
148
+ List of WorkRecommendation objects sorted by priority
149
+
150
+ Raises:
151
+ AnalysisError: If scoring computation fails
152
+
153
+ Performance: O(n) with caching
154
+
155
+ Examples:
156
+ >>> recs = repo.recommend_next_work(filters={"status": "todo"})
157
+ >>> assert len(recs) <= 10
158
+ >>> assert all(r.priority_score >= 0.0 for r in recs)
159
+ >>> best = recs[0]
160
+ >>> print(f"Do {best.item_id}: {best.rationale}")
161
+ """
162
+ ...
163
+
164
+ @abstractmethod
165
+ def analyze_dependencies(self, item_id: str) -> DependencyAnalysis:
166
+ """
167
+ Compute complete dependency analysis for a work item.
168
+
169
+ Analyzes:
170
+ - All transitive dependencies (things this must wait for)
171
+ - All blocking items (things waiting on this)
172
+ - Critical path status
173
+ - Blocked/blocking counts
174
+
175
+ Args:
176
+ item_id: Item to analyze (feature or track)
177
+
178
+ Returns:
179
+ DependencyAnalysis with complete graph information
180
+
181
+ Raises:
182
+ InvalidItemError: If item not found
183
+ AnalysisError: If graph traversal fails
184
+
185
+ Performance: O(n) graph traversal, cached
186
+
187
+ Examples:
188
+ >>> analysis = repo.analyze_dependencies("feat-auth")
189
+ >>> if analysis.is_blocked:
190
+ ... print(f"Blocked by: {analysis.blocked_by}")
191
+ >>> print(f"Unblocks {analysis.blocking_count} items")
192
+ >>> if analysis.critical_path:
193
+ ... print("On critical path!")
194
+ """
195
+ ...
196
+
197
+ @abstractmethod
198
+ def calculate_priority(self, item_id: str) -> float:
199
+ """
200
+ Calculate normalized priority score for item.
201
+
202
+ Score 0-1 based on:
203
+ - Item's explicit priority level
204
+ - How many other items depend on it
205
+ - Position on critical path
206
+ - Business value weight
207
+
208
+ Args:
209
+ item_id: Item to score
210
+
211
+ Returns:
212
+ Priority score 0.0-1.0 (higher = more important)
213
+
214
+ Raises:
215
+ InvalidItemError: If item not found
216
+ AnalysisError: If scoring fails
217
+
218
+ Performance: O(1) if cached, O(n) if not
219
+
220
+ Examples:
221
+ >>> score = repo.calculate_priority("feat-001")
222
+ >>> assert 0.0 <= score <= 1.0
223
+ >>> if score > 0.8:
224
+ ... print("This is critical!")
225
+ """
226
+ ...
227
+
228
+ # ===== WORK ITEM QUERIES =====
229
+
230
+ @abstractmethod
231
+ def get_work_items(
232
+ self, status: str | None = None, include_tracks: bool = True
233
+ ) -> list[Any]:
234
+ """
235
+ Get all work items (features and optionally tracks).
236
+
237
+ Args:
238
+ status: Filter by status (e.g., 'todo', 'in-progress', 'done')
239
+ include_tracks: If True, include both features and tracks
240
+
241
+ Returns:
242
+ List of work item objects (Features or Tracks)
243
+
244
+ Raises:
245
+ ValueError: If status is invalid
246
+
247
+ Performance: O(n) with early termination
248
+
249
+ Examples:
250
+ >>> todo_items = repo.get_work_items(status="todo")
251
+ >>> all_items = repo.get_work_items()
252
+ >>> assert len(all_items) > 0
253
+ """
254
+ ...
255
+
256
+ @abstractmethod
257
+ def find_blocked_items(self) -> list[str]:
258
+ """
259
+ Find all work items currently blocked by incomplete dependencies.
260
+
261
+ Returns item IDs that cannot proceed until dependencies complete.
262
+
263
+ Returns:
264
+ List of blocked item IDs
265
+
266
+ Performance: O(n) with caching
267
+
268
+ Examples:
269
+ >>> blocked = repo.find_blocked_items()
270
+ >>> for item_id in blocked:
271
+ ... analysis = repo.analyze_dependencies(item_id)
272
+ ... print(f"{item_id} blocked by {analysis.blocked_by}")
273
+ """
274
+ ...
275
+
276
+ @abstractmethod
277
+ def find_blocking_items(self, item_id: str) -> list[str]:
278
+ """
279
+ Find what items are blocked by the given item.
280
+
281
+ Inverse of dependencies: returns items that depend ON this item.
282
+
283
+ Args:
284
+ item_id: Item to find blockers for
285
+
286
+ Returns:
287
+ List of item IDs blocked by given item
288
+
289
+ Raises:
290
+ InvalidItemError: If item not found
291
+
292
+ Performance: O(1) lookup
293
+
294
+ Examples:
295
+ >>> blocking = repo.find_blocking_items("feat-database")
296
+ >>> print(f"Completing this unblocks {len(blocking)} items")
297
+ """
298
+ ...
299
+
300
+ # ===== CRITICAL PATH ANALYSIS =====
301
+
302
+ @abstractmethod
303
+ def get_critical_path(self) -> list[str]:
304
+ """
305
+ Get items on the critical path to project completion.
306
+
307
+ Critical path = longest chain of dependent items that determines
308
+ minimum time to complete project.
309
+
310
+ Returns:
311
+ List of item IDs on critical path (in dependency order)
312
+
313
+ Raises:
314
+ AnalysisError: If critical path cannot be computed
315
+
316
+ Performance: O(n) computed once per session
317
+
318
+ Examples:
319
+ >>> path = repo.get_critical_path()
320
+ >>> print(f"Critical path has {len(path)} items")
321
+ >>> for item_id in path:
322
+ ... item = repo.get_item(item_id)
323
+ ... print(f" {item.title}")
324
+ """
325
+ ...
326
+
327
+ @abstractmethod
328
+ def is_on_critical_path(self, item_id: str) -> bool:
329
+ """
330
+ Check if item is on critical path.
331
+
332
+ Args:
333
+ item_id: Item to check
334
+
335
+ Returns:
336
+ True if on critical path, False otherwise
337
+
338
+ Raises:
339
+ InvalidItemError: If item not found
340
+
341
+ Performance: O(1) with cached critical path
342
+
343
+ Examples:
344
+ >>> if repo.is_on_critical_path("feat-auth"):
345
+ ... print("This is blocking project completion!")
346
+ """
347
+ ...
348
+
349
+ # ===== METRICS & HEALTH =====
350
+
351
+ @abstractmethod
352
+ def cache_metrics(self) -> dict[str, Any]:
353
+ """
354
+ Get cache performance metrics.
355
+
356
+ Returns statistics about cache efficiency:
357
+ - Hit count and rate
358
+ - Miss count
359
+ - Eviction count
360
+ - Average load time
361
+ - Memory usage
362
+
363
+ Returns:
364
+ Dict with metrics (keys: hits, misses, rate, memory_kb, etc.)
365
+
366
+ Examples:
367
+ >>> metrics = repo.cache_metrics()
368
+ >>> print(f"Cache hit rate: {metrics['hit_rate']:.1%}")
369
+ >>> print(f"Memory: {metrics['memory_kb']} KB")
370
+ """
371
+ ...
372
+
373
+ @abstractmethod
374
+ def invalidate_analytics_cache(self, item_id: str | None = None) -> None:
375
+ """
376
+ Invalidate cached analytics for item or all items.
377
+
378
+ Called when underlying data changes:
379
+ - When feature/track status changes
380
+ - When dependencies added/removed
381
+ - When explicit refresh needed
382
+
383
+ Args:
384
+ item_id: Specific item to invalidate, or None for all
385
+
386
+ Examples:
387
+ >>> repo.invalidate_analytics_cache("feat-001") # Single item
388
+ >>> repo.invalidate_analytics_cache() # Clear all analytics cache
389
+ """
390
+ ...
391
+
392
+ # ===== ADVANCED QUERIES =====
393
+
394
+ @abstractmethod
395
+ def find_dependency_cycles(self) -> list[list[str]]:
396
+ """
397
+ Find any circular dependencies in the project graph.
398
+
399
+ Returns:
400
+ List of cycles, each cycle is list of item IDs forming loop
401
+
402
+ Returns empty list if no cycles found.
403
+
404
+ Performance: O(n) graph traversal
405
+
406
+ Examples:
407
+ >>> cycles = repo.find_dependency_cycles()
408
+ >>> if cycles:
409
+ ... for cycle in cycles:
410
+ ... print(f"Cycle detected: {' -> '.join(cycle)}")
411
+ >>> else:
412
+ ... print("No circular dependencies!")
413
+ """
414
+ ...
415
+
416
+ @abstractmethod
417
+ def suggest_parallelizable_work(self) -> list[list[str]]:
418
+ """
419
+ Suggest groups of work that can be done in parallel.
420
+
421
+ Returns:
422
+ List of groups, each group is list of item IDs with no dependencies
423
+
424
+ Useful for team coordination and optimal scheduling.
425
+
426
+ Performance: O(n)
427
+
428
+ Examples:
429
+ >>> groups = repo.suggest_parallelizable_work()
430
+ >>> for i, group in enumerate(groups):
431
+ ... print(f"Parallel wave {i+1}: {group}")
432
+ """
433
+ ...
434
+
435
+ @abstractmethod
436
+ def project_completion_estimate(self) -> dict[str, Any]:
437
+ """
438
+ Estimate project completion time based on current state.
439
+
440
+ Returns:
441
+ Dict with estimates:
442
+ - items_remaining: count of incomplete items
443
+ - critical_path_length: items on critical path
444
+ - estimated_days: days to completion (based on team velocity)
445
+ - blocking_items: count of items blocking others
446
+ - worst_case_days: pessimistic estimate
447
+
448
+ Performance: O(n)
449
+
450
+ Examples:
451
+ >>> estimate = repo.project_completion_estimate()
452
+ >>> print(f"Estimated completion: {estimate['estimated_days']} days")
453
+ >>> print(f"Critical path: {estimate['critical_path_length']} items")
454
+ """
455
+ ...