tenseleyflow/loader / aa8600f

Browse files

Extend concrete retry budget

Authored by espadonne
SHA
aa8600f4a1d13f21ccfee69808bd22b85e019cc7
Parents
1e0f091
Tree
f230948

2 changed files

StatusFile+-
M src/loader/runtime/repair.py 5 3
M tests/test_repair.py 46 0
src/loader/runtime/repair.pymodified
@@ -383,9 +383,11 @@ class ResponseRepairer:
383383
         if dod is None:
384384
             return base_max_empty_retries
385385
         completed_artifacts, missing_artifacts = self._planned_artifact_counts(dod)
386
-        if completed_artifacts < 3 or missing_artifacts == 0:
387
-            return base_max_empty_retries
388
-        return base_max_empty_retries + _LATE_STAGE_EMPTY_RETRY_EXTRA
386
+        if completed_artifacts >= 3 and missing_artifacts > 0:
387
+            return base_max_empty_retries + _LATE_STAGE_EMPTY_RETRY_EXTRA
388
+        if self._has_concrete_next_output_step(dod):
389
+            return base_max_empty_retries + _LATE_STAGE_EMPTY_RETRY_EXTRA
390
+        return base_max_empty_retries
389391
 
390392
     def _should_compact_empty_retry_message(self, dod: DefinitionOfDone) -> bool:
391393
         completed_artifacts, missing_artifacts = self._planned_artifact_counts(dod)
tests/test_repair.pymodified
@@ -450,6 +450,52 @@ def test_empty_response_retry_budget_extends_for_late_stage_multi_artifact_progr
450450
     assert "Follow the same one-file-at-a-time mutation pattern" in decision.retry_message
451451
 
452452
 
453
+def test_empty_response_retry_budget_extends_when_concrete_next_output_is_known(
454
+    temp_dir: Path,
455
+) -> None:
456
+    context = build_context(
457
+        temp_dir=temp_dir,
458
+        use_react=False,
459
+    )
460
+    repairer = ResponseRepairer(context)
461
+
462
+    implementation_plan = temp_dir / "implementation.md"
463
+    implementation_plan.write_text(
464
+        "\n".join(
465
+            [
466
+                "# Implementation Plan",
467
+                "",
468
+                "## File Changes",
469
+                f"- `{temp_dir / 'guides' / 'nginx' / 'index.html'}`",
470
+                f"- `{temp_dir / 'guides' / 'nginx' / 'chapters'}`",
471
+                "",
472
+            ]
473
+        )
474
+    )
475
+
476
+    dod = create_definition_of_done("Create a multi-file nginx guide.")
477
+    dod.implementation_plan = str(implementation_plan)
478
+    dod.pending_items.append("Develop the main index.html file for the nginx guide")
479
+
480
+    decision = repairer.handle_empty_response(
481
+        task="Create a multi-file nginx guide.",
482
+        original_task=None,
483
+        empty_retry_count=3,
484
+        max_empty_retries=2,
485
+        dod=dod,
486
+    )
487
+
488
+    assert decision.should_continue is True
489
+    assert decision.retry_message is not None
490
+    assert "retry 3/4" in decision.retry_message
491
+    assert "Next missing planned artifact: `index.html`" in decision.retry_message
492
+    assert (
493
+        "Resume with this exact next step: continue `Develop the main index.html file for the nginx guide` "
494
+        "by creating `index.html`."
495
+        in decision.retry_message
496
+    )
497
+
498
+
453499
 def test_empty_response_retry_uses_compact_prompt_after_substantial_progress(
454500
     temp_dir: Path,
455501
 ) -> None: