pdd-cli 0.0.90__py3-none-any.whl → 0.0.121__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 (151) hide show
  1. pdd/__init__.py +38 -6
  2. pdd/agentic_bug.py +323 -0
  3. pdd/agentic_bug_orchestrator.py +506 -0
  4. pdd/agentic_change.py +231 -0
  5. pdd/agentic_change_orchestrator.py +537 -0
  6. pdd/agentic_common.py +533 -770
  7. pdd/agentic_crash.py +2 -1
  8. pdd/agentic_e2e_fix.py +319 -0
  9. pdd/agentic_e2e_fix_orchestrator.py +582 -0
  10. pdd/agentic_fix.py +118 -3
  11. pdd/agentic_update.py +27 -9
  12. pdd/agentic_verify.py +3 -2
  13. pdd/architecture_sync.py +565 -0
  14. pdd/auth_service.py +210 -0
  15. pdd/auto_deps_main.py +63 -53
  16. pdd/auto_include.py +236 -3
  17. pdd/auto_update.py +125 -47
  18. pdd/bug_main.py +195 -23
  19. pdd/cmd_test_main.py +345 -197
  20. pdd/code_generator.py +4 -2
  21. pdd/code_generator_main.py +118 -32
  22. pdd/commands/__init__.py +6 -0
  23. pdd/commands/analysis.py +113 -48
  24. pdd/commands/auth.py +309 -0
  25. pdd/commands/connect.py +358 -0
  26. pdd/commands/fix.py +155 -114
  27. pdd/commands/generate.py +5 -0
  28. pdd/commands/maintenance.py +3 -2
  29. pdd/commands/misc.py +8 -0
  30. pdd/commands/modify.py +225 -163
  31. pdd/commands/sessions.py +284 -0
  32. pdd/commands/utility.py +12 -7
  33. pdd/construct_paths.py +334 -32
  34. pdd/context_generator_main.py +167 -170
  35. pdd/continue_generation.py +6 -3
  36. pdd/core/__init__.py +33 -0
  37. pdd/core/cli.py +44 -7
  38. pdd/core/cloud.py +237 -0
  39. pdd/core/dump.py +68 -20
  40. pdd/core/errors.py +4 -0
  41. pdd/core/remote_session.py +61 -0
  42. pdd/crash_main.py +219 -23
  43. pdd/data/llm_model.csv +4 -4
  44. pdd/docs/prompting_guide.md +864 -0
  45. pdd/docs/whitepaper_with_benchmarks/data_and_functions/benchmark_analysis.py +495 -0
  46. pdd/docs/whitepaper_with_benchmarks/data_and_functions/creation_compare.py +528 -0
  47. pdd/fix_code_loop.py +208 -34
  48. pdd/fix_code_module_errors.py +6 -2
  49. pdd/fix_error_loop.py +291 -38
  50. pdd/fix_main.py +208 -6
  51. pdd/fix_verification_errors_loop.py +235 -26
  52. pdd/fix_verification_main.py +269 -83
  53. pdd/frontend/dist/assets/index-B5DZHykP.css +1 -0
  54. pdd/frontend/dist/assets/index-CUWd8al1.js +450 -0
  55. pdd/frontend/dist/index.html +376 -0
  56. pdd/frontend/dist/logo.svg +33 -0
  57. pdd/generate_output_paths.py +46 -5
  58. pdd/generate_test.py +212 -151
  59. pdd/get_comment.py +19 -44
  60. pdd/get_extension.py +8 -9
  61. pdd/get_jwt_token.py +309 -20
  62. pdd/get_language.py +8 -7
  63. pdd/get_run_command.py +7 -5
  64. pdd/insert_includes.py +2 -1
  65. pdd/llm_invoke.py +531 -97
  66. pdd/load_prompt_template.py +15 -34
  67. pdd/operation_log.py +342 -0
  68. pdd/path_resolution.py +140 -0
  69. pdd/postprocess.py +122 -97
  70. pdd/preprocess.py +68 -12
  71. pdd/preprocess_main.py +33 -1
  72. pdd/prompts/agentic_bug_step10_pr_LLM.prompt +182 -0
  73. pdd/prompts/agentic_bug_step1_duplicate_LLM.prompt +73 -0
  74. pdd/prompts/agentic_bug_step2_docs_LLM.prompt +129 -0
  75. pdd/prompts/agentic_bug_step3_triage_LLM.prompt +95 -0
  76. pdd/prompts/agentic_bug_step4_reproduce_LLM.prompt +97 -0
  77. pdd/prompts/agentic_bug_step5_root_cause_LLM.prompt +123 -0
  78. pdd/prompts/agentic_bug_step6_test_plan_LLM.prompt +107 -0
  79. pdd/prompts/agentic_bug_step7_generate_LLM.prompt +172 -0
  80. pdd/prompts/agentic_bug_step8_verify_LLM.prompt +119 -0
  81. pdd/prompts/agentic_bug_step9_e2e_test_LLM.prompt +289 -0
  82. pdd/prompts/agentic_change_step10_identify_issues_LLM.prompt +1006 -0
  83. pdd/prompts/agentic_change_step11_fix_issues_LLM.prompt +984 -0
  84. pdd/prompts/agentic_change_step12_create_pr_LLM.prompt +140 -0
  85. pdd/prompts/agentic_change_step1_duplicate_LLM.prompt +73 -0
  86. pdd/prompts/agentic_change_step2_docs_LLM.prompt +101 -0
  87. pdd/prompts/agentic_change_step3_research_LLM.prompt +126 -0
  88. pdd/prompts/agentic_change_step4_clarify_LLM.prompt +164 -0
  89. pdd/prompts/agentic_change_step5_docs_change_LLM.prompt +981 -0
  90. pdd/prompts/agentic_change_step6_devunits_LLM.prompt +1005 -0
  91. pdd/prompts/agentic_change_step7_architecture_LLM.prompt +1044 -0
  92. pdd/prompts/agentic_change_step8_analyze_LLM.prompt +1027 -0
  93. pdd/prompts/agentic_change_step9_implement_LLM.prompt +1077 -0
  94. pdd/prompts/agentic_e2e_fix_step1_unit_tests_LLM.prompt +90 -0
  95. pdd/prompts/agentic_e2e_fix_step2_e2e_tests_LLM.prompt +91 -0
  96. pdd/prompts/agentic_e2e_fix_step3_root_cause_LLM.prompt +89 -0
  97. pdd/prompts/agentic_e2e_fix_step4_fix_e2e_tests_LLM.prompt +96 -0
  98. pdd/prompts/agentic_e2e_fix_step5_identify_devunits_LLM.prompt +91 -0
  99. pdd/prompts/agentic_e2e_fix_step6_create_unit_tests_LLM.prompt +106 -0
  100. pdd/prompts/agentic_e2e_fix_step7_verify_tests_LLM.prompt +116 -0
  101. pdd/prompts/agentic_e2e_fix_step8_run_pdd_fix_LLM.prompt +120 -0
  102. pdd/prompts/agentic_e2e_fix_step9_verify_all_LLM.prompt +146 -0
  103. pdd/prompts/agentic_fix_primary_LLM.prompt +2 -2
  104. pdd/prompts/agentic_update_LLM.prompt +192 -338
  105. pdd/prompts/auto_include_LLM.prompt +22 -0
  106. pdd/prompts/change_LLM.prompt +3093 -1
  107. pdd/prompts/detect_change_LLM.prompt +571 -14
  108. pdd/prompts/fix_code_module_errors_LLM.prompt +8 -0
  109. pdd/prompts/fix_errors_from_unit_tests_LLM.prompt +1 -0
  110. pdd/prompts/generate_test_LLM.prompt +19 -1
  111. pdd/prompts/generate_test_from_example_LLM.prompt +366 -0
  112. pdd/prompts/insert_includes_LLM.prompt +262 -252
  113. pdd/prompts/prompt_code_diff_LLM.prompt +123 -0
  114. pdd/prompts/prompt_diff_LLM.prompt +82 -0
  115. pdd/remote_session.py +876 -0
  116. pdd/server/__init__.py +52 -0
  117. pdd/server/app.py +335 -0
  118. pdd/server/click_executor.py +587 -0
  119. pdd/server/executor.py +338 -0
  120. pdd/server/jobs.py +661 -0
  121. pdd/server/models.py +241 -0
  122. pdd/server/routes/__init__.py +31 -0
  123. pdd/server/routes/architecture.py +451 -0
  124. pdd/server/routes/auth.py +364 -0
  125. pdd/server/routes/commands.py +929 -0
  126. pdd/server/routes/config.py +42 -0
  127. pdd/server/routes/files.py +603 -0
  128. pdd/server/routes/prompts.py +1347 -0
  129. pdd/server/routes/websocket.py +473 -0
  130. pdd/server/security.py +243 -0
  131. pdd/server/terminal_spawner.py +217 -0
  132. pdd/server/token_counter.py +222 -0
  133. pdd/summarize_directory.py +236 -237
  134. pdd/sync_animation.py +8 -4
  135. pdd/sync_determine_operation.py +329 -47
  136. pdd/sync_main.py +272 -28
  137. pdd/sync_orchestration.py +289 -211
  138. pdd/sync_order.py +304 -0
  139. pdd/template_expander.py +161 -0
  140. pdd/templates/architecture/architecture_json.prompt +41 -46
  141. pdd/trace.py +1 -1
  142. pdd/track_cost.py +0 -13
  143. pdd/unfinished_prompt.py +2 -1
  144. pdd/update_main.py +68 -26
  145. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/METADATA +15 -10
  146. pdd_cli-0.0.121.dist-info/RECORD +229 -0
  147. pdd_cli-0.0.90.dist-info/RECORD +0 -153
  148. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/WHEEL +0 -0
  149. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/entry_points.txt +0 -0
  150. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/licenses/LICENSE +0 -0
  151. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/top_level.txt +0 -0
