tenseleyflow/loader / a1cd87d

Browse files

Lean first child retries

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
a1cd87dbf2510de1b96550effcf9b5fa6e871134
Parents
47db0c5
Tree
b5cab87

2 changed files

StatusFile+-
M src/loader/runtime/repair.py 2 210
M tests/test_repair.py 14 40
src/loader/runtime/repair.pymodified
@@ -418,7 +418,7 @@ class ResponseRepairer:
418418
             )
419419
         else:
420420
             first_line = f"Create `{concrete_target.name}` now."
421
-        compact_retry = retry_number >= 4
421
+        compact_retry = True
422422
 
423423
         lines = [
424424
             first_line,
@@ -434,19 +434,6 @@ class ResponseRepairer:
434434
         )
435435
         if html_scaffold_line:
436436
             lines.append(html_scaffold_line)
437
-        reference_line = self._known_reference_structure_line(
438
-            concrete_target,
439
-            require_first_substantive_output=True,
440
-        )
441
-        if reference_line and not compact_retry:
442
-            lines.append(reference_line)
443
-        reference_cues_line = self._known_reference_cues_line(
444
-            concrete_target,
445
-            require_first_substantive_output=True,
446
-            retry_number=retry_number,
447
-        )
448
-        if reference_cues_line and not compact_retry:
449
-            lines.append(reference_cues_line)
450437
         html_starter_line = self._known_html_starter_shape_line(
451438
             concrete_target,
452439
             require_first_substantive_output=True,
@@ -455,14 +442,6 @@ class ResponseRepairer:
455442
         )
456443
         if html_starter_line:
457444
             lines.append(html_starter_line)
