Refresh quality repair focus
Authored by
mfwolffe <wolffemf@dukes.jmu.edu>
- SHA
d950aff823a6924066a5617ad3f5b9a2646661d3- Parents
-
a3cc97b - Tree
9a0407f
d950aff
d950aff823a6924066a5617ad3f5b9a2646661d3a3cc97b
9a0407f| Status | File | + | - |
|---|---|---|---|
| M |
src/loader/runtime/tool_batches.py
|
25 | 1 |
| M |
tests/test_repair_focus.py
|
34 | 0 |
| M |
tests/test_tool_batches.py
|
7 | 3 |
src/loader/runtime/tool_batches.pymodified@@ -2,6 +2,7 @@ | ||
| 2 | 2 | |
| 3 | 3 | from __future__ import annotations |
| 4 | 4 | |
| 5 | +import re | |
| 5 | 6 | import shlex |
| 6 | 7 | from collections.abc import Awaitable, Callable |
| 7 | 8 | from dataclasses import dataclass, field |
@@ -1009,6 +1010,12 @@ class ToolBatchRunner: | ||
| 1009 | 1010 | changed_path=changed_path, |
| 1010 | 1011 | ) |
| 1011 | 1012 | if next_target: |
| 1013 | + repair_issue = _quality_repair_issue_for_target(repair, next_target) | |
| 1014 | + issue_line = ( | |
| 1015 | + f"- {repair_issue}\n" | |
| 1016 | + if repair_issue | |
| 1017 | + else f"- Improve `{next_target}` until it satisfies the active content-quality verifier.\n" | |
| 1018 | + ) | |
| 1012 | 1019 | self.context.queue_steering_message( |
| 1013 | 1020 | "The active HTML content-quality repair target was updated. " |
| 1014 | 1021 | "If the current file now comfortably clears its stated threshold, " |
@@ -1016,7 +1023,12 @@ class ToolBatchRunner: | ||
| 1016 | 1023 | "using one substantial write/edit/patch anchored to current content. " |
| 1017 | 1024 | "If it still looks thin, expand this same file further now. " |
| 1018 | 1025 | "Do not rerun verification, reopen unrelated references, or summarize " |
| 1019 | - "completion after only one quality target." | |
| 1026 | + "completion after only one quality target.\n\n" | |
| 1027 | + "Repair focus:\n" | |
| 1028 | + f"{issue_line}" | |
| 1029 | + f"- Immediate next step: edit `{next_target}`.\n" | |
| 1030 | + "- Continue with one concrete `edit`, `patch`, or `write` call that " | |
| 1031 | + "actually changes the current generated file." | |
| 1020 | 1032 | ) |
| 1021 | 1033 | return |
| 1022 | 1034 | |
@@ -1808,6 +1820,9 @@ class ToolBatchRunner: | ||
| 1808 | 1820 | return |
| 1809 | 1821 | if not all_planned_artifact_outputs_exist(dod, project_root=self.context.project_root): |
| 1810 | 1822 | return |
| 1823 | + repair = extract_active_repair_context(self.context.session.messages) | |
| 1824 | + if repair is not None and _repair_context_is_html_quality(repair): | |
| 1825 | + return | |
| 1811 | 1826 | |
| 1812 | 1827 | next_pending = preferred_pending_todo_item( |
| 1813 | 1828 | dod, |
@@ -3458,6 +3473,15 @@ def _quality_repair_issue_for_target(repair: Any, target: str) -> str: | ||
| 3458 | 3473 | return clean_line |
| 3459 | 3474 | if normalized_target and normalized_target in clean_line: |
| 3460 | 3475 | return clean_line |
| 3476 | + for candidate in re.findall(r"`([^`]+)`", clean_line): | |
| 3477 | + try: | |
| 3478 | + normalized_candidate = str( | |
| 3479 | + Path(candidate).expanduser().resolve(strict=False) | |
| 3480 | + ) | |
| 3481 | + except (OSError, RuntimeError, ValueError): | |
| 3482 | + normalized_candidate = str(Path(candidate).expanduser()) | |
| 3483 | + if normalized_target and normalized_candidate == normalized_target: | |
| 3484 | + return clean_line | |
| 3461 | 3485 | |
| 3462 | 3486 | return first_quality_line |
| 3463 | 3487 | |
tests/test_repair_focus.pymodified@@ -53,3 +53,37 @@ def test_extract_active_repair_context_keeps_immediate_target_first( | ||
| 53 | 53 | assert context.artifact_path == str(index_path.resolve(strict=False)) |
| 54 | 54 | assert context.allowed_paths[0] == str(index_path.resolve(strict=False)) |
| 55 | 55 | assert str(chapter_path.resolve(strict=False)) in context.allowed_paths |
| 56 | + | |
| 57 | + | |
| 58 | +def test_extract_active_repair_context_uses_latest_quality_handoff( | |
| 59 | + tmp_path: Path, | |
| 60 | +) -> None: | |
| 61 | + first = tmp_path / "guide" / "chapters" / "01-introduction.html" | |
| 62 | + second = tmp_path / "guide" / "chapters" / "02-installation.html" | |
| 63 | + | |
| 64 | + context = extract_active_repair_context( | |
| 65 | + [ | |
| 66 | + Message( | |
| 67 | + role=Role.USER, | |
| 68 | + content=( | |
| 69 | + "Repair focus:\n" | |
| 70 | + f"- Improve `{first}`: thin content (400 text chars, expected at least 1758).\n" | |
| 71 | + f"- Immediate next step: edit `{first}`.\n" | |
| 72 | + ), | |
| 73 | + ), | |
| 74 | + Message( | |
| 75 | + role=Role.USER, | |
| 76 | + content=( | |
| 77 | + "The active HTML content-quality repair target was updated.\n\n" | |
| 78 | + "Repair focus:\n" | |
| 79 | + f"- Improve `{second}`: insufficient structured content (6 blocks, expected at least 18).\n" | |
| 80 | + f"- Immediate next step: edit `{second}`.\n" | |
| 81 | + "- Continue with one concrete edit, patch, or write call.\n" | |
| 82 | + ), | |
| 83 | + ), | |
| 84 | + ] | |
| 85 | + ) | |
| 86 | + | |
| 87 | + assert context is not None | |
| 88 | + assert context.artifact_path == str(second.resolve(strict=False)) | |
| 89 | + assert context.allowed_paths[0] == str(second.resolve(strict=False)) | |
tests/test_tool_batches.pymodified@@ -7309,9 +7309,13 @@ async def test_tool_batch_runner_quality_repair_success_hands_to_next_target( | ||
| 7309 | 7309 | ) |
| 7310 | 7310 | |
| 7311 | 7311 | assert queued |
| 7312 | - assert any("next listed quality target" in message for message in queued) | |
| 7313 | - assert any(str(second.resolve(strict=False)) in message for message in queued) | |
| 7314 | - assert any("Do not rerun verification" in message for message in queued) | |
| 7312 | + handoff = next(message for message in queued if "next listed quality target" in message) | |
| 7313 | + assert str(second.resolve(strict=False)) in handoff | |
| 7314 | + assert "Do not rerun verification" in handoff | |
| 7315 | + assert "Repair focus:" in handoff | |
| 7316 | + assert "insufficient structured content" in handoff | |
| 7317 | + assert f"Immediate next step: edit `{second.resolve(strict=False)}`" in handoff | |
| 7318 | + assert all("All explicitly planned artifacts now exist" not in message for message in queued) | |
| 7315 | 7319 | |
| 7316 | 7320 | |
| 7317 | 7321 | @pytest.mark.asyncio |