pdd/agentic_fix.py CHANGED
@@ -4,6 +4,7 @@ import os
4
4
  import re
5
5
  import shutil
6
6
  import subprocess
7
+ import sys
7
8
  import difflib
8
9
  import tempfile
9
10
  from pathlib import Path
@@ -56,6 +57,68 @@ def _verbose(msg: str) -> None:
56
57
  if _IS_VERBOSE:
57
58
  console.print(msg)
58
59
 
60
+
61
+ def _detect_suspicious_files(cwd: Path, context: str = "") -> List[Path]:
62
+ """
63
+ Detect suspicious single-character files (like C, E, T) in a directory.
64
+
65
+ This is a diagnostic function to help identify when/where these files are created.
66
+ Issue #186: Empty files named C, E, T (first letters of Code, Example, Test)
67
+ have been appearing during agentic operations.
68
+
69
+ Args:
70
+ cwd: Directory to scan
71
+ context: Description of what operation just ran (for logging)
72
+
73
+ Returns:
74
+ List of suspicious file paths found
75
+ """
76
+ suspicious: List[Path] = []
77
+ try:
78
+ for f in cwd.iterdir():
79
+ if f.is_file() and len(f.name) <= 2 and not f.name.startswith('.'):
80
+ suspicious.append(f)
81
+
82
+ if suspicious:
83
+ import datetime
84
+ timestamp = datetime.datetime.now().isoformat()
85
+ _always(f"[bold red]⚠️ SUSPICIOUS FILES DETECTED (Issue #186)[/bold red]")
86
+ _always(f"[red]Timestamp: {timestamp}[/red]")
87
+ _always(f"[red]Context: {context}[/red]")
88
+ _always(f"[red]Directory: {cwd}[/red]")
89
+ for sf in suspicious:
90
+ try:
91
+ size = sf.stat().st_size
92
+ _always(f"[red] - {sf.name} (size: {size} bytes)[/red]")
93
+ except Exception:
94
+ _always(f"[red] - {sf.name} (could not stat)[/red]")
95
+
96
+ # Also log to a file for persistence
97
+ log_file = Path.home() / ".pdd" / "suspicious_files.log"
98
+ log_file.parent.mkdir(parents=True, exist_ok=True)
99
+ with open(log_file, "a") as lf:
100
+ lf.write(f"\n{'='*60}\n")
101
+ lf.write(f"Timestamp: {timestamp}\n")
102
+ lf.write(f"Context: {context}\n")
103
+ lf.write(f"Directory: {cwd}\n")
104
+ lf.write(f"CWD at detection: {Path.cwd()}\n")
105
+ for sf in suspicious:
106
+ try:
107
+ size = sf.stat().st_size
108
+ lf.write(f" - {sf.name} (size: {size} bytes)\n")
109
+ except Exception as e:
110
+ lf.write(f" - {sf.name} (error: {e})\n")
111
+ # Log stack trace to help identify caller
112
+ import traceback
113
+ lf.write("Stack trace:\n")
114
+ lf.write(traceback.format_stack()[-10:][0] if traceback.format_stack() else "N/A")
115
+ lf.write("\n")
116
+ except Exception as e:
117
+ _verbose(f"[yellow]Could not scan for suspicious files: {e}[/yellow]")
118
+
119
+ return suspicious
120
+
121
+
59
122
  def _begin_marker(path: Path) -> str:
