anvil-dev-framework 0.1.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.
Files changed (190) hide show
  1. package/README.md +719 -0
  2. package/VERSION +1 -0
  3. package/docs/ANVIL-REPO-IMPLEMENTATION-PLAN.md +441 -0
  4. package/docs/FIRST-SKILL-TUTORIAL.md +408 -0
  5. package/docs/INSTALLATION-RETRO-NOTES.md +458 -0
  6. package/docs/INSTALLATION.md +984 -0
  7. package/docs/anvil-hud.md +469 -0
  8. package/docs/anvil-init.md +255 -0
  9. package/docs/anvil-state.md +210 -0
  10. package/docs/boris-cherny-ralph-wiggum-insights.md +608 -0
  11. package/docs/command-reference.md +2022 -0
  12. package/docs/hooks-tts.md +368 -0
  13. package/docs/implementation-guide.md +810 -0
  14. package/docs/linear-github-integration.md +247 -0
  15. package/docs/local-issues.md +677 -0
  16. package/docs/patterns/README.md +419 -0
  17. package/docs/planning-responsibilities.md +139 -0
  18. package/docs/session-workflow.md +573 -0
  19. package/docs/simplification-plan-template.md +297 -0
  20. package/docs/simplification-principles.md +129 -0
  21. package/docs/specifications/CCS-RALPH-INTEGRATION-DESIGN.md +633 -0
  22. package/docs/specifications/CCS-RESEARCH-REPORT.md +169 -0
  23. package/docs/specifications/PLAN-ANV-verification-ralph-wiggum.md +403 -0
  24. package/docs/specifications/PLAN-parallel-tracks-anvil-memory-ccs.md +494 -0
  25. package/docs/specifications/SPEC-ANV-VRW/component-01-verify.md +208 -0
  26. package/docs/specifications/SPEC-ANV-VRW/component-02-stop-gate.md +226 -0
  27. package/docs/specifications/SPEC-ANV-VRW/component-03-posttooluse.md +209 -0
  28. package/docs/specifications/SPEC-ANV-VRW/component-04-ralph-wiggum.md +604 -0
  29. package/docs/specifications/SPEC-ANV-VRW/component-05-atomic-actions.md +311 -0
  30. package/docs/specifications/SPEC-ANV-VRW/component-06-verify-subagent.md +264 -0
  31. package/docs/specifications/SPEC-ANV-VRW/component-07-claude-md.md +363 -0
  32. package/docs/specifications/SPEC-ANV-VRW/index.md +182 -0
  33. package/docs/specifications/SPEC-ANV-anvil-memory.md +573 -0
  34. package/docs/specifications/SPEC-ANV-context-checkpoints.md +781 -0
  35. package/docs/specifications/SPEC-ANV-verification-ralph-wiggum.md +789 -0
  36. package/docs/sync.md +122 -0
  37. package/global/CLAUDE.md +140 -0
  38. package/global/agents/verify-app.md +164 -0
  39. package/global/commands/anvil-settings.md +527 -0
  40. package/global/commands/anvil-sync.md +121 -0
  41. package/global/commands/change.md +197 -0
  42. package/global/commands/clarify.md +252 -0
  43. package/global/commands/cleanup.md +292 -0
  44. package/global/commands/commit-push-pr.md +207 -0
  45. package/global/commands/decay-review.md +127 -0
  46. package/global/commands/discover.md +158 -0
  47. package/global/commands/doc-coverage.md +122 -0
  48. package/global/commands/evidence.md +307 -0
  49. package/global/commands/explore.md +121 -0
  50. package/global/commands/force-exit.md +135 -0
  51. package/global/commands/handoff.md +191 -0
  52. package/global/commands/healthcheck.md +302 -0
  53. package/global/commands/hud.md +84 -0
  54. package/global/commands/insights.md +319 -0
  55. package/global/commands/linear-setup.md +184 -0
  56. package/global/commands/lint-fix.md +198 -0
  57. package/global/commands/orient.md +510 -0
  58. package/global/commands/plan.md +228 -0
  59. package/global/commands/ralph.md +346 -0
  60. package/global/commands/ready.md +182 -0
  61. package/global/commands/release.md +305 -0
  62. package/global/commands/retro.md +96 -0
  63. package/global/commands/shard.md +166 -0
  64. package/global/commands/spec.md +227 -0
  65. package/global/commands/sprint.md +184 -0
  66. package/global/commands/tasks.md +228 -0
  67. package/global/commands/test-and-commit.md +151 -0
  68. package/global/commands/validate.md +132 -0
  69. package/global/commands/verify.md +251 -0
  70. package/global/commands/weekly-review.md +156 -0
  71. package/global/hooks/__pycache__/ralph_context_monitor.cpython-314.pyc +0 -0
  72. package/global/hooks/__pycache__/statusline_agent_sync.cpython-314.pyc +0 -0
  73. package/global/hooks/anvil_memory_observe.ts +322 -0
  74. package/global/hooks/anvil_memory_session.ts +166 -0
  75. package/global/hooks/anvil_memory_stop.ts +187 -0
  76. package/global/hooks/parse_transcript.py +116 -0
  77. package/global/hooks/post_merge_cleanup.sh +132 -0
  78. package/global/hooks/post_tool_format.sh +215 -0
  79. package/global/hooks/ralph_context_monitor.py +240 -0
  80. package/global/hooks/ralph_stop.sh +502 -0
  81. package/global/hooks/statusline.sh +1110 -0
  82. package/global/hooks/statusline_agent_sync.py +224 -0
  83. package/global/hooks/stop_gate.sh +250 -0
  84. package/global/lib/.claude/anvil-state.json +21 -0
  85. package/global/lib/__pycache__/agent_registry.cpython-314.pyc +0 -0
  86. package/global/lib/__pycache__/claim_service.cpython-314.pyc +0 -0
  87. package/global/lib/__pycache__/coderabbit_service.cpython-314.pyc +0 -0
  88. package/global/lib/__pycache__/config_service.cpython-314.pyc +0 -0
  89. package/global/lib/__pycache__/coordination_service.cpython-314.pyc +0 -0
  90. package/global/lib/__pycache__/doc_coverage_service.cpython-314.pyc +0 -0
  91. package/global/lib/__pycache__/gate_logger.cpython-314.pyc +0 -0
  92. package/global/lib/__pycache__/github_service.cpython-314.pyc +0 -0
  93. package/global/lib/__pycache__/hygiene_service.cpython-314.pyc +0 -0
  94. package/global/lib/__pycache__/issue_models.cpython-314.pyc +0 -0
  95. package/global/lib/__pycache__/issue_provider.cpython-314.pyc +0 -0
  96. package/global/lib/__pycache__/linear_data_service.cpython-314.pyc +0 -0
  97. package/global/lib/__pycache__/linear_provider.cpython-314.pyc +0 -0
  98. package/global/lib/__pycache__/local_provider.cpython-314.pyc +0 -0
  99. package/global/lib/__pycache__/quality_service.cpython-314.pyc +0 -0
  100. package/global/lib/__pycache__/ralph_state.cpython-314.pyc +0 -0
  101. package/global/lib/__pycache__/state_manager.cpython-314.pyc +0 -0
  102. package/global/lib/__pycache__/transcript_parser.cpython-314.pyc +0 -0
  103. package/global/lib/__pycache__/verification_runner.cpython-314.pyc +0 -0
  104. package/global/lib/__pycache__/verify_iteration.cpython-314.pyc +0 -0
  105. package/global/lib/__pycache__/verify_subagent.cpython-314.pyc +0 -0
  106. package/global/lib/agent_registry.py +995 -0
  107. package/global/lib/anvil-state.sh +435 -0
  108. package/global/lib/claim_service.py +515 -0
  109. package/global/lib/coderabbit_service.py +314 -0
  110. package/global/lib/config_service.py +423 -0
  111. package/global/lib/coordination_service.py +331 -0
  112. package/global/lib/doc_coverage_service.py +1305 -0
  113. package/global/lib/gate_logger.py +316 -0
  114. package/global/lib/github_service.py +310 -0
  115. package/global/lib/handoff_generator.py +775 -0
  116. package/global/lib/hygiene_service.py +712 -0
  117. package/global/lib/issue_models.py +257 -0
  118. package/global/lib/issue_provider.py +339 -0
  119. package/global/lib/linear_data_service.py +210 -0
  120. package/global/lib/linear_provider.py +987 -0
  121. package/global/lib/linear_provider.py.backup +671 -0
  122. package/global/lib/local_provider.py +486 -0
  123. package/global/lib/orient_fast.py +457 -0
  124. package/global/lib/quality_service.py +470 -0
  125. package/global/lib/ralph_prompt_generator.py +563 -0
  126. package/global/lib/ralph_state.py +1202 -0
  127. package/global/lib/state_manager.py +417 -0
  128. package/global/lib/transcript_parser.py +597 -0
  129. package/global/lib/verification_runner.py +557 -0
  130. package/global/lib/verify_iteration.py +490 -0
  131. package/global/lib/verify_subagent.py +250 -0
  132. package/global/skills/README.md +155 -0
  133. package/global/skills/quality-gates/SKILL.md +252 -0
  134. package/global/skills/skill-template/SKILL.md +109 -0
  135. package/global/skills/testing-strategies/SKILL.md +337 -0
  136. package/global/templates/CHANGE-template.md +105 -0
  137. package/global/templates/HANDOFF-template.md +63 -0
  138. package/global/templates/PLAN-template.md +111 -0
  139. package/global/templates/SPEC-template.md +93 -0
  140. package/global/templates/ralph/PROMPT.md.template +89 -0
  141. package/global/templates/ralph/fix_plan.md.template +31 -0
  142. package/global/templates/ralph/progress.txt.template +23 -0
  143. package/global/tests/__pycache__/test_doc_coverage.cpython-314.pyc +0 -0
  144. package/global/tests/test_doc_coverage.py +520 -0
  145. package/global/tests/test_issue_models.py +299 -0
  146. package/global/tests/test_local_provider.py +323 -0
  147. package/global/tools/README.md +178 -0
  148. package/global/tools/__pycache__/anvil-hud.cpython-314.pyc +0 -0
  149. package/global/tools/anvil-hud.py +3622 -0
  150. package/global/tools/anvil-hud.py.bak +3318 -0
  151. package/global/tools/anvil-issue.py +432 -0
  152. package/global/tools/anvil-memory/CLAUDE.md +49 -0
  153. package/global/tools/anvil-memory/README.md +42 -0
  154. package/global/tools/anvil-memory/bun.lock +25 -0
  155. package/global/tools/anvil-memory/bunfig.toml +9 -0
  156. package/global/tools/anvil-memory/package.json +23 -0
  157. package/global/tools/anvil-memory/src/__tests__/ccs/context-monitor.test.ts +535 -0
  158. package/global/tools/anvil-memory/src/__tests__/ccs/edge-cases.test.ts +645 -0
  159. package/global/tools/anvil-memory/src/__tests__/ccs/fixtures.ts +363 -0
  160. package/global/tools/anvil-memory/src/__tests__/ccs/index.ts +8 -0
  161. package/global/tools/anvil-memory/src/__tests__/ccs/integration.test.ts +417 -0
  162. package/global/tools/anvil-memory/src/__tests__/ccs/prompt-generator.test.ts +571 -0
  163. package/global/tools/anvil-memory/src/__tests__/ccs/ralph-stop.test.ts +440 -0
  164. package/global/tools/anvil-memory/src/__tests__/ccs/test-utils.ts +252 -0
  165. package/global/tools/anvil-memory/src/__tests__/commands.test.ts +657 -0
  166. package/global/tools/anvil-memory/src/__tests__/db.test.ts +641 -0
  167. package/global/tools/anvil-memory/src/__tests__/hooks.test.ts +272 -0
  168. package/global/tools/anvil-memory/src/__tests__/performance.test.ts +427 -0
  169. package/global/tools/anvil-memory/src/__tests__/test-utils.ts +113 -0
  170. package/global/tools/anvil-memory/src/commands/checkpoint.ts +197 -0
  171. package/global/tools/anvil-memory/src/commands/get.ts +115 -0
  172. package/global/tools/anvil-memory/src/commands/init.ts +94 -0
  173. package/global/tools/anvil-memory/src/commands/observe.ts +163 -0
  174. package/global/tools/anvil-memory/src/commands/search.ts +112 -0
  175. package/global/tools/anvil-memory/src/db.ts +638 -0
  176. package/global/tools/anvil-memory/src/index.ts +205 -0
  177. package/global/tools/anvil-memory/src/types.ts +122 -0
  178. package/global/tools/anvil-memory/tsconfig.json +29 -0
  179. package/global/tools/ralph-loop.sh +359 -0
  180. package/package.json +45 -0
  181. package/scripts/anvil +822 -0
  182. package/scripts/extract_patterns.py +222 -0
  183. package/scripts/init-project.sh +541 -0
  184. package/scripts/install.sh +229 -0
  185. package/scripts/postinstall.js +41 -0
  186. package/scripts/rollback.sh +188 -0
  187. package/scripts/sync.sh +623 -0
  188. package/scripts/test-statusline.sh +248 -0
  189. package/scripts/update_claude_md.py +224 -0
  190. package/scripts/verify.sh +255 -0
