Clarify declared HTML link recovery
Authored by
mfwolffe <wolffemf@dukes.jmu.edu>
- SHA
a8b3bca17a53734d51790aa3d7cef85b74bafdad- Parents
-
eabd30b - Tree
d31745d
a8b3bca
a8b3bca17a53734d51790aa3d7cef85b74bafdadeabd30b
d31745d| Status | File | + | - |
|---|---|---|---|
| M |
src/loader/runtime/safeguard_services.py
|
37 | 0 |
| M |
src/loader/runtime/tool_batches.py
|
12 | 1 |
| M |
tests/test_safeguard_services.py
|
5 | 0 |
| M |
tests/test_tool_batches.py
|
5 | 3 |
src/loader/runtime/safeguard_services.pymodified@@ -2,6 +2,7 @@ | ||
| 2 | 2 | |
| 3 | 3 | from __future__ import annotations |
| 4 | 4 | |
| 5 | +import os | |
| 5 | 6 | import re |
| 6 | 7 | import shlex |
| 7 | 8 | from dataclasses import dataclass |
@@ -1535,6 +1536,16 @@ class PreActionValidator: | ||
| 1535 | 1536 | ) |
| 1536 | 1537 | if declared_preview: |
| 1537 | 1538 | suggestion += f". Already-declared local targets include: {declared_preview}" |
| 1539 | + allowed_hrefs = self._declared_html_hrefs_for_file( | |
| 1540 | + root, | |
| 1541 | + normalized, | |
| 1542 | + declared_targets, | |
| 1543 | + ) | |
| 1544 | + if allowed_hrefs: | |
| 1545 | + allowed_preview = ", ".join(allowed_hrefs[:6]) | |
| 1546 | + if len(allowed_hrefs) > 6: | |
| 1547 | + allowed_preview += ", ..." | |
| 1548 | + suggestion += f". Allowed hrefs from this file include: {allowed_preview}" | |
| 1538 | 1549 | declared_suggestions = self._suggest_declared_html_targets( |
| 1539 | 1550 | declared_targets, |
| 1540 | 1551 | undeclared_targets, |
@@ -1787,6 +1798,32 @@ class PreActionValidator: | ||
| 1787 | 1798 | suggestions.append(candidate) |
| 1788 | 1799 | |
| 1789 | 1800 | return suggestions |
| 1801 | + | |
| 1802 | + def _declared_html_hrefs_for_file( | |
| 1803 | + self, | |
| 1804 | + root: Path, | |
| 1805 | + file_path: Path, | |
| 1806 | + declared_targets: set[str], | |
| 1807 | + ) -> list[str]: | |
| 1808 | + try: | |
| 1809 | + source_directory = file_path.parent.resolve(strict=False) | |
| 1810 | + root_index = (root / "index.html").resolve(strict=False) | |
| 1811 | + except OSError: | |
| 1812 | + source_directory = file_path.parent.expanduser() | |
| 1813 | + root_index = (root / "index.html").expanduser() | |
| 1814 | + | |
| 1815 | + hrefs: list[str] = [] | |
| 1816 | + if file_path.name.lower() != "index.html": | |
| 1817 | + hrefs.append(os.path.relpath(root_index, source_directory).replace(os.sep, "/")) | |
| 1818 | + | |
| 1819 | + for target in sorted(declared_targets): | |
| 1820 | + target_path = (root / target).resolve(strict=False) | |
| 1821 | + href = os.path.relpath(target_path, source_directory).replace(os.sep, "/") | |
| 1822 | + if href == "." or href in hrefs: | |
| 1823 | + continue | |
| 1824 | + hrefs.append(href) | |
| 1825 | + return hrefs | |
| 1826 | + | |
| 1790 | 1827 | def _validate_path(self, file_path: str) -> ValidationResult: |
| 1791 | 1828 | if '\x00' in file_path: |
| 1792 | 1829 | return ValidationResult( |
src/loader/runtime/tool_batches.pymodified@@ -1258,12 +1258,23 @@ class ToolBatchRunner: | ||
| 1258 | 1258 | event_content, |
| 1259 | 1259 | "Already-declared local targets include:", |
| 1260 | 1260 | ) |
| 1261 | + allowed_hrefs = _extract_blocked_html_target_list( | |
| 1262 | + event_content, | |
| 1263 | + "Allowed hrefs from this file include:", | |
| 1264 | + ) | |
| 1261 | 1265 | |
| 1262 | 1266 | guidance = ( |
| 1263 | 1267 | "That HTML mutation introduced sibling targets outside the current declared local-link set. " |
| 1264 | 1268 | f"Stay on `{target}`." |
| 1265 | 1269 | ) |
| 1266 | - if closest_targets: | |
| 1270 | + if allowed_hrefs: | |
| 1271 | + guidance += ( | |
| 1272 | + " Remove the invented hrefs and use only these exact href values " | |
| 1273 | + "from this file, or omit navigation links entirely: " | |
| 1274 | + + ", ".join(f"`{candidate}`" for candidate in allowed_hrefs[:6]) | |
| 1275 | + + "." | |
| 1276 | + ) | |
| 1277 | + elif closest_targets: | |
| 1267 | 1278 | guidance += ( |
| 1268 | 1279 | " Remove the invented hrefs or replace them with the closest declared target(s): " |
| 1269 | 1280 | + ", ".join(f"`{candidate}`" for candidate in closest_targets[:3]) |
tests/test_safeguard_services.pymodified@@ -1078,6 +1078,9 @@ def test_pre_action_validator_blocks_chapter_write_with_undeclared_missing_sibli | ||
| 1078 | 1078 | == "HTML page introduces new local targets outside the current declared artifact set" |
| 1079 | 1079 | ) |
| 1080 | 1080 | assert "advanced.html" in result.suggestion |
| 1081 | + assert "Allowed hrefs from this file include:" in result.suggestion | |
| 1082 | + assert "../index.html" in result.suggestion | |
| 1083 | + assert "installation.html" in result.suggestion | |
| 1081 | 1084 | |
| 1082 | 1085 | |
| 1083 | 1086 | def test_pre_action_validator_blocks_chapter_write_with_existing_but_undeclared_sibling( |
@@ -1121,6 +1124,8 @@ def test_pre_action_validator_blocks_chapter_write_with_existing_but_undeclared_ | ||
| 1121 | 1124 | ) |
| 1122 | 1125 | assert "04-locations-and-servers.html" in result.suggestion |
| 1123 | 1126 | assert "04-advanced-configuration.html" in result.suggestion |
| 1127 | + assert "Allowed hrefs from this file include:" in result.suggestion | |
| 1128 | + assert "../index.html" in result.suggestion | |
| 1124 | 1129 | |
| 1125 | 1130 | |
| 1126 | 1131 | def test_pre_action_validator_does_not_suggest_unrelated_declared_html_target( |
tests/test_tool_batches.pymodified@@ -7812,13 +7812,15 @@ def test_tool_batch_runner_blocked_html_declared_target_nudge_without_close_matc | ||
| 7812 | 7812 | "introducing new sibling targets that the guide root does not declare; remove or replace " |
| 7813 | 7813 | "undeclared hrefs like: troubleshooting.html. " |
| 7814 | 7814 | "Already-declared local targets include: chapters/introduction.html, chapters/installation.html, " |
| 7815 | - "chapters/configuration.html." | |
| 7815 | + "chapters/configuration.html. Allowed hrefs from this file include: ../index.html, " | |
| 7816 | + "installation.html, configuration.html." | |
| 7816 | 7817 | ), |
| 7817 | 7818 | ) |
| 7818 | 7819 | |
| 7819 | 7820 | assert queued |
| 7820 | - assert "Remove the invented hrefs or keep local links within the declared target set" in queued[0] | |
| 7821 | - assert "`chapters/installation.html`" in queued[0] | |
| 7821 | + assert "use only these exact href values" in queued[0] | |
| 7822 | + assert "`installation.html`" in queued[0] | |
| 7823 | + assert "`../index.html`" in queued[0] | |
| 7822 | 7824 | assert "closest declared target(s)" not in queued[0] |
| 7823 | 7825 | |
| 7824 | 7826 | |