60
123
  """Marker that must wrap the BEGIN of a corrected file block emitted by the agent."""
61
124
  return f"<<<BEGIN_FILE:{path}>>>"
@@ -130,10 +193,41 @@ _MULTI_FILE_BLOCK_RE = re.compile(
130
193
  re.DOTALL,
131
194
  )
132
195
 
196
+
197
+ def _is_suspicious_path(path: str) -> bool:
198
+ """
199
+ Reject paths that look like LLM artifacts or template variables.
200
+
201
+ This defends against:
202
+ - Single/double character filenames (e.g., 'C', 'E', 'T' from agent misbehavior)
203
+ - Template variables like {path}, {code_abs} captured by regex
204
+ - Other LLM-generated garbage patterns
205
+
206
+ Returns True if the path should be rejected.
207
+ """
208
+ if not path:
209
+ return True
210
+ # Get the basename for validation
211
+ base_name = Path(path).name
212
+ # Reject single or double character filenames (too short to be legitimate)
213
+ if len(base_name) <= 2:
214
+ return True
215
+ # Reject template variable patterns like {path}, {code_abs}
216
+ if '{' in base_name or '}' in base_name:
217
+ return True
218
+ # Reject paths that are just dots like "..", "..."
219
+ if base_name.strip('.') == '':
220
+ return True
221
+ return False
222
+
223
+
133
224
  def _extract_files_from_output(*blobs: str) -> Dict[str, str]:
