@@ -1360,6 +1360,91 @@ def test_compact_first_substantive_retry_reuses_known_reference_structure( |
| 1360 | 1360 | "of `<h2>` sections with short body text, and a back link to `../index.html`." |
| 1361 | 1361 | in decision.retry_message |
| 1362 | 1362 | ) |
| 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 | + ) |
| 1368 | + |
| 1369 | + |
| 1370 | +def test_first_substantive_retry_activates_on_second_empty_turn( |
| 1371 | + temp_dir: Path, |
| 1372 | +) -> None: |
| 1373 | + context = build_context( |
| 1374 | + temp_dir=temp_dir, |
| 1375 | + use_react=False, |
| 1376 | + ) |
| 1377 | + repairer = ResponseRepairer(context) |
| 1378 | + |
| 1379 | + guide_root = temp_dir / "guides" / "nginx" |
| 1380 | + chapters = guide_root / "chapters" |
| 1381 | + chapters.mkdir(parents=True) |
| 1382 | + index_path = guide_root / "index.html" |
| 1383 | + reference_chapter = temp_dir / "guides" / "fortran" / "chapters" / "01-introduction.html" |
| 1384 | + reference_chapter.parent.mkdir(parents=True) |
| 1385 | + reference_chapter.write_text("<h1>Chapter 1: Introduction to Fortran</h1>\n") |
| 1386 | + index_path.write_text( |
| 1387 | + "\n".join( |
| 1388 | + [ |
| 1389 | + "<html>", |
| 1390 | + '<a href="chapters/01-introduction.html">Chapter 1: Introduction to Nginx</a>', |
| 1391 | + "</html>", |
| 1392 | + ] |
| 1393 | + ) |
| 1394 | + + "\n" |
| 1395 | + ) |
| 1396 | + context.session.append( |
| 1397 | + Message( |
| 1398 | + role=Role.ASSISTANT, |
| 1399 | + content="", |
| 1400 | + tool_calls=[ |
| 1401 | + ToolCall( |
| 1402 | + id="call_ref", |
| 1403 | + name="read", |
| 1404 | + arguments={"file_path": str(reference_chapter)}, |
| 1405 | + ) |
| 1406 | + ], |
| 1407 | + ) |
| 1408 | + ) |
| 1409 | + |
| 1410 | + implementation_plan = temp_dir / "implementation.md" |
| 1411 | + implementation_plan.write_text( |
| 1412 | + "\n".join( |
| 1413 | + [ |
| 1414 | + "# Implementation Plan", |
| 1415 | + "", |
| 1416 | + "## File Changes", |
| 1417 | + f"- `{guide_root}/`", |
| 1418 | + f"- `{chapters}/`", |
| 1419 | + f"- `{index_path}`", |
| 1420 | + "", |
| 1421 | + ] |
| 1422 | + ) |
| 1423 | + ) |
| 1424 | + |
| 1425 | + dod = create_definition_of_done("Create a multi-file nginx guide.") |
| 1426 | + dod.implementation_plan = str(implementation_plan) |
| 1427 | + dod.touched_files.append(str(index_path)) |
| 1428 | + dod.completed_items.append("Develop the main index.html file with proper structure") |
| 1429 | + dod.pending_items.append("Create the nginx chapters content") |
| 1430 | + |
| 1431 | + decision = repairer.handle_empty_response( |
| 1432 | + task="Create a multi-file nginx guide.", |
| 1433 | + original_task=None, |
| 1434 | + empty_retry_count=2, |
| 1435 | + max_empty_retries=4, |
| 1436 | + dod=dod, |
| 1437 | + ) |
| 1438 | + |
| 1439 | + assert decision.should_continue is True |
| 1440 | + assert decision.retry_message is not None |
| 1441 | + assert "Emit this tool shape now" in decision.retry_message |
| 1442 | + 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 | + ) |
| 1363 | 1448 | |
| 1364 | 1449 | |
| 1365 | 1450 | def test_late_first_substantive_retry_trims_context_to_core_write_cues( |
@@ -1512,8 +1597,11 @@ def test_empty_response_retry_prefers_output_index_over_reference_index_with_sam |
| 1512 | 1597 | assert decision.should_continue is True |
| 1513 | 1598 | assert decision.retry_message is not None |
| 1514 | 1599 | assert ( |
| 1515 | | - "Prefer one `write(content=...)` call for " |
| 1516 | | - f"`{display_runtime_path(output_index)}` before more research." |
| 1600 | + f"Continue `Develop the nginx index.html file` by creating `{output_index.name}`." |
| 1601 | + in decision.retry_message |
| 1602 | + ) |
| 1603 | + assert ( |
| 1604 | + f'Emit this tool shape now: `write(file_path="{display_runtime_path(output_index)}", content="...")`.' |
| 1517 | 1605 | in decision.retry_message |
| 1518 | 1606 | ) |
| 1519 | 1607 | assert str(reference_index) not in decision.retry_message |