458
-        html_template_line = self._known_html_starter_template_line(
459
-            concrete_target,
460
-            require_first_substantive_output=True,
461
-            retry_number=retry_number,
462
-            outline_label=outline_label,
463
-        )
464
-        if html_template_line:
465
-            lines.append(html_template_line)
466445
         if (
467446
             not compact_retry
468447
             and _should_encourage_initial_version(
@@ -929,25 +908,6 @@ class ResponseRepairer:
929908
                 lines.append(
930909
                     f"Use the existing outline label `{outline_label}` for that file so it matches the current guide structure."
931910
                 )
932
-            reference_line = self._known_reference_structure_line(
933
-                concrete_target,
934
-                require_first_substantive_output=(
935
-                    has_confirmed_output_file_progress
936
-                    and not has_confirmed_substantive_output_file_progress
937
-                ),
938
-            )
939
-            if reference_line:
940
-                lines.append(reference_line)
941
-            reference_cues_line = self._known_reference_cues_line(
942
-                concrete_target,
943
-                require_first_substantive_output=(
944
-                    has_confirmed_output_file_progress
945
-                    and not has_confirmed_substantive_output_file_progress
946
-                ),
947
-                retry_number=retry_number,
948
-            )
949
-            if reference_cues_line:
950
-                lines.append(reference_cues_line)
951911
             html_scaffold_line = self._known_existing_html_scaffold_line(
952912
                 concrete_target,
953913
                 require_first_substantive_output=(
@@ -1257,19 +1217,6 @@ class ResponseRepairer:
12571217
             has_confirmed_output_file_progress
12581218
             and not has_confirmed_substantive_output_file_progress
12591219
         )
1260
-        reference_line = self._known_reference_structure_line(
1261
-            target,
1262
-            require_first_substantive_output=first_substantive_output,
1263
-        )
1264
-        if reference_line:
1265
-            lines.append(reference_line)
1266
-        reference_cues_line = self._known_reference_cues_line(
1267
-            target,
1268
-            require_first_substantive_output=first_substantive_output,
1269
-            retry_number=retry_number,
1270
-        )
1271
-        if reference_cues_line:
1272
-            lines.append(reference_cues_line)
12731220
         html_scaffold_line = self._known_existing_html_scaffold_line(
12741221
             target,
12751222
             require_first_substantive_output=first_substantive_output,
@@ -1485,40 +1432,6 @@ class ResponseRepairer:
14851432
                 return first_line or None
14861433
         return None
14871434
 
1488
-    def _known_reference_structure_line(
1489
-        self,
1490
-        target: Path,
1491
-        *,
1492
-        require_first_substantive_output: bool,
1493
-    ) -> str | None:
1494
-        if not require_first_substantive_output:
1495
-            return None
1496
-        reference = self._best_known_reference_path(target)
1497
-        if reference is None:
1498
-            return None
1499
-        return (
1500
-            f"You already read `{display_runtime_path(reference)}`; reuse its overall "
1501
-            "structure as the starting pattern for this new file, then adapt the content "
1502
-            "to the current target."
1503
-        )
1504
-
1505
-    def _known_reference_cues_line(
1506
-        self,
1507
-        target: Path,
1508
-        *,
1509
-        require_first_substantive_output: bool,
1510
-        retry_number: int,
1511
-    ) -> str | None:
1512
-        if not require_first_substantive_output or retry_number < 2:
1513
-            return None
1514
-        reference = self._best_known_reference_path(target)
1515
-        if reference is None:
1516
-            return None
1517
-        cues = self._reference_content_cues(reference)
1518
-        if not cues:
1519
-            return None
1520
-        return f"Reference cues from `{display_runtime_path(reference)}`: {cues}"
1521
-
15221435
     def _known_existing_html_scaffold_line(
15231436
         self,
15241437
         target: Path,
@@ -1578,31 +1491,6 @@ class ResponseRepairer:
15781491
             "sections with short body text, and a back link to `../index.html`."
15791492
         )
15801493
 
1581
-    def _known_html_starter_template_line(
1582
-        self,
1583
-        target: Path,
1584
-        *,
1585
-        require_first_substantive_output: bool,
1586
-        retry_number: int,
1587
-        outline_label: str | None,
1588
-    ) -> str | None:
1589
-        if not require_first_substantive_output or retry_number < 1:
1590
-            return None
1591
-        if target.suffix.lower() not in {".html", ".htm"}:
1592
-            return None
1593
-        label = outline_label.strip() if outline_label and outline_label.strip() else "this chapter"
1594
-        snippet = (
1595
-            "<!DOCTYPE html> <html lang=\"en\"> <head> <meta charset=\"UTF-8\"> "
1596
-            f"<title>{label}</title> </head> <body> <div class=\"container\"> "
1597
-            f"<h1>{label}</h1> <p>...</p> <h2>Overview</h2> <p>...</p> "
1598
-            "<p><a href=\"../index.html\">← Back to Main Guide Index</a></p> "
1599
-            "</div> </body> </html>"
1600
-        )
1601
-        return (
1602
-            "If blanking continues, use this minimal HTML starter as the `content` value "
1603
-            f"and adapt it: `{snippet}`."
1604
-        )
1605
-
16061494
     def _best_known_root_html_scaffold(self, target: Path) -> Path | None:
16071495
         normalized_target = target.expanduser().resolve(strict=False)
16081496
         if normalized_target.suffix.lower() not in {".html", ".htm"}:
@@ -1638,87 +1526,6 @@ class ResponseRepairer:
16381526
         )
16391527
         return siblings[0]
16401528
 
1641
-    def _best_known_reference_path(self, target: Path) -> Path | None:
1642
-        normalized_target = target.expanduser().resolve(strict=False)
1643
-        target_tokens = {
1644
-            token
1645
-            for token in re.split(r"[^a-z0-9]+", normalized_target.stem.lower())
1646
-            if token
1647
-        }
1648
-        target_number = _leading_numeric_prefix(normalized_target.stem)
1649
-        messages = list(getattr(self.context.session, "messages", []) or [])
1650
-        candidates: list[tuple[int, str, Path]] = []
1651
-
1652
-        for message in messages:
1653
-            for tool_call in getattr(message, "tool_calls", []) or []:
1654
-                if getattr(tool_call, "name", "") != "read":
1655
-                    continue
1656
-                raw_path = str(tool_call.arguments.get("file_path") or "").strip()
1657
-                if not raw_path:
1658
-                    continue
1659
-                candidate = Path(raw_path).expanduser().resolve(strict=False)
1660
-                if candidate == normalized_target or not candidate.suffix:
1661
-                    continue
1662
-                if candidate.suffix.lower() != normalized_target.suffix.lower():
1663
-                    continue
1664
-                score = 0
1665
-                if candidate.name.lower() == normalized_target.name.lower():
1666
-                    score += 8
1667
-                if candidate.parent.name.lower() == normalized_target.parent.name.lower():
1668
-                    score += 2
1669
-                if target_number and _leading_numeric_prefix(candidate.stem) == target_number:
1670
-                    score += 3
1671
-                candidate_tokens = {
1672
-                    token
1673
-                    for token in re.split(r"[^a-z0-9]+", candidate.stem.lower())
1674
-                    if token
1675
-                }
1676
-                score += min(3, len(target_tokens & candidate_tokens))
1677
-                if score <= 0:
1678
-                    continue
1679
-                candidates.append((score, str(candidate), candidate))
1680
-
1681
-        if not candidates:
1682
-            return None
1683
-        candidates.sort(key=lambda item: (item[0], item[1]), reverse=True)
1684
-        return candidates[0][2]
1685
-
1686
-    def _reference_content_cues(self, reference: Path) -> str | None:
1687
-        try:
1688
-            content = reference.read_text()
1689
-        except OSError:
1690
-            return None
1691
-
1692
-        suffix = reference.suffix.lower()
1693
-        cues: list[str] = []
1694
-        if suffix in {".html", ".htm"}:
1695
-            for raw_line in content.splitlines():
1696
-                stripped = " ".join(raw_line.strip().split())
1697
-                if not stripped:
1698
-                    continue
1699
-                lowered = stripped.lower()
1700
-                if not any(
1701
-                    token in lowered
1702
-                    for token in ("<title", "<h1", "<h2", "<p", "<li", "<a ")
1703
-                ):
1704
-                    continue
1705
-                cues.append(_truncate_reference_cue(stripped))
1706
-                if len(cues) >= 3:
1707
-                    break
1708
-        if not cues:
1709
-            for raw_line in content.splitlines():
1710
-                stripped = " ".join(raw_line.strip().split())
1711
-                if not stripped:
1712
-                    continue
1713
-                if sum(ch.isalpha() for ch in stripped) < 6:
1714
-                    continue
1715
-                cues.append(_truncate_reference_cue(stripped))
1716
-                if len(cues) >= 3:
1717
-                    break
1718
-        if not cues:
1719
-            return None
1720
-        return " | ".join(cues)
1721
-
17221529
     @staticmethod
17231530
     def _mutation_tool_scaffold(path: Path, *, tool_name: str) -> str:
17241531
         normalized_path = json.dumps(display_runtime_path(path))
@@ -1759,19 +1566,4 @@ def _should_encourage_initial_version(
17591566
     has_confirmed_output_file_progress: bool,
17601567
     has_confirmed_substantive_output_file_progress: bool,
17611568
 ) -> bool:
1762
-    if not has_confirmed_output_file_progress:
1763
-        return True
1764
-    if _is_summary_artifact_path(target):
1765
-        return False
1766
-    return not has_confirmed_substantive_output_file_progress
1767
-
1768
-
1769
-def _leading_numeric_prefix(stem: str) -> str:
1770
-    match = re.match(r"^(\d+)", stem)
1771
-    return match.group(1) if match else ""
1772
-
1773
-
1774
-def _truncate_reference_cue(value: str, *, max_chars: int = 96) -> str:
1775
-    if len(value) <= max_chars:
1776
-        return value
1777
-    return value[: max_chars - 3].rstrip() + "..."
1569
+    return not has_confirmed_output_file_progress
tests/test_repair.pymodified
@@ -1091,7 +1091,7 @@ def test_empty_response_retry_uses_concrete_file_language_for_aggregate_chapter_
10911091
     )
10921092
     assert (
10931093
         "Write a compact but real initial version of this file now, then refine or expand it in later edits."
1094
-        in decision.retry_message
1094
+        not in decision.retry_message
10951095
     )
10961096
     assert "No narration, no TodoWrite, no rereads, and no empty response" in decision.retry_message
10971097
     assert "Follow the same full-payload one-file-at-a-time write pattern" not in decision.retry_message
@@ -1177,7 +1177,7 @@ def test_empty_response_retry_keeps_concrete_second_chapter_for_aggregate_chapte
11771177
     assert "Follow the same full-payload one-file-at-a-time write pattern" in decision.retry_message
11781178
 
11791179
 
1180
-def test_empty_response_retry_reuses_known_reference_structure_for_first_substantive_file(
1180
+def test_empty_response_retry_keeps_first_substantive_retry_lean(
11811181
     temp_dir: Path,
11821182
 ) -> None:
11831183
     context = build_context(
@@ -1248,11 +1248,6 @@ def test_empty_response_retry_reuses_known_reference_structure_for_first_substan
12481248
 
12491249
     assert decision.should_continue is True
12501250
     assert decision.retry_message is not None
1251
-    assert (
1252
-        f"You already read `{display_runtime_path(reference_chapter)}`; reuse its overall structure "
1253
-        "as the starting pattern for this new file, then adapt the content to the current target."
1254
-        in decision.retry_message
1255
-    )
12561251
     assert (
12571252
         f"Reuse the existing `{display_runtime_path(index_path)}` head/style/container pattern "
12581253
         "for this chapter so the guide stays visually consistent; only adapt the title, heading, "
@@ -1265,9 +1260,13 @@ def test_empty_response_retry_reuses_known_reference_structure_for_first_substan
12651260
         "of `<h2>` sections with short body text, and a back link to `../index.html`."
12661261
         in decision.retry_message
12671262
     )
1263
+    assert display_runtime_path(reference_chapter) not in decision.retry_message
1264
+    assert "Reference cues from" not in decision.retry_message
1265
+    assert "If blanking continues, use this minimal HTML starter" not in decision.retry_message
1266
+    assert "Write a compact but real initial version of this file now" not in decision.retry_message
12681267
 
12691268
 
1270
-def test_compact_first_substantive_retry_reuses_known_reference_structure(
1269
+def test_late_first_substantive_retry_stays_lean(
12711270
     temp_dir: Path,
12721271
 ) -> None:
12731272
     context = build_context(
@@ -1338,16 +1337,6 @@ def test_compact_first_substantive_retry_reuses_known_reference_structure(
13381337
 
13391338
     assert decision.should_continue is True
13401339
     assert decision.retry_message is not None
1341
-    assert (
1342
-        f"You already read `{display_runtime_path(reference_chapter)}`; reuse its overall structure "
1343
-        "as the starting pattern for this new file, then adapt the content to the current target."
1344
-        in decision.retry_message
1345
-    )
1346
-    assert (
1347
-        f"Reference cues from `{display_runtime_path(reference_chapter)}`: "
1348
-        "<h1>Chapter 1: Introduction to Fortran</h1>"
1349
-        in decision.retry_message
1350
-    )
13511340
     assert (
13521341
         f"Reuse the existing `{display_runtime_path(index_path)}` head/style/container pattern "
13531342
         "for this chapter so the guide stays visually consistent; only adapt the title, heading, "
@@ -1360,11 +1349,10 @@ def test_compact_first_substantive_retry_reuses_known_reference_structure(
13601349
         "of `<h2>` sections with short body text, and a back link to `../index.html`."
13611350
         in decision.retry_message
13621351
     )
1363
-    assert (
1364
-        "If blanking continues, use this minimal HTML starter as the `content` value "
1365
-        "and adapt it:"
1366
-        in decision.retry_message
1367
-    )
1352
+    assert display_runtime_path(reference_chapter) not in decision.retry_message
1353
+    assert "Reference cues from" not in decision.retry_message
1354
+    assert "If blanking continues, use this minimal HTML starter" not in decision.retry_message
1355
+    assert "Write a compact but real initial version of this file now" not in decision.retry_message
13681356
 
13691357
 
13701358
 def test_first_substantive_retry_activates_on_first_empty_turn(
@@ -1440,11 +1428,7 @@ def test_first_substantive_retry_activates_on_first_empty_turn(
14401428
     assert decision.retry_message is not None
14411429
     assert "Emit this tool shape now" in decision.retry_message
14421430
     assert "01-introduction.html" in decision.retry_message
1443
-    assert (
1444
-        "If blanking continues, use this minimal HTML starter as the `content` value "
1445
-        "and adapt it:"
1446
-        in decision.retry_message
1447
-    )
1431
+    assert "If blanking continues, use this minimal HTML starter" not in decision.retry_message
14481432
 
14491433
 
14501434
 def test_late_first_substantive_retry_trims_context_to_core_write_cues(
@@ -1530,19 +1514,14 @@ def test_late_first_substantive_retry_trims_context_to_core_write_cues(
15301514
         "of `<h2>` sections with short body text, and a back link to `../index.html`."
15311515
         in decision.retry_message
15321516
     )
1533
-    assert (
1534
-        "If blanking continues, use this minimal HTML starter as the `content` value "
1535
-        "and adapt it:"
1536
-        in decision.retry_message
1537
-    )
15381517
     assert "<title>Chapter 1: Introduction to Nginx</title>" in decision.retry_message
1539
-    assert '<p><a href="../index.html">← Back to Main Guide Index</a></p>' in decision.retry_message
15401518
     assert (
15411519
         f"You already read `{display_runtime_path(reference_chapter)}`; reuse its overall structure "
15421520
         "as the starting pattern for this new file, then adapt the content to the current target."
15431521
         not in decision.retry_message
15441522
     )
15451523
     assert "Reference cues from" not in decision.retry_message
1524
+    assert "If blanking continues, use this minimal HTML starter" not in decision.retry_message
15461525
     assert "Write a compact but real initial version of this file now" not in decision.retry_message
15471526
 
15481527
 
@@ -2380,9 +2359,4 @@ def test_empty_response_retry_names_next_file_from_observed_sibling_directory(
23802359
         in decision.retry_message
23812360
     )
23822361
     assert "Next observed output pattern under `chapters/`" not in decision.retry_message
2383
-    assert (
2384
-        f"You already read `{display_runtime_path(reference_chapters / '01-introduction.html')}`; "
2385
-        "reuse its overall structure as the starting pattern for this new file, then adapt the "
2386
-        "content to the current target."
2387
-        in decision.retry_message
2388
-    )
2362
+    assert display_runtime_path(reference_chapters / "01-introduction.html") not in decision.retry_message