134
225
  """
135
226
  Parse stdout/stderr blobs and collect all emitted file blocks into {path: content}.
136
227
  Returns an empty dict if none found.
228
+
229
+ Note: Suspicious paths (single-char, template variables) are rejected to prevent
230
+ LLM artifacts from being written to disk.
137
231
  """
138
232
  out: Dict[str, str] = {}
139
233
  for blob in blobs:
@@ -143,6 +237,9 @@ def _extract_files_from_output(*blobs: str) -> Dict[str, str]:
143
237
  path = (m.group(1) or "").strip()
144
238
  body = m.group(2) or ""
145
239
  if path and body != "":
240
+ if _is_suspicious_path(path):
241
+ _info(f"[yellow]Skipping suspicious path from LLM output: {path!r}[/yellow]")
242
+ continue
146
243
  out[path] = body
147
244
  return out
148
245
 
@@ -401,6 +498,12 @@ def _run_anthropic_variants(prompt_text: str, cwd: Path, total_timeout: int, lab
401
498
  return last
402
499
  finally:
403
500
  prompt_file.unlink(missing_ok=True)
501
+ # Issue #186: Scan for suspicious files after Anthropic agent runs
502
+ _detect_suspicious_files(cwd, f"After _run_anthropic_variants ({label})")
503
+ # Also scan project root in case agent created files there
504
+ project_root = Path.cwd()
505
+ if project_root != cwd:
506
+ _detect_suspicious_files(project_root, f"After _run_anthropic_variants ({label}) - project root")
404
507
 
405
508
  def _run_cli_args_google(args: List[str], cwd: Path, timeout: int) -> subprocess.CompletedProcess:
406
509
  """Subprocess runner for Google commands with common sanitized env."""
@@ -460,6 +563,12 @@ def _run_google_variants(prompt_text: str, cwd: Path, total_timeout: int, label:
460
563
  return last
461
564
  finally:
462
565
  prompt_file.unlink(missing_ok=True)
566
+ # Issue #186: Scan for suspicious files after Google agent runs
567
+ _detect_suspicious_files(cwd, f"After _run_google_variants ({label})")
568
+ # Also scan project root in case agent created files there
569
+ project_root = Path.cwd()
570
+ if project_root != cwd:
571
+ _detect_suspicious_files(project_root, f"After _run_google_variants ({label}) - project root")
463
572
 
464
573
  def _run_testcmd(cmd: str, cwd: Path) -> bool:
465
574
  """
@@ -498,7 +607,7 @@ def _verify_and_log(unit_test_file: str, cwd: Path, *, verify_cmd: Optional[str]
498
607
  return _run_testcmd(run_cmd, cwd)
499
608
  # Fallback: try running with Python if no run command found
500
609
  verify = subprocess.run(
501
- [os.sys.executable, str(Path(unit_test_file).resolve())],
610
+ [sys.executable, str(Path(unit_test_file).resolve())],
502
611
  capture_output=True,
503
612
  text=True,
504
613
  check=False,
@@ -549,10 +658,16 @@ def _normalize_target_path(
549
658
  ) -> Optional[Path]:
550
659
  """
551
660
  Resolve an emitted path to a safe file path we should write:
661
+ - reject suspicious paths (single-char, template variables)
552
662
  - make path absolute under project root
553
663
  - allow direct match, primary-file match (with/without _fixed), or basename search
554
664
  - create new files only if allow_new is True
555
665
  """
666
+ # Early rejection of suspicious paths (defense against LLM artifacts)
667
+ if _is_suspicious_path(emitted_path):
668
+ _info(f"[yellow]Skipping suspicious path: {emitted_path!r}[/yellow]")
669
+ return None
670
+
556
671
  p = Path(emitted_path)
557
672
  if not p.is_absolute():
558
673
  p = (project_root / emitted_path).resolve()
@@ -760,7 +875,7 @@ def _try_harvest_then_verify(
760
875
  newest = code_path.read_text(encoding="utf-8")
761
876
  _print_diff(code_snapshot, newest, code_path)
762
877
  ok = _post_apply_verify_or_testcmd(
763
- provider, unit_test_file, working_dir,
878
+ provider, unit_test_file, cwd,
764
879
  verify_cmd=verify_cmd, verify_enabled=verify_enabled,
765
880
  stdout=res.stdout or "", stderr=res.stderr or ""
766
881
  )
@@ -952,7 +1067,7 @@ def run_agentic_fix(
952
1067
  else:
953
1068
  # Fallback: run directly with Python interpreter
954
1069
  pre = subprocess.run(
955
- [os.sys.executable, str(Path(unit_test_file).resolve())],
1070
+ [sys.executable, str(Path(unit_test_file).resolve())],
956
1071
  capture_output=True,
957
1072
  text=True,
958
1073
  check=False,
pdd/agentic_update.py CHANGED
@@ -16,8 +16,9 @@ import os
16
16
  import traceback
17
17
 
18
18
  from rich.console import Console
19
+ from rich.markdown import Markdown
19
20
 
20
- from .agentic_common import get_available_agents, run_agentic_task
21
+ from .agentic_common import get_available_agents, run_agentic_task, DEFAULT_MAX_RETRIES
21
22
  from .load_prompt_template import load_prompt_template
22
23
 
23
24
  # Optional globals from package root; ignore if not present.
@@ -107,17 +108,23 @@ def _detect_changed_files(
107
108
  return sorted({p.resolve() for p in changed})
108
109
 
109
110
 
110
- def _discover_test_files(code_path: Path) -> List[Path]:
111
+ def _discover_test_files(
112
+ code_path: Path,
113
+ tests_dir: Optional[Path] = None,
114
+ ) -> List[Path]:
111
115
  """
112
116
  Discover test files associated with a given code file.
113
117
 
114
118
  Uses pattern: ``test_{code_stem}*{code_suffix}`` and searches in:
115
- 1. ``tests/`` relative to the code file directory
116
- 2. The same directory as the code file
117
- 3. Project root ``tests/``
119
+ 1. Configured tests_dir from .pddrc (if provided)
120
+ 2. ``tests/`` relative to the code file directory
121
+ 3. The same directory as the code file
122
+ 4. Sibling ``tests/`` directory (../tests/)
123
+ 5. Project root ``tests/``
118
124
 
119
125
  Args:
120
126
  code_path: Path to the main code file.
127
+ tests_dir: Optional path to tests directory from .pddrc config.
121
128
 
122
129
  Returns:
123
130
  Ordered list of discovered test file paths (deduplicated).
@@ -127,11 +134,15 @@ def _discover_test_files(code_path: Path) -> List[Path]:
127
134
  suffix = code_path.suffix
128
135
  pattern = f"test_{stem}*{suffix}"
129
136
 
130
- search_dirs: List[Path] = [
137
+ search_dirs: List[Path] = []
138
+ if tests_dir is not None:
139
+ search_dirs.append(Path(tests_dir).resolve())
140
+ search_dirs.extend([
131
141
  code_path.parent / "tests",
132
142
  code_path.parent,
143
+ code_path.parent.parent / "tests", # Sibling tests dir (../tests/)
133
144
  PROJECT_ROOT / "tests",
134
- ]
145
+ ])
135
146
 
136
147
  seen: set[Path] = set()
137
148
  discovered: List[Path] = []
@@ -183,6 +194,7 @@ def run_agentic_update(
183
194
  code_file: str,
184
195
  test_files: Optional[List[Path]] = None,
185
196
  *,
197
+ tests_dir: Optional[Path] = None,
186
198
  verbose: bool = False,
187
199
  quiet: bool = False,
188
200
  ) -> Tuple[bool, str, float, str, List[str]]:
@@ -205,6 +217,8 @@ def run_agentic_update(
205
217
  test files are auto-discovered using the pattern
206
218
  ``test_{code_stem}*{code_suffix}`` in the configured search
207
219
  locations.
220
+ tests_dir: Optional path to tests directory from .pddrc config.
221
+ Used for auto-discovery when test_files is None.
208
222
  verbose: If True, enable verbose logging for the underlying agent task.
209
223
  quiet: If True, suppress informational logging from this function.
210
224
  (Passed through to the agent as well; ``quiet`` takes precedence
@@ -268,7 +282,7 @@ def run_agentic_update(
268
282
  return False, error, 0.0, "", []
269
283
  selected_tests = normalized_tests or []
270
284
  else:
271
- selected_tests = _discover_test_files(code_path)
285
+ selected_tests = _discover_test_files(code_path, tests_dir=tests_dir)
272
286
 
273
287
  # Paths to track *before* running the agent (for mtime comparison)
274
288
  before_paths: set[Path] = {prompt_path.resolve(), code_path.resolve()}
@@ -323,6 +337,7 @@ def run_agentic_update(
323
337
  verbose=bool(verbose and not quiet),
324
338
  quiet=quiet,
325
339
  label=f"agentic_update:{code_path.stem}",
340
+ max_retries=DEFAULT_MAX_RETRIES,
326
341
  )
327
342
  except Exception as exc:
328
343
  message = f"Agentic task failed with an exception: {exc}"
@@ -361,7 +376,10 @@ def run_agentic_update(
361
376
 
362
377
  if not quiet:
363
378
  if success:
364
- console.print(f"[green]{base_msg}[/green]")
379
+ console.print("[green]Prompt file updated successfully.[/green]")
380
+ if output_message:
381
+ console.print("\n[bold]Agent output:[/bold]")
382
+ console.print(Markdown(output_message))
365
383
  else:
366
384
  console.print(f"[yellow]{base_msg}[/yellow]")
367
385
 
pdd/agentic_verify.py CHANGED
@@ -8,7 +8,7 @@ from typing import Any
8
8
 
9
9
  from rich.console import Console
10
10
 
11
- from .agentic_common import run_agentic_task
11
+ from .agentic_common import run_agentic_task, DEFAULT_MAX_RETRIES
12
12
  from .load_prompt_template import load_prompt_template
13
13
 
14
14
  console = Console()
@@ -133,7 +133,8 @@ def run_agentic_verify(
133
133
  cwd=project_root,
134
134
  verbose=verbose,
135
135
  quiet=quiet,
136
- label="verify-explore"
136
+ label="verify-explore",
137
+ max_retries=DEFAULT_MAX_RETRIES,
137
138
  )
138
139
 
139
140
  # 6. Record State After Execution & Detect Changes