@@ -0,0 +1,490 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ verify_iteration.py - Verification iteration logic for /verify command (ANV-150)
4
+
5
+ Handles the iteration loop for fixing verification failures:
6
+ - Tracks iteration count and state
7
+ - Generates fix context with failing file contents
8
+ - Determines when to escalate after max attempts
9
+ - Re-runs verification after each fix attempt
10
+
11
+ Usage:
12
+ from verify_iteration import VerificationIterator
13
+
14
+ iterator = VerificationIterator(max_iterations=3)
15
+
16
+ while not iterator.is_complete():
17
+ result = iterator.run_verification()
18
+ if result.passed:
19
+ break
20
+
21
+ # Get context for fixing
22
+ fix_context = iterator.get_fix_context(result)
23
+ # ... apply fixes ...
24
+
25
+ iterator.record_attempt(result)
26
+ """
27
+
28
+ import json
29
+ from dataclasses import dataclass, field
30
+ from datetime import datetime
31
+ from pathlib import Path
32
+ from typing import Any, Dict, List, Literal, Optional
33
+
34
+ from verification_runner import (
35
+ VerificationConfig,
36
+ VerificationError,
37
+ VerificationResult,
38
+ VerificationRunner,
39
+ )
40
+
41
+
42
+ # =============================================================================
43
+ # Data Models
44
+ # =============================================================================
45
+
46
+ @dataclass
47
+ class FixContext:
48
+ """Context for fixing a verification error."""
49
+ error: VerificationError
50
+ file_content: str
51
+ file_path: str
52
+ relevant_lines: List[str]
53
+ line_start: int
54
+ line_end: int
55
+ suggestion: str = ""
56
+
57
+ def to_dict(self) -> Dict[str, Any]:
58
+ return {
59
+ "file": self.file_path,
60
+ "error": self.error.to_dict(),
61
+ "line_start": self.line_start,
62
+ "line_end": self.line_end,
63
+ "relevant_lines": self.relevant_lines,
64
+ "suggestion": self.suggestion,
65
+ }
66
+
67
+
68
+ @dataclass
69
+ class IterationState:
70
+ """State tracking for verification iterations."""
71
+ current_iteration: int = 0
72
+ max_iterations: int = 3
73
+ started_at: Optional[str] = None
74
+ attempts: List[Dict[str, Any]] = field(default_factory=list)
75
+ status: Literal["pending", "running", "passed", "failed", "escalated"] = "pending"
76
+ last_result: Optional[VerificationResult] = None
77
+ fixed_errors: List[str] = field(default_factory=list) # file paths
78
+ remaining_errors: List[VerificationError] = field(default_factory=list)
79
+
80
+ def to_dict(self) -> Dict[str, Any]:
81
+ return {
82
+ "current_iteration": self.current_iteration,
83
+ "max_iterations": self.max_iterations,
84
+ "started_at": self.started_at,
85
+ "status": self.status,
86
+ "attempts_count": len(self.attempts),
87
+ "fixed_count": len(self.fixed_errors),
88
+ "remaining_count": len(self.remaining_errors),
89
+ }
90
+
91
+
92
+ # =============================================================================
93
+ # Fix Context Generation
94
+ # =============================================================================
95
+
96
+ class FixContextGenerator:
97
+ """Generate context for fixing verification errors."""
98
+
99
+ CONTEXT_LINES = 10 # Lines before/after error to include
100
+
101
+ @classmethod
102
+ def read_file_with_context(
103
+ cls,
104
+ file_path: str,
105
+ error_line: Optional[int],
106
+ project_root: Path,
107
+ ) -> FixContext:
108
+ """Read file and extract relevant context around error."""
109
+ full_path = project_root / file_path
110
+
111
+ try:
112
+ with open(full_path, 'r') as f:
113
+ content = f.read()
114
+ lines = content.splitlines()
115
+ except Exception as e:
116
+ return FixContext(
117
+ error=VerificationError(file=file_path, message=f"Could not read: {e}"),
118
+ file_content="",
119
+ file_path=file_path,
120
+ relevant_lines=[],
121
+ line_start=0,
122
+ line_end=0,
123
+ )
124
+
125
+ if error_line is None:
126
+ # No specific line, return first 50 lines
127
+ relevant = lines[:50]
128
+ return FixContext(
129
+ error=VerificationError(file=file_path),
130
+ file_content=content,
131
+ file_path=file_path,
132
+ relevant_lines=relevant,
133
+ line_start=1,
134
+ line_end=min(50, len(lines)),
135
+ )
136
+
137
+ # Extract context around error line
138
+ start = max(0, error_line - cls.CONTEXT_LINES - 1)
139
+ end = min(len(lines), error_line + cls.CONTEXT_LINES)
140
+ relevant = lines[start:end]
141
+
142
+ return FixContext(
143
+ error=VerificationError(file=file_path, line=error_line),
144
+ file_content=content,
145
+ file_path=file_path,
146
+ relevant_lines=relevant,
147
+ line_start=start + 1,
148
+ line_end=end,
149
+ )
150
+
151
+ @classmethod
152
+ def generate_fix_suggestion(cls, error: VerificationError) -> str:
153
+ """Generate a fix suggestion based on error type."""
154
+ message = error.message.lower()
155
+
156
+ # Common error patterns and suggestions
157
+ suggestions = {
158
+ "undefined": "Check for typos in variable/function names or missing imports",
159
+ "not defined": "Add missing import or variable declaration",
160
+ "cannot find": "Verify the import path or check if the module exists",
161
+ "type error": "Check type annotations and ensure compatible types",
162
+ "expected": "Review the expected vs actual values in the assertion",
163
+ "timeout": "Consider increasing timeout or mocking slow operations",
164
+ "mock": "Verify mock setup and return values match expected behavior",
165
+ "import": "Check import statements and module paths",
166
+ "syntax": "Review syntax near the error location",
167
+ "indent": "Fix indentation (spaces vs tabs, consistent levels)",
168
+ }
169
+
170
+ for pattern, suggestion in suggestions.items():
171
+ if pattern in message:
172
+ return suggestion
173
+
174
+ return "Review the error message and related code carefully"
175
+
176
+
177
+ # =============================================================================
178
+ # Iteration Manager
179
+ # =============================================================================
180
+
181
+ class VerificationIterator:
182
+ """Manage verification iteration loop."""
183
+
184
+ STATE_FILE = ".claude/verify-iteration-state.json"
185
+
186
+ def __init__(
187
+ self,
188
+ project_root: Optional[Path] = None,
189
+ max_iterations: int = 3,
190
+ config: Optional[VerificationConfig] = None,
191
+ ):
192
+ self.project_root = project_root or Path.cwd()
193
+ self.max_iterations = max_iterations
194
+ self.runner = VerificationRunner(
195
+ project_root=self.project_root,
196
+ config=config,
197
+ )
198
+ self.state = self._load_state() or IterationState(
199
+ max_iterations=max_iterations,
200
+ started_at=datetime.now().isoformat(),
201
+ )
202
+ self.context_generator = FixContextGenerator()
203
+
204
+ def _state_path(self) -> Path:
205
+ return self.project_root / self.STATE_FILE
206
+
207
+ def _load_state(self) -> Optional[IterationState]:
208
+ """Load iteration state from file."""
209
+ path = self._state_path()
210
+ if not path.exists():
211
+ return None
212
+
213
+ try:
214
+ with open(path) as f:
215
+ data = json.load(f)
216
+ return IterationState(
217
+ current_iteration=data.get("current_iteration", 0),
218
+ max_iterations=data.get("max_iterations", self.max_iterations),
219
+ started_at=data.get("started_at"),
220
+ attempts=data.get("attempts", []),
221
+ status=data.get("status", "pending"),
222
+ fixed_errors=data.get("fixed_errors", []),
223
+ )
224
+ except Exception:
225
+ return None
226
+
227
+ def _save_state(self) -> None:
228
+ """Save iteration state to file."""
229
+ path = self._state_path()
230
+ path.parent.mkdir(parents=True, exist_ok=True)
231
+
232
+ data = {
233
+ "current_iteration": self.state.current_iteration,
234
+ "max_iterations": self.state.max_iterations,
235
+ "started_at": self.state.started_at,
236
+ "status": self.state.status,
237
+ "attempts": self.state.attempts,
238
+ "fixed_errors": self.state.fixed_errors,
239
+ "remaining_errors": [e.to_dict() for e in self.state.remaining_errors],
240
+ }
241
+
242
+ with open(path, "w") as f:
243
+ json.dump(data, f, indent=2)
244
+
245
+ def reset(self) -> None:
246
+ """Reset iteration state for a fresh run."""
247
+ self.state = IterationState(
248
+ max_iterations=self.max_iterations,
249
+ started_at=datetime.now().isoformat(),
250
+ )
251
+ self._save_state()
252
+
253
+ def is_complete(self) -> bool:
254
+ """Check if iteration is complete (passed or max reached)."""
255
+ return self.state.status in ("passed", "escalated")
256
+
257
+ def can_iterate(self) -> bool:
258
+ """Check if more iterations are allowed."""
259
+ return self.state.current_iteration < self.state.max_iterations
260
+
261
+ def run_verification(
262
+ self,
263
+ skip_tests: bool = False,
264
+ skip_lint: bool = False,
265
+ skip_types: bool = False,
266
+ ) -> VerificationResult:
267
+ """Run verification and update state."""
268
+ self.state.status = "running"
269
+
270
+ result = self.runner.run_all(
271
+ skip_tests=skip_tests,
272
+ skip_lint=skip_lint,
273
+ skip_types=skip_types,
274
+ )
275
+
276
+ self.state.last_result = result
277
+
278
+ if result.passed:
279
+ self.state.status = "passed"
280
+ else:
281
+ self.state.remaining_errors = result.errors
282
+
283
+ self._save_state()
284
+ return result
285
+
286
+ def get_fix_context(
287
+ self,
288
+ result: Optional[VerificationResult] = None,
289
+ ) -> List[FixContext]:
290
+ """Generate fix context for all errors."""
291
+ if result is None:
292
+ result = self.state.last_result
293
+
294
+ if result is None or result.passed:
295
+ return []
296
+
297
+ contexts = []
298
+ seen_files = set()
299
+
300
+ for error in result.errors:
301
+ if error.file in seen_files:
302
+ continue
303
+ seen_files.add(error.file)
304
+
305
+ context = self.context_generator.read_file_with_context(
306
+ file_path=error.file,
307
+ error_line=error.line,
308
+ project_root=self.project_root,
309
+ )
310
+ context.error = error
311
+ context.suggestion = self.context_generator.generate_fix_suggestion(error)
312
+ contexts.append(context)
313
+
314
+ return contexts
315
+
316
+ def record_attempt(
317
+ self,
318
+ result: VerificationResult,
319
+ fixes_applied: Optional[List[str]] = None,
320
+ ) -> None:
321
+ """Record an iteration attempt."""
322
+ self.state.current_iteration += 1
323
+
324
+ attempt = {
325
+ "iteration": self.state.current_iteration,
326
+ "timestamp": datetime.now().isoformat(),
327
+ "passed": result.passed,
328
+ "error_count": len(result.errors),
329
+ "fixes_applied": fixes_applied or [],
330
+ }
331
+ self.state.attempts.append(attempt)
332
+
333
+ if fixes_applied:
334
+ self.state.fixed_errors.extend(fixes_applied)
335
+
336
+ if result.passed:
337
+ self.state.status = "passed"
338
+ elif not self.can_iterate():
339
+ self.state.status = "escalated"
340
+ else:
341
+ self.state.status = "failed"
342
+
343
+ self._save_state()
344
+
345
+ def get_escalation_report(self) -> Dict[str, Any]:
346
+ """Generate report when max iterations reached."""
347
+ return {
348
+ "status": "escalated",
349
+ "message": f"Could not fix after {self.state.current_iteration} attempts",
350
+ "iterations": self.state.current_iteration,
351
+ "remaining_errors": [e.to_dict() for e in self.state.remaining_errors],
352
+ "fixed_errors": self.state.fixed_errors,
353
+ "suggestions": [
354
+ "Review the remaining errors manually",
355
+ "Check if external dependencies are needed",
356
+ "Consider if the test requires environment setup",
357
+ "Look for patterns in the failed fixes",
358
+ ],
359
+ }
360
+
361
+ def get_progress_summary(self) -> str:
362
+ """Get human-readable progress summary."""
363
+ if self.state.status == "passed":
364
+ if self.state.current_iteration == 0:
365
+ return "✅ Verification passed on first run"
366
+ return f"✅ Verification passed after {self.state.current_iteration} iteration(s)"
367
+
368
+ if self.state.status == "escalated":
369
+ return f"⚠️ Verification failed after {self.state.max_iterations} attempts. Manual intervention required."
370
+
371
+ if self.state.status == "running":
372
+ return f"🔄 Running verification (iteration {self.state.current_iteration + 1}/{self.state.max_iterations})"
373
+
374
+ remaining = len(self.state.remaining_errors)
375
+ return f"❌ {remaining} error(s) remaining (iteration {self.state.current_iteration}/{self.state.max_iterations})"
376
+
377
+ def cleanup(self) -> None:
378
+ """Clean up state file after completion."""
379
+ path = self._state_path()
380
+ if path.exists():
381
+ path.unlink()
382
+
383
+
384
+ # =============================================================================
385
+ # Prompt Generation for Claude
386
+ # =============================================================================
387
+
388
+ class FixPromptGenerator:
389
+ """Generate prompts for Claude to fix verification errors."""
390
+
391
+ @classmethod
392
+ def generate_fix_prompt(
393
+ cls,
394
+ contexts: List[FixContext],
395
+ iteration: int,
396
+ max_iterations: int,
397
+ ) -> str:
398
+ """Generate a prompt for Claude to fix errors."""
399
+ lines = [
400
+ f"## Verification Fix Request (Iteration {iteration}/{max_iterations})",
401
+ "",
402
+ "The following verification errors need to be fixed:",
403
+ "",
404
+ ]
405
+
406
+ for i, ctx in enumerate(contexts, 1):
407
+ error = ctx.error
408
+ loc = f"{error.file}:{error.line}" if error.line else error.file
409
+
410
+ lines.extend([
411
+ f"### Error {i}: {loc}",
412
+ f"**Source**: {error.source}",
413
+ f"**Message**: {error.message}",
414
+ f"**Suggestion**: {ctx.suggestion}",
415
+ "",
416
+ "**Relevant code:**",
417
+ "```",
418
+ ])
419
+
420
+ for j, line in enumerate(ctx.relevant_lines):
421
+ line_num = ctx.line_start + j
422
+ marker = ">>> " if line_num == error.line else " "
423
+ lines.append(f"{marker}{line_num}: {line}")
424
+
425
+ lines.extend([
426
+ "```",
427
+ "",
428
+ ])
429
+
430
+ lines.extend([
431
+ "## Instructions",
432
+ "",
433
+ "1. Read each error and its context carefully",
434
+ "2. Make minimal, targeted fixes to address each error",
435
+ "3. Do not change unrelated code",
436
+ "4. After fixing, verification will re-run automatically",
437
+ "",
438
+ ])
439
+
440
+ return "\n".join(lines)
441
+
442
+
443
+ # =============================================================================
444
+ # CLI Interface
445
+ # =============================================================================
446
+
447
+ def main():
448
+ """CLI entry point for verification iteration."""
449
+ import argparse
450
+
451
+ parser = argparse.ArgumentParser(description="Verification iteration manager")
452
+ parser.add_argument("command", choices=["run", "status", "reset", "cleanup"])
453
+ parser.add_argument("--max", type=int, default=3, help="Max iterations")
454
+ parser.add_argument("--project", type=str, help="Project root path")
455
+
456
+ args = parser.parse_args()
457
+
458
+ project_root = Path(args.project) if args.project else Path.cwd()
459
+ iterator = VerificationIterator(
460
+ project_root=project_root,
461
+ max_iterations=args.max,
462
+ )
463
+
464
+ if args.command == "run":
465
+ result = iterator.run_verification()
466
+ print(iterator.get_progress_summary())
467
+ if not result.passed:
468
+ contexts = iterator.get_fix_context(result)
469
+ prompt = FixPromptGenerator.generate_fix_prompt(
470
+ contexts,
471
+ iterator.state.current_iteration + 1,
472
+ iterator.state.max_iterations,
473
+ )
474
+ print("\n" + prompt)
475
+
476
+ elif args.command == "status":
477
+ print(iterator.get_progress_summary())
478
+ print(json.dumps(iterator.state.to_dict(), indent=2))
479
+
480
+ elif args.command == "reset":
481
+ iterator.reset()
482
+ print("Iteration state reset")
483
+
484
+ elif args.command == "cleanup":
485
+ iterator.cleanup()
486
+ print("Iteration state cleaned up")
487
+
488
+
489
+ if __name__ == "__main__":
490
+ main()