devsquad 3.6.0__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 (95) hide show
  1. devsquad-3.6.0.dist-info/METADATA +944 -0
  2. devsquad-3.6.0.dist-info/RECORD +95 -0
  3. devsquad-3.6.0.dist-info/WHEEL +5 -0
  4. devsquad-3.6.0.dist-info/entry_points.txt +2 -0
  5. devsquad-3.6.0.dist-info/licenses/LICENSE +21 -0
  6. devsquad-3.6.0.dist-info/top_level.txt +2 -0
  7. scripts/__init__.py +0 -0
  8. scripts/ai_semantic_matcher.py +512 -0
  9. scripts/alert_manager.py +505 -0
  10. scripts/api/__init__.py +43 -0
  11. scripts/api/models.py +386 -0
  12. scripts/api/routes/__init__.py +20 -0
  13. scripts/api/routes/dispatch.py +348 -0
  14. scripts/api/routes/lifecycle.py +330 -0
  15. scripts/api/routes/metrics_gates.py +347 -0
  16. scripts/api_server.py +318 -0
  17. scripts/auth.py +451 -0
  18. scripts/cli/__init__.py +1 -0
  19. scripts/cli/cli_visual.py +642 -0
  20. scripts/cli.py +1094 -0
  21. scripts/collaboration/__init__.py +212 -0
  22. scripts/collaboration/_version.py +1 -0
  23. scripts/collaboration/agent_briefing.py +656 -0
  24. scripts/collaboration/ai_semantic_matcher.py +260 -0
  25. scripts/collaboration/anchor_checker.py +281 -0
  26. scripts/collaboration/anti_rationalization.py +470 -0
  27. scripts/collaboration/async_integration_example.py +255 -0
  28. scripts/collaboration/batch_scheduler.py +149 -0
  29. scripts/collaboration/checkpoint_manager.py +561 -0
  30. scripts/collaboration/ci_feedback_adapter.py +351 -0
  31. scripts/collaboration/code_map_generator.py +247 -0
  32. scripts/collaboration/concern_pack_loader.py +352 -0
  33. scripts/collaboration/confidence_score.py +496 -0
  34. scripts/collaboration/config_loader.py +188 -0
  35. scripts/collaboration/consensus.py +244 -0
  36. scripts/collaboration/context_compressor.py +533 -0
  37. scripts/collaboration/coordinator.py +668 -0
  38. scripts/collaboration/dispatcher.py +1636 -0
  39. scripts/collaboration/dual_layer_context.py +128 -0
  40. scripts/collaboration/enhanced_worker.py +539 -0
  41. scripts/collaboration/feature_usage_tracker.py +206 -0
  42. scripts/collaboration/five_axis_consensus.py +334 -0
  43. scripts/collaboration/input_validator.py +401 -0
  44. scripts/collaboration/integration_example.py +287 -0
  45. scripts/collaboration/intent_workflow_mapper.py +350 -0
  46. scripts/collaboration/language_parsers.py +269 -0
  47. scripts/collaboration/lifecycle_protocol.py +1446 -0
  48. scripts/collaboration/llm_backend.py +453 -0
  49. scripts/collaboration/llm_cache.py +448 -0
  50. scripts/collaboration/llm_cache_async.py +347 -0
  51. scripts/collaboration/llm_retry.py +387 -0
  52. scripts/collaboration/llm_retry_async.py +389 -0
  53. scripts/collaboration/mce_adapter.py +597 -0
  54. scripts/collaboration/memory_bridge.py +1607 -0
  55. scripts/collaboration/models.py +537 -0
  56. scripts/collaboration/null_providers.py +297 -0
  57. scripts/collaboration/operation_classifier.py +289 -0
  58. scripts/collaboration/output_slicer.py +225 -0
  59. scripts/collaboration/performance_monitor.py +462 -0
  60. scripts/collaboration/permission_guard.py +865 -0
  61. scripts/collaboration/prompt_assembler.py +756 -0
  62. scripts/collaboration/prompt_variant_generator.py +483 -0
  63. scripts/collaboration/protocols.py +267 -0
  64. scripts/collaboration/report_formatter.py +352 -0
  65. scripts/collaboration/retrospective.py +279 -0
  66. scripts/collaboration/role_matcher.py +92 -0
  67. scripts/collaboration/role_template_market.py +352 -0
  68. scripts/collaboration/rule_collector.py +678 -0
  69. scripts/collaboration/scratchpad.py +346 -0
  70. scripts/collaboration/skill_registry.py +151 -0
  71. scripts/collaboration/skillifier.py +878 -0
  72. scripts/collaboration/standardized_role_template.py +317 -0
  73. scripts/collaboration/task_completion_checker.py +237 -0
  74. scripts/collaboration/test_quality_guard.py +695 -0
  75. scripts/collaboration/unified_gate_engine.py +598 -0
  76. scripts/collaboration/usage_tracker.py +309 -0
  77. scripts/collaboration/user_friendly_error.py +176 -0
  78. scripts/collaboration/verification_gate.py +312 -0
  79. scripts/collaboration/warmup_manager.py +635 -0
  80. scripts/collaboration/worker.py +513 -0
  81. scripts/collaboration/workflow_engine.py +684 -0
  82. scripts/dashboard.py +1088 -0
  83. scripts/generate_benchmark_report.py +786 -0
  84. scripts/history_manager.py +604 -0
  85. scripts/mcp_server.py +289 -0
  86. skills/__init__.py +32 -0
  87. skills/dispatch/handler.py +52 -0
  88. skills/intent/handler.py +59 -0
  89. skills/registry.py +67 -0
  90. skills/retrospective/__init__.py +0 -0
  91. skills/retrospective/handler.py +125 -0
  92. skills/review/handler.py +356 -0
  93. skills/security/handler.py +454 -0
  94. skills/test/__init__.py +0 -0
  95. skills/test/handler.py +78 -0
