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.
- package/README.md +719 -0
- package/VERSION +1 -0
- package/docs/ANVIL-REPO-IMPLEMENTATION-PLAN.md +441 -0
- package/docs/FIRST-SKILL-TUTORIAL.md +408 -0
- package/docs/INSTALLATION-RETRO-NOTES.md +458 -0
- package/docs/INSTALLATION.md +984 -0
- package/docs/anvil-hud.md +469 -0
- package/docs/anvil-init.md +255 -0
- package/docs/anvil-state.md +210 -0
- package/docs/boris-cherny-ralph-wiggum-insights.md +608 -0
- package/docs/command-reference.md +2022 -0
- package/docs/hooks-tts.md +368 -0
- package/docs/implementation-guide.md +810 -0
- package/docs/linear-github-integration.md +247 -0
- package/docs/local-issues.md +677 -0
- package/docs/patterns/README.md +419 -0
- package/docs/planning-responsibilities.md +139 -0
- package/docs/session-workflow.md +573 -0
- package/docs/simplification-plan-template.md +297 -0
- package/docs/simplification-principles.md +129 -0
- package/docs/specifications/CCS-RALPH-INTEGRATION-DESIGN.md +633 -0
- package/docs/specifications/CCS-RESEARCH-REPORT.md +169 -0
- package/docs/specifications/PLAN-ANV-verification-ralph-wiggum.md +403 -0
- package/docs/specifications/PLAN-parallel-tracks-anvil-memory-ccs.md +494 -0
- package/docs/specifications/SPEC-ANV-VRW/component-01-verify.md +208 -0
- package/docs/specifications/SPEC-ANV-VRW/component-02-stop-gate.md +226 -0
- package/docs/specifications/SPEC-ANV-VRW/component-03-posttooluse.md +209 -0
- package/docs/specifications/SPEC-ANV-VRW/component-04-ralph-wiggum.md +604 -0
- package/docs/specifications/SPEC-ANV-VRW/component-05-atomic-actions.md +311 -0
- package/docs/specifications/SPEC-ANV-VRW/component-06-verify-subagent.md +264 -0
- package/docs/specifications/SPEC-ANV-VRW/component-07-claude-md.md +363 -0
- package/docs/specifications/SPEC-ANV-VRW/index.md +182 -0
- package/docs/specifications/SPEC-ANV-anvil-memory.md +573 -0
- package/docs/specifications/SPEC-ANV-context-checkpoints.md +781 -0
- package/docs/specifications/SPEC-ANV-verification-ralph-wiggum.md +789 -0
- package/docs/sync.md +122 -0
- package/global/CLAUDE.md +140 -0
- package/global/agents/verify-app.md +164 -0
- package/global/commands/anvil-settings.md +527 -0
- package/global/commands/anvil-sync.md +121 -0
- package/global/commands/change.md +197 -0
- package/global/commands/clarify.md +252 -0
- package/global/commands/cleanup.md +292 -0
- package/global/commands/commit-push-pr.md +207 -0
- package/global/commands/decay-review.md +127 -0
- package/global/commands/discover.md +158 -0
- package/global/commands/doc-coverage.md +122 -0
- package/global/commands/evidence.md +307 -0
- package/global/commands/explore.md +121 -0
- package/global/commands/force-exit.md +135 -0
- package/global/commands/handoff.md +191 -0
- package/global/commands/healthcheck.md +302 -0
- package/global/commands/hud.md +84 -0
- package/global/commands/insights.md +319 -0
- package/global/commands/linear-setup.md +184 -0
- package/global/commands/lint-fix.md +198 -0
- package/global/commands/orient.md +510 -0
- package/global/commands/plan.md +228 -0
- package/global/commands/ralph.md +346 -0
- package/global/commands/ready.md +182 -0
- package/global/commands/release.md +305 -0
- package/global/commands/retro.md +96 -0
- package/global/commands/shard.md +166 -0
- package/global/commands/spec.md +227 -0
- package/global/commands/sprint.md +184 -0
- package/global/commands/tasks.md +228 -0
- package/global/commands/test-and-commit.md +151 -0
- package/global/commands/validate.md +132 -0
- package/global/commands/verify.md +251 -0
- package/global/commands/weekly-review.md +156 -0
- package/global/hooks/__pycache__/ralph_context_monitor.cpython-314.pyc +0 -0
- package/global/hooks/__pycache__/statusline_agent_sync.cpython-314.pyc +0 -0
- package/global/hooks/anvil_memory_observe.ts +322 -0
- package/global/hooks/anvil_memory_session.ts +166 -0
- package/global/hooks/anvil_memory_stop.ts +187 -0
- package/global/hooks/parse_transcript.py +116 -0
- package/global/hooks/post_merge_cleanup.sh +132 -0
- package/global/hooks/post_tool_format.sh +215 -0
- package/global/hooks/ralph_context_monitor.py +240 -0
- package/global/hooks/ralph_stop.sh +502 -0
- package/global/hooks/statusline.sh +1110 -0
- package/global/hooks/statusline_agent_sync.py +224 -0
- package/global/hooks/stop_gate.sh +250 -0
- package/global/lib/.claude/anvil-state.json +21 -0
- package/global/lib/__pycache__/agent_registry.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/claim_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/coderabbit_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/config_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/coordination_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/doc_coverage_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/gate_logger.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/github_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/hygiene_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/issue_models.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/issue_provider.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/linear_data_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/linear_provider.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/local_provider.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/quality_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/ralph_state.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/state_manager.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/transcript_parser.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/verification_runner.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/verify_iteration.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/verify_subagent.cpython-314.pyc +0 -0
- package/global/lib/agent_registry.py +995 -0
- package/global/lib/anvil-state.sh +435 -0
- package/global/lib/claim_service.py +515 -0
- package/global/lib/coderabbit_service.py +314 -0
- package/global/lib/config_service.py +423 -0
- package/global/lib/coordination_service.py +331 -0
- package/global/lib/doc_coverage_service.py +1305 -0
- package/global/lib/gate_logger.py +316 -0
- package/global/lib/github_service.py +310 -0
- package/global/lib/handoff_generator.py +775 -0
- package/global/lib/hygiene_service.py +712 -0
- package/global/lib/issue_models.py +257 -0
- package/global/lib/issue_provider.py +339 -0
- package/global/lib/linear_data_service.py +210 -0
- package/global/lib/linear_provider.py +987 -0
- package/global/lib/linear_provider.py.backup +671 -0
- package/global/lib/local_provider.py +486 -0
- package/global/lib/orient_fast.py +457 -0
- package/global/lib/quality_service.py +470 -0
- package/global/lib/ralph_prompt_generator.py +563 -0
- package/global/lib/ralph_state.py +1202 -0
- package/global/lib/state_manager.py +417 -0
- package/global/lib/transcript_parser.py +597 -0
- package/global/lib/verification_runner.py +557 -0
- package/global/lib/verify_iteration.py +490 -0
- package/global/lib/verify_subagent.py +250 -0
- package/global/skills/README.md +155 -0
- package/global/skills/quality-gates/SKILL.md +252 -0
- package/global/skills/skill-template/SKILL.md +109 -0
- package/global/skills/testing-strategies/SKILL.md +337 -0
- package/global/templates/CHANGE-template.md +105 -0
- package/global/templates/HANDOFF-template.md +63 -0
- package/global/templates/PLAN-template.md +111 -0
- package/global/templates/SPEC-template.md +93 -0
- package/global/templates/ralph/PROMPT.md.template +89 -0
- package/global/templates/ralph/fix_plan.md.template +31 -0
- package/global/templates/ralph/progress.txt.template +23 -0
- package/global/tests/__pycache__/test_doc_coverage.cpython-314.pyc +0 -0
- package/global/tests/test_doc_coverage.py +520 -0
- package/global/tests/test_issue_models.py +299 -0
- package/global/tests/test_local_provider.py +323 -0
- package/global/tools/README.md +178 -0
- package/global/tools/__pycache__/anvil-hud.cpython-314.pyc +0 -0
- package/global/tools/anvil-hud.py +3622 -0
- package/global/tools/anvil-hud.py.bak +3318 -0
- package/global/tools/anvil-issue.py +432 -0
- package/global/tools/anvil-memory/CLAUDE.md +49 -0
- package/global/tools/anvil-memory/README.md +42 -0
- package/global/tools/anvil-memory/bun.lock +25 -0
- package/global/tools/anvil-memory/bunfig.toml +9 -0
- package/global/tools/anvil-memory/package.json +23 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/context-monitor.test.ts +535 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/edge-cases.test.ts +645 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/fixtures.ts +363 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/index.ts +8 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/integration.test.ts +417 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/prompt-generator.test.ts +571 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/ralph-stop.test.ts +440 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/test-utils.ts +252 -0
- package/global/tools/anvil-memory/src/__tests__/commands.test.ts +657 -0
- package/global/tools/anvil-memory/src/__tests__/db.test.ts +641 -0
- package/global/tools/anvil-memory/src/__tests__/hooks.test.ts +272 -0
- package/global/tools/anvil-memory/src/__tests__/performance.test.ts +427 -0
- package/global/tools/anvil-memory/src/__tests__/test-utils.ts +113 -0
- package/global/tools/anvil-memory/src/commands/checkpoint.ts +197 -0
- package/global/tools/anvil-memory/src/commands/get.ts +115 -0
- package/global/tools/anvil-memory/src/commands/init.ts +94 -0
- package/global/tools/anvil-memory/src/commands/observe.ts +163 -0
- package/global/tools/anvil-memory/src/commands/search.ts +112 -0
- package/global/tools/anvil-memory/src/db.ts +638 -0
- package/global/tools/anvil-memory/src/index.ts +205 -0
- package/global/tools/anvil-memory/src/types.ts +122 -0
- package/global/tools/anvil-memory/tsconfig.json +29 -0
- package/global/tools/ralph-loop.sh +359 -0
- package/package.json +45 -0
- package/scripts/anvil +822 -0
- package/scripts/extract_patterns.py +222 -0
- package/scripts/init-project.sh +541 -0
- package/scripts/install.sh +229 -0
- package/scripts/postinstall.js +41 -0
- package/scripts/rollback.sh +188 -0
- package/scripts/sync.sh +623 -0
- package/scripts/test-statusline.sh +248 -0
- package/scripts/update_claude_md.py +224 -0
- 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()
|