@@ -442,10 +442,17 @@ class ResponseRepairer: |
| 442 | 442 | ) |
| 443 | 443 | if reference_cues_line: |
| 444 | 444 | lines.append(reference_cues_line) |
| 445 | + html_scaffold_line = self._known_existing_html_scaffold_line( |
| 446 | + concrete_target, |
| 447 | + require_first_substantive_output=True, |
| 448 | + ) |
| 449 | + if html_scaffold_line: |
| 450 | + lines.append(html_scaffold_line) |
| 445 | 451 | html_starter_line = self._known_html_starter_shape_line( |
| 446 | 452 | concrete_target, |
| 447 | 453 | require_first_substantive_output=True, |
| 448 | 454 | retry_number=retry_number, |
| 455 | + outline_label=outline_label, |
| 449 | 456 | ) |
| 450 | 457 | if html_starter_line: |
| 451 | 458 | lines.append(html_starter_line) |
@@ -931,6 +938,15 @@ class ResponseRepairer: |
| 931 | 938 | ) |
| 932 | 939 | if reference_cues_line: |
| 933 | 940 | lines.append(reference_cues_line) |
| 941 | + html_scaffold_line = self._known_existing_html_scaffold_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 | + ) |
| 948 | + if html_scaffold_line: |
| 949 | + lines.append(html_scaffold_line) |
| 934 | 950 | html_starter_line = self._known_html_starter_shape_line( |
| 935 | 951 | concrete_target, |
| 936 | 952 | require_first_substantive_output=( |
@@ -938,6 +954,7 @@ class ResponseRepairer: |
| 938 | 954 | and not has_confirmed_substantive_output_file_progress |
| 939 | 955 | ), |
| 940 | 956 | retry_number=retry_number, |
| 957 | + outline_label=outline_label, |
| 941 | 958 | ) |
| 942 | 959 | if html_starter_line: |
| 943 | 960 | lines.append(html_starter_line) |
@@ -1025,6 +1042,15 @@ class ResponseRepairer: |
| 1025 | 1042 | ) |
| 1026 | 1043 | if reference_cues_line: |
| 1027 | 1044 | lines.append(reference_cues_line) |
| 1045 | + html_scaffold_line = self._known_existing_html_scaffold_line( |
| 1046 | + inferred_pending_target, |
| 1047 | + require_first_substantive_output=( |
| 1048 | + has_confirmed_output_file_progress |
| 1049 | + and not has_confirmed_substantive_output_file_progress |
| 1050 | + ), |
| 1051 | + ) |
| 1052 | + if html_scaffold_line: |
| 1053 | + lines.append(html_scaffold_line) |
| 1028 | 1054 | html_starter_line = self._known_html_starter_shape_line( |
| 1029 | 1055 | inferred_pending_target, |
| 1030 | 1056 | require_first_substantive_output=( |
@@ -1032,6 +1058,7 @@ class ResponseRepairer: |
| 1032 | 1058 | and not has_confirmed_substantive_output_file_progress |
| 1033 | 1059 | ), |
| 1034 | 1060 | retry_number=retry_number, |
| 1061 | + outline_label=outline_label, |
| 1035 | 1062 | ) |
| 1036 | 1063 | if html_starter_line: |
| 1037 | 1064 | lines.append(html_starter_line) |
@@ -1154,6 +1181,15 @@ class ResponseRepairer: |
| 1154 | 1181 | ) |
| 1155 | 1182 | if reference_cues_line: |
| 1156 | 1183 | lines.append(reference_cues_line) |
| 1184 | + html_scaffold_line = self._known_existing_html_scaffold_line( |
| 1185 | + next_output_file, |
| 1186 | + require_first_substantive_output=( |
| 1187 | + has_confirmed_output_file_progress |
| 1188 | + and not has_confirmed_substantive_output_file_progress |
| 1189 | + ), |
| 1190 | + ) |
| 1191 | + if html_scaffold_line: |
| 1192 | + lines.append(html_scaffold_line) |
| 1157 | 1193 | html_starter_line = self._known_html_starter_shape_line( |
| 1158 | 1194 | next_output_file, |
| 1159 | 1195 | require_first_substantive_output=( |
@@ -1161,6 +1197,7 @@ class ResponseRepairer: |
| 1161 | 1197 | and not has_confirmed_substantive_output_file_progress |
| 1162 | 1198 | ), |
| 1163 | 1199 | retry_number=retry_number, |
| 1200 | + outline_label=outline_label, |
| 1164 | 1201 | ) |
| 1165 | 1202 | if html_starter_line: |
| 1166 | 1203 | lines.append(html_starter_line) |
@@ -1479,23 +1516,53 @@ class ResponseRepairer: |
| 1479 | 1516 | return None |
| 1480 | 1517 | return f"Reference cues from `{display_runtime_path(reference)}`: {cues}" |
| 1481 | 1518 | |
| 1519 | + def _known_existing_html_scaffold_line( |
| 1520 | + self, |
| 1521 | + target: Path, |
| 1522 | + *, |
| 1523 | + require_first_substantive_output: bool, |
| 1524 | + ) -> str | None: |
| 1525 | + if not require_first_substantive_output: |
| 1526 | + return None |
| 1527 | + if target.suffix.lower() not in {".html", ".htm"}: |
| 1528 | + return None |
| 1529 | + scaffold = self._best_known_root_html_scaffold(target) |
| 1530 | + if scaffold is None: |
| 1531 | + return None |
| 1532 | + return ( |
| 1533 | + f"Reuse the existing `{display_runtime_path(scaffold)}` head/style/container " |
| 1534 | + "pattern for this chapter so the guide stays visually consistent; only adapt " |
| 1535 | + "the title, heading, and chapter body content." |
| 1536 | + ) |
| 1537 | + |
| 1482 | 1538 | def _known_html_starter_shape_line( |
| 1483 | 1539 | self, |
| 1484 | 1540 | target: Path, |
| 1485 | 1541 | *, |
| 1486 | 1542 | require_first_substantive_output: bool, |
| 1487 | 1543 | retry_number: int, |
| 1544 | + outline_label: str | None, |
| 1488 | 1545 | ) -> str | None: |
| 1489 | 1546 | if not require_first_substantive_output or retry_number < 1: |
| 1490 | 1547 | return None |
| 1491 | 1548 | if target.suffix.lower() not in {".html", ".htm"}: |
| 1492 | 1549 | return None |
| 1550 | + label = outline_label.strip() if outline_label and outline_label.strip() else "this chapter" |
| 1493 | 1551 | return ( |
| 1494 | | - "For this first HTML content file, a minimal acceptable starter is: " |
| 1495 | | - "matching `<title>` and `<h1>`, one introductory paragraph, a few section " |
| 1496 | | - "blocks, and a back link to `../index.html`." |
| 1552 | + f"If you get stuck, start with `<title>{label}</title>`, " |
| 1553 | + f"`<h1>{label}</h1>`, one introductory paragraph, a couple of `<h2>` " |
| 1554 | + "sections with short body text, and a back link to `../index.html`." |
| 1497 | 1555 | ) |
| 1498 | 1556 | |
| 1557 | + def _best_known_root_html_scaffold(self, target: Path) -> Path | None: |
| 1558 | + normalized_target = target.expanduser().resolve(strict=False) |
| 1559 | + if normalized_target.suffix.lower() not in {".html", ".htm"}: |
| 1560 | + return None |
| 1561 | + candidate = normalized_target.parent.parent / "index.html" |
| 1562 | + if candidate == normalized_target or not candidate.exists(): |
| 1563 | + return None |
| 1564 | + return candidate |
| 1565 | + |
| 1499 | 1566 | def _best_known_reference_path(self, target: Path) -> Path | None: |
| 1500 | 1567 | normalized_target = target.expanduser().resolve(strict=False) |
| 1501 | 1568 | target_tokens = { |