@@ -0,0 +1,537 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ 协作系统数据模型
5
+
6
+ 定义 Coordinator + Scratchpad + Worker 协作模式的所有核心数据结构。
7
+
8
+ 设计决策(门禁条件解决):
9
+ - 门禁1: Scratchpad 并发写入 → 采用"时间戳排序+版本号"方案,简单可靠
10
+ - 门禁2: Consensus 升级 → 采用"权重投票+否决权+升级到人工"机制
11
+ - 门禁3: 存储选型 → Phase 1 采用内存+JSON文件持久化,无外部依赖
12
+ - 异常场景: 每个组件都有 timeout/retry/cancel 支持
13
+ """
14
+
15
+ from enum import Enum
16
+ from dataclasses import dataclass, field
17
+ from typing import Dict, List, Optional, Any, Set
18
+ from datetime import datetime
19
+ import uuid
20
+
21
+
22
+ class EntryType(Enum):
23
+ FINDING = "finding"
24
+ DECISION = "decision"
25
+ CONFLICT = "conflict"
26
+ QUESTION = "question"
27
+ SUGGESTION = "suggestion"
28
+ WARNING = "warning"
29
+
30
+
31
+ class EntryStatus(Enum):
32
+ ACTIVE = "active"
33
+ RESOLVED = "resolved"
34
+ SUPERSEDED = "superseded"
35
+ REJECTED = "rejected"
36
+
37
+
38
+ class ReferenceType(Enum):
39
+ SUPPORTS = "supports"
40
+ CONTRADICTS = "contradicts"
41
+ EXTENDS = "extends"
42
+ CLARIFIES = "clarifies"
43
+
44
+
45
+ @dataclass
46
+ class Reference:
47
+ reference_type: ReferenceType
48
+ target_entry_id: str
49
+ summary: str = ""
50
+
51
+
52
+ @dataclass
53
+ class ScratchpadEntry:
54
+ entry_id: str = field(default_factory=lambda: f"entry-{uuid.uuid4().hex[:12]}")
55
+ worker_id: str = ""
56
+ role_id: str = ""
57
+ timestamp: datetime = field(default_factory=datetime.now)
58
+ entry_type: EntryType = EntryType.FINDING
59
+ content: str = ""
60
+ confidence: float = 0.5
61
+ tags: List[str] = field(default_factory=list)
62
+ references: List[Reference] = field(default_factory=list)
63
+ status: EntryStatus = EntryStatus.ACTIVE
64
+ version: int = 1
65
+
66
+ def to_dict(self) -> Dict[str, Any]:
67
+ return {
68
+ "entry_id": self.entry_id,
69
+ "worker_id": self.worker_id,
70
+ "role_id": self.role_id,
71
+ "timestamp": self.timestamp.isoformat(),
72
+ "entry_type": self.entry_type.value,
73
+ "content": self.content,
74
+ "confidence": self.confidence,
75
+ "tags": self.tags,
76
+ "references": [
77
+ {"type": r.reference_type.value, "target": r.target_entry_id, "summary": r.summary}
78
+ for r in self.references
79
+ ],
80
+ "status": self.status.value,
81
+ "version": self.version,
82
+ }
83
+
84
+ @classmethod
85
+ def from_dict(cls, data: Dict[str, Any]) -> "ScratchpadEntry":
86
+ refs = [
87
+ Reference(
88
+ reference_type=ReferenceType(r["type"]),
89
+ target_entry_id=r["target"],
90
+ summary=r.get("summary", ""),
91
+ )
92
+ for r in data.get("references", [])
93
+ ]
94
+ return cls(
95
+ entry_id=data.get("entry_id", f"entry-{uuid.uuid4().hex[:12]}"),
96
+ worker_id=data.get("worker_id", ""),
97
+ role_id=data.get("role_id", ""),
98
+ timestamp=datetime.fromisoformat(data["timestamp"]) if "timestamp" in data else datetime.now(),
99
+ entry_type=EntryType(data.get("entry_type", "finding")),
100
+ content=data.get("content", ""),
101
+ confidence=data.get("confidence", 0.5),
102
+ tags=data.get("tags", []),
103
+ references=refs,
104
+ status=EntryStatus(data.get("status", "active")),
105
+ version=data.get("version", 1),
106
+ )
107
+
108
+
109
+ @dataclass
110
+ class TaskNotification:
111
+ from_worker: str
112
+ to_workers: List[str]
113
+ notification_type: str
114
+ priority: str = "medium"
115
+ timestamp: datetime = field(default_factory=datetime.now)
116
+ summary: str = ""
117
+ details: str = ""
118
+ references: List[str] = field(default_factory=list)
119
+ action_required: str = ""
120
+
121
+ def to_xml(self) -> str:
122
+ refs_xml = "".join(f"<ref>{r}</ref>" for r in self.references)
123
+ to_xml = ",".join(self.to_workers)
124
+ return (
125
+ f'<task-notification\n'
126
+ f' from-worker="{self.from_worker}"\n'
127
+ f' to-workers="{to_xml}"\n'
128
+ f' type="{self.notification_type}"\n'
129
+ f' priority="{self.priority}"\n'
130
+ f' timestamp="{self.timestamp.isoformat()}">\n'
131
+ f' <summary>{self.summary}</summary>\n'
132
+ f' <details>{self.details}</details>\n'
133
+ f' <references>{refs_xml}</references>\n'
134
+ f' <action-required>{self.action_required}</action-required>\n'
135
+ f'</task-notification>'
136
+ )
137
+
138
+
139
+ @dataclass
140
+ class TaskDefinition:
141
+ task_id: str = field(default_factory=lambda: f"task-{uuid.uuid4().hex[:8]}")
142
+ description: str = ""
143
+ role_id: str = ""
144
+ role_prompt: str = ""
145
+ stage_id: Optional[str] = None
146
+ input_data: Dict[str, Any] = field(default_factory=dict)
147
+ dependencies: List[str] = field(default_factory=list)
148
+ is_read_only: bool = True
149
+ timeout_seconds: int = 300
150
+ retry_count: int = 3
151
+
152
+
153
+ @dataclass
154
+ class WorkerResult:
155
+ worker_id: str
156
+ task_id: str
157
+ success: bool
158
+ output: Any = None
159
+ error: Optional[str] = None
160
+ scratchpad_entries_written: int = 0
161
+ notifications_sent: int = 0
162
+ duration_seconds: float = 0.0
163
+
164
+
165
+ @dataclass
166
+ class Vote:
167
+ voter_id: str
168
+ voter_role: str
169
+ decision: bool
170
+ reason: str = ""
171
+ weight: float = 1.0
172
+ confidence: float = 0.7
173
+ timestamp: datetime = field(default_factory=datetime.now)
174
+
175
+
176
+ @dataclass
177
+ class DecisionProposal:
178
+ proposal_id: str = field(default_factory=lambda: f"prop-{uuid.uuid4().hex[:8]}")
179
+ topic: str = ""
180
+ proposer_id: str = ""
181
+ proposal_content: str = ""
182
+ options: List[str] = field(default_factory=list)
183
+ deadline: Optional[datetime] = None
184
+ votes: List[Vote] = field(default_factory=list)
185
+ status: str = "open"
186
+
187
+
188
+ class DecisionOutcome(Enum):
189
+ APPROVED = "approved"
190
+ REJECTED = "rejected"
191
+ SPLIT = "split"
192
+ ESCALATED = "escalated"
193
+ TIMEOUT = "timeout"
194
+
195
+
196
+ @dataclass
197
+ class ConsensusRecord:
198
+ record_id: str = field(default_factory=lambda: f"consensus-{uuid.uuid4().hex[:8]}")
199
+ topic: str = ""
200
+ outcome: DecisionOutcome = DecisionOutcome.APPROVED
201
+ final_decision: str = ""
202
+ votes_for: int = 0
203
+ votes_against: int = 0
204
+ votes_abstain: int = 0
205
+ total_weight_for: float = 0.0
206
+ total_weight_against: float = 0.0
207
+ participants: List[str] = field(default_factory=list)
208
+ escalation_reason: Optional[str] = None
209
+ timestamp: datetime = field(default_factory=datetime.now)
210
+
211
+
212
+ @dataclass
213
+ class ExecutionPlan:
214
+ plan_id: str = field(default_factory=lambda: f"plan-{uuid.uuid4().hex[:8]}")
215
+ batches: List[Any] = field(default_factory=list)
216
+ total_tasks: int = 0
217
+ estimated_parallelism: float = 0.0
218
+
219
+
220
+ class BatchMode(Enum):
221
+ PARALLEL = "parallel"
222
+ SERIAL = "serial"
223
+
224
+
225
+ @dataclass
226
+ class TaskBatch:
227
+ batch_id: str = field(default_factory=lambda: f"batch-{uuid.uuid4().hex[:8]}")
228
+ mode: BatchMode = BatchMode.PARALLEL
229
+ tasks: List[TaskDefinition] = field(default_factory=list)
230
+ max_concurrency: int = 5
231
+ dependencies: List[str] = field(default_factory=list)
232
+ timeout_seconds: int = 600
233
+
234
+
235
+ @dataclass
236
+ class ScheduleResult:
237
+ success: bool = False
238
+ total_tasks: int = 0
239
+ completed_tasks: int = 0
240
+ failed_tasks: int = 0
241
+ results: List[WorkerResult] = field(default_factory=list)
242
+ duration_seconds: float = 0.0
243
+ errors: List[str] = field(default_factory=list)
244
+
245
+
246
+ ROLE_WEIGHTS = {
247
+ "architect": 1.5,
248
+ "product-manager": 1.2,
249
+ "security": 1.1,
250
+ "tester": 1.0,
251
+ "solo-coder": 1.0,
252
+ "devops": 1.0,
253
+ "ui-designer": 0.9,
254
+ }
255
+
256
+
257
+ @dataclass
258
+ class RoleDefinition:
259
+ role_id: str
260
+ name: str
261
+ aliases: List[str]
262
+ prompt: str
263
+ keywords: List[str]
264
+ weight: float
265
+ description: str
266
+ status: str = "core"
267
+
268
+
269
+ ROLE_REGISTRY: Dict[str, RoleDefinition] = {
270
+ "architect": RoleDefinition(
271
+ role_id="architect",
272
+ name="架构师",
273
+ aliases=["arch"],
274
+ prompt="你是系统架构师。负责:\n1. 系统架构设计(分层、模块化、接口定义)\n2. 技术选型和评估\n3. 性能架构设计(缓存架构、CDN策略、分库分表方案)\n4. 安全架构设计(认证授权方案、加密策略、安全边界)\n5. 数据架构设计(数据模型、数据仓库架构、ETL架构)\n6. 输出:架构文档、技术方案、模块设计",
275
+ keywords=["架构", "设计", "选型", "性能", "模块", "接口", "微服务", "数据架构",
276
+ "architecture", "design", "microservice", "module", "interface", "performance", "scalability", "system"],
277
+ weight=1.5,
278
+ description="System design, tech stack, API design, performance/security/data architecture",
279
+ status="core",
280
+ ),
281
+ "product-manager": RoleDefinition(
282
+ role_id="product-manager",
283
+ name="产品经理",
284
+ aliases=["pm"],
285
+ prompt="你是产品经理。负责:\n1. 需求分析和PRD编写\n2. 用户故事和验收标准\n3. 竞品分析\n4. 输出:需求文档、用户故事、功能规格",
286
+ keywords=["需求", "PRD", "用户故事", "竞品", "验收", "体验", "功能",
287
+ "requirement", "prd", "user story", "acceptance", "feature", "product", "specification"],
288
+ weight=1.2,
289
+ description="Requirements analysis, user stories, acceptance criteria",
290
+ status="core",
291
+ ),
292
+ "tester": RoleDefinition(
293
+ role_id="tester",
294
+ name="测试专家",
295
+ aliases=["test", "qa"],
296
+ prompt="你是测试专家。负责:\n1. 测试策略和用例设计\n2. 自动化测试方案\n3. 质量评估和缺陷追踪\n4. 输出:测试计划、测试用例、质量报告",
297
+ keywords=["测试", "质量", "验收", "自动化", "性能测试", "缺陷", "门禁",
298
+ "test", "quality", "qa", "automated", "coverage", "bug", "validation"],
299
+ weight=1.0,
300
+ description="Test strategy, quality assurance, edge cases",
301
+ status="core",
302
+ ),
303
+ "solo-coder": RoleDefinition(
304
+ role_id="solo-coder",
305
+ name="独立开发者",
306
+ aliases=["coder", "dev"],
307
+ prompt="你是全栈开发者。负责:\n1. 功能实现和代码编写\n2. 代码审查与质量把关(风格一致性、最佳实践、设计模式合规)\n3. 性能优化实现(算法优化、内存优化、并发优化、SQL调优)\n4. 代码重构和优化\n5. Bug修复\n6. 数据迁移实现\n7. 输出:源代码、测试、技术文档",
308
+ keywords=["实现", "开发", "代码", "修复", "优化", "重构", "审查", "最佳实践",
309
+ "implement", "develop", "code", "fix", "optimize", "refactor", "review", "debug"],
310
+ weight=1.0,
311
+ description="Implementation, code review, performance optimization, refactoring",
312
+ status="core",
313
+ ),
314
+ "ui-designer": RoleDefinition(
315
+ role_id="ui-designer",
316
+ name="UI设计师",
317
+ aliases=["ui"],
318
+ prompt="你是UI/UX设计师。负责:\n1. 界面设计和交互原型\n2. 设计系统和组件规范\n3. 视觉稿和设计交付\n4. 输出:设计稿、原型、设计规范",
319
+ keywords=["UI", "界面", "前端", "视觉", "交互", "原型", "设计",
320
+ "ui", "interface", "frontend", "visual", "interaction", "prototype", "ux", "accessibility"],
321
+ weight=0.9,
322
+ description="UX design, interaction logic, accessibility",
323
+ status="core",
324
+ ),
325
+ "devops": RoleDefinition(
326
+ role_id="devops",
327
+ name="DevOps工程师",
328
+ aliases=["infra"],
329
+ prompt="你是DevOps工程师。负责:\n1. CI/CD流水线设计与实现(GitHub Actions、GitLab CI、Jenkins)\n2. 容器化与编排(Docker、Kubernetes、Docker Compose)\n3. 基础设施即代码(Terraform、Pulumi、CloudFormation)\n4. 监控告警体系搭建(Prometheus、Grafana、ELK、Sentry)\n5. 部署策略设计(蓝绿部署、金丝雀发布、滚动更新)\n6. 环境管理(开发/测试/预生产/生产环境配置与隔离)\n7. 输出:CI/CD配置、Dockerfile、K8s Manifests、监控配置、部署文档",
330
+ keywords=["CI/CD", "部署", "监控", "运维", "Docker", "Kubernetes", "基础设施", "容器",
331
+ "deploy", "monitor", "infrastructure", "container", "pipeline", "devops", "ci/cd", "cloud"],
332
+ weight=1.0,
333
+ description="CI/CD pipeline, containerization, monitoring, infrastructure",
334
+ status="core",
335
+ ),
336
+ "security": RoleDefinition(
337
+ role_id="security",
338
+ name="安全专家",
339
+ aliases=["sec"],
340
+ prompt="你是安全专家。负责:\n1. 威胁建模(STRIDE、DREAD攻击树分析)\n2. 漏洞审计(OWASP Top 10、CWE常见弱点枚举)\n3. 认证与授权安全审查(OAuth2、JWT、RBAC/ABAC)\n4. 数据安全评估(加密方案、密钥管理、数据脱敏)\n5. 依赖安全扫描与供应链安全(Snyk、Dependabot、SBOM)\n6. 合规性检查(GDPR、SOC2、HIPAA、PCI-DSS)\n7. 安全编码规范与最佳实践\n8. 输出:威胁模型、漏洞报告、安全建议、合规评估",
341
+ keywords=["安全", "漏洞", "审计", "威胁", "加密", "认证", "授权", "OWASP",
342
+ "security", "vulnerability", "audit", "threat", "encrypt", "auth", "compliance", "owasp"],
343
+ weight=1.1,
344
+ description="Threat modeling, vulnerability audit, compliance, security review",
345
+ status="core",
346
+ ),
347
+ }
348
+
349
+
350
+ def _build_role_aliases() -> Dict[str, str]:
351
+ aliases = {}
352
+ for rid, rdef in ROLE_REGISTRY.items():
353
+ for alias in rdef.aliases:
354
+ aliases[alias] = rid
355
+ return aliases
356
+
357
+
358
+ ROLE_ALIASES: Dict[str, str] = _build_role_aliases()
359
+
360
+
361
+ def resolve_role_id(role_id: str) -> str:
362
+ if role_id in ROLE_REGISTRY:
363
+ return role_id
364
+ return ROLE_ALIASES.get(role_id, role_id)
365
+
366
+
367
+ def get_core_roles() -> Dict[str, RoleDefinition]:
368
+ return {rid: rdef for rid, rdef in ROLE_REGISTRY.items() if rdef.status == "core"}
369
+
370
+
371
+ def get_planned_roles() -> Dict[str, RoleDefinition]:
372
+ return {rid: rdef for rid, rdef in ROLE_REGISTRY.items() if rdef.status == "planned"}
373
+
374
+
375
+ def get_all_role_ids() -> List[str]:
376
+ return list(ROLE_REGISTRY.keys())
377
+
378
+
379
+ def get_cli_role_list() -> List[str]:
380
+ result = []
381
+ for rid, rdef in ROLE_REGISTRY.items():
382
+ result.append(rdef.aliases[0] if rdef.aliases else rid)
383
+ return result
384
+
385
+ CONSENSUS_THRESHOLDS = {
386
+ "simple_majority": 0.51,
387
+ "super_majority": 0.67,
388
+ "unanimous": 1.0,
389
+ }
390
+
391
+
392
+ # ============================================================
393
+ # V3.6.0 "Anchor & Retrospect" Data Models
394
+ # ============================================================
395
+
396
+ class GoalItemStatus(Enum):
397
+ PENDING = "pending"
398
+ PARTIALLY_COVERED = "partially_covered"
399
+ FULLY_COVERED = "fully_covered"
400
+ EXCEEDED = "exceeded"
401
+
402
+
403
+ class AnchorTrigger(Enum):
404
+ STEP_COMPLETE = "step_complete"
405
+ PHASE_GATE = "phase_gate"
406
+ DIRECTION_CHANGE = "direction_change"
407
+ CONFLICT = "conflict"
408
+ MILESTONE = "milestone"
409
+
410
+
411
+ class DriftSeverity(Enum):
412
+ NONE = "none"
413
+ LOW = "low"
414
+ MEDIUM = "medium"
415
+ HIGH = "high"
416
+ CRITICAL = "critical"
417
+
418
+
419
+ @dataclass
420
+ class GoalItem:
421
+ item_id: str
422
+ description: str
423
+ keywords: List[str] = field(default_factory=list)
424
+ status: GoalItemStatus = GoalItemStatus.PENDING
425
+ coverage_score: float = 0.0
426
+ evidence: List[str] = field(default_factory=list)
427
+
428
+
429
+ @dataclass
430
+ class StructuredGoal:
431
+ goal_id: str = ""
432
+ original_description: str = ""
433
+ items: List[GoalItem] = field(default_factory=list)
434
+ created_at: str = ""
435
+
436
+ @property
437
+ def overall_coverage(self) -> float:
438
+ if not self.items:
439
+ return 0.0
440
+ return sum(i.coverage_score for i in self.items) / len(self.items)
441
+
442
+ @property
443
+ def uncovered_items(self) -> List[GoalItem]:
444
+ return [i for i in self.items if i.status in (GoalItemStatus.PENDING, GoalItemStatus.PARTIALLY_COVERED)]
445
+
446
+
447
+ @dataclass
448
+ class DriftItem:
449
+ content: str
450
+ severity: DriftSeverity = DriftSeverity.LOW
451
+ reason: str = ""
452
+
453
+
454
+ @dataclass
455
+ class AnchorResult:
456
+ aligned: bool = True
457
+ trigger: AnchorTrigger = AnchorTrigger.STEP_COMPLETE
458
+ coverage: float = 1.0
459
+ drift_score: float = 0.0
460
+ drifts: List[DriftItem] = field(default_factory=list)
461
+ uncovered_goals: List[str] = field(default_factory=list)
462
+ recommendation: str = ""
463
+ checked_at: str = ""
464
+
465
+ @property
466
+ def severity(self) -> DriftSeverity:
467
+ if self.drift_score < 0.1:
468
+ return DriftSeverity.NONE
469
+ elif self.drift_score < 0.2:
470
+ return DriftSeverity.LOW
471
+ elif self.drift_score < 0.3:
472
+ return DriftSeverity.MEDIUM
473
+ elif self.drift_score < 0.5:
474
+ return DriftSeverity.HIGH
475
+ return DriftSeverity.CRITICAL
476
+
477
+
478
+ @dataclass
479
+ class DeviationRecord:
480
+ step_description: str
481
+ deviation_type: str
482
+ reason: str
483
+ impact: str = ""
484
+ suggestion: str = ""
485
+
486
+
487
+ @dataclass
488
+ class RetrospectiveReport:
489
+ task_goal: str = ""
490
+ goal_id: str = ""
491
+ deviations: List[DeviationRecord] = field(default_factory=list)
492
+ redundant_steps: List[str] = field(default_factory=list)
493
+ improvements: List[str] = field(default_factory=list)
494
+ anchor_check_count: int = 0
495
+ anchor_drift_count: int = 0
496
+ final_coverage: float = 1.0
497
+ summary: str = ""
498
+ created_at: str = ""
499
+
500
+ def to_dict(self) -> Dict[str, Any]:
501
+ return {
502
+ "task_goal": self.task_goal,
503
+ "goal_id": self.goal_id,
504
+ "deviation_count": len(self.deviations),
505
+ "redundant_step_count": len(self.redundant_steps),
506
+ "improvement_count": len(self.improvements),
507
+ "anchor_check_count": self.anchor_check_count,
508
+ "anchor_drift_count": self.anchor_drift_count,
509
+ "final_coverage": self.final_coverage,
510
+ "summary": self.summary,
511
+ "created_at": self.created_at,
512
+ }
513
+
514
+ def to_markdown(self) -> str:
515
+ lines = [
516
+ "# Retrospective Report",
517
+ "",
518
+ f"**Task**: {self.task_goal}",
519
+ f"**Goal ID**: {self.goal_id}",
520
+ f"**Final Coverage**: {self.final_coverage:.0%}",
521
+ f"**Anchor Checks**: {self.anchor_check_count} (drifts: {self.anchor_drift_count})",
522
+ "",
523
+ ]
524
+ if self.deviations:
525
+ lines.append("## Deviations")
526
+ for d in self.deviations:
527
+ lines.append(f"- **{d.deviation_type}**: {d.reason}")
528
+ if d.suggestion:
529
+ lines.append(f" → {d.suggestion}")
530
+ lines.append("")
531
+ if self.improvements:
532
+ lines.append("## Improvements for Next Time")
533
+ for imp in self.improvements:
534
+ lines.append(f"- {imp}")
535
+ lines.append("")
536
+ lines.append(f"---\n*Generated by RetrospectiveEngine V3.6.0*")
537
+ return "\n